MCP Server Overview
The MCP server is mounted inside the Nest API as a normal module (src/mcp/). It exposes one
endpoint and speaks the Model Context Protocol over streamable HTTP.
Endpoint
| Method | Path | Behaviour |
|---|---|---|
POST | /mcp | JSON-RPC MCP requests (initialize, tools/list, tools/call). |
GET | /mcp | 405 — the endpoint is stateless, there is no SSE session. |
DELETE | /mcp | 405 — no sessions to tear down. |
Default base URL in local dev: http://localhost:4000/mcp (the API listens on PORT, default
4000).
Authentication
The endpoint is protected by the same guard as the rest of the API (ApiKeyOrClerkGuard). It
accepts either:
- an Unkey API key (
Authorization: Bearer vld_…) — the intended path for machine clients, or - a Clerk session JWT (dashboard users).
The workspace is derived from the credential — there is no :workspaceId in the path. An API
key is bound to exactly one workspace, so every tool call it makes is automatically scoped to that
workspace. This is what keeps tenants isolated even though all clients hit the same URL.
Create an API key from the dashboard (or the API-keys endpoints) before connecting; the plaintext key is shown only once.
Statelessness
Each request builds a fresh MCP server and a fresh transport
(StreamableHTTPServerTransport with no session id), handles the request, and tears both down when
the response closes. There are no long-lived sessions to store or expire, which keeps the endpoint
horizontally scalable and simple to reason about.
How tools do their work
Tools don't reimplement anything — they delegate to the same services the REST API uses:
- Lists — verify the target list belongs to the caller's workspace.
- Columns — ensure/create the column that results are written into.
- AI execution — run the AI-column pipeline over the list's records.
For research_company, that pipeline fans out one Hatchet ai-column-execution run per record, and
each result is persisted into the record's fields under the column's key. Because execution is
asynchronous, the Hatchet worker must be running (bun run worker:dev) for cells to fill in.
Prerequisites checklist
- API running —
bun run start:dev - Worker running —
bun run worker:dev - API keys configured —
UNKEY_ROOT_KEYandUNKEY_API_IDset - AI gateway enabled —
AI_GATEWAY_ENABLEDnot set tofalse - A workspace API key created (
vld_…)