Schemas, Auth, Context, and Progress
Use schemas, auth, context, and progress with the current published Askr packages.
@askrjs/askr0.0.59
How to use schemas, auth, context, and progress
Create one MCP server, register typed primitives on it, and let the selected transport supply auth, cancellation, sessions, and progress.
- Import the published @askrjs/askr entrypoint.
- Keep schemas, auth, context, and progress 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) }],
})
);Input schemas
Tool `input`, tool `output`, and prompt `arguments` are all `@askrjs/schema` schemas, and each is exposed to clients through generated JSON Schema (`inputSchema`, `outputSchema`) in the corresponding `list` response. Validation runs via `safeParse` before your handler executes; a failed parse short-circuits to a `-32602 Invalid tool arguments` (or `Invalid prompt arguments`) error carrying the schema's `issues`, so handlers never see malformed input. Skip `input` or `arguments` entirely and the server substitutes an empty object schema, which is useful for tools and prompts that take no parameters but still want to appear correctly in a client's UI.
Authorization
Every primitive accepts an optional `auth: AuthRequirement` in its options — the same requirement functions from `@askrjs/auth` used on HTTP routes, like `requireScope('mcp:write')` or `requirePermission('reports:read')`. Before a tool, resource, template, or prompt is listed or invoked, the server calls that requirement against the current `AuthContext` and checks `.allowed`; entries that fail are filtered out of `list` responses entirely and return `Tool not found` / `Resource not found` / `Prompt not found` if called directly, rather than a distinct "forbidden" error. That means unauthorized primitives are invisible, not merely blocked — a client with insufficient scope never learns the tool exists.
Handler context
Every handler receives an `McpContext<Dependencies>` with `dependencies` (whatever you passed into the transport's `dependencies` option), `auth`, the negotiated `client` info and `clientCapabilities` from `initialize`, `protocolRevision`, `transport` (`"http"` or `"stdio"`), an optional `sessionId`, and an `AbortSignal` that fires on cancellation or disconnect. It's the same context shape across transports, so a tool handler doesn't need to branch on whether it's being called over HTTP or stdio. `context.progress(...)` and `context.log(...)` are also part of this object — see progress notifications below.
Progress notifications
Call `context.progress(progress, total?, message?)` from inside a handler to emit a `notifications/progress` message, but only if the original request carried a `_meta.progressToken` — without one, the call is a no-op. `context.log(level, data, logger?)` sends `notifications/message` with one of the standard MCP log levels (`debug` through `emergency`). Both rely on `environment.send`, which is only wired up for transports that support server-initiated messages: the HTTP adapter provides it once a client has an open SSE channel for the session, and stdio provides it for every call since it can always write another line to stdout.