Surflet

Claude Code Quickstart

Add Surflet to Claude Code in under 2 minutes — publish pages from your agent directly to your phone

Status: internal dev / testing. The install steps below use the "build from source" path. A published npm package is coming later.

Add Surflet to Claude Code

This guide wires Surflet into Claude Code two ways:

  1. MCP server — your Claude Code session exposes a surflet_publish tool. You (or the agent) call it explicitly when you want to turn output into a page.
  2. Post-task hook — every time Claude Code finishes a task, a briefing page is published automatically. Opt-in, useful for long-running or background sessions.

You only need one of them to get started, but both work well together.

What you'll see at the end

A Claude Code turn finishes, prints a http://localhost:3001/p/<id> URL, and you open that URL — on your phone, another laptop, or a browser tab — to read a rendered page showing what Claude Code just did, with structured sections instead of scrollback.

That's the whole pitch: agent output on a real surface, not in a chat bubble.

Prerequisites

  • Node.js 22+
  • pnpm (or adapt to your package manager)
  • Claude Code installed (claude on your $PATH)
  • A running Surflet API — see quickstart for the dockerized local dev setup. For this guide we assume http://localhost:3001 with the test key sk_test_surflet123.
  • jq and curl (only for the hook — the MCP server is pure Node)

Step 1 — Build the MCP server

From the Surflet repo root:

pnpm install
pnpm --filter @surflet/mcp-server build

That produces packages/@surflet/mcp-server/dist/index.js. Note the absolute path to this file — you'll need it in step 2.

# Example: print the absolute path
realpath packages/@surflet/mcp-server/dist/index.js

Step 2 — Register the MCP server with Claude Code

Either via the CLI:

claude mcp add surflet node /ABSOLUTE/PATH/TO/dist/index.js \
  --env SURFLET_API_URL=http://localhost:3001 \
  --env SURFLET_API_KEY=sk_test_surflet123

Or by editing ~/.claude/mcp.json directly:

{
  "mcpServers": {
    "surflet": {
      "command": "node",
      "args": ["/ABSOLUTE/PATH/TO/dist/index.js"],
      "env": {
        "SURFLET_API_URL": "http://localhost:3001",
        "SURFLET_API_KEY": "sk_test_surflet123"
      }
    }
  }
}

Restart Claude Code. You should see surflet listed in the active MCP servers.

Step 3 — Use the tool

In a Claude Code session, ask the agent to publish something:

Use the surflet_publish tool to create a briefing page titled "Hello from Claude Code" with the content "It works!" as markdown.

Claude Code will call surflet_publish, the MCP server forwards to the Surflet API, and the response includes a pageUrl. Open it — you should see a rendered Surflet page with the title and content.

That's the 2-minute happy path. Everything below is optional power-user config.


Optional — Wire the post-task hook (automatic publish on every turn)

The MCP tool is great when you want to ask the agent to publish something. The post-task hook is better when you want every long-running Claude Code turn to produce a page automatically, so you can walk away from the laptop and read the briefing on your phone later.

The hook is opt-in (disabled by default) — see packages/@surflet/mcp-server/hooks/README.md in the repo for the full setup and environment variables.

Quick setup

  1. Copy the hook script to a stable location:

    mkdir -p ~/.claude/hooks
    cp packages/@surflet/mcp-server/hooks/claude-code-post-task.sh \
       ~/.claude/hooks/surflet-post-task.sh
    chmod +x ~/.claude/hooks/surflet-post-task.sh
  2. Register it in ~/.claude/settings.json:

    {
      "hooks": {
        "Stop": [
          {
            "matcher": "*",
            "hooks": [
              {
                "type": "command",
                "command": "/ABSOLUTE/PATH/TO/.claude/hooks/surflet-post-task.sh",
                "timeout": 15
              }
            ]
          }
        ]
      }
    }
  3. Enable it by exporting the env vars before launching Claude Code:

    export SURFLET_HOOK_ENABLED=1
    export SURFLET_API_URL=http://localhost:3001
    export SURFLET_API_KEY=sk_test_surflet123
    claude code

Every Stop event now publishes a briefing page with the current git branch, last commit, working-tree status, and the raw stop-event payload. URLs are logged to $CLAUDE_PROJECT_DIR/.surflet-hook.log (or /tmp/surflet-hook.log if the project dir isn't writable).

Why opt-in

Short interactive Claude Code turns shouldn't publish — you'd end up with dozens of noisy pages. The SURFLET_HOOK_ENABLED=1 gate means the hook only fires when you explicitly want it, typically for overnight runs, background agents, or long multi-hour sessions.


Troubleshooting

surflet doesn't appear in Claude Code's MCP server list:

  • Confirm the absolute path in your config points to the compiled dist/index.js, not the source src/index.ts.
  • Restart Claude Code (MCP servers are loaded at startup).
  • Check claude mcp list to see what's registered.

Tool call returns 401 or 403:

  • SURFLET_API_KEY is missing or invalid. Verify it matches the API server's config.

Tool call returns ECONNREFUSED:

  • The Surflet API isn't running. Start it: docker compose -f infra/docker/docker-compose.yml up -d && pnpm --filter @surflet/api dev.

Hook doesn't publish anything:

  • Check that SURFLET_HOOK_ENABLED=1 is actually visible to the hook. Claude Code hooks run in an isolated environment — exporting the var in your shell before launching Claude Code is the simplest way.
  • Check the log file: cat $CLAUDE_PROJECT_DIR/.surflet-hook.log or cat /tmp/surflet-hook.log.

What to do next

  • Read concepts to understand Surflet's page / block model.
  • Try the MCP server guide for the full set of 13 tools (append blocks, list inbox, get analytics, execute actions, etc.).
  • See reference/blocks for the 25 block types you can compose into a page.