WeeShip

Features

AI connectors (API, CLI & MCP)

Make your SaaS queryable by AI agents: a customer creates an API token in your app, pastes it into Claude Code, Cursor or claude.ai, and their agent answers questions with your product's data. Battle-tested in production on MRRscope (July 2026), including a working claude.ai/Cowork custom connector.

One contract, four paths

  • Versioned REST API (/v1) — scripts and backends, Bearer tok_… tokens
  • npm CLI — terminal, CI, shell agents (zero runtime dependencies)
  • Local MCP server (stdio) — Claude Code, Claude Desktop, Cursor
  • Remote MCP server (HTTP) — claude.ai / Claude Cowork custom connectors

The CLI and both MCP servers are thin adapters over the public API: auth, subscription gating and scoping live in ONE place. Never duplicate a business rule in an adapter.

The full implementation ships in the Supabase variant (weefast-kit): migration 0006_api_tokens, edge functions public-api / account-tokens / mcp-server, a cli/ template and an ApiTokensCard component. See AI-CONNECTORS.md in the kit for the complete guide. The architecture applies as-is to the Next.js/Mongo stack — port the four blocks if you need them there.

Setup order (Supabase kit)

terminal

supabase db push                                      # 0006: api_tokens table + RPCs
supabase functions deploy account-tokens              # token management from the app (JWT)
supabase functions deploy public-api  --no-verify-jwt # token-authenticated public API
supabase functions deploy mcp-server  --no-verify-jwt # remote MCP adapter

Then drop <ApiTokensCard /> into your dashboard, add your product routes to public-api (the “PRODUCT ROUTES” section), and mirror your tool catalog in mcp-server/handler.ts AND cli/src/mcp.ts — keep both identical.

Non-negotiable rules (paid for in debugging)

  1. Store the hash only; show the token once. No “view my token again”.
  2. Never return a token that was not persisted (guaranteed silent 401 later).
  3. Gate on subscription AT USAGE TIME, fail-closed — a trial token dies with the trial without being deleted.
  4. Agents get read-only tools by default: no token management over MCP, no writes without an explicit decision. The agent holds the token.
  5. A public contract is a commitment: /v1, stable error codes { error, code }, amounts in cents with a _cents suffix (no unit ambiguity for an LLM).
  6. Tool descriptions are what the LLM reads — spell out units, bounds and defaults.

The two claude.ai connector pitfalls

1 — A 401 on claude.ai's initial probe switches it to OAuth discovery + dynamic client registration, which fails with “Unable to register with the sign-in service”. Fix (wired in mcp-server): initialize, tools/list and ping answer WITHOUT auth; the token is only required at tools/call, and a missing token returns an isError result with HTTP 200 — never a 401.

2 — OAuth discovery hits /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server on YOUR domain. A SPA fallback answering 200 with HTML (soft-404) makes claude.ai believe the server is OAuth-protected. Fix: an explicit 404 before the SPA fallback:

public/_redirects

/.well-known/*  /404.html  404
/*    /index.html   200

Bonus: claude.ai connectors cannot set headers, so the token travels in the URL (Zapier pattern) — prefer a path segment (/mcp/<tok_…>) over a query string, which some clients strip. The URL IS the secret: say so in your user docs, and remind users the token is read-only and revocable. After a failed connector attempt, delete the entry and re-create it — claude.ai remembers the auth mode per entry.

Test recipe

terminal

# API: clean 401 without a token, data with one
curl -s -X POST https://<ref>.supabase.co/functions/v1/public-api/me

# Remote MCP: handshake WITHOUT a token must be 200 (not 401!)
curl -s -X POST https://<ref>.supabase.co/functions/v1/mcp-server \
  -H "content-type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}'

# well-known MUST be 404 on your domain
curl -s -o /dev/null -w "%{http_code}\n" https://<domain>/.well-known/oauth-protected-resource

# Local MCP in Claude Code
claude mcp add <product> --env <PRODUCT>_TOKEN=tok_xxx -- npx -y <package> mcp

Automated tests ship with the kit: deno test supabase/tests/mcp_handler_test.ts (MCP core) and cd cli && npm test (arg parsing + stdio core).