Development

This page covers running the MCP Worker locally, testing it, its configuration, deploying it, and registering it in an MCP client. For what the tools do see the Tools reference; for how it is wired see the Architecture.

Prerequisites

  • Node.js (20+ recommended) and npm.
  • wrangler (installed as a dev dependency; run via npm run).
  • For deploys: a Cloudflare account with access to the configured account_id.

Local development

npm install
cp .dev.vars.example .dev.vars     # fill in SWAPPS_API_TOKEN and EDGE_TOKENS
npm run dev                        # wrangler dev on http://127.0.0.1:8787

.dev.vars is gitignored and holds the local secrets:

# Single read-only service DRF token used for all downstream API calls.
SWAPPS_API_TOKEN=
# JSON map of per-user edge token -> username. Generate tokens with `openssl rand -hex 32`.
EDGE_TOKENS={"localtoken":"dev@swapps.com"}

The dev script runs wrangler dev --ip 0.0.0.0, so the server is reachable on the LAN as well as 127.0.0.1:8787.

Smoke test

The MCP Streamable HTTP transport requires the Accept header to include text/event-stream:

# tools/list
curl -sS -X POST http://127.0.0.1:8787/mcp \
  -H "Authorization: token localtoken" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

A health check needs no token:

curl -sS http://127.0.0.1:8787/health
# {"status":"OK","ts":"..."}

Quality checks

Command What it does
npm run typecheck tsc --noEmit — type-checks the project.
npm run lint eslint . — lints with typescript-eslint.
npm test vitest — runs the unit tests.

Tests (Vitest)

Tests live in tests/ and run in the node environment (vitest.config.ts), with the @/* alias resolving to src/*:

  • tests/auth.test.tsextractToken (Authorization Token/Bearer, X-MCP-Token fallback, empty) and validateEdgeToken (valid/unknown/empty token, malformed/empty EDGE_TOKENS).
  • tests/api-client.test.tsapiGet URL building, auth + User-Agent headers, pretty-printed JSON, leading-slash stripping, non-2xx error formatting, empty body → "null", and thrown-error formatting.

Run a single file:

npx vitest run tests/auth.test.ts

Configuration

Secrets are set with wrangler secret put (never committed); vars live in wrangler.toml. Names only — never store secret values in the repo.

Name Kind Where Purpose
SWAPPS_API_TOKEN Secret wrangler secret put / .dev.vars Read-only service DRF token for all downstream calls.
EDGE_TOKENS Secret wrangler secret put / .dev.vars JSON map of per-user edge token → username.
SWAPPS_API_BASE_URL Var wrangler.toml [vars] Downstream API base URL (default https://app.swapps.com/api).
SWAPPS_API_USER_AGENT Var wrangler.toml [vars] Downstream User-Agent (default swapps-mcp/1.0).

Deployment

# Secrets (never committed):
wrangler secret put SWAPPS_API_TOKEN   # service DRF token (read-only user)
wrangler secret put EDGE_TOKENS        # JSON: {"<token>":"<username>", ...}

npm run deploy                         # wrangler deploy
  • The first deploy lands at https://swapps-mcp-worker.<account>.workers.dev/mcp.
  • The (currently commented) [[routes]] block in wrangler.toml provisions the custom domain https://mcp.swapps.com/mcp (DNS + cert automatic; the swapps.com zone is already in the account). Re-enable it once the wrangler token has zone permissions for swapps.com.

Deploy via the platform skill

Within the platform workspace, the deploy-worker skill knows the correct per-project command (preview vs production, secrets, rollback) for swapps-mcp-worker.

Managing edge tokens

EDGE_TOKENS is a secret holding a JSON token -> username map:

{ "a1b2c3...": "dev@swapps.com", "d4e5f6...": "lcuevas@swapps.com" }
  • Generate a token: openssl rand -hex 32.
  • Revoke a user: remove their entry and re-run wrangler secret put EDGE_TOKENS.

Registering it in an MCP client

Claude Code

claude mcp add --transport http swapps-app https://mcp.swapps.com/mcp \
  --header "Authorization: token <YOUR_EDGE_TOKEN>"

If your Claude version does not forward the Authorization header correctly, use the fallback header:

claude mcp add --transport http swapps-app https://mcp.swapps.com/mcp \
  --header "X-MCP-Token: <YOUR_EDGE_TOKEN>"

Claude Desktop

claude_desktop_config.json:

{
  "mcpServers": {
    "swapps-app": {
      "type": "http",
      "url": "https://mcp.swapps.com/mcp",
      "headers": { "Authorization": "token <YOUR_EDGE_TOKEN>" }
    }
  }
}

For local development point the client at http://127.0.0.1:8787/mcp and use a token present in your .dev.vars EDGE_TOKENS map (e.g. localtoken).