defineMcpServer from bijection/mcp. It takes
an async function that returns the tools you publish and references to a few
ordinary functions that you write in the same app: a grant query, two admission
mutations and two dispatch functions. The function runs for each request, so it
can await operation references.
This page builds a complete endpoint in bijection/mcp.ts. The grant query is
covered in Authentication and grants.
Selecting tools
A tool never contains business logic of its own. Each one selects a function you already have and derives its MCP input and output schemas from that function’s validators.Query tools
mcpQuery publishes a public query. Pass the
function reference, the registered query itself, and a description for the
agent:
bijection/tasks.ts
bijection/mcp.ts
returns validator;
mcpQuery throws if it does not. The query runs as the calling agent, so
ctx.auth and your access rules see the agent’s identity,
just as they would for a query called from a client.
Operation tools
mcpOperation publishes a business operation defined
with defineOperation. Pass an operation reference and a description:
bijection/mcp.ts
Acceptance is a local receipt. If your operation makes external calls, an
accepted result does not mean an external system has applied the change.
Publish a status tool so the agent can check.Operation status tools
mcpOperationStatus publishes a read-only tool that reports the execution state
of an accepted invocation:
bijection/mcp.ts
{ invocation_id } and returns invocation_id, the review state,
each external call’s delivery and publication state, and
is_external_outcome_unknown. It never returns the operation’s business result,
reviewer identities or internal diagnostics. When is_external_outcome_unknown
is true, the outcome must be reconciled; the tool description tells the agent
not to report completion or resubmit.
Mutations and actions
You can’t publish a mutation or an action as a tool directly. To let an agent change data, define an operation and publish it withmcpOperation; operations give each
write the stable identity, deduplication and recovery that an agent retrying
over the network needs.
Tool names, descriptions and values
Tool names are the keys of thetools object. A name starts with a letter and
contains only letters, digits and underscores, up to 64 characters. A
description is at most 4,096 characters, and an endpoint publishes between 1
and 32 tools.
Every tool’s arguments must be an object validator. Tool inputs and outputs use
JSON, with these encodings for values that JSON can’t represent exactly:
Unknown fields and non-finite numbers are rejected. Publishing fails when a
tool uses
v.any(), or a union whose variants look the same on the wire, such
as v.union(v.string(), v.int64()). Use an object union with a required
v.literal discriminator instead.
Dispatch functions
Tool calls run through two internal functions that you export. Their handlers are the endpoint’sdispatch.read and dispatch.write, which check the
publication revision and the caller’s current grant inside the same transaction
as the tool’s work:
bijection/mcp.ts
read.
Operation invocations run through write.
Admission
Every request to the endpoint, including discovery, first acquires a lease from youradmission.acquire mutation and releases it through admission.release
when it finishes. You own these limits and their tables, so rate and concurrency
counters commit transactionally with the rest of your app.
acquire receives grant_id, tool (the tool name, or the MCP method for
requests that aren’t tool calls), a fresh request_id, and is_poll, which is
true for status tools and recovery requests. It returns either { lease_id }
or { retry_after_ms }. Returning retry_after_ms refuses the request with
429 capacity_exhausted and a Retry-After header.
This example keeps a per-minute counter and a list of active leases for each
grant:
bijection/schema.ts
bijection/mcp.ts
release
fails, the lease simply expires, and an operation that was accepted stays
accepted.
Serving the endpoint
defineMcpServer returns a handler HTTP action for MCP requests, a metadata
HTTP action for OAuth protected-resource metadata, and a publication()
function that returns the current revision. Mount both actions in
bijection/http.ts:
bijection/http.ts
bijection/mcp.ts
https://<your deployment name>.bijection.site/mcp/<revision>.
The revision covers your whole deployed program, not just mcp.ts: when
bijection dev or bijection deploy pushes code, the CLI computes a digest of
every bundled module, schema, auth configuration and component, and the
endpoint combines it with its name, resource, authorization servers, tool
descriptions, bindings and schemas. Any change produces a new revision and a new
URL. Environment variables and data are not part of the revision; current
grants and access rules are checked on every call instead.
Tool results
tools/list returns each granted tool with its inputSchema and an
outputSchema of the form { result: ... }. Query tools are annotated
readOnlyHint and idempotentHint; operation tools are annotated
destructiveHint and carry a bijection/operation entry in _meta that
clients use for recovery.
A successful tools/call returns the value as structuredContent.result and as
the same JSON in a text content block. A failed call returns isError: true with
an error code such as tool_refused or read_unavailable. Error messages thrown
by your own functions are not passed to the agent. See Limits
for every code.