ZTS Docs

MCP

Model Context Protocol server for Cursor and other agent hosts.

MCP exposes selected tRPC procedures as tools for IDE agents (Cursor, Claude Desktop, etc.). It shares the same router as REST and tRPC but uses the MCP transport at /api/mcp.

For HTTP integrations and typed clients, use REST and the SDK instead.

Endpoints

SurfaceURL
MCP/api/mcp
OAuth discovery/.well-known/oauth-protected-resource
tRPC (first-party)/api/trpc
REST/api/rest
OpenAPI spec/api/openapi.json

Handler: apps/web/src/app/api/[transport]/route.ts (via trpc-to-mcp).

Cursor configuration

{
  "mcpServers": {
    "zts": {
      "url": "http://localhost:3000/api/mcp"
    }
  }
}

For staging or production, replace the host with your deployed URL (e.g. https://zts-monorepo.server.kitze.io/api/mcp).

Authentication

  1. Create an API key in the app (zts_ prefix via Better Auth’s API key plugin).
  2. MCP clients discover OAuth via /.well-known/oauth-protected-resource (routes under apps/web/src/app/.well-known/).
  3. Tool calls use the issued OAuth token or API key.

Better Auth’s MCP plugin (packages/auth) and OAuth helpers under apps/web/src/server/mcp/ back this flow.

After pulling schema changes for API key / MCP OAuth tables, run pnpm db:migrate.

Exposed tools (today)

Only procedures with apiExposure() in packages/trpc/src/openapi-meta.ts become MCP tools.

MCP toolREST pathtRPC procedure
get_current_userPOST /users/meuser.getCurrentUser
get_user_for_editing_profilePOST /users/me/edituser.getUserForEditingProfile
mark_user_onboardedPOST /users/me/onboardeduser.markUserAsOnboarded
reset_user_onboardingPOST /users/me/onboarding/resetuser.resetUserOnboarding
update_user_profilePATCH /users/meuser.updateProfile
get_user_preferencesPOST /users/preferencesuser.getPreferences
get_user_preferencePOST /users/preferences/getuser.getSinglePreference
update_user_preferencePATCH /users/preferencesuser.updatePreference
delete_upload_imageDELETE /images/{id}utImage.delete

Not exposed: admin.*, auth.changePassword, polar.*, and any procedure without apiExposure().

Adding a new MCP tool

  1. Implement the procedure in packages/trpc/src/routers/.
  2. Tag it with apiExposure() — the mcpName becomes the tool name:
import { z } from "zod";
import { apiExposure, emptyInput } from "../openapi-meta";
 
export const myProcedure = protectedProcedure
  .meta(
    apiExposure({
      description: "What this tool does",
      mcpName: "my_tool_name",
      path: "/my-resource",
      summary: "Short label",
      tags: ["MyTag"],
    })
  )
  .input(emptyInput)
  .output(z.any())
  .query(async ({ ctx }) => {
    // ...
  });
  1. Use Zod v4 for inputs (emptyInput for no-body POSTs).
  2. Regenerate OpenAPI and SDK:
pnpm --filter @zts/trpc run generate:openapi
pnpm sdk:build

The same change automatically updates REST, OpenAPI, SDK, and MCP — no separate MCP registration step.

MCP vs in-app AI agent

These are different surfaces:

MCP (/api/mcp)In-app chat agent
PurposeExternal IDE / automation toolsProduct chat UI at /chat
AuthAPI key / MCP OAuthSession cookie
ToolsapiExposure() tRPC proceduresBuilt-in tools + optional tRPC-as-tools
DocsThis pageAI overview

See Agents for the ToolLoopAgent used inside the web chat route.

  • REST API — same procedures, HTTP transport
  • SDK — generated REST client
  • Better Auth — API keys and MCP OAuth plugin
  • Product repo: docs/MCP_SETUP.md

On this page