opencode-effect-enforcer 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +278 -0
  3. package/guidance/effect-first-development.md +1247 -0
  4. package/guidance/post__effect-and-the-near-inexpressible-majesty-of-layers.md +490 -0
  5. package/guidance/post__parse-dont-validate.md +109 -0
  6. package/guidance/progressive-disclosure-guidance.md +38 -0
  7. package/package.json +63 -0
  8. package/patterns/avoid-any.md +37 -0
  9. package/patterns/avoid-data-tagged-error.md +34 -0
  10. package/patterns/avoid-direct-json.md +51 -0
  11. package/patterns/avoid-direct-tag-checks.md +54 -0
  12. package/patterns/avoid-expect-in-if.md +52 -0
  13. package/patterns/avoid-mutable-state.md +70 -0
  14. package/patterns/avoid-native-fetch.md +61 -0
  15. package/patterns/avoid-node-imports.md +86 -0
  16. package/patterns/avoid-non-null-assertion.md +44 -0
  17. package/patterns/avoid-object-type.md +46 -0
  18. package/patterns/avoid-option-getorthrow.md +39 -0
  19. package/patterns/avoid-platform-coupling.md +43 -0
  20. package/patterns/avoid-process-env.md +43 -0
  21. package/patterns/avoid-react-hooks.md +73 -0
  22. package/patterns/avoid-schema-suffix.md +45 -0
  23. package/patterns/avoid-sync-fs.md +68 -0
  24. package/patterns/avoid-try-catch.md +47 -0
  25. package/patterns/avoid-ts-ignore.md +38 -0
  26. package/patterns/avoid-untagged-errors.md +67 -0
  27. package/patterns/avoid-yield-ref.md +46 -0
  28. package/patterns/casting-awareness.md +46 -0
  29. package/patterns/context-tag-extends.md +84 -0
  30. package/patterns/effect-catchall-default.md +61 -0
  31. package/patterns/effect-promise-vs-trypromise.md +47 -0
  32. package/patterns/effect-run-in-body.md +58 -0
  33. package/patterns/imperative-loops.md +76 -0
  34. package/patterns/prefer-arr-sort.md +52 -0
  35. package/patterns/prefer-duration-values.md +56 -0
  36. package/patterns/prefer-effect-fn.md +161 -0
  37. package/patterns/prefer-match-over-switch.md +48 -0
  38. package/patterns/prefer-option-over-null.md +56 -0
  39. package/patterns/prefer-redacted-config.md +70 -0
  40. package/patterns/prefer-schema-class.md +54 -0
  41. package/patterns/require-effect-concurrency.md +83 -0
  42. package/patterns/stream-large-files.md +63 -0
  43. package/patterns/throw-in-effect-gen.md +62 -0
  44. package/patterns/use-clock-service.md +45 -0
  45. package/patterns/use-command-executor-service.md +54 -0
  46. package/patterns/use-console-service.md +54 -0
  47. package/patterns/use-filesystem-service.md +59 -0
  48. package/patterns/use-http-client-service.md +77 -0
  49. package/patterns/use-path-service.md +53 -0
  50. package/patterns/use-random-service.md +45 -0
  51. package/patterns/use-temp-file-scoped.md +66 -0
  52. package/patterns/vm-in-wrong-file.md +51 -0
  53. package/patterns/yield-in-for-loop.md +61 -0
  54. package/skills/effect-ai-chat/SKILL.md +472 -0
  55. package/skills/effect-ai-language-model/SKILL.md +652 -0
  56. package/skills/effect-ai-prompt/SKILL.md +752 -0
  57. package/skills/effect-ai-provider/SKILL.md +668 -0
  58. package/skills/effect-ai-streaming/SKILL.md +418 -0
  59. package/skills/effect-ai-tool/SKILL.md +1132 -0
  60. package/skills/effect-atom-rpc/SKILL.md +488 -0
  61. package/skills/effect-atom-state/SKILL.md +640 -0
  62. package/skills/effect-batching/SKILL.md +614 -0
  63. package/skills/effect-cache/SKILL.md +570 -0
  64. package/skills/effect-cli/SKILL.md +523 -0
  65. package/skills/effect-command-executor/SKILL.md +675 -0
  66. package/skills/effect-concurrency-testing/SKILL.md +612 -0
  67. package/skills/effect-config/SKILL.md +580 -0
  68. package/skills/effect-context-witness/SKILL.md +274 -0
  69. package/skills/effect-domain-modeling/SKILL.md +1212 -0
  70. package/skills/effect-domain-predicates/SKILL.md +867 -0
  71. package/skills/effect-error-handling/SKILL.md +1581 -0
  72. package/skills/effect-fiber/SKILL.md +731 -0
  73. package/skills/effect-filesystem/SKILL.md +624 -0
  74. package/skills/effect-graph/SKILL.md +571 -0
  75. package/skills/effect-http-api/SKILL.md +1760 -0
  76. package/skills/effect-http-client/SKILL.md +989 -0
  77. package/skills/effect-http-server/SKILL.md +920 -0
  78. package/skills/effect-incremental-migration/SKILL.md +362 -0
  79. package/skills/effect-layer-design/SKILL.md +642 -0
  80. package/skills/effect-managed-runtime/SKILL.md +395 -0
  81. package/skills/effect-mcp-server/SKILL.md +608 -0
  82. package/skills/effect-observability/SKILL.md +719 -0
  83. package/skills/effect-optics/SKILL.md +554 -0
  84. package/skills/effect-parallelization/SKILL.md +668 -0
  85. package/skills/effect-path/SKILL.md +296 -0
  86. package/skills/effect-pattern-matching/SKILL.md +914 -0
  87. package/skills/effect-platform-abstraction/SKILL.md +1175 -0
  88. package/skills/effect-platform-layers/SKILL.md +514 -0
  89. package/skills/effect-pubsub-event-bus/SKILL.md +384 -0
  90. package/skills/effect-react-composition/SKILL.md +986 -0
  91. package/skills/effect-react-vm/SKILL.md +675 -0
  92. package/skills/effect-rpc-api/SKILL.md +624 -0
  93. package/skills/effect-rpc-client/SKILL.md +666 -0
  94. package/skills/effect-rpc-cluster/SKILL.md +1623 -0
  95. package/skills/effect-rpc-server/SKILL.md +767 -0
  96. package/skills/effect-scheduling/SKILL.md +124 -0
  97. package/skills/effect-schema-composition/SKILL.md +975 -0
  98. package/skills/effect-schema-v4/SKILL.md +691 -0
  99. package/skills/effect-scope/SKILL.md +682 -0
  100. package/skills/effect-service-implementation/SKILL.md +656 -0
  101. package/skills/effect-socket/SKILL.md +703 -0
  102. package/skills/effect-sql/SKILL.md +781 -0
  103. package/skills/effect-stream/SKILL.md +765 -0
  104. package/skills/effect-testing/SKILL.md +1331 -0
  105. package/skills/effect-typeclass-design/SKILL.md +161 -0
  106. package/skills/effect-wide-events/Article.md +66 -0
  107. package/skills/effect-wide-events/SKILL.md +95 -0
  108. package/skills/effect-workflow/SKILL.md +810 -0
  109. package/src/agent-policy.ts +22 -0
  110. package/src/enforcer.ts +104 -0
  111. package/src/frontmatter.ts +34 -0
  112. package/src/guidance.ts +66 -0
  113. package/src/index.ts +38 -0
  114. package/src/pattern-catalog.ts +115 -0
  115. package/src/pattern-matcher.ts +178 -0
  116. package/src/pattern.ts +97 -0
  117. package/src/skills.ts +29 -0
  118. package/src/write-projection.ts +66 -0
@@ -0,0 +1,608 @@
1
+ ---
2
+ name: effect-mcp-server
3
+ description: Build MCP (Model Context Protocol) servers with Effect using McpServer, McpSchema, Tool, and Toolkit. Use this skill when implementing MCP servers that expose tools, resources, and prompts to LLM clients via stdio or HTTP transports.
4
+ ---
5
+
6
+ You are an Effect TypeScript expert specializing in building MCP (Model Context Protocol) servers using Effect's built-in MCP module.
7
+
8
+ ## Effect Source Reference
9
+
10
+ The Effect v4 source is available at `~/.local/share/opencode/repos/github.com/Effect-TS/effect@main/`.
11
+ Browse and read files there directly to look up APIs, types, and implementations.
12
+
13
+ Reference these files:
14
+
15
+ - `packages/effect/MCP.md` — primary MCP guide with full examples
16
+ - `packages/effect/src/unstable/ai/McpServer.ts` — server implementation and API
17
+ - `packages/effect/src/unstable/ai/McpSchema.ts` — schema types, param helper, error classes
18
+
19
+ ## What is MCP
20
+
21
+ Model Context Protocol (MCP) is a standard protocol for LLM tool integration. It allows AI clients (Claude, etc.) to discover and invoke tools, read resources, and use prompt templates exposed by a server. Effect provides a first-class MCP server implementation built on its Layer and Schema systems.
22
+
23
+ ## Core Imports
24
+
25
+ ```typescript
26
+ import { Cause, Context, Effect, Layer, Logger } from 'effect';
27
+ import { Schema } from 'effect';
28
+ import { McpProtocol, McpServer, McpSchema, Tool, Toolkit } from 'effect/unstable/ai';
29
+ ```
30
+
31
+ For platform-specific transports:
32
+
33
+ ```typescript
34
+ // Node.js stdio transport
35
+ import { NodeRuntime, NodeSink, NodeStream } from '@effect/platform-node';
36
+
37
+ // Or using NodeStdio
38
+ import { NodeRuntime, NodeStdio } from '@effect/platform-node';
39
+ ```
40
+
41
+ ## Architecture Overview
42
+
43
+ An MCP server is composed of three kinds of **part layers** merged together, then provided with a **transport layer**:
44
+
45
+ ```
46
+ Layer.mergeAll(
47
+ ...resources, // McpServer.resource(...)
48
+ ...prompts, // McpServer.prompt(...)
49
+ toolkitLayer // McpServer.toolkit(...) with implementations
50
+ ).pipe(
51
+ Layer.provide(transportLayer), // McpServer.layerStdio(...) or McpServer.layerHttp(...)
52
+ Layer.provide(loggingLayer) // Logger — stderr for stdio transport
53
+ )
54
+ ```
55
+
56
+ Every server runner now requires a non-empty `protocols` option. Put the preferred fallback revision first; an exact initialization offer is selected when present, otherwise the first adapter is used where the transport permits fallback. Streamable HTTP rejects an explicit unsupported `MCP-Protocol-Version` header with `400`.
57
+
58
+ ## Protocol Revisions
59
+
60
+ Effect ships four dated adapters:
61
+
62
+ ```typescript
63
+ const protocols = [
64
+ McpProtocol.v2025_11_25,
65
+ McpProtocol.v2025_06_18,
66
+ McpProtocol.v2025_03_26,
67
+ McpProtocol.v2024_11_05
68
+ ] as const;
69
+ ```
70
+
71
+ - `2024-11-05` and `2025-03-26` are compatibility revisions.
72
+ - `2025-06-18` supports form elicitation.
73
+ - `2025-11-25` adds sampling with tools, independently advertised form/URL elicitation modes, descriptor icons, and elicitation-complete notifications.
74
+ - Duplicate versions or an empty protocol declaration fail layer construction with `Cause.IllegalArgumentError`.
75
+ - `v2024_11_05` over `layerHttp` uses Effect's single-endpoint Streamable HTTP compatibility transport. It does not recreate the historical two-endpoint HTTP+SSE transport, GET SSE, event resumption, session expiry, or client session termination.
76
+
77
+ Each part layer has type `Layer.Layer<never, never, ...>` — they register themselves with the McpServer service and produce no output type.
78
+
79
+ ## Tools and Toolkit
80
+
81
+ ### Defining Tools
82
+
83
+ Tools are defined with `Tool.make` specifying a name, description, parameter schemas, and success schema:
84
+
85
+ ```typescript
86
+ const GreetTool = Tool.make('GreetTool', {
87
+ description: 'Generate a greeting message',
88
+ parameters: {
89
+ name: Schema.String,
90
+ style: Schema.Union([
91
+ Schema.Literal('formal'),
92
+ Schema.Literal('casual')
93
+ ])
94
+ },
95
+ success: Schema.String
96
+ });
97
+
98
+ const CalculatorTool = Tool.make('CalculatorTool', {
99
+ description: 'Perform basic arithmetic',
100
+ parameters: {
101
+ operation: Schema.Union([
102
+ Schema.Literal('add'),
103
+ Schema.Literal('subtract'),
104
+ Schema.Literal('multiply'),
105
+ Schema.Literal('divide')
106
+ ]),
107
+ a: Schema.Number,
108
+ b: Schema.Number
109
+ },
110
+ success: Schema.Number
111
+ });
112
+
113
+ const NotifyTool = Tool.make('NotifyTool', {
114
+ description: 'Send an optional notification',
115
+ parameters: {
116
+ message: Schema.String,
117
+ channel: Schema.optionalKey(Schema.String)
118
+ },
119
+ success: Schema.Void
120
+ });
121
+ ```
122
+
123
+ MCP callers may omit fields declared with `Schema.optionalKey`; do not assume the protocol sends an `arguments` object containing every field. A `Schema.Void` tool may return `Effect.void`; `undefined` is a successful tool result, not an internal error.
124
+
125
+ ### Creating a Toolkit
126
+
127
+ Group tools into a `Toolkit`:
128
+
129
+ ```typescript
130
+ const MyToolkit = Toolkit.make(GreetTool, CalculatorTool);
131
+ ```
132
+
133
+ ### Registering with McpServer
134
+
135
+ `McpServer.toolkit(toolkit)` creates a layer that registers the toolkit. Implementations are provided via `Toolkit.toLayer`:
136
+
137
+ ```typescript
138
+ const ToolkitLayer = McpServer.toolkit(MyToolkit).pipe(
139
+ Layer.provideMerge(
140
+ MyToolkit.toLayer({
141
+ GreetTool: ({ name, style }) => {
142
+ const greeting =
143
+ style === 'formal' ? `Good day, ${name}.` : `Hey ${name}!`;
144
+ return Effect.succeed(greeting);
145
+ },
146
+ CalculatorTool: ({ operation, a, b }) => {
147
+ switch (operation) {
148
+ case 'add':
149
+ return Effect.succeed(a + b);
150
+ case 'subtract':
151
+ return Effect.succeed(a - b);
152
+ case 'multiply':
153
+ return Effect.succeed(a * b);
154
+ case 'divide':
155
+ return Effect.succeed(a / b);
156
+ }
157
+ }
158
+ })
159
+ )
160
+ );
161
+ ```
162
+
163
+ The pattern is always: `McpServer.toolkit(tk).pipe(Layer.provideMerge(tk.toLayer({...})))`.
164
+
165
+ ### MCP Tool Annotations
166
+
167
+ Effect tool annotations are emitted as MCP tool hints:
168
+
169
+ - `Tool.Readonly` → `readOnlyHint`, default `false`
170
+ - `Tool.Destructive` → `destructiveHint`, default `true`
171
+ - `Tool.Idempotent` → `idempotentHint`, default `false`
172
+ - `Tool.OpenWorld` → `openWorldHint`, default `true`
173
+
174
+ These hints help clients decide how to present tools, but they are not authorization decisions. Always enforce access control in your server handlers.
175
+
176
+ ## Resources
177
+
178
+ ### Static Resources
179
+
180
+ Expose a fixed URI resource:
181
+
182
+ ```typescript
183
+ const ReadmeResource = McpServer.resource({
184
+ uri: 'file:///README.md',
185
+ name: 'README',
186
+ description: 'Project README file',
187
+ mimeType: 'text/markdown',
188
+ content: Effect.succeed('# My Project\n\nProject documentation.')
189
+ });
190
+ ```
191
+
192
+ The `content` field is an `Effect` that can produce a `string`, `Uint8Array`, or a `ReadResourceResult`.
193
+
194
+ ### Parameterized Resource Templates
195
+
196
+ Use tagged template literals with `McpSchema.param` for dynamic URIs:
197
+
198
+ ```typescript
199
+ const idParam = McpSchema.param('id', Schema.NumberFromString);
200
+
201
+ const UserResource = McpServer.resource`file://users/${idParam}.json`({
202
+ name: 'User Data',
203
+ description: 'User information by ID',
204
+ completion: {
205
+ id: (_input: string) => Effect.succeed([1, 2, 3, 4, 5])
206
+ },
207
+ content: Effect.fn(function* (_uri, id) {
208
+ return JSON.stringify({ id, name: `User ${id}` }, null, 2);
209
+ }),
210
+ mimeType: 'application/json'
211
+ });
212
+ ```
213
+
214
+ Key points:
215
+
216
+ - `McpSchema.param(name, schema)` creates a named URI parameter with automatic codec (e.g. `Schema.NumberFromString` for path segments)
217
+ - `completion` provides auto-completion values for each parameter
218
+ - `content` receives `(uri, ...params)` — the full URI string followed by decoded parameter values
219
+ - `audience` can be `["assistant"]`, `["user"]`, or `["assistant", "user"]`
220
+
221
+ ## Prompts
222
+
223
+ Define reusable prompt templates:
224
+
225
+ ```typescript
226
+ const AnalysisPrompt = McpServer.prompt({
227
+ name: 'Analyze Data',
228
+ description: 'Analyze data and provide insights',
229
+ parameters: {
230
+ dataType: Schema.String,
231
+ focus: Schema.Union([
232
+ Schema.Literal('summary'),
233
+ Schema.Literal('details')
234
+ ])
235
+ },
236
+ completion: {
237
+ dataType: () => Effect.succeed(['sales', 'users', 'metrics']),
238
+ focus: () => Effect.succeed(['summary', 'details'])
239
+ },
240
+ content: ({ dataType, focus }) =>
241
+ Effect.succeed(
242
+ `Please analyze the ${dataType} data and provide a ${focus} analysis.`
243
+ )
244
+ });
245
+ ```
246
+
247
+ - `parameters` uses `Schema.Struct.Fields` (same as Schema.Struct field definitions)
248
+ - `completion` provides auto-complete values per parameter
249
+ - `content` receives the decoded parameters and returns `Effect<string | Array<PromptMessage>>`
250
+
251
+ ## Elicitation
252
+
253
+ `McpServer.elicit` requests form-based structured input from the current client and decodes accepted content:
254
+
255
+ ```typescript
256
+ const result = McpServer.elicit({
257
+ message: `Please answer ("yes" | "no"):`,
258
+ schema: Schema.Struct({
259
+ answer: Schema.Union([Schema.Literal('yes'), Schema.Literal('no')])
260
+ })
261
+ }).pipe(
262
+ Effect.catchTag('ElicitationDeclined', () =>
263
+ Effect.succeed({ answer: 'no' as const })
264
+ )
265
+ );
266
+ ```
267
+
268
+ - Returns `Effect<S["Type"], ElicitationDeclined, McpServerClient>`
269
+ - Handle `ElicitationDeclined` with `catchTag` for fallback behavior
270
+ - If the user cancels, the effect is interrupted
271
+ - The negotiated client must advertise form elicitation. In `2025-11-25`, an empty `elicitation` capability is treated as form support; explicit `elicitation.form` and `elicitation.url` capabilities are otherwise gated independently.
272
+
273
+ URL elicitation is a `2025-11-25` reverse-client operation. Use the scoped client facade, then notify that specific client when the external flow completes:
274
+
275
+ ```typescript
276
+ const mcpClient = yield* McpSchema.McpServerClient;
277
+ const reverseClient = yield* mcpClient.getClient;
278
+
279
+ const response = yield* reverseClient.elicit(
280
+ new McpSchema.ElicitRequestURLParams({
281
+ mode: 'url',
282
+ message: 'Authorize access',
283
+ elicitationId: 'authorization-1',
284
+ url: 'https://example.com/authorize'
285
+ })
286
+ );
287
+
288
+ const server = yield* McpServer.McpServer;
289
+ yield* server.notifyElicitationComplete({
290
+ clientId: mcpClient.clientId,
291
+ elicitationId: 'authorization-1'
292
+ });
293
+ ```
294
+
295
+ ### Sampling With Tools
296
+
297
+ Server-initiated sampling is available through the same scoped reverse client. The `tools`, `toolChoice`, `tool_use`, and `tool_result` shapes require `2025-11-25` and a client that advertises `sampling.tools`; Effect rejects unsupported requests before sending them.
298
+
299
+ ```typescript
300
+ const mcpClient = yield* McpSchema.McpServerClient;
301
+ const reverseClient = yield* mcpClient.getClient;
302
+
303
+ const sampled = yield* reverseClient.createMessage(
304
+ McpSchema.CreateMessage.payloadSchema.make({
305
+ messages: [
306
+ McpSchema.SamplingMessage.make({
307
+ role: 'user',
308
+ content: McpSchema.TextContent.make({ text: 'What is the weather?' })
309
+ })
310
+ ],
311
+ tools: [
312
+ new McpSchema.Tool({
313
+ name: 'weather',
314
+ inputSchema: {
315
+ type: 'object',
316
+ properties: { city: { type: 'string' } },
317
+ required: ['city']
318
+ }
319
+ })
320
+ ],
321
+ toolChoice: new McpSchema.ToolChoice({ mode: 'required' }),
322
+ maxTokens: 128
323
+ })
324
+ );
325
+ ```
326
+
327
+ The reverse client also gates `includeContext` on `sampling.context`. Unsupported reverse operations fail with `McpReverseOperationUnsupported`; projection or transport failures use `McpReverseOperationError`.
328
+
329
+ ## Icons And Server Metadata
330
+
331
+ Server runners accept `description`, `websiteUrl`, and `icons`. Icons use `McpSchema.Icon` with `src` plus optional `mimeType`, `sizes`, and `theme`:
332
+
333
+ ```typescript
334
+ const serverIcon = new McpSchema.Icon({
335
+ src: 'https://example.com/server.svg',
336
+ mimeType: 'image/svg+xml',
337
+ sizes: ['48x48', 'any'],
338
+ theme: 'dark'
339
+ });
340
+ ```
341
+
342
+ `McpSchema.Resource`, `McpSchema.ResourceTemplate`, `McpSchema.Prompt`, and `McpSchema.Tool` also accept `icons`. Add descriptor icons through the low-level `McpServer` registration surface (`addResource`, `addResourceTemplate`, `addPrompt`, or `addTool`); the high-level `resource`, `prompt`, and Effect `Toolkit` adapters do not currently expose an icon option. Revisions that do not support icons omit them during protocol projection.
343
+
344
+ ## Transport Layers
345
+
346
+ ### stdio Transport
347
+
348
+ For CLI-based MCP servers (most common — used by Claude Desktop, etc.):
349
+
350
+ ```typescript
351
+ // The Stdio requirement is satisfied by NodeStdio.layer
352
+ Layer.mergeAll(/* parts */).pipe(
353
+ Layer.provide(
354
+ McpServer.layerStdio({
355
+ name: 'My Server',
356
+ version: '1.0.0',
357
+ protocols: [McpProtocol.v2025_11_25]
358
+ })
359
+ ),
360
+ Layer.provide(NodeStdio.layer),
361
+ Layer.provide(Layer.succeed(Logger.LogToStderr)(true))
362
+ );
363
+ ```
364
+
365
+ **Critical**: When using stdio transport, logs MUST go to stderr. Any stdout output interferes with protocol communication. Use `Logger.consolePretty({ stderr: true })` or `Logger.LogToStderr`.
366
+
367
+ ### HTTP Transport
368
+
369
+ For web-based MCP servers using Streamable HTTP:
370
+
371
+ ```typescript
372
+ McpServer.layerHttp({
373
+ name: 'My MCP Server',
374
+ version: '1.0.0',
375
+ path: '/mcp',
376
+ protocols: [McpProtocol.v2025_11_25]
377
+ });
378
+ ```
379
+
380
+ - Requires `HttpRouter.HttpRouter` in the context
381
+ - Implements single-endpoint Streamable HTTP with JSON-RPC
382
+ - The `path` parameter sets the HTTP endpoint path
383
+ - Non-`initialize` HTTP requests with no session id return `400`; an unknown `Mcp-Session-Id` returns `404`. Clients must keep and resend the session id from initialization.
384
+
385
+ ### Type signatures
386
+
387
+ ```typescript
388
+ // stdio: requires Stdio service
389
+ layerStdio: (options: {
390
+ name: string;
391
+ version: string;
392
+ description?: string;
393
+ websiteUrl?: string;
394
+ icons?: ReadonlyArray<McpSchema.Icon>;
395
+ protocols: readonly [McpProtocol.ProtocolAdapter, ...Array<McpProtocol.ProtocolAdapter>];
396
+ extensions?: NonNullable<typeof McpSchema.ServerCapabilities.Type['extensions']>;
397
+ }) => Layer.Layer<McpServer.McpServer | McpSchema.McpServerClient, Cause.IllegalArgumentError, Stdio>;
398
+
399
+ // HTTP: requires HttpRouter
400
+ layerHttp: (options: {
401
+ name: string;
402
+ version: string;
403
+ path: HttpRouter.PathInput;
404
+ description?: string;
405
+ websiteUrl?: string;
406
+ icons?: ReadonlyArray<McpSchema.Icon>;
407
+ protocols: readonly [McpProtocol.ProtocolAdapter, ...Array<McpProtocol.ProtocolAdapter>];
408
+ extensions?: NonNullable<typeof McpSchema.ServerCapabilities.Type['extensions']>;
409
+ allowedOrigins?: ReadonlyArray<string>;
410
+ }) => Layer.Layer<McpServer.McpServer | McpSchema.McpServerClient, Cause.IllegalArgumentError, HttpRouter.HttpRouter>;
411
+ ```
412
+
413
+ ## Client Capabilities
414
+
415
+ Access the connecting client's capabilities from within tool/resource handlers:
416
+
417
+ ```typescript
418
+ const caps = yield* McpServer.clientCapabilities;
419
+ // caps: ClientCapabilities
420
+ ```
421
+
422
+ ## Conditional Tool/Resource/Prompt Enabling
423
+
424
+ Use `McpSchema.EnabledWhen` to conditionally list prompts, resources, resource templates, or tools based on initialized client data. The filter runs against the client initialization payload.
425
+
426
+ ```typescript
427
+ import { Context, Effect, Layer } from 'effect';
428
+ import { Schema } from 'effect';
429
+ import { McpSchema, McpServer, Tool } from 'effect/unstable/ai';
430
+
431
+ const requiresRoots = Context.make(
432
+ McpSchema.EnabledWhen,
433
+ (client) => client.capabilities.roots !== undefined
434
+ );
435
+
436
+ const ReadWorkspace = Tool.make('ReadWorkspace', {
437
+ description: 'Read workspace roots when the client supports roots',
438
+ success: Schema.String
439
+ }).annotateMerge(requiresRoots);
440
+
441
+ const WorkspacePrompt = McpServer.prompt({
442
+ name: 'Workspace Summary',
443
+ description: 'Summarize workspace roots',
444
+ annotations: requiresRoots,
445
+ content: () => Effect.succeed('Summarize the available workspace roots.')
446
+ });
447
+
448
+ const WorkspaceResource = Layer.effectDiscard(
449
+ McpServer.registerResource({
450
+ uri: 'workspace://roots',
451
+ name: 'Workspace Roots',
452
+ annotations: requiresRoots,
453
+ content: Effect.succeed('[]')
454
+ })
455
+ );
456
+ ```
457
+
458
+ ## Relationship to OpenAI MCP Tools
459
+
460
+ `McpServer` exposes a server that MCP clients connect to over stdio or HTTP. `OpenAiTool.Mcp` is different: it is an OpenAI provider-defined tool that lets an OpenAI model call a remote MCP server. When using OpenAI's hosted MCP integration, use the canonical `OpenAiMcp` custom name from `OpenAiTool.Mcp` and handle provider approval flow through normal tool approval request/response parts.
461
+
462
+ ## Complete Example — stdio Server
463
+
464
+ ```typescript
465
+ import { NodeRuntime, NodeStdio } from '@effect/platform-node';
466
+ import { Effect, Layer, Logger } from 'effect';
467
+ import { Schema } from 'effect';
468
+ import { McpProtocol, McpSchema, McpServer, Tool, Toolkit } from 'effect/unstable/ai';
469
+
470
+ // --- Tools ---
471
+ const GreetTool = Tool.make('GreetTool', {
472
+ description: 'Generate a greeting',
473
+ parameters: { name: Schema.String },
474
+ success: Schema.String
475
+ });
476
+
477
+ const MyToolkit = Toolkit.make(GreetTool);
478
+
479
+ // --- Resources ---
480
+ const ReadmeResource = McpServer.resource({
481
+ uri: 'file:///README.md',
482
+ name: 'README',
483
+ mimeType: 'text/markdown',
484
+ content: Effect.succeed('# Demo MCP Server')
485
+ });
486
+
487
+ const idParam = McpSchema.param('id', Schema.NumberFromString);
488
+
489
+ const ItemResource = McpServer.resource`file://items/${idParam}`({
490
+ name: 'Item',
491
+ completion: { id: () => Effect.succeed([1, 2, 3]) },
492
+ content: Effect.fn(function* (_uri, id) {
493
+ return JSON.stringify({ id, name: `Item ${id}` });
494
+ }),
495
+ mimeType: 'application/json'
496
+ });
497
+
498
+ // --- Prompts ---
499
+ const HelpPrompt = McpServer.prompt({
500
+ name: 'Help',
501
+ description: 'Get help on a topic',
502
+ parameters: { topic: Schema.String },
503
+ completion: { topic: () => Effect.succeed(['setup', 'usage', 'api']) },
504
+ content: ({ topic }) => Effect.succeed(`Help me understand ${topic}`)
505
+ });
506
+
507
+ // --- Server ---
508
+ const ServerLayer = Layer.mergeAll(
509
+ ReadmeResource,
510
+ ItemResource,
511
+ HelpPrompt,
512
+ McpServer.toolkit(MyToolkit).pipe(
513
+ Layer.provideMerge(
514
+ MyToolkit.toLayer({
515
+ GreetTool: ({ name }) => Effect.succeed(`Hello, ${name}!`)
516
+ })
517
+ )
518
+ )
519
+ ).pipe(
520
+ Layer.provide(
521
+ McpServer.layerStdio({
522
+ name: 'Demo MCP Server',
523
+ version: '1.0.0',
524
+ protocols: [McpProtocol.v2025_11_25]
525
+ })
526
+ ),
527
+ Layer.provide(NodeStdio.layer),
528
+ Layer.provide(Logger.layer([Logger.consolePretty({ stderr: true })]))
529
+ );
530
+
531
+ Layer.launch(ServerLayer).pipe(NodeRuntime.runMain);
532
+ ```
533
+
534
+ ## Common Patterns
535
+
536
+ ### Tool with effectful implementation
537
+
538
+ ```typescript
539
+ const FetchTool = Tool.make('FetchData', {
540
+ description: 'Fetch data from database',
541
+ parameters: { id: Schema.String },
542
+ success: Schema.String
543
+ });
544
+
545
+ const FetchToolkit = Toolkit.make(FetchTool);
546
+
547
+ // Tool handler can use services from the context
548
+ McpServer.toolkit(FetchToolkit).pipe(
549
+ Layer.provideMerge(
550
+ FetchToolkit.toLayer({
551
+ FetchData: ({ id }) =>
552
+ Effect.gen(function* () {
553
+ const db = yield* DatabaseService;
554
+ const result = yield* db.findById(id);
555
+ return JSON.stringify(result);
556
+ })
557
+ })
558
+ )
559
+ );
560
+ ```
561
+
562
+ ### Multiple toolkits
563
+
564
+ ```typescript
565
+ const ServerLayer = Layer.mergeAll(
566
+ McpServer.toolkit(ReadToolkit).pipe(
567
+ Layer.provideMerge(
568
+ ReadToolkit.toLayer({
569
+ /* ... */
570
+ })
571
+ )
572
+ ),
573
+ McpServer.toolkit(WriteToolkit).pipe(
574
+ Layer.provideMerge(
575
+ WriteToolkit.toLayer({
576
+ /* ... */
577
+ })
578
+ )
579
+ )
580
+ // resources and prompts...
581
+ );
582
+ ```
583
+
584
+ ### Resource content from Effect services
585
+
586
+ ```typescript
587
+ const ConfigResource = McpServer.resource({
588
+ uri: 'app://config',
589
+ name: 'App Config',
590
+ content: Effect.gen(function* () {
591
+ const config = yield* ConfigService;
592
+ return JSON.stringify(yield* config.getAll());
593
+ })
594
+ });
595
+ ```
596
+
597
+ ## Key Rules
598
+
599
+ 1. **Always merge part layers with `Layer.mergeAll`** — resources, prompts, and toolkit layers are independent and produce `Layer<never, never, ...>`
600
+ 2. **Provide transport last** — `Layer.provide(McpServer.layerStdio(...))` or `Layer.provide(McpServer.layerHttp(...))`
601
+ 3. **stderr for stdio** — Never log to stdout when using stdio transport
602
+ 4. **Toolkit pattern** — `McpServer.toolkit(tk).pipe(Layer.provideMerge(tk.toLayer({...})))` is the canonical pattern
603
+ 5. **Schema for parameters** — All tool parameters and resource template params use Effect Schema
604
+ 6. **`McpSchema.param`** — Use for resource URI template parameters with automatic string codec
605
+ 7. **`Effect.fn`** — Use for resource template content handlers that receive multiple arguments
606
+ 8. **Launch with `Layer.launch`** — The server runs as a long-lived layer: `Layer.launch(ServerLayer).pipe(NodeRuntime.runMain)`
607
+ 9. **Declare protocols explicitly** — Pass a non-empty `protocols` array to every `run`, `layer`, `layerStdio`, or `layerHttp`; put the fallback revision first
608
+ 10. **Gate reverse operations by negotiated capabilities** — Sampling tools and URL elicitation are `2025-11-25` features and fail before transport when unsupported