@invokta/mcp
@invokta/mcp maps every engine capability to one MCP tool and keeps the
official MCP SDK behind the adapter boundary. Every tool call executes through
engine.invoke.
The compatibility baseline is MCP 2025-11-25; the approved TypeScript SDK is
pinned to 1.30.0 in the package manifest.
Install
Section titled “Install”yarn add @invokta/mcpThe package is native ESM and requires Node.js 22.20.0 or later. SDK and transport types are not part of its public API.
The package root exports:
serveMcpStdioandServeMcpStdioOptions;serveMcpHttpandServeMcpHttpOptions;McpHttpAuthOptions;McpHttpAuthenticationRequestandMcpHttpHeaderView;McpHttpProtectedResourceMetadata; andMcpHttpServerAddressandMcpHttpServerHandle.
Tool mapping
Section titled “Tool mapping”| Capability | MCP tool |
|---|---|
| Engine map key | name |
title |
title |
description |
description |
| Input JSON Schema | inputSchema |
| Output JSON Schema | outputSchema |
readOnly |
readOnlyHint |
destructive |
destructiveHint |
idempotent |
idempotentHint |
openWorld |
openWorldHint |
Engine name and version become the MCP server identity. Omitted tool arguments
default to {} before input validation.
A successful call returns the validated result as structuredContent plus a
JSON text fallback. Invocation failures return isError: true with one safe JSON
text error and no structuredContent. Unsafe or unknown errors fall back to:
{"code":"EXECUTION_FAILED","message":"Capability execution failed."}An unknown tool is a protocol InvalidParams error rather than an invocation
CAPABILITY_NOT_FOUND. Valid request IDs such as numeric 0 and the empty
string retain their wire identity, and cancellation reaches only its matching
request.
serveMcpStdio
Section titled “serveMcpStdio”interface ServeMcpStdioOptions { readonly principal?: Principal | null; readonly maxReadBufferBytes?: number;}
function serveMcpStdio<Capabilities extends CapabilityMap>( engine: Engine<Capabilities>, options?: ServeMcpStdioOptions,): Promise<void>;await serveMcpStdio(engine, { principal: { id: "local:mcp-host" }, maxReadBufferBytes: 10 * 1024 * 1024,});The principal defaults to null. The read buffer defaults to 10,485,760 bytes
and must be a positive safe integer. A message that keeps the buffer at the
limit is accepted; crossing it closes the transport, aborts active requests,
removes stream listeners, and rejects the promise.
serveMcpStdio remains pending for the protocol connection lifetime. Clean
stdin end or close shuts down the server and aborts active requests. A broken
stdout pipe is treated as a normal client disconnect; another output failure is
reported. Standard output is reserved for protocol messages, so diagnostics
belong on standard error.
serveMcpHttp
Section titled “serveMcpHttp”interface ServeMcpHttpOptions { readonly host?: string; readonly port?: number; readonly allowedHosts?: ReadonlyArray<string>; readonly allowedOrigins?: ReadonlyArray<string>; readonly maxRequestBodyBytes?: number; readonly auth: McpHttpAuthOptions;}
interface McpHttpServerHandle { address(): { readonly host: string; readonly port: number }; close(): Promise<void>;}const server = await serveMcpHttp(engine, { host: "127.0.0.1", port: 3000, allowedHosts: ["engine.example"], allowedOrigins: ["https://console.example"], maxRequestBodyBytes: 1024 * 1024, auth: { mode: "required", authenticate, },});Defaults are 127.0.0.1, port 3000, and a 1,048,576-byte body limit. Use port
0 in tests to let the operating system select an available port, then read it
with server.address(). The address is the listener address, not a public URL.
server.close() is idempotent, shares concurrent close calls, aborts active
request signals, and closes their connections.
Stateless profile
Section titled “Stateless profile”Each accepted POST creates a fresh MCP server and transport. The adapter has no session ID, resumption, event store, server-to-client SSE, cross-request cancellation, resources, prompts, sampling, elicitation, or tasks.
The sole protocol endpoint is the exact canonical POST /mcp. When Protected
Resource Metadata is configured, its generated well-known GET route is the only
non-protocol route. Dot segments, percent-encoded aliases, queries, fragments,
absolute-form targets, and batches never reach protocol dispatch.
HTTP request boundary
Section titled “HTTP request boundary”Checks occur before capability execution:
- exactly one raw
Hostheader and an allowed host; - an optional but valid and allowed
Origin; - canonical route, supported method, and declared body size;
- exactly one unambiguous Authorization value for protected requests;
- authentication and principal snapshotting;
- exact JSON content type, required accepted response media, streamed body bound, strict UTF-8, and one JSON-RPC message; and
- MCP dispatch to
engine.invoke.
A non-loopback bind requires a non-empty allowedHosts list. Forwarded-host
headers are ignored. An absent Origin is allowed for non-browser clients; any
present Origin must match a configured normalized HTTP(S) origin exactly,
including scheme and any non-default port.
Accept must include both application/json and text/event-stream with
positive quality. One raw Content-Type header must identify
application/json. The body-size boundary is inclusive, and decoding is strict
incremental UTF-8.
| Status | Boundary meaning |
|---|---|
200 |
MCP response, including capability FORBIDDEN as a tool error |
202 |
Accepted notification with no protocol result body |
400 |
Invalid target/header ambiguity, JSON parsing, or top-level batch |
401 |
Missing/invalid credentials or malformed returned Principal |
403 |
Rejected Host or Origin |
404 |
Unknown route |
405 |
Unsupported method |
406 |
Required response media types not accepted |
413 |
Declared or streamed body crossed the configured limit |
415 |
Missing, duplicated, or unsupported content type |
500 |
Authentication infrastructure or internal boundary failure |
HTTP authentication
Section titled “HTTP authentication”type McpHttpAuthOptions = | { readonly mode: "required"; readonly authenticate: ( request: McpHttpAuthenticationRequest, ) => Principal | null | Promise<Principal | null>; readonly resourceMetadata?: McpHttpProtectedResourceMetadata; } | { readonly mode: "dangerously-disabled-for-development"; };The authentication request contains normalized path, method, a read-only
header view, and a cancellation signal. Returning a principal authenticates the
request; returning null produces 401; throwing produces a sanitized 500. The
adapter validates and deep-snapshots the principal before engine.invoke.
Domain authorization stays in the capability access rule. Read the HTTP authentication guide for verifier mapping, Protected Resource Metadata, proxy boundaries, and secret handling.
Development mode
Section titled “Development mode”auth: { mode: "dangerously-disabled-for-development",}This mode supplies principal = null. Keep it on an isolated loopback process;
authenticated capabilities remain closed. It is not a production authentication
strategy.
Host responsibilities and limits
Section titled “Host responsibilities and limits”The adapter provides no TLS termination, proxy-trust policy, rate limit, concurrency limit, authentication retry, or verifier timeout. The host and deployment own those controls. Verifiers and capability dependencies must observe their request signals and apply their own finite outbound deadlines.
See execution channels for composition roots and scope and limits for the complete framework boundary.