# MCP Server

The **Model Context Protocol** (MCP) is a JSON-RPC protocol that lets an MCP
client — Claude Desktop, an IDE assistant, or any other host — discover and use
capabilities you expose: **tools** the model can call, **resources** it can read,
**prompts** it can fill in, and **completions** for argument values. Effect's
`McpServer` module turns your toolkits, resources, and prompts into a fully
wired MCP server that speaks the protocol over **stdio** or **HTTP**.

The two modules involved:

- **`McpServer`** — the service and layers that run a server and the registration
  helpers (`registerToolkit`, `resource`, `prompt`, `elicit`, ...).
- **`McpSchema`** — the protocol vocabulary: every request, notification, result,
  and error modeled as an Effect `Schema` / `Rpc`. You rarely touch most of it
  directly, but it is there when you need to construct or match a protocol value.

Both live under `effect/unstable/ai`.

```ts
import { McpServer, McpSchema } from "effect/unstable/ai"
```
**Built on RPC:** An MCP server is an [RPC](https://effect.plants.sh/rpc/) server with a fixed RPC group (the MCP
  methods) and a JSON-RPC / NDJSON-RPC serialization. The transport layers
  (`layerStdio`, `layerHttp`) install the underlying `RpcServer.Protocol` for
  you, so you only register capabilities.

## A complete stdio server

Here is a full server that registers a [toolkit](https://effect.plants.sh/ai/tools/), a resource
template, and a prompt, then runs as a stdio entrypoint. This is the shape of a
real MCP server you would point Claude Desktop at.

```ts
import { Effect, Layer, Logger, Schema } from "effect"
import { McpServer, Tool, Toolkit } from "effect/unstable/ai"
import { NodeRuntime, NodeStdio } from "@effect/platform-node"

// 1. A tool the model can call. Tools come from the AI `Tool`/`Toolkit` API.
const GetWeather = Tool.make("GetWeather", {
  description: "Get the current weather for a city",
  parameters: Schema.Struct({ city: Schema.String }),
  success: Schema.Struct({ tempC: Schema.Number, summary: Schema.String })
})

const WeatherToolkit = Toolkit.make(GetWeather)

const ToolkitLayer = McpServer.toolkit(WeatherToolkit).pipe(
  Layer.provide(
    WeatherToolkit.toLayer({
      GetWeather: ({ city }) =>
        Effect.succeed({ tempC: city === "London" ? 12 : 24, summary: "Clear" })
    })
  )
)

// 2. A resource template. `McpSchema.param` names a URL parameter and gives it
//    a schema used to decode the value out of the matched URI.
const idParam = McpSchema.param("id", Schema.NumberFromString)

const ReadmeResource = McpServer.resource`file://readme/${idParam}`({
  name: "Project README",
  description: "Returns a generated README for the given project id",
  // Optional per-parameter completion: suggest candidate ids to the client.
  completion: {
    id: () => Effect.succeed([1, 2, 3])
  },
  // Returning a string becomes a single text resource at the requested uri.
  content: (_uri, id) => Effect.succeed(`# Project ${id}\n\nGenerated docs.`)
})

// 3. A prompt the client can render and send to the model.
const SummarizePrompt = McpServer.prompt({
  name: "summarize",
  description: "Summarize a document in a chosen tone",
  parameters: {
    tone: Schema.Literals(["concise", "playful"])
  },
  completion: {
    tone: () => Effect.succeed(["concise", "playful"])
  },
  content: ({ tone }) =>
    Effect.succeed(`Summarize the attached document in a ${tone} tone.`)
})

// 4. Merge all registrations, then provide the stdio transport.
const ServerLayer = Layer.mergeAll(
  ToolkitLayer,
  ReadmeResource,
  SummarizePrompt
).pipe(
  Layer.provide(
    McpServer.layerStdio({ name: "Demo Server", version: "1.0.0" })
  ),
  Layer.provide(NodeStdio.layer),
  // MCP uses stdout for the JSON-RPC stream, so logs must go to stderr.
  Layer.provide(Layer.succeed(Logger.LogToStderr)(true))
)

Layer.launch(ServerLayer).pipe(NodeRuntime.runMain)
```

A few things to note:

- Each registration helper (`toolkit`, `resource`, `prompt`) returns a `Layer`.
  You merge them and `Layer.provide` the transport underneath.
- The transport layer (`layerStdio`) provides the `McpServer` service that the
  registration layers depend on.
- Over stdio, **stdout is the protocol channel**. Route your logs to stderr with
  `Logger.LogToStderr`, or the client will choke on the mixed output.
**Where do tools come from?:** Tools and toolkits are the same `Tool` / `Toolkit` API used everywhere in the
  AI modules — see [Tools](https://effect.plants.sh/ai/tools/). `McpServer.registerToolkit` exposes a
  toolkit's tools as MCP tools and wires `tools/call` to your handlers.

## stdio vs HTTP

Both transports run the same server; only the wiring differs.

`layerStdio` installs the NDJSON-RPC serialization and reads/writes the current
`Stdio` service. This is the transport local desktop clients launch as a
subprocess.

```ts
import { Layer, Logger } from "effect"
import { McpServer } from "effect/unstable/ai"
import { NodeStdio } from "@effect/platform-node"

const transport = McpServer.layerStdio({
  name: "Demo Server",
  version: "1.0.0"
}).pipe(
  Layer.provide(NodeStdio.layer),
  Layer.provide(Layer.succeed(Logger.LogToStderr)(true))
)
```

`layerHttp` registers a JSON-RPC POST route on an existing `HttpRouter` at the
given `path`. HTTP clients must complete MCP initialization first; the server
tracks each session with an `Mcp-Session-Id` header.

```ts
import { Layer } from "effect"
import { McpServer } from "effect/unstable/ai"
import { HttpRouter } from "effect/unstable/http"

const transport = McpServer.layerHttp({
  name: "Demo Server",
  version: "1.0.0",
  path: "/mcp"
}).pipe(Layer.provide(HttpRouter.layer))
```

Compose the registration layers on top exactly as in the stdio example, then
serve the `HttpRouter` with your platform's HTTP server layer.

If you already have a custom `RpcServer.Protocol`, use the lower-level
[`layer`](#layer) (no transport) or call [`run`](#run) directly inside your own
layer graph.

---

## `McpServer` reference

### `McpServer`

The `Context.Service` that stores registered tools, resources, prompts, and
completions and resolves incoming MCP requests. You normally obtain it via one
of the layers rather than constructing it yourself; the registration helpers
require it in their environment.

```ts
import { Effect } from "effect"
import { McpServer } from "effect/unstable/ai"

const program = Effect.gen(function* () {
  const server = yield* McpServer
  // server.tools, server.resources, server.prompts, ...
})
```

`McpServer.layer` provides both `McpServer` and `McpServerClient` but installs
no transport.

### `run`

Runs an MCP server over the ambient `RpcServer.Protocol`. It performs
initialization and session handling, serves registered capabilities, and
forwards queued server notifications. Returns an `Effect<never>` you fork inside
a scope.

```ts
import { Effect } from "effect"
import { McpServer } from "effect/unstable/ai"

// Requires McpServer | RpcServer.Protocol in the environment.
const main = McpServer.run({ name: "Demo Server", version: "1.0.0" })
// => Effect<never, never, McpServer | RpcServer.Protocol>
```

### `layer`

Base layer that forks `run` and provides `McpServer` + `McpServerClient`, but
does **not** install a transport — the surrounding graph must supply
`RpcServer.Protocol`. Use this for custom transports. The `extensions` option
advertises non-standard capabilities (keys must be `vendor/name`).

```ts
import { McpServer } from "effect/unstable/ai"

const base = McpServer.layer({
  name: "Demo Server",
  version: "1.0.0",
  extensions: { "acme/telemetry": { enabled: true } }
})
// => Layer<McpServer | McpServerClient, never, RpcServer.Protocol>
```

### `layerStdio`

Runs the server over stdio. Composes `layer` with the stdio RPC protocol and
NDJSON-RPC serialization; requires the `Stdio` service (e.g. `NodeStdio.layer`).

| Option       | Type                                            | Description                          |
| ------------ | ----------------------------------------------- | ------------------------------------ |
| `name`       | `string`                                        | Server name reported on initialize.  |
| `version`    | `string`                                        | Server version reported on initialize. |
| `extensions` | ``Record<`${string}/${string}`, unknown>?``     | Optional non-standard capabilities.  |

```ts
import { McpServer } from "effect/unstable/ai"

const transport = McpServer.layerStdio({
  name: "Demo Server",
  version: "1.0.0"
})
// => Layer<McpServer | McpServerClient, never, Stdio>
```

### `layerHttp`

Registers an HTTP POST JSON-RPC route at `path` on the current `HttpRouter`.
Same options as `layerStdio` plus a required `path`.

```ts
import { McpServer } from "effect/unstable/ai"

const transport = McpServer.layerHttp({
  name: "Demo Server",
  version: "1.0.0",
  path: "/mcp"
})
// => Layer<McpServer | McpServerClient, never, HttpRouter>
```

### `registerToolkit`

Effect that registers every tool in a [`Toolkit`](https://effect.plants.sh/ai/tools/) as an MCP tool and
wires `tools/call` to the toolkit's handlers. Tool descriptions, JSON schemas,
and hint annotations (read-only / destructive / idempotent / open-world) are
derived from the tool definitions. Requires `McpServer` and the toolkit handlers
in its environment.

```ts
import { Effect } from "effect"
import { McpServer, Tool, Toolkit } from "effect/unstable/ai"
import { Schema } from "effect"

const Echo = Tool.make("Echo", {
  description: "Echo a message back",
  parameters: Schema.Struct({ message: Schema.String }),
  success: Schema.String
})

const kit = Toolkit.make(Echo)

const register = Effect.gen(function* () {
  yield* McpServer.registerToolkit(kit)
  // => void; tools/list now reports "Echo"
})
```

### `toolkit`

The layer form of `registerToolkit`. Registers the toolkit into the server when
the layer is built; provide the toolkit's handler layer underneath.

```ts
import { Layer } from "effect"
import { McpServer } from "effect/unstable/ai"

// const ToolkitLayer = McpServer.toolkit(kit).pipe(Layer.provide(kit.toLayer({ ... })))
// => Layer<never, never, Tool.HandlersFor<...>>
```

### `registerResource`

Registers an MCP resource (or resource template) from an Effect program. It has
two forms.

**Static resource** — pass a fixed `uri` and an Effect that produces the
content. Returning a `string` yields a text resource, a `Uint8Array` yields a
blob, or return a full `ReadResourceResult` for fine control.

```ts
import { Effect } from "effect"
import { McpServer } from "effect/unstable/ai"

const register = McpServer.registerResource({
  uri: "config://app",
  name: "App Config",
  mimeType: "application/json",
  content: Effect.succeed(JSON.stringify({ theme: "dark" }))
})
// => Effect<void, never, McpServer>
```

**Resource template** — call it as a tagged template with `McpSchema.param`
interpolations. Each param's schema decodes the matched URI segment, and your
`content` function receives the decoded params in order.

```ts
import { Effect, Schema } from "effect"
import { McpServer, McpSchema } from "effect/unstable/ai"

const userId = McpSchema.param("userId", Schema.NumberFromString)

const register = McpServer.registerResource`user://${userId}/profile`({
  name: "User Profile",
  content: (uri, id) =>
    Effect.succeed({
      contents: [{ uri, text: `Profile for user #${id}` }]
    })
})
// reading user://42/profile => "Profile for user #42"
```

### `resource`

The layer form of `registerResource`, with the same two overloads (static URI or
tagged template). Use it to compose resource registration into a server layer.

```ts
import { Effect } from "effect"
import { McpServer } from "effect/unstable/ai"

const ConfigResource = McpServer.resource({
  uri: "config://app",
  name: "App Config",
  content: Effect.succeed("theme=dark")
})
// => Layer<never, never, never>
```

### `ResourceCompletions`

Utility type that maps a resource template's parameter schemas to a record of
completion handlers — keyed by the explicit `param` name (or `paramN` when
unnamed). Each handler takes the partial input string and returns candidate
values of the parameter's type. You use it implicitly through a template's
`completion` option.

```ts
import { Effect, Schema } from "effect"
import { McpServer, McpSchema } from "effect/unstable/ai"

const repo = McpSchema.param("repo", Schema.String)

const register = McpServer.registerResource`repo://${repo}`({
  name: "Repo",
  // ResourceCompletions<[Param<"repo">]> = { repo: (input: string) => Effect<string[]> }
  completion: {
    repo: (input) =>
      Effect.succeed(["effect", "effect-smol"].filter((r) => r.startsWith(input)))
  },
  content: (uri, name) => Effect.succeed({ contents: [{ uri, text: name }] })
})
```

### `ValidateCompletions`

Utility type that validates a completion-handler record against the allowed
parameter keys: any key not in the parameter set resolves to `never`, producing
a type error at the call site. It backs the `completion` option on resources and
prompts, so a typo in a completion key is caught at compile time.

```ts
// type Valid = ValidateCompletions<{ id: (s: string) => any }, "id">  // ok
// type Bad   = ValidateCompletions<{ xyz: (s: string) => any }, "id"> // xyz: never => error
```

### `registerPrompt`

Registers an MCP prompt from an Effect program. `parameters` is a `Schema`
fields object that is decoded from the client's arguments; `content` returns
either a string (turned into a single user text message) or an array of
`PromptMessage`. `completion` suggests values per argument.

```ts
import { Effect, Schema } from "effect"
import { McpServer } from "effect/unstable/ai"

const register = McpServer.registerPrompt({
  name: "translate",
  description: "Translate text to a target language",
  parameters: {
    language: Schema.String,
    text: Schema.String
  },
  completion: {
    language: () => Effect.succeed(["French", "German", "Japanese"])
  },
  content: ({ language, text }) =>
    Effect.succeed(`Translate the following into ${language}:\n\n${text}`)
})
// => Effect<void, never, McpServer>
```

### `prompt`

The layer form of `registerPrompt`. Returns a `Layer` you merge into the server.

```ts
import { Effect, Schema } from "effect"
import { McpServer } from "effect/unstable/ai"

const GreetPrompt = McpServer.prompt({
  name: "greet",
  parameters: { name: Schema.String },
  content: ({ name }) => Effect.succeed(`Say hello to ${name}.`)
})
// => Layer<never, never, never>
```

### `elicit`

Asks the connected client to collect structured input from the user, decoding
the accepted response with the supplied `schema`. A declined request fails with
`ElicitationDeclined`; a canceled request interrupts the fiber. Only usable
inside a handler where `McpServerClient` is available (i.e. while serving a
request).

```ts
import { Effect, Schema } from "effect"
import { McpServer } from "effect/unstable/ai"

const askForName = McpServer.elicit({
  message: "What name should I use?",
  schema: Schema.Struct({ name: Schema.String })
})
// => Effect<{ name: string }, ElicitationDeclined, McpServerClient>
```

### `clientCapabilities`

Reads the current client's advertised capabilities (sampling, elicitation,
roots, ...) from its initialize payload. Useful for branching on what the
connected client supports.

```ts
import { Effect } from "effect"
import { McpServer } from "effect/unstable/ai"

const canElicit = McpServer.clientCapabilities.pipe(
  Effect.map((caps) => caps.elicitation !== undefined)
)
// => Effect<boolean, never, McpServerClient>
```

---

## `McpSchema` reference

`McpSchema` models the entire MCP wire protocol with Effect `Schema` and `Rpc`
definitions. Most servers never construct these directly — the registration
helpers above build them for you — but they are the source of truth for the
protocol and are useful when writing clients, middleware, or matching raw
messages.

### Schema helpers

MCP distinguishes an absent field from a field present with value `undefined`.
These helpers make that distinction encode correctly.

#### `optional`

Marks a struct field optional such that explicit `undefined` is **omitted**
during encoding (rather than serialized as `undefined`).

```ts
import { Schema } from "effect"
import { McpSchema } from "effect/unstable/ai"

const S = Schema.Struct({ note: McpSchema.optional(Schema.String) })
// decode {}            => {}
// encode { note: undefined } => {}   (field omitted, not serialized)
```

#### `optionalWithDefault`

Marks a field optional and supplies a default when it is absent, both when
decoding and when constructing.

```ts
import { Schema } from "effect"
import { McpSchema } from "effect/unstable/ai"

const S = Schema.Struct({
  retries: McpSchema.optionalWithDefault(Schema.Number, () => 3)
})
// decode {} => { retries: 3 }
```

### IDs and metadata

| Schema                  | Shape                                                   |
| ----------------------- | ------------------------------------------------------- |
| `RequestId`             | `string \| number` — JSON-RPC request id.               |
| `ProgressToken`         | `string \| number` — links progress to a request.       |
| `Cursor`                | `string` — opaque pagination token.                     |
| `RequestMeta`           | `{ _meta?: { progressToken? } }`                        |
| `ResultMeta`            | `{ _meta?: Record<string, Json> }`                      |
| `NotificationMeta`      | `{ _meta?: Record<string, Json> }`                      |
| `PaginatedRequestMeta`  | `RequestMeta` + `{ cursor? }`                           |
| `PaginatedResultMeta`   | `ResultMeta` + `{ nextCursor? }`                        |

```ts
import { McpSchema } from "effect/unstable/ai"

const meta = McpSchema.PaginatedRequestMeta.make({ cursor: "page-2" })
// => { cursor: "page-2" }
```

### Capabilities and implementation

#### `Implementation`

Name + version (and optional title) of a client or server implementation,
exchanged during initialize.

```ts
import { McpSchema } from "effect/unstable/ai"

const impl = McpSchema.Implementation.make({ name: "Demo Server", version: "1.0.0" })
// => { name: "Demo Server", version: "1.0.0" }
```

#### `ClientCapabilities`

What an MCP client supports: `roots`, `sampling`, `elicitation`, plus open
`experimental` / `extensions` records. This is the value `clientCapabilities`
returns.

```ts
import { McpSchema } from "effect/unstable/ai"

const caps = McpSchema.ClientCapabilities.make({ elicitation: {} })
// => { elicitation: {} }  (client can be elicited from)
```

#### `ServerCapabilities`

What the server advertises: `tools`, `resources`, `prompts`, `completions`,
`logging`, each optionally signalling `listChanged` / `subscribe`. The server
fills this in automatically based on what you registered.

```ts
import { McpSchema } from "effect/unstable/ai"

const caps = McpSchema.ServerCapabilities.make({
  tools: { listChanged: true },
  completions: {}
})
```

#### `Role`

Conversation role literal, `"user"` or `"assistant"`.

```ts
import { McpSchema } from "effect/unstable/ai"

const role: McpSchema.Role = "assistant"
```

#### `Annotations`

Client-facing hints on an object: intended `audience` (roles) and a `priority`
between 0 and 1.

```ts
import { McpSchema } from "effect/unstable/ai"

const ann = McpSchema.Annotations.make({ audience: ["user"], priority: 0.8 })
// => { audience: ["user"], priority: 0.8 }
```

### Errors and codes

All MCP errors extend `McpErrorBase` (`{ code, message, data? }`) and are tagged
`Schema` errors with a fixed numeric `code`. `McpError` is the union of all of
them. Servers raise `InvalidParams` / `InternalError` from handlers; the
framework maps failures to JSON-RPC error responses.

| Error / value          | Code      | Meaning                              |
| ---------------------- | --------- | ------------------------------------ |
| `ParseError`           | `-32700`  | JSON could not be parsed.            |
| `InvalidRequest`       | `-32600`  | Not a valid request object.          |
| `MethodNotFound`       | `-32601`  | Unknown / unavailable method.        |
| `InvalidParams`        | `-32602`  | Parameters don't match the schema.   |
| `InternalError`        | `-32603`  | Unexpected server-side failure.      |
| `McpErrorBase`         | any       | Base shape for a custom error.       |

The matching constants are `PARSE_ERROR_CODE`, `INVALID_REQUEST_ERROR_CODE`,
`METHOD_NOT_FOUND_ERROR_CODE`, `INVALID_PARAMS_ERROR_CODE`, and
`INTERNAL_ERROR_CODE`.

```ts
import { McpSchema } from "effect/unstable/ai"

const err = new McpSchema.InvalidParams({ message: "Unknown tool 'frobnicate'" })
err.code // => -32602
McpSchema.INVALID_PARAMS_ERROR_CODE // => -32602

// A ready-made "not implemented" internal error:
McpSchema.InternalError.notImplemented // => InternalError { message: "Not implemented" }
```

### Requests, notifications, and results

These are the `Rpc` definitions and result schemas that make up the protocol.
Each `Rpc.make` entry carries the method name, `payload`, `success`, and `error`
schemas; the server's RPC groups (below) bundle them. The registration helpers
produce these results for you — this table is the reference for matching or
constructing them by hand.

| Method / schema                       | Kind          | Notes                                              |
| ------------------------------------- | ------------- | -------------------------------------------------- |
| `Ping`                                | request       | `"ping"` liveness check; empty result.             |
| `Initialize` / `InitializeResult`     | request       | Handshake; exchanges capabilities + protocol version. |
| `InitializedNotification`             | notification  | `"notifications/initialized"` after handshake.     |
| `CancelledNotification`               | notification  | `"notifications/cancelled"` cancels a request.     |
| `ProgressNotification`                | notification  | `"notifications/progress"` for long operations.    |
| `ResourceListChangedNotification`     | notification  | Tells clients to re-list resources.                |
| `Resource`                            | schema        | A readable resource descriptor.                    |
| `ResourceTemplate`                    | schema        | RFC 6570 URI-template resource.                    |
| `ResourceContents`                    | schema        | Base contents (`uri`, `mimeType?`).                |
| `TextResourceContents`                | schema        | Contents + `text`.                                 |
| `BlobResourceContents`                | schema        | Contents + `blob` (`Uint8Array`).                  |
| `ListResources` / `ListResourcesResult`         | request | `"resources/list"`.                       |
| `ListResourceTemplates` / `ListResourceTemplatesResult` | request | `"resources/templates/list"`.      |
| `ReadResource` / `ReadResourceResult` | request       | `"resources/read"`.                                |

#### `Initialize` / `InitializeResult`

The first request a client sends. The server replies with its capabilities,
`serverInfo`, and the negotiated `protocolVersion`.

```ts
import { McpSchema } from "effect/unstable/ai"

const result = McpSchema.InitializeResult.make({
  protocolVersion: "2025-06-18",
  capabilities: McpSchema.ServerCapabilities.make({ tools: { listChanged: true } }),
  serverInfo: McpSchema.Implementation.make({ name: "Demo", version: "1.0.0" })
})
```

#### `Resource`

A concrete resource the server can read. Built automatically by
`registerResource`'s static form.

```ts
import { McpSchema } from "effect/unstable/ai"

const res = new McpSchema.Resource({
  uri: "config://app",
  name: "App Config",
  mimeType: "application/json"
})
// => Resource { uri: "config://app", name: "App Config", ... }
```

#### `ReadResourceResult`

The reply to `resources/read`: a list of `TextResourceContents` or
`BlobResourceContents`. This is the full-control return type for a resource's
`content`.

```ts
import { McpSchema } from "effect/unstable/ai"

const reply = McpSchema.ReadResourceResult.make({
  contents: [{ uri: "config://app", text: "theme=dark" }]
})
// => { contents: [{ uri: "config://app", text: "theme=dark" }] }
```

#### RPC groups

`McpSchema` bundles the protocol into RPC groups used by the server transport:

- `ClientRequestRpcs` — requests clients send (initialize, list/read/call, ...),
  with `McpServerClientMiddleware` installed.
- `ClientNotificationRpcs` — client notifications (cancelled, progress,
  initialized, roots list changed).
- `ClientRpcs` — the merge of the two; this is what the server handles.
- `ServerRequestRpcs` — requests the server can send back to a client (ping,
  sampling `CreateMessage`, `ListRoots`, `Elicit`).
- `ServerNotificationRpcs` — server notifications (logging, list-changed,
  resource-updated, progress, cancelled).

```ts
import { McpSchema } from "effect/unstable/ai"

// The methods the server answers:
McpSchema.ClientRpcs.requests.has("tools/call") // => true
```

There are matching `*Encoded` types (`ClientRequestEncoded`,
`ServerNotificationEncoded`, `FromClientEncoded`, `FromServerEncoded`, ...) that
describe the wire shapes, derived via `RequestEncoded`, `NotificationEncoded`,
`SuccessEncoded`, and `FailureEncoded`.

### Parameters and conditional enablement

#### `param`

Wraps a schema with a name so it can be used as a resource URI-template
parameter. The wrapped schema decodes the matched segment; the name drives
template compilation and completion lookup. `isParam` tests for it.

```ts
import { Schema } from "effect"
import { McpSchema } from "effect/unstable/ai"

const id = McpSchema.param("id", Schema.NumberFromString)
McpSchema.isParam(id) // => true
// Used in: McpServer.resource`thing://${id}`({ ... })
```

#### `EnabledWhen`

A context annotation that conditionally shows a tool, resource, or prompt based
on the connecting client's initialize payload. Attach it via the `annotations`
option; the server filters list results per client.

```ts
import { Context } from "effect"
import { McpSchema } from "effect/unstable/ai"

// Only expose to clients that support elicitation:
const annotations = Context.make(
  McpSchema.EnabledWhen,
  (init) => init.capabilities.elicitation !== undefined
)
// pass as: McpServer.prompt({ ..., annotations })
```
**Sampling, logging, and roots:** `McpSchema` also models the sampling (`CreateMessage`, `SamplingMessage`,
  `ModelPreferences`), logging (`LoggingLevel`, `SetLevel`,
  `LoggingMessageNotification`), autocomplete (`Complete`, `CompleteResult`),
  and roots (`Root`, `ListRoots`) surfaces. They follow the same `Rpc.make` /
  `Schema.Class` patterns shown above; reach for them when building a client or
  custom middleware.

## Related

- [Tools](https://effect.plants.sh/ai/tools/) — define the `Tool` / `Toolkit` values you expose via
  `registerToolkit`.
- [RPC](https://effect.plants.sh/rpc/) — the request/response machinery an MCP server is built on.