AgentMcp.Handler
interface · packages/agent-mcp/src/handler.ts:33
interface Handler {
readonly handle: (message: unknown) => Promise<Response | undefined>
readonly close: () => void
}Agent / foldkit-agent-mcp
namespace · packages/agent-mcp/src/agentMcp.ts:1
export * as AgentMcp from './agentMcp.js'AgentMcp.Handlerinterface · packages/agent-mcp/src/handler.ts:33
interface Handler {
readonly handle: (message: unknown) => Promise<Response | undefined>
readonly close: () => void
}AgentMcp.handlerfunction · packages/agent-mcp/src/handler.ts:64
function handler<Model, Context_, Principal, ByName, ByTag>(
options: HandlerOptions<Model, Context_, Principal, ByName, ByTag>,
): HandlerMaps a bound agent contract onto MCP, with no transport attached.
Every protocol decision lives here, so it can be tested by handing it messages.
AgentMcp.HandlerOptionsinterface · packages/agent-mcp/src/handler.ts:19
interface HandlerOptions<Model, Context_, Principal, ByName, ByTag> {
readonly agent: Agent.AgentRuntime<Model, Context_, Principal, ByName, ByTag>
readonly serverInfo?:
| {
readonly name: string
readonly version: string
}
| undefined
readonly onNotification?: ((notification: Notification) => void) | undefined
readonly debounceMs?: number | undefined
}AgentMcp.httpAppfunction · packages/agent-mcp/src/httpApp.ts:78
function httpApp<Model, Context_, Principal, ByName, ByTag>(
options: HttpAppOptions<Model, Context_, Principal, ByName, ByTag>,
): Effect.Effect<
HttpServerResponse.HttpServerResponse,
never,
HttpServerRequest.HttpServerRequest
>Serves a contract as an Effect HTTP application.
This is the entry point to prefer: it runs behind any Effect HTTP server, or
as a plain web handler via HttpEffect.toWebHandler, on Node, Bun, Deno or a
worker. The session and security rules live in the transport-neutral core;
this is the I/O.
const handler = HttpEffect.toWebHandler(AgentMcp.httpApp({ createAgent, authenticate }))AgentMcp.HttpAppOptionstype · packages/agent-mcp/src/httpApp.ts:59
type HttpAppOptions<Model, Context_, Principal, ByName, ByTag> =
| (HttpHandlerOptions<Model, Context_, Principal, ByName, ByTag> & {
readonly server?: undefined
})
| {
readonly server: HttpHandler
}Either the options to create a handler, or an existing handler to reuse so sessions can be shared. Not both: a reused handler would ignore the rest.
AgentMcp.HttpHandlerinterface · packages/agent-mcp/src/http.ts:124
interface HttpHandler {
readonly handle: (request: HttpRequest) => Promise<HttpResponse>
readonly sessions: () => ReadonlyArray<string>
readonly close: () => void
}AgentMcp.httpHandlerfunction · packages/agent-mcp/src/http.ts:227
function httpHandler<Model, Context_, Principal, ByName, ByTag>(
options: HttpHandlerOptions<Model, Context_, Principal, ByName, ByTag>,
): HttpHandlerServes a contract over the Streamable HTTP transport.
One endpoint answers POST, GET and DELETE. A session is created on
initialize and identified by Mcp-Session-Id afterwards; an unknown
session is a 404 so the client re-initializes.
AgentMcp.HttpHandlerOptionsinterface · packages/agent-mcp/src/http.ts:70
interface HttpHandlerOptions<Model, Context_, Principal, ByName, ByTag> {
readonly createAgent: (context: {
readonly principal: Principal
readonly sessionId: string
}) => Agent.AgentRuntime<Model, Context_, Principal, ByName, ByTag>
readonly authenticate?:
| ((request: HttpRequest) => Effect.Effect<Principal, Unauthenticated>)
| undefined
readonly principalId?: ((principal: Principal) => string) | undefined
readonly allowedOrigins?: ReadonlyArray<string> | undefined
readonly sessionTtlMs?: number | undefined
readonly clock?: (() => number) | undefined
readonly replayBuffer?: number | undefined
readonly serverInfo?:
| {
readonly name: string
readonly version: string
}
| undefined
}AgentMcp.HttpRequestinterface · packages/agent-mcp/src/http.ts:15
interface HttpRequest {
readonly method: string
readonly headers: Readonly<Record<string, string | undefined>>
readonly body?: unknown
}A transport-neutral request, so this can sit behind any HTTP server.
AgentMcp.HttpResponseinterface · packages/agent-mcp/src/http.ts:48
interface HttpResponse {
readonly status: number
readonly headers: Readonly<Record<string, string>>
readonly body?: unknown
readonly stream?: SseStream
}AgentMcp.LineInputinterface · packages/agent-mcp/src/stdio.ts:5
interface LineInput {
on(event: 'data', listener: (chunk: Buffer | string) => void): unknown
on(event: 'end', listener: () => void): unknown
off(event: 'data', listener: (chunk: Buffer | string) => void): unknown
off(event: 'end', listener: () => void): unknown
}The part of a readable stream stdio uses. process.stdin satisfies it.
AgentMcp.LineOutputinterface · packages/agent-mcp/src/stdio.ts:13
interface LineOutput {
write(line: string): unknown
}The part of a writable stream stdio uses. process.stdout satisfies it.
AgentMcp.PROTOCOL_VERSIONconst · packages/agent-mcp/src/handler.ts:17
const PROTOCOL_VERSION: '2025-06-18'The protocol version this handler speaks.
AgentMcp.SseEventinterface · packages/agent-mcp/src/http.ts:24
interface SseEvent {
readonly id: string
readonly data: string
}One server-sent event. id is the cursor a client resumes from.
AgentMcp.SseStreaminterface · packages/agent-mcp/src/http.ts:30
interface SseStream {
readonly backlog: ReadonlyArray<SseEvent>
readonly subscribe: (
send: (event: SseEvent) => void,
end?: () => void,
) => () => void
}An open SSE stream.
AgentMcp.stdiofunction · packages/agent-mcp/src/stdio.ts:28
function stdio<Model, Context_, Principal, ByName, ByTag>(
options: Omit<
HandlerOptions<Model, Context_, Principal, ByName, ByTag>,
'onNotification'
> &
StdioOptions,
): HandlerServes a contract as newline-delimited JSON-RPC over stdin and stdout.
stdout carries MCP messages and nothing else -- a stray console.log
corrupts the stream, which is why diagnostics belong on stderr.
AgentMcp.StdioOptionsinterface · packages/agent-mcp/src/stdio.ts:17
interface StdioOptions {
readonly input?: LineInput | undefined
readonly output?: LineOutput | undefined
}AgentMcp.Unauthenticatedinterface · packages/agent-mcp/src/http.ts:61
interface Unauthenticated {
readonly _tag: 'Unauthenticated'
readonly reason?: string | undefined
}Why authenticate refused a caller: answered as a 401 with reason. Remote's
RemoteUnauthenticated (RemoteServer.unauthenticated(reason)) is one, so
the resolver a Remote host uses can refuse here too.
AgentMcp.unauthenticatedfunction · packages/agent-mcp/src/http.ts:67
function unauthenticated(reason?: string): UnauthenticatedRefuses a caller from authenticate: Effect.fail(AgentMcp.unauthenticated('Token expired')).