Streamable HTTP MCP server for Vitally.io, deployed on
Vercel and gated by Clerk OAuth (Google sign-in restricted to allowed
domains). Built on Next.js 16 with mcp-handler.
| Path | Purpose |
|---|---|
/mcp |
MCP Streamable HTTP transport |
/.well-known/oauth-protected-resource/mcp |
OAuth Protected Resource Metadata (RFC 9728) |
/ |
One-screen landing page for humans |
app/mcp/route.ts—mcp-handlerroute wrapped in Clerk'swithMcpAuth. Hosts every Vitally tool. Streamable HTTP only — SSE is disabled.app/.well-known/oauth-protected-resource/mcp/route.ts— publishes the Clerk-backed protected resource metadata.proxy.ts— Clerk middleware (Next.js 16 renamedmiddleware.ts→proxy.ts).lib/vitally/— TypeScript port of the Vitally REST client. Reads env vars at first instantiation and throws if any are missing.
Set these env vars locally in .env.local and in Vercel project settings:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_...
CLERK_SECRET_KEY=sk_...
VITALLY_API_SUBDOMAIN=...
VITALLY_API_KEY=...
VITALLY_DATA_CENTER=US
The Vitally vars are required — the server fails loudly at cold start if any are missing. There is no demo or mock mode.
- Create a new Clerk application.
- Enable Sign in with Google as the only auth method.
- Restrict sign-ups via allowlist or domain restriction (e.g.
@mailmojo.no). - In the application's API settings, enable Dynamic Client Registration under the OAuth/MCP section so Claude clients can register on first connect.
- Copy the publishable and secret keys into your env vars.
npm install
npm run dev
# In another terminal:
npx @modelcontextprotocol/inspectorConnect Inspector to http://localhost:3000/mcp. You'll see a 401 → Clerk
OAuth flow → tool list.
- Push this branch and connect the repo as a new Vercel project.
- Enable Fluid compute (Functions → Fluid) — required for the longer
maxDurationwe use on the MCP route. - Set framework preset to Next.js (auto-detected).
- Add the env vars above to Production + Preview.
- Deploy. Endpoints will be:
https://<project>.vercel.app/mcphttps://<project>.vercel.app/.well-known/oauth-protected-resource/mcp
Optionally bind a custom domain (e.g. vitally-mcp.mailmojo.no).
In Claude Desktop or Claude Web: Settings → Custom Connectors → Add a
connector and paste the /mcp URL. The first connect pops a Clerk login.
- Discovery:
search_tools - Accounts:
search_accounts,get_account_details,get_account_users,refresh_accounts,get_account_health - Traits:
list_custom_traits,update_account_traits - Users:
search_users,get_user_details - Conversations / Tasks / Notes:
get_account_conversations,get_account_tasks,get_account_notes,get_note_by_id,create_account_note - NPS:
get_account_nps - Projects:
get_account_projects - Custom objects:
list_custom_objects,get_custom_object,list_custom_object_instances,search_custom_object_instances
See app/mcp/route.ts for input schemas.
Vitally Elements (custom properties defined in Vitally) appear as keys
prefixed with vitally.custom. inside an account's or user's traits
object. List endpoints accept includeTraits / traitKeys to opt into
returning these without bloating the default payload.
Originally created by John Jung, containerised by Dan Searle, ported to streamable HTTP + Clerk OAuth by the mailmojo.no fork.