McpServer unstable
Builds Model Context Protocol (MCP) servers with Effect.
The McpServer service stores the tools, resources, resource templates,
prompts, completions, and outgoing notifications exposed by a server. This
module also includes the server runner, custom protocol,
stdio, and HTTP layers, registration helpers, and APIs that let handlers ask
the connected client for structured input or read its advertised
capabilities.
Accessors
clientCapabilities
unstableAccesses the current client's capabilities.
Signature
declare const clientCapabilities: Effect.Effect<ClientCapabilities, never, McpRequestContext>Collects structured input from the current MCP client and decodes the
accepted response with schema.
Details
Accepted content is decoded with the supplied schema, declined requests fail
with ElicitationDeclined, and canceled requests interrupt the effect.
Signature
declare const elicit: <S extends Schema.ConstraintEncoder<Record<string, unknown>, unknown>>(options: { readonly message: string; readonly schema: S;}) => Effect.Effect<S["Type"], ElicitationDeclined, McpServerClient | S["DecodingServices"]>Decoding
decodeNameHeader
unstableDecodes an Mcp-Name header, including base64-wrapped values.
Returns undefined for malformed values.
Details
Use in HttpRouter.middleware to identify the tool, prompt or resource
targeted by a 2026-07-28 request. Earlier protocols carry the name only
in the JSON-RPC body.
Signature
declare function decodeNameHeader(value: string): string | undefinedHandlers
registerPrompt
unstableRegisters an MCP prompt from an Effect program.
Details
Decodes parameters and encodes completion suggestions using their schemas. String content becomes a user text message.
See
- prompt for the layer-based prompt registration wrapper
Signature
declare function registerPrompt<E, R, Params extends Fields = {}, Completions extends { [K in string | number | symbol]: (input: string, context: { readonly arguments?: { [key: string]: string; };} | undefined) => Effect<Array<Params[K]["Type"]>, any, any> } = {}>(options: { readonly annotations?: Context<never>; readonly completion?: ValidateCompletions<Completions, Extract<keyof Params, string>>; readonly content: (params: View<Params>) => Effect<string | Array<PromptMessage>, E, R>; readonly description?: string; readonly name: string; readonly parameters?: Params; readonly title?: string;}): Effect<void, never, PromptRequirements<Params, R, Completions>>registerResource
unstableRegisters an MCP resource or resource template from an Effect program.
See
- resource for the layer-based resource registration wrapper
Signature
declare const registerResource: { <E, R>(options: { readonly annotations?: Context.Context<never>; readonly audience?: ReadonlyArray<"user" | "assistant">; readonly content: Effect.Effect<ReadResourceResult | string | Uint8Array, E, R>; readonly description?: string; readonly mimeType?: string; readonly name: string; readonly priority?: number; readonly uri: string; }): Effect<void, never, ResourceRequirements<R>>; <Schemas extends readonly Array<Constraint>>(segments: TemplateStringsArray, ...schemas: Schemas): <E, R, Completions extends Partial<ResourceCompletions<Schemas>> = {}>(options: { readonly annotations?: Context.Context<never>; readonly audience?: ReadonlyArray<"user" | "assistant">; readonly completion?: ValidateCompletions<Completions, keyof ResourceCompletions<Schemas>>; readonly content: (uri: string, ...params: { [K in keyof Schemas]: Schemas[K]["Type"] }) => Effect.Effect<ReadResourceResult | string | Uint8Array, E, R>; readonly description?: string; readonly mimeType?: string; readonly name: string; readonly priority?: number; }) => Effect<void, never, ResourceTemplateRequirements<Schemas, R, Completions>>;}registerSkill
unstableRegisters an Agent Skill under the MCP Skills extension (SEP-2640).
Details
Each file is registered as a resource, and the skill is listed by
skills/list and skills/get with a manifest of every file's SHA-256
digest and size, computed from the bytes served. Every directory of the
skill namespace is readable with resources/directory/read. The server
declares the extension once any skill is registered.
Fails when the skill breaks the extension's rules: a name that does not
match the URI, a SKILL.md without YAML frontmatter, frontmatter that is
not valid McpSchema.SkillFrontmatter, a file path that leaves the
skill, or more than 512 files or 16 MiB in all. It also fails when the
skill's directory contains or lies inside another skill's, or one of its
files is already registered as a resource. Registering a resource at the
URI of a skill's file afterwards is a defect.
SHA-256 runs on crypto.subtle.
See
- skill for the layer-based skill registration wrapper
Signature
declare const registerSkill: (options: SkillOptions) => Effect.Effect<void, Cause.IllegalArgumentError, McpServer>registerToolkit
unstableRegisters a Toolkit with the McpServer.
Details
The MCP middleware in the current context, provided with
Effect.provide(middleware.layer), runs around every call to the toolkit.
See
- ToolkitRequirements for which services are request-level
Signature
declare const registerToolkit: <Tools extends Record<string, Tool.Any>>(toolkit: Toolkit.Toolkit<Tools>) => Effect.Effect<void, never, ToolkitRequirements<Tools>>Layers
Creates a layer that starts an MCP server over an existing
RpcServer.Protocol and provides McpServer. Request handlers receive
McpRequestContext; initialized legacy requests additionally receive
McpServerClient.
When to use
Use when you already have a custom or externally provided
RpcServer.Protocol and want to start an MCP server as part of a layer
graph.
Details
The returned layer forks run(options) in the layer scope and merges
McpServer.layer, so registration layers can use the McpServer service
while the server is running.
Pass registration layers as registrations to build them before the server
reads its first request. The transport discharges the request-level
dependencies it supplies (see ProvidedServices); any other McpRequest
marker stays in the layer's requirements and must be met by MCP middleware.
Gotchas
Unlike layerStdio and layerHttp, this layer does not install a concrete
transport. The surrounding layer graph must provide RpcServer.Protocol.
See
- run for the effect form used by this layer
- layerStdio for a stdio-backed layer that installs the MCP protocol and NDJSON-RPC serialization
- layerHttp for an HTTP-backed layer that registers with
HttpRouterand installs JSON-RPC serialization
Signature
declare function layer<Protocols extends readonly [ProtocolAdapter<ProtocolVersion, RuntimeDescriptor>, ProtocolAdapter<ProtocolVersion, RuntimeDescriptor>], RA = never, RE = never, RR = never>(options: { readonly cacheHints?: CacheHints; readonly description?: string; readonly extensions?: { [key: `${string}/${string}`]: unknown; }; readonly icons?: readonly Array<Icon>; readonly instructions?: string; readonly name: string; readonly protocols: Protocols; readonly registrations?: Layer<RA, RE, RR>; readonly version: string; readonly websiteUrl?: string;}): Layer<McpServer | McpRequest<"Requires", Scope> | From<"Requires", Protocols[number] extends StatefulProtocolAdapter ? McpServerClient : never>, IllegalArgumentError | RE, Protocol | Exclude<Without<RR>, McpServer> | From<"Requires", Exclude<Only<"Requires", RR>, ProvidedServices<Protocols, false>>>>Registers a Streamable HTTP MCP endpoint at options.path.
When to use
Use to expose an MCP server through an existing HttpRouter.
Details
POST serves JSON-RPC and accepted notification-only requests return 202.
Header mismatches, unsupported protocol versions, and invalid session-header
usage return JSON-RPC errors with status 400.
Unknown modern RPC methods return a JSON-RPC method-not-found error with
status 404. Session requests without MCP-Protocol-Version use the version
negotiated at initialization. Unsupported HTTP methods return 405.
Requests with an Origin header are rejected unless the exact origin
appears in allowedOrigins; Origin-less non-browser clients
remain valid. The surrounding HTTP server remains responsible for binding
to an appropriate interface and installing authentication.
With allowSessionTermination, a DELETE carrying Mcp-Session-Id ends that
session with 204 and interrupts its in-flight requests; later requests with
that id get 404. DELETE validates the session and MCP-Protocol-Version
headers like POST. Without the option, or when only sessionless revisions
such as v2026_07_28 are configured, DELETE returns 405. Any caller
holding a session id can end that session, so authenticate requests in the
surrounding router.
Set allowSubscriptions: false to disable v2026_07_28 subscriptions.
Discovery advertises listChanged: false, and subscriptions/listen returns
Method not found. Request-scoped progress and logs, legacy protocols, and
default subscription behavior are unchanged.
The layer runs one long-lived MCP server that every POST shares. For
serverless hosts where execution must belong to each request, use
layerHttpRequestScoped instead.
Pass registration layers as registrations to build them before the
endpoint serves its first request. The transport discharges the
request-level dependencies it supplies (see ProvidedServices) and converts
the remaining McpRequest markers into HttpRouter.Request markers, so
HttpRouter.middleware can provide them, for example to authenticate the
request and resolve the current user.
layerHttp always implements the single-endpoint Streamable HTTP topology.
Using v2024_11_05 here is a custom compatibility transport for that
revision's schema. It does not implement the historical two-endpoint
HTTP+SSE transport, GET SSE, event resumption, or session expiry.
Gotchas
This layer starts a scoped server fiber. For sharing it across Cloudflare
Workers requests, see HttpRouter.toWebHandler for setup and limitations.
See
- layerStdio for exposing the server over stdio
- layer for the base MCP server layer without a transport protocol
Signature
declare function layerHttp<Protocols extends readonly [ProtocolAdapter<ProtocolVersion, RuntimeDescriptor>, ProtocolAdapter<ProtocolVersion, RuntimeDescriptor>], RA = never, RE = never, RR = never>(options: { readonly allowedOrigins?: readonly Array<string>; readonly allowSessionTermination?: boolean; readonly allowSubscriptions?: boolean; readonly cacheHints?: CacheHints; readonly description?: string; readonly extensions?: { [key: `${string}/${string}`]: unknown; }; readonly icons?: readonly Array<Icon>; readonly instructions?: string; readonly name: string; readonly path: PathInput; readonly protocols: Protocols; readonly registrations?: Layer<RA, RE, RR>; readonly version: string; readonly websiteUrl?: string;}): Layer<McpServer | McpRequest<"Requires", Scope> | McpRequest<"Requires", HttpServerRequest> | From<"Requires", Protocols[number] extends StatefulProtocolAdapter ? McpServerClient : never>, IllegalArgumentError | RE, HttpRouter | Exclude<Without<RR>, McpServer> | From<"Requires", Exclude<Only<"Requires", RR>, ProvidedServices<Protocols, true>>>>layerHttpRequestScoped
unstableRegisters a request-scoped Streamable HTTP MCP endpoint at options.path.
When to use
Use when serving on serverless hosts such as Cloudflare Workers, where execution must belong to the HTTP request rather than a shared long-lived server.
Details
Each POST runs its own server in the request scope, with no background
fiber, timer, or queue consumer left after the response body completes.
Only sessionless protocols such as v2026_07_28 are accepted. Tools,
resources, prompts, completions, multi round-trip input requests, and the
request's own progress and log notifications are supported. Legacy reverse
requests through McpServer.elicit are not available.
Discovery advertises listChanged: false, subscriptions/listen returns
Method not found, and DELETE returns 405. Request-scoped clients receive
no broadcast notifications such as list changes. Those still reach clients
of a layerHttp endpoint sharing the same McpServer, and are dropped when
no such endpoint exists. Use layerHttp on a long-lived host for sessions,
subscriptions, or legacy reverse requests.
HTTP admission, origin checks, and JSON-RPC errors follow layerHttp.
Configuring a sessionful protocol through an untyped caller fails layer
construction with IllegalArgumentError.
Registrations passed as registrations are built once, before the routes
are added, and shared by every POST. Remaining McpRequest markers become
HttpRouter.Request markers, as with layerHttp.
See
- layerHttp for the long-lived HTTP transport and HTTP admission policy
Signature
declare function layerHttpRequestScoped<Protocols extends readonly [ProtocolAdapter<ProtocolVersion, StatelessRuntimeDescriptor>, ProtocolAdapter<ProtocolVersion, StatelessRuntimeDescriptor>], RA = never, RE = never, RR = never>(options: { readonly allowedOrigins?: readonly Array<string>; readonly cacheHints?: CacheHints; readonly description?: string; readonly extensions?: { [key: `${string}/${string}`]: unknown; }; readonly icons?: readonly Array<Icon>; readonly instructions?: string; readonly name: string; readonly path: PathInput; readonly protocols: Protocols; readonly registrations?: Layer<RA, RE, RR>; readonly version: string; readonly websiteUrl?: string;}): Layer<McpServer | McpRequest<"Requires", Scope> | McpRequest<"Requires", HttpServerRequest> | From<"Requires", Protocols[number] extends StatefulProtocolAdapter ? McpServerClient : never>, IllegalArgumentError | RE, HttpRouter | Exclude<Without<RR>, McpServer> | From<"Requires", Exclude<Only<"Requires", RR>, ProvidedServices<Protocols, true>>>>layerStdio
unstableCreates a layer that runs an MCP server over standard input and output.
When to use
Use when an MCP client launches the server as a subprocess and communicates through newline-delimited JSON-RPC messages.
Details
The selected protocol adapter controls the dated RPC schemas and JSON-RPC
batch policy. The layer provides McpServer, supplies request-scoped
McpRequestContext and legacy McpServerClient services to handlers, and
requires Stdio.
Pass registration layers as registrations to build them before the server
reads its first request. The transport discharges the request-level
dependencies it supplies (see ProvidedServices); any other McpRequest
marker stays in the layer's requirements and must be met by MCP middleware.
See
Signature
declare function layerStdio<Protocols extends readonly [ProtocolAdapter<ProtocolVersion, RuntimeDescriptor>, ProtocolAdapter<ProtocolVersion, RuntimeDescriptor>], RA = never, RE = never, RR = never>(options: { readonly cacheHints?: CacheHints; readonly description?: string; readonly extensions?: { [key: `${string}/${string}`]: unknown; }; readonly icons?: readonly Array<Icon>; readonly instructions?: string; readonly name: string; readonly protocols: Protocols; readonly registrations?: Layer<RA, RE, RR>; readonly version: string; readonly websiteUrl?: string;}): Layer<McpServer | McpRequest<"Requires", Scope> | From<"Requires", Protocols[number] extends StatefulProtocolAdapter ? McpServerClient : never>, IllegalArgumentError | RE, Stdio | Exclude<Without<RR>, McpServer> | From<"Requires", Exclude<Only<"Requires", RR>, ProvidedServices<Protocols, false>>>>Creates a layer that registers an MCP prompt.
Details
Services read by the content and completion handlers are request-level, so
providing them with Layer.provide doesn't satisfy them. Read
construction-time services in an enclosing Layer.unwrap and close over
them, and provide per-request services with MCP middleware.
See
- registerPrompt for the Effect-level prompt registration API
- middleware for providing request-level dependencies
Signature
declare function prompt<E, R, Params extends Fields = {}, Completions extends { [K in string | number | symbol]: (input: string, context: { readonly arguments?: { [key: string]: string; };} | undefined) => Effect<Array<Params[K]["Type"]>, any, any> } = {}>(options: { readonly annotations?: Context<never>; readonly completion?: ValidateCompletions<Completions, Extract<keyof Params, string>>; readonly content: (params: View<Params>) => Effect<string | Array<PromptMessage>, E, R>; readonly description?: string; readonly name: string; readonly parameters?: Params; readonly title?: string;}): Layer<never, never, PromptRequirements<Params, R, Completions>>Creates a layer that registers an MCP resource or resource template.
Details
Services read by the content and completion handlers are request-level, so
providing them with Layer.provide doesn't satisfy them. Read
construction-time services in an enclosing Layer.unwrap and close over
them, and provide per-request services with MCP middleware.
See
- registerResource for the Effect-level resource registration API
- middleware for providing request-level dependencies
Signature
declare const resource: { <E, R>(options: { readonly audience?: ReadonlyArray<"user" | "assistant">; readonly content: Effect.Effect<ReadResourceResult | string | Uint8Array, E, R>; readonly description?: string; readonly mimeType?: string; readonly name: string; readonly priority?: number; readonly uri: string; }): Layer<never, never, ResourceRequirements<R>>; <Schemas extends readonly Array<Constraint>>(segments: TemplateStringsArray, ...schemas: Schemas): <E, R, Completions extends Partial<ResourceCompletions<Schemas>> = {}>(options: { readonly audience?: ReadonlyArray<"user" | "assistant">; readonly completion?: ValidateCompletions<Completions, keyof ResourceCompletions<Schemas>>; readonly content: (uri: string, ...params: { [K in keyof Schemas]: Schemas[K]["Type"] }) => Effect.Effect<ReadResourceResult | string | Uint8Array, E, R>; readonly description?: string; readonly mimeType?: string; readonly name: string; readonly priority?: number; }) => Layer<never, never, ResourceTemplateRequirements<Schemas, R, Completions>>;}Creates a layer that registers an Agent Skill under the MCP Skills extension (SEP-2640).
See
- registerSkill for the Effect-level skill registration API
Signature
declare function skill(options: SkillOptions): Layer<never, IllegalArgumentError, McpServer>Example Publishing a skill with a supporting file
import { Layer } from "effect"import { McpProtocol, McpServer } from "effect/ai"
const RefundsSkill = McpServer.skill({ uri: "skill://billing/refunds/SKILL.md", files: { "SKILL.md": `---name: refundsdescription: Process refunds using the billing tools---
Read references/policy.md before issuing a refund.`, "references/policy.md": "Refunds above CHF 500 require manager approval.\n" }})
const ServerLayer = RefundsSkill.pipe( Layer.provide(McpServer.layerHttp({ name: "billing", version: "1.0.0", path: "/mcp", protocols: [McpProtocol.v2026_07_28, McpProtocol.v2025_11_25] })))Creates a layer that registers a Toolkit with the McpServer.
Details
The layer requires McpServer. Pass it to a transport through the
registrations option, or provide the transport layer to it.
See
- ToolkitRequirements for which services are request-level
Signature
declare function toolkit<Tools extends Record<string, Any>>(toolkit: Toolkit<Tools>): Layer<never, never, ToolkitRequirements<Tools>>Middleware
middleware
unstableCreates MCP middleware that runs around tools/call, resources/read,
prompts/get and completion/complete requests.
Details
Middleware is how request-level dependencies are satisfied: declare the
services it provides with middleware<{ provides: S }>() and provide them to
the handler effect. It receives the request headers, session id and request
context, so it can resolve the current user per request.
Pass a function, or an Effect that builds the function. Services the Effect
reads are requirements of the middleware layer. Services the function reads
per request, other than McpRequestContext, are per-request requirements
of the result; see Middleware for how they are met.
Failures must be McpErrorBase values. A middleware failure is sent as a
JSON-RPC error with its code, message and data, never as a tool
result. Pick custom codes from the implementation-defined range
(-32000 to -32019): any numeric code is sent as is. Defects are logged,
reported to the ErrorReporter and sent as InternalError("Internal error").
Interruption propagates.
Local middleware runs inside global middleware, and middleware provided both
locally and globally runs once. Middleware never runs for initialize. It
runs for list requests only when it wraps a visibility, so that the
visibility check can read the services it provides.
Signature
declare const middleware: middleware.Make<never> & <Config extends { provides?: any;} = {}>() => middleware.Make<Config extends { provides: infer P;} ? P : never>Example Resolving the current user per request
import { Context, Effect, Layer, Schema } from "effect"import { McpSchema, McpServer, Tool, Toolkit } from "effect/ai"
class CurrentUser extends Context.Service<CurrentUser, { readonly id: string }>()("CurrentUser") {}
class Unauthorized extends Schema.Error<Unauthorized>("Unauthorized")({ ...McpSchema.McpErrorBase.fields, _tag: Schema.tag("Unauthorized"), code: Schema.tag(-32001)}) {}
const WhoAmI = Tool.make("WhoAmI", { success: Schema.String, dependencies: [CurrentUser] })const Tools = Toolkit.make(WhoAmI)
const Auth = McpServer.middleware<{ provides: CurrentUser }>()((effect, { headers }) => { const token = headers["authorization"] return token === undefined ? Effect.fail(new Unauthorized({ message: "Missing credentials" })) : Effect.provideService(effect, CurrentUser, { id: token })})
const Registrations = McpServer.toolkit(Tools).pipe( Layer.provide(Auth.layer), Layer.provide(Tools.toLayer({ WhoAmI: () => Effect.map(CurrentUser, (user) => user.id) })))middleware
unstableTypes used by middleware.
Composable MCP middleware.
Details
Provide layer to registration layers, or to the Effect form of a
registration, to run the middleware for those registrations. Use
layerGlobal to run it for every request on the server.
Services the middleware reads per request are McpRequest<"Requires", S>
requirements of layer and layerGlobal, like the request-level
dependencies of a registration. A middleware that provides S meets them
when it runs outside this one: a.combine(b) runs b outside a, and so
does providing b.layer or b.layerGlobal outside a.layer, though
layerGlobal installs b for the whole server. The transport discharges
the services it supplies, and layerHttp turns the rest into
HttpRouter.Request requirements that an HttpRouter.middleware can meet.
See
- middleware for creating MCP middleware
Signature
interface Middleware<Config extends MiddlewareConfig> { readonly "~effect/ai/McpServer/Middleware": Config; readonly combine: <Config2 extends MiddlewareConfig>(other: Middleware<Config2>) => Middleware<{ error: Config2["error"] | Config["error"]; layerError: Config["layerError"] | Config2["layerError"]; layerRequires: Config["layerRequires"] | Config2["layerRequires"]; provides: Config["provides"] | Config2["provides"]; requires: Exclude<Config["requires"], Config2["provides"]> | Config2["requires"]; }>; readonly layer: MiddlewareLayer<Config, never>; readonly layerGlobal: MiddlewareLayer<Config, McpServer>;}The request methods that run MCP middleware. More methods may be added, so
don't switch on it exhaustively without a default case.
Signature
type MiddlewareMethod = "tools/call" | "resources/read" | "prompts/get" | "completion/complete" | "tools/list" | "resources/list" | "resources/templates/list" | "prompts/list" | "skills/list" | "skills/get" | "resources/directory/read"Request metadata passed to MCP middleware.
Details
target is the tool name, the requested resource URI, or the prompt name.
For completion/complete it is the prompt name, or the URI template of the
resource template being completed. For skills/get it is the URI of the
skill's SKILL.md. payload holds the request parameters.
On list requests, middleware runs only when it wraps a visibility,
once for each registration it wraps. target is then the tool name, the
resource URI, the URI template or the prompt name of that registration, and
payload is undefined. skills/list and resources/directory/read are
list requests whose target is the SKILL.md URI of each skill; a
directory read checks only the skills that have the directory. A skill's
files are resources, checked as resources/list and resources/read with
their own URIs.
headers are the HTTP request headers. They are empty on stdio, on custom
transports and for in-process calls. sessionId is the Mcp-Session-Id
header, so it is undefined outside sessionful HTTP.
Signature
interface MiddlewareOptions { readonly headers: Headers; readonly method: MiddlewareMethod; readonly payload: unknown; readonly requestContext: { readonly clientCapabilities: ClientCapabilities; readonly clientId: number; readonly clientInfo?: Implementation; readonly inputResponses?: Readonly<Record<string, Readonly<Record<string, unknown>>>>; readonly protocolVersion: string; readonly requestMetadata?: Readonly<Record<string, unknown>> | { [key: string]: unknown; readonly progressToken?: string | number; }; readonly requestState?: string; }; readonly sessionId: string | undefined; readonly target: string;}Opaque handler result. Middleware can wrap execution and provide services, but cannot inspect or replace the result.
Signature
interface MiddlewareSuccess { readonly _: typeof _;}visibility
unstableCreates middleware that hides registrations per request.
Details
The check runs for every registration the middleware is provided to, on list
requests and on calls, reads, gets and completions. When it returns false
the registration is left out of list results, and requests for it fail as if
it was not registered.
The result is a Middleware and composes like one. The check receives the
same MiddlewareOptions as middleware, and services it reads per
request, other than McpRequestContext, are met the same way. Scope a
visibility to some tools by providing its layer to their toolkit only, or
branch on target.
On a list request, the check and the middleware outside the innermost visibility run once for each registration. Middleware inside it does not run. A failure of that middleware or of a check fails the whole list request. It never hides a registration.
Unlike EnabledWhen, a visibility runs the middleware around it on list
requests, and middleware sees requests for hidden registrations.
See
- middleware for providing the services a check reads
Signature
declare const visibility: visibility.MakeExample Hiding admin tools from other users
import { Context, Effect, Layer, Schema } from "effect"import { McpServer, Tool, Toolkit } from "effect/ai"
class CurrentUser extends Context.Service<CurrentUser, { readonly isAdmin: boolean }>()("CurrentUser") {}
const AdminTools = Toolkit.make(Tool.make("DeleteUser", { success: Schema.String }))
const Auth = McpServer.middleware<{ provides: CurrentUser }>()((effect, { headers }) => Effect.provideService(effect, CurrentUser, { isAdmin: headers["x-role"] === "admin" }))const AdminOnly = McpServer.visibility(() => Effect.map(CurrentUser, (user) => user.isAdmin))
const Registrations = McpServer.toolkit(AdminTools).pipe( Layer.provide(AdminOnly.combine(Auth).layer), Layer.provide(AdminTools.toLayer({ DeleteUser: () => Effect.succeed("deleted") })))visibility
unstableTypes used by visibility.
Models
A client cache hint for one kind of v2026_07_28 result.
Details
ttl is how long a client may treat the result as fresh. It is sent as a
whole number of milliseconds, rounded down, so it must be finite,
nonnegative and at most Number.MAX_SAFE_INTEGER milliseconds. Only
"private" scope is supported, so shared caches must not store the result.
Signature
interface CacheHint { readonly cacheScope: "private"; readonly ttl: Input;}Client cache hints for v2026_07_28 discovery and list results.
Details
Each entry sets the freshness and cacheScope of one result kind:
server/discover, tools/list, resources/list,
resources/templates/list and prompts/list. skills covers the Skills
extension's skills/list, skills/get and resources/directory/read.
Omitted entries, and resources/read results, keep
{ ttlMs: 0, cacheScope: "private" }. Earlier protocol revisions have no
cache hints and ignore this option.
These are hints for clients. The server does not cache results, and visibility and invocation checks still run on every request.
Gotchas
A positive ttl lets a client keep showing a list after registrations or
per-request visibility change, until the hint expires. Private scope does
not key a client's cache by user, so a client shared between users can
reuse one user's list for another.
Signature
interface CacheHints { readonly discover?: CacheHint; readonly prompts?: CacheHint; readonly resources?: CacheHint; readonly resourceTemplates?: CacheHint; readonly skills?: CacheHint; readonly tools?: CacheHint;}Completion-handler map for a resource URI template.
Details
Each schema interpolation contributes a parameter key, using an explicit
Param name when present or paramN otherwise, and each handler returns
candidate values for that parameter.
Signature
type ResourceCompletions<Schemas extends ReadonlyArray<Schema.Constraint>> = { [K in Extract<keyof Schemas, `${number}`>]: (input: string, context: CompletionContext) => Effect.Effect<Array<Schemas[K]["Type"]>, any, any> }A file of a skill: its text, its bytes, or either with an explicit MIME type. Without one, the MIME type is inferred from the file extension.
Signature
type SkillFile = string | Uint8Array | { readonly content: string | Uint8Array; readonly mimeType?: string;}Options for registerSkill and skill.
Details
uri addresses the skill's SKILL.md, as
skill://<skill-path>/SKILL.md. Its last path segment is the skill's name,
which must equal the frontmatter's name. Other schemes work the same way.
files maps paths relative to the skill root to the skill's files,
SKILL.md included. Files are served verbatim, and the skill's listed
frontmatter is parsed from SKILL.md. Quote frontmatter strings that a YAML
1.1 parser reads as another type, such as yes or 1.0.
Signature
interface SkillOptions { readonly annotations?: Context<never>; readonly files: { readonly "SKILL.md": SkillFile; } & Readonly<Record<string, SkillFile>>; readonly uri: string;}Running
Runs an MCP server over the current RpcServer.Protocol.
Details
The server serves registered tools, resources, and prompts. The selected MCP runtime handles protocol lifecycle state and determines which clients receive queued server notifications.
Signature
declare const run: (options: { readonly cacheHints?: CacheHints; readonly description?: string; readonly extensions?: ServerExtensions; readonly icons?: ReadonlyArray<McpSchema.Icon>; readonly instructions?: string; readonly name: string; readonly protocols: Arr.NonEmptyReadonlyArray<McpProtocol.ProtocolAdapter>; readonly version: string; readonly websiteUrl?: string;}) => Effect.Effect<never, Cause.IllegalArgumentError, McpServer | RpcServer.Protocol>Services
Service that stores and serves an MCP server's registered tools, resources, prompts, completions, and outgoing notifications.
Details
Handlers use this service to register capabilities and resolve incoming MCP requests.
Signature
declare class McpServer extends Shape<"effect/ai/McpServer", { readonly addPrompt: (options: { readonly annotations: Context<never>; readonly completions: Record<string, (input: string, context: CompletionContext) => Effect.Effect<CompleteResult, InternalError, McpRequestContext>>; readonly handle: (params: Record<string, string>) => Effect<GetPromptResult | InputRequired, InvalidParams | InternalError, McpRequestContext>; readonly prompt: Prompt; readonly services?: Context<never>; }) => Effect<void>; readonly addResource: (options: { readonly annotations: Context<never>; readonly handle: Effect<ReadResourceResult, InternalError, McpRequestContext>; readonly resource: Resource; readonly services?: Context<never>; }) => Effect<void>; readonly addResourceTemplate: (options: { readonly annotations: Context<never>; readonly completions: Record<string, (input: string, context: CompletionContext) => Effect.Effect<CompleteResult, InternalError>>; readonly handle: (uri: string, params: Array<string>) => Effect<ReadResourceResult, InvalidParams | InternalError, McpRequestContext>; readonly routerPath: string; readonly services?: Context<never>; readonly template: ResourceTemplate; }) => Effect<void>; readonly addTool: (options: { readonly annotations: Context<never>; readonly handle: (payload: any) => Effect<CallToolResult | InputRequired, InvalidParams | InternalError, McpRequestContext>; readonly services?: Context<never>; readonly tool: Tool; }) => Effect<void>; readonly callTool: (requests: { readonly _meta?: { [key: string]: unknown; readonly progressToken?: string | number; }; readonly arguments?: { [key: string]: any; }; readonly name: string; }) => Effect<CallToolResult, McpErrorBase | InvalidParams | InternalError, McpServerClient>; readonly completion: (complete: { readonly argument: { readonly name: string; readonly value: string; }; readonly context?: { readonly arguments?: { [key: string]: string; }; }; readonly ref: ResourceReference | PromptReference; }) => Effect<CompleteResult, McpErrorBase | InvalidParams | InternalError, McpServerClient>; readonly findResource: (uri: string) => Effect<ReadResourceResult, McpErrorBase | InvalidParams | InternalError, McpServerClient>; readonly getPromptResult: (request: { readonly _meta?: { [key: string]: unknown; readonly progressToken?: string | number; }; readonly arguments?: { [key: string]: string; }; readonly name: string; readonly title?: string; }) => Effect<GetPromptResult, McpErrorBase | InvalidParams | InternalError, McpServerClient>; readonly notifications: { "notifications/cancelled": <AsQueue extends boolean = false, Discard = false>(input: { readonly _meta?: { [key: string]: unknown; }; readonly reason?: string; readonly requestId: string | number; }, options?: { readonly context?: Context<never>; readonly discard?: Discard; readonly headers?: Input; }) => Effect<Discard extends true ? void : void, Discard extends true ? never : never, never>; "notifications/message": <AsQueue extends boolean = false, Discard = false>(input: { readonly _meta?: { [key: string]: unknown; }; readonly data: any; readonly level: "error" | "debug" | "info" | "notice" | "warning" | "critical" | "alert" | "emergency"; readonly logger?: string; }, options?: { readonly context?: Context<never>; readonly discard?: Discard; readonly headers?: Input; }) => Effect<Discard extends true ? void : void, Discard extends true ? never : never, never>; "notifications/progress": <AsQueue extends boolean = false, Discard = false>(input: { readonly _meta?: { [key: string]: unknown; }; readonly message?: string; readonly progress: number; readonly progressToken: string | number; readonly total?: number; }, options?: { readonly context?: Context<never>; readonly discard?: Discard; readonly headers?: Input; }) => Effect<Discard extends true ? void : void, Discard extends true ? never : never, never>; "notifications/prompts/list_changed": <AsQueue extends boolean = false, Discard = false>(input: { readonly _meta?: { [key: string]: unknown; }; } | undefined, options?: { readonly context?: Context<never>; readonly discard?: Discard; readonly headers?: Input; }) => Effect<Discard extends true ? void : void, Discard extends true ? never : never, never>; "notifications/resources/list_changed": <AsQueue extends boolean = false, Discard = false>(input: { readonly _meta?: { [key: string]: unknown; }; } | undefined, options?: { readonly context?: Context<never>; readonly discard?: Discard; readonly headers?: Input; }) => Effect<Discard extends true ? void : void, Discard extends true ? never : never, never>; "notifications/resources/updated": <AsQueue extends boolean = false, Discard = false>(input: { readonly _meta?: { [key: string]: unknown; }; readonly uri: string; }, options?: { readonly context?: Context<never>; readonly discard?: Discard; readonly headers?: Input; }) => Effect<Discard extends true ? void : void, Discard extends true ? never : never, never>; "notifications/tools/list_changed": <AsQueue extends boolean = false, Discard = false>(input: { readonly _meta?: { [key: string]: unknown; }; } | undefined, options?: { readonly context?: Context<never>; readonly discard?: Discard; readonly headers?: Input; }) => Effect<Discard extends true ? void : void, Discard extends true ? never : never, never>; }; readonly notifyElicitationComplete: (options: { readonly clientId: number; readonly elicitationId: string; }) => Effect<void>; readonly prompts: readonly Array<{ readonly annotations: Context<never>; readonly prompt: Prompt; }>; readonly resources: readonly Array<{ readonly annotations: Context<never>; readonly resource: Resource; }>; readonly resourceTemplates: readonly Array<{ readonly annotations: Context<never>; readonly template: ResourceTemplate; }>; readonly tools: readonly Array<{ readonly annotations: Context<never>; readonly tool: Tool; }>;}, this> { constructor(_: never); static readonly layer: Layer<McpServer>; static readonly make: Effect<{ readonly addPrompt: (options: { readonly annotations: Context<never>; readonly completions: Record<string, (input: string, context: CompletionContext) => Effect.Effect<CompleteResult, InternalError, McpRequestContext>>; readonly handle: (params: Record<string, string>) => Effect<GetPromptResult | InputRequired, InvalidParams | InternalError, McpRequestContext>; readonly prompt: Prompt; readonly services?: Context<never>; }) => Effect<void>; readonly addResource: (options: { readonly annotations: Context<never>; readonly handle: Effect<ReadResourceResult, InternalError, McpRequestContext>; readonly resource: Resource; readonly services?: Context<never>; }) => Effect<void>; readonly addResourceTemplate: (options: { readonly annotations: Context<never>; readonly completions: Record<string, (input: string, context: CompletionContext) => Effect.Effect<CompleteResult, InternalError>>; readonly handle: (uri: string, params: Array<string>) => Effect<ReadResourceResult, InvalidParams | InternalError, McpRequestContext>; readonly routerPath: string; readonly services?: Context<never>; readonly template: ResourceTemplate; }) => Effect<void>; readonly addTool: (options: { readonly annotations: Context<never>; readonly handle: (payload: any) => Effect<CallToolResult | InputRequired, InvalidParams | InternalError, McpRequestContext>; readonly services?: Context<never>; readonly tool: Tool; }) => Effect<void>; readonly callTool: (requests: { readonly _meta?: { [key: string]: unknown; readonly progressToken?: string | number; }; readonly arguments?: { [key: string]: any; }; readonly name: string; }) => Effect<CallToolResult, McpErrorBase | InvalidParams | InternalError, McpServerClient>; readonly completion: (complete: { readonly argument: { readonly name: string; readonly value: string; }; readonly context?: { readonly arguments?: { [key: string]: string; }; }; readonly ref: ResourceReference | PromptReference; }) => Effect<CompleteResult, McpErrorBase | InvalidParams | InternalError, McpServerClient>; readonly findResource: (uri: string) => Effect<ReadResourceResult, McpErrorBase | InvalidParams | InternalError, McpServerClient>; readonly getPromptResult: (request: { readonly _meta?: { [key: string]: unknown; readonly progressToken?: string | number; }; readonly arguments?: { [key: string]: string; }; readonly name: string; readonly title?: string; }) => Effect<GetPromptResult, McpErrorBase | InvalidParams | InternalError, McpServerClient>; readonly notifications: { "notifications/cancelled": <AsQueue extends boolean = false, Discard = false>(input: { readonly _meta?: { [key: string]: unknown; }; readonly reason?: string; readonly requestId: string | number; }, options?: { readonly context?: Context<never>; readonly discard?: Discard; readonly headers?: Input; }) => Effect<Discard extends true ? void : void, Discard extends true ? never : never, never>; "notifications/message": <AsQueue extends boolean = false, Discard = false>(input: { readonly _meta?: { [key: string]: unknown; }; readonly data: any; readonly level: "error" | "debug" | "info" | "notice" | "warning" | "critical" | "alert" | "emergency"; readonly logger?: string; }, options?: { readonly context?: Context<never>; readonly discard?: Discard; readonly headers?: Input; }) => Effect<Discard extends true ? void : void, Discard extends true ? never : never, never>; "notifications/progress": <AsQueue extends boolean = false, Discard = false>(input: { readonly _meta?: { [key: string]: unknown; }; readonly message?: string; readonly progress: number; readonly progressToken: string | number; readonly total?: number; }, options?: { readonly context?: Context<never>; readonly discard?: Discard; readonly headers?: Input; }) => Effect<Discard extends true ? void : void, Discard extends true ? never : never, never>; "notifications/prompts/list_changed": <AsQueue extends boolean = false, Discard = false>(input: { readonly _meta?: { [key: string]: unknown; }; } | undefined, options?: { readonly context?: Context<never>; readonly discard?: Discard; readonly headers?: Input; }) => Effect<Discard extends true ? void : void, Discard extends true ? never : never, never>; "notifications/resources/list_changed": <AsQueue extends boolean = false, Discard = false>(input: { readonly _meta?: { [key: string]: unknown; }; } | undefined, options?: { readonly context?: Context<never>; readonly discard?: Discard; readonly headers?: Input; }) => Effect<Discard extends true ? void : void, Discard extends true ? never : never, never>; "notifications/resources/updated": <AsQueue extends boolean = false, Discard = false>(input: { readonly _meta?: { [key: string]: unknown; }; readonly uri: string; }, options?: { readonly context?: Context<never>; readonly discard?: Discard; readonly headers?: Input; }) => Effect<Discard extends true ? void : void, Discard extends true ? never : never, never>; "notifications/tools/list_changed": <AsQueue extends boolean = false, Discard = false>(input: { readonly _meta?: { [key: string]: unknown; }; } | undefined, options?: { readonly context?: Context<never>; readonly discard?: Discard; readonly headers?: Input; }) => Effect<Discard extends true ? void : void, Discard extends true ? never : never, never>; }; readonly notifyElicitationComplete: (options: { readonly clientId: number; readonly elicitationId: string; }) => Effect<void>; readonly prompts: readonly Array<{ readonly annotations: Context<never>; readonly prompt: Prompt; }>; readonly resources: readonly Array<{ readonly annotations: Context<never>; readonly resource: Resource; }>; readonly resourceTemplates: readonly Array<{ readonly annotations: Context<never>; readonly template: ResourceTemplate; }>; readonly tools: readonly Array<{ readonly annotations: Context<never>; readonly tool: Tool; }>; }, never, Scope>;}Utility Types
Utility type that collects the services required by the handlers of a completion-handler record, including handlers declared as optional.
Signature
type CompletionServices<Completions> = { [K in keyof Completions]: NonNullable<Completions[K]> extends (...args: any) => infer Ret ? Effect.Services<Ret> : never }[keyof Completions]McpRequest
unstableHelper types for McpRequest markers.
Marks a request-level dependency of an MCP registration or middleware.
Details
Registrations carry handler dependencies as McpRequest<"Requires", S>.
MCP middleware and transports discharge these markers; providing S with
Layer.provide or Effect.provide does not.
layerHttp and layerHttpRequestScoped convert the markers they don't
discharge into HttpRouter.Request markers, so HttpRouter.middleware can
satisfy them. On stdio and custom transports an undischarged marker is a
compile error.
See
- middleware for discharging request-level dependencies
Signature
interface McpRequest<Kind extends string, T> { readonly _: typeof _; readonly kind: Kind; readonly type: T;}The requirements of a prompt registration.
Details
Services read by the content and completion handlers are request-level. Parameter decoding services, and the encoding services of parameters with a completion handler, are required when the prompt is registered, unless a handler also reads them.
Signature
type PromptRequirements<Params extends Schema.Struct.Fields, R, Completions> = RegistrationRequirements<PromptSchemaServices<Params, Completions>, R | CompletionServices<Completions>>Request-level services supplied by a transport.
Details
Every transport supplies Scope. McpServerClient is supplied only when
every configured protocol is known to be sessionful: a v2026_07_28 request
has no client session, and a protocol array widened to ProtocolAdapter
might contain one. HTTP transports also supply HttpServerRequest.
Signature
type ProvidedServices<Protocols extends ReadonlyArray<McpProtocol.ProtocolAdapter>, Http extends boolean = false> = Scope.Scope | Protocols[number] extends StatefulProtocolAdapter ? McpServerClient : never | Http extends true ? HttpServerRequest.HttpServerRequest : neverThe requirements of a resource registration: the content handler's services are request-level.
Signature
type ResourceRequirements<R> = RegistrationRequirements<never, R>The requirements of a resource template registration.
Details
Services read by the content and completion handlers are request-level. Parameter decoding services, and the encoding services of parameters with a completion handler, are required when the template is registered, unless a handler also reads them.
Signature
type ResourceTemplateRequirements<Schemas extends ReadonlyArray<Schema.Constraint>, R, Completions> = RegistrationRequirements<TemplateSchemaServices<Schemas, Completions>, R | CompletionServices<Completions>>The requirements of a toolkit registration.
Details
Services a tool declares with dependencies or Tool.addDependency are
request-level: they are carried as McpRequest<"Requires", S> markers and
must be provided by MCP middleware or the transport. Parameter decoding and
result encoding services are required when the toolkit is registered,
unless a tool also declares them.
Signature
type ToolkitRequirements<Tools extends Record<string, Tool.Any>> = 0 extends 1 & Tool.Requirements<Tools[keyof Tools]> ? any : Tool.HandlersFor<Tools> | RegistrationRequirements<Tool.SchemaServices<Tools[keyof Tools]>, Tool.Requirements<Tools[keyof Tools]>>Utility type that validates a completion-handler record against the allowed parameter keys.
Signature
type ValidateCompletions<Completions, Keys extends string> = Completions & { [K in keyof Completions]: K extends Keys ? (input: string, context: CompletionContext) => any : never }