# MCP

> MCP at the Askr server boundary, with validated input and explicit failure states.

Source: [https://askrjs.com/docs/mcp](https://askrjs.com/docs/mcp)

Status: stable. Packages: @askrjs/server/mcp, @askrjs/node/mcp.

**Published packages are authoritative.** Examples may lag behind a published contract. When guidance differs, verify the exports and TypeScript declarations in your installed package, then file an issue.

## Example

Create one MCP server, register typed primitives on it, and let the selected transport supply auth, cancellation, sessions, and progress.

```tsx
import { schema } from '@askrjs/schema';
import { createMcpServer } from '@askrjs/server/mcp';

export const mcp = createMcpServer({
  name: 'project-tools',
  version: '1.0.0',
}).tool(
  'lookup-project',
  { input: schema.object({ id: schema.uuid() }) },
  async (context, { id }) => ({
    content: [{ type: 'text', text: await lookup(id, context.signal) }],
  })
);
```

## Server model

`createMcpServer(options)` from `@askrjs/server/mcp` builds a single in-process registry of tools, resources, resource templates, and prompts, addressed by name or URI. Registration methods (`tool`, `resource`, `resourceTemplate`, `prompt`) return the same server instance, so you chain them at startup instead of scattering registration across files. The server itself is transport-agnostic: it only knows how to validate a JSON-RPC message and call `handle(message, environment)`, leaving HTTP, SSE, and stdio delivery to separate adapters. `options.pageSize` (default 50, minimum 1) controls how many items `tools/list`, `resources/list`, and `prompts/list` return per page.

## Tools and resources

`server.tool(name, options, handler)` registers a callable with an optional `input` and `output` schema; if you omit `input`, an empty object schema is used, and arguments are validated with `safeParse` before your handler ever runs. `server.resource(uri, options, handler)` binds a fixed URI to a handler that returns one `McpContent` or an array of them, while `server.resourceTemplate(template, options, handler)` matches variable segments like `{id}` against incoming URIs and hands the extracted variables to the handler. A tool handler's thrown errors are caught and turned into a result with `isError: true` rather than a JSON-RPC transport error, so callers always get a structured response for `tools/call`.

## Transports

Two transports ship today: `registerMcpRoutes` from `@askrjs/server/mcp` mounts HTTP (plus optional SSE) routes on an existing `Router`, and `connectMcpStdio` from `@askrjs/node/mcp` wires the same `McpServer` to `process.stdin`/`process.stdout` for CLI-style clients. Both adapters do the same job: turn a raw message plus a transport-specific `McpRequestEnvironment` (dependencies, auth, signal, optional `send`) into a call to `mcp.handle(...)`. Because the server has no transport code of its own, adding a new one is a matter of constructing that environment object correctly, not reimplementing dispatch.

## Operational checklist

Before shipping, confirm `pageSize` matches what your clients expect for large tool/resource lists, and that every primitive requiring auth sets `options.auth` to an `AuthRequirement` rather than relying on transport-level checks. For HTTP, decide whether you need `stateful: true` for session-scoped push notifications, and set `allowedOrigins`/`allowedHosts` if the endpoint is reachable from a browser. For stdio, make sure `diagnostics` is wired to a stream your process supervisor captures, since stdio errors are written there rather than thrown.

## Documentation navigation

[Previous](https://askrjs.com/docs/http-contracts/open-api/index.md) | [Next](https://askrjs.com/docs/mcp/primitives/index.md)
