Skip to content

McpServer

Builds Model Context Protocol (MCP) servers with Effect.

The McpServer service stores the tools, resources, resource templates, prompts, completions, initialized clients, 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.

15 exports Added in v4.0.0 Source

Accessors

Accesses the current client's capabilities.

Signature

declare const clientCapabilities: Effect.Effect<ClientCapabilities, never, McpServerClient>;

elicit

Added in v4.0.0 Source

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"]>;

Handlers

Registers an MCP prompt from an Effect program.

When to use

Use when you are already inside an Effect program with an McpServer service and need to add a prompt handler directly.

Details

Parameters are decoded with the supplied schema, completion handlers encode per-parameter suggestions, and string prompt content is converted into 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]>, any, any>;
  } = {},
>(options: {
  readonly annotations?: Context<never>;
  readonly completion?: ValidateCompletions<Completions, Extract<keyof Params, string>>;
  readonly content: (params: Params) => Effect<string | Array<PromptMessage>, E, R>;
  readonly description?: string;
  readonly name: string;
  readonly parameters?: Params;
}): Effect<
  void,
  never,
  McpServer | Exclude<R, McpServerClient> | Exclude<DecodingServices<Params>, McpServerClient>
>;

Registers an MCP resource or resource template from an Effect program.

When to use

Use when you are already inside an Effect program with an McpServer service and need to add a concrete resource or URI-template resource directly.

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<typeof ReadResourceResult.Type | string | Uint8Array, E, R>;
    readonly description?: string;
    readonly mimeType?: string;
    readonly name: string;
    readonly priority?: number;
    readonly uri: string;
  }): Effect<void, never, McpServer | Exclude<R, McpServerClient>>;
  <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<typeof ReadResourceResult.Type | string | Uint8Array, E, R>;
    readonly description?: string;
    readonly mimeType?: string;
    readonly name: string;
    readonly priority?: number;
  }) => Effect<void, never, McpServer | Exclude<R, McpServerClient> | Exclude<Schemas[number]["DecodingServices"], McpServerClient> | Exclude<Schemas[number]["EncodingServices"], McpServerClient> | Exclude<Completions[keyof Completions] extends (input: string) => Ret ? Ret extends Effect<_A, _E, _R> ? _R : never : never, McpServerClient>>;
}

Registers a Toolkit with the McpServer.

Signature

declare const registerToolkit: <Tools extends Record<string, Tool.Any>>(
  toolkit: Toolkit.Toolkit<Tools>,
) => Effect.Effect<
  void,
  never,
  McpServer | Tool.HandlersFor<Tools> | Exclude<Tool.HandlerServices<Tools>, McpServerClient>
>;

Layers

layer

Added in v4.0.0 Source

Creates a layer that starts an MCP server over an existing RpcServer.Protocol and provides the McpServer and McpServerClient services.

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.

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 HttpRouter and installs JSON-RPC serialization

Signature

declare function layer(options: {
  readonly extensions?: Record<`${string}/${string}`, unknown>;
  readonly name: string;
  readonly protocols: readonly [ProtocolAdapter, ProtocolAdapter];
  readonly version: string;
}): Layer<McpServerClient | McpServer, IllegalArgumentError, Protocol>;

layerHttp

Added in v4.0.0 Source

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. Unsupported protocol versions return 400; methods without MCP handlers return 405. Browser Origins are rejected unless listed in allowedOrigins.

See

  • layerStdio for exposing the server over stdio
  • layer for the base MCP server layer without a transport protocol

Signature

declare function layerHttp(options: {
  readonly allowedOrigins?: readonly Array<string>;
  readonly extensions?: Record<`${string}/${string}`, unknown>;
  readonly name: string;
  readonly path: PathInput;
  readonly protocols: readonly [ProtocolAdapter, ProtocolAdapter];
  readonly version: string;
}): Layer<McpServerClient | McpServer, IllegalArgumentError, HttpRouter>

layerStdio

Added in v4.0.0 Source

Runs the McpServer, using stdio for input and output.

Signature

declare function layerStdio(options: {
  readonly extensions?: Record<`${string}/${string}`, unknown>;
  readonly name: string;
  readonly protocols: readonly [ProtocolAdapter, ProtocolAdapter];
  readonly version: string;
}): Layer<McpServerClient | McpServer, IllegalArgumentError, Stdio>;

prompt

Added in v4.0.0 Source

Creates a layer that registers an MCP prompt.

When to use

Use to compose prompt registration into an MCP server layer.

Details

Parameters are decoded with the supplied schema, completion handlers encode per-parameter suggestions, and string prompt content is converted into a user text message.

See

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;
}): Layer<
  never,
  never,
  Exclude<R, McpServerClient> | Exclude<DecodingServices<Params>, McpServerClient>
>;

resource

Added in v4.0.0 Source

Creates a layer that registers an MCP resource or resource template.

When to use

Use to compose resource registration into an MCP server layer.

See

Signature

declare const resource: {
  <E, R>(options: {
    readonly audience?: ReadonlyArray<"user" | "assistant">;
    readonly content: Effect.Effect<typeof ReadResourceResult.Type | string | Uint8Array, E, R>;
    readonly description?: string;
    readonly mimeType?: string;
    readonly name: string;
    readonly priority?: number;
    readonly uri: string;
  }): Layer<never, never, Exclude<R, McpServerClient>>;
  <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<typeof ReadResourceResult.Type | string | Uint8Array, E, R>;
    readonly description?: string;
    readonly mimeType?: string;
    readonly name: string;
    readonly priority?: number;
  }) => Layer<never, never, Exclude<R, McpServerClient> | Exclude<Completions[keyof Completions] extends (input: string) => Ret ? Ret extends Effect<_A, _E, _R> ? _R : never : never, McpServerClient>>;
}

toolkit

Added in v4.0.0 Source

Registers an AiToolkit with the McpServer.

Signature

declare function toolkit<Tools extends Record<string, Any>>(
  toolkit: Toolkit<Tools>,
): Layer<never, never, HandlersFor<Tools> | Exclude<HandlerServices<Tools>, McpServerClient>>;

Models

ResourceCompletions type

Added in v4.0.0 Source

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>;
};

Running

run

Added in v4.0.0 Source

Runs an MCP server over the current RpcServer.Protocol.

Details

The server performs initialization and session handling, serves registered tools, resources, and prompts, and forwards queued server notifications to initialized clients.

Signature

declare const run: (options: {
  readonly extensions?: Record<`${string}/${string}`, unknown>;
  readonly name: string;
  readonly protocols: Arr.NonEmptyReadonlyArray<McpProtocol.ProtocolAdapter>;
  readonly version: string;
}) => Effect.Effect<never, Cause.IllegalArgumentError, McpServer | RpcServer.Protocol>;

Services

McpServer

Added in v4.0.0 Source

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, McpServerClient>>;
    readonly handle: (params: Record<string, string>) => Effect<GetPromptResult, InvalidParams | InternalError, McpServerClient>;
    readonly prompt: Prompt;
  }) => Effect<void>;
  readonly addResource: (options: {
    readonly annotations: Context<never>;
    readonly handle: Effect<ReadResourceResult, InternalError, McpServerClient>;
    readonly resource: Resource;
  }) => 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, McpServerClient>;
    readonly routerPath: string;
    readonly template: ResourceTemplate;
  }) => Effect<void>;
  readonly addTool: (options: {
    readonly annotations: Context<never>;
    readonly handle: (payload: any) => Effect<CallToolResult, InvalidParams | InternalError, McpServerClient>;
    readonly tool: Tool;
  }) => Effect<void>;
  readonly callTool: (requests: {
    readonly _meta?: {
      readonly progressToken?: string | number;
    };
    readonly arguments?: {
      [key: string]: any;
    };
    readonly name: string;
  }) => Effect<CallToolResult, InvalidParams | InternalError, McpServerClient>;
  readonly completion: (complete: {
    readonly argument: {
      readonly name: string;
      readonly value: string;
    };
    readonly context?: {
      readonly arguments?: {
        [key: string]: string;
      };
    };
    readonly ref: PromptReference | ResourceReference;
  }) => Effect<CompleteResult, InvalidParams | InternalError, McpServerClient>;
  readonly findResource: (uri: string) => Effect<ReadResourceResult, McpErrorBase | InvalidParams | InternalError, McpServerClient>;
  readonly getPromptResult: (request: {
    readonly _meta?: {
      readonly progressToken?: string | number;
    };
    readonly arguments?: {
      [key: string]: string;
    };
    readonly name: string;
    readonly title?: string;
  }) => Effect<GetPromptResult, InvalidParams | InternalError, McpServerClient>;
  readonly initializedClients: Set<number>;
  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 notificationsQueue: Dequeue<Request<any>>;
  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<McpServerClient | McpServer>;
  static readonly make: Effect<{
    readonly addPrompt: (options: {
      readonly annotations: Context<never>;
      readonly completions: Record<string, (input: string, context: CompletionContext) => Effect.Effect<CompleteResult, InternalError, McpServerClient>>;
      readonly handle: (params: Record<string, string>) => Effect<GetPromptResult, InvalidParams | InternalError, McpServerClient>;
      readonly prompt: Prompt;
    }) => Effect<void>;
    readonly addResource: (options: {
      readonly annotations: Context<never>;
      readonly handle: Effect<ReadResourceResult, InternalError, McpServerClient>;
      readonly resource: Resource;
    }) => 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, McpServerClient>;
      readonly routerPath: string;
      readonly template: ResourceTemplate;
    }) => Effect<void>;
    readonly addTool: (options: {
      readonly annotations: Context<never>;
      readonly handle: (payload: any) => Effect<CallToolResult, InvalidParams | InternalError, McpServerClient>;
      readonly tool: Tool;
    }) => Effect<void>;
    readonly callTool: (requests: {
      readonly _meta?: {
        readonly progressToken?: string | number;
      };
      readonly arguments?: {
        [key: string]: any;
      };
      readonly name: string;
    }) => Effect<CallToolResult, InvalidParams | InternalError, McpServerClient>;
    readonly completion: (complete: {
      readonly argument: {
        readonly name: string;
        readonly value: string;
      };
      readonly context?: {
        readonly arguments?: {
          [key: string]: string;
        };
      };
      readonly ref: PromptReference | ResourceReference;
    }) => Effect<CompleteResult, InvalidParams | InternalError, McpServerClient>;
    readonly findResource: (uri: string) => Effect<ReadResourceResult, McpErrorBase | InvalidParams | InternalError, McpServerClient>;
    readonly getPromptResult: (request: {
      readonly _meta?: {
        readonly progressToken?: string | number;
      };
      readonly arguments?: {
        [key: string]: string;
      };
      readonly name: string;
      readonly title?: string;
    }) => Effect<GetPromptResult, InvalidParams | InternalError, McpServerClient>;
    readonly initializedClients: Set<number>;
    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 notificationsQueue: Dequeue<Request<any>>;
    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

ValidateCompletions type

Added in v4.0.0 Source

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;
};