MCP
Use mcp with the current published Askr packages.
@askrjs/server/mcp0.0.3@askrjs/node/mcp0.0.3
How to use mcp
Create one MCP server, register typed primitives on it, and let the selected transport supply auth, cancellation, sessions, and progress.
- Import the published @askrjs/server/mcp and @askrjs/node/mcp entrypoint.
- Keep mcp configuration next to the component, route, or server composition root that owns it.
- Handle pending, unavailable, cancellation, and failure states, then verify the production path.
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.