# Expose an MCP server from your app

Your app can be an **MCP server**: AI assistants (Claude as a connector, Claude Code, LM Studio…)
connect to it and call tools you define, authenticated **per end-user via OAuth**. The platform
handles the whole transport (JSON-RPC 2.0 over Streamable HTTP) and the OAuth — you just register
tools.

## One call: `mountMcp`

```typescript
import { mountMcp, currentUser } from "./.onvibe/helpers.ts";

export default async function handler(req: Request): Promise<Response> {
  const mcp = await mountMcp(req, {
    auth: "oauth",                       // authenticate each MCP client per end-user
    tools: [
      {
        name: "list_notes",
        description: "List the signed-in user's notes",
        inputSchema: { type: "object", properties: {} },
        handler: async (_args, ctx) => await listNotes(ctx.user!.id),
      },
      {
        name: "create_note",
        description: "Create a note",
        inputSchema: {
          type: "object",
          properties: { text: { type: "string" } },
          required: ["text"],
        },
        handler: async (args, ctx) => await createNote(ctx.user!.id, String(args.text)),
      },
    ],
  });
  if (mcp) return mcp;   // it was an MCP request (/mcp) — handled

  // ...the rest of your app (normal web UI)
}
```

`mountMcp` returns a `Response` for MCP requests (path `/mcp`) and `null` otherwise, so it composes
with your existing routes. It handles `initialize`, `ping`, `tools/list`, `tools/call`,
notifications, batching, CORS and the session headers for you.

Each tool `handler(args, ctx)` receives the call arguments and `ctx.user` — the authenticated
end-user `{ id, email }` (or `null` if `auth: "none"`). Use `ctx.user.id` as the stable key for that
person's data. Return a string, a JSON-serializable value, or an MCP content object; `mountMcp`
wraps it.

## Turning on OAuth (required for `auth: "oauth"`)

MCP OAuth authenticates against your app's **managed users**, so the app must have user accounts:

```
set_access_policy({ project_id: "my-app", policy: "signup" })   // or "allowlist"
```

That's it. The platform then acts as the OAuth Authorization Server for your app:
- It serves discovery (`/.well-known/oauth-protected-resource`, `/.well-known/oauth-authorization-server`),
  Dynamic Client Registration, and the authorize/token endpoints on your app's domain.
- When a user adds your app's `/mcp` URL in their AI client, they're redirected to **your app's
  login** (the same managed-auth login), approve access, and a scoped token is issued — no API keys
  to copy by hand.
- That token arrives at your `/mcp` verified; `currentUser()` / `ctx.user` resolves to that end-user.

The client's connection URL is `https://<your-app-domain>/mcp` (works on the `.onvibe.run` URL and
on custom domains).

## Notes

- The `/mcp` path is reserved for this — keep your MCP endpoint there.
- `auth: "none"` exposes tools with no authentication (public data only). Prefer `oauth` for
  anything user-specific.
- Manage who can connect with the usual account tools (`create_app_user`, `add_allowed_email`,
  `remove_app_user`) — MCP access follows the same accounts as the rest of your app.
