@effect-uai/mcp 0.17.0 → 0.19.0-dev-20261109183959

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 (42) hide show
  1. package/dist/Client.d.mts +4 -4
  2. package/dist/Client.mjs.map +1 -1
  3. package/dist/Toolkit.d.mts +9 -5
  4. package/dist/Toolkit.d.mts.map +1 -1
  5. package/dist/Toolkit.mjs +42 -24
  6. package/dist/Toolkit.mjs.map +1 -1
  7. package/dist/auth-CRIPfiPB.d.mts +47 -0
  8. package/dist/auth-CRIPfiPB.d.mts.map +1 -0
  9. package/dist/internal/auth.d.mts +1 -1
  10. package/dist/internal/httpTransport.d.mts +3 -3
  11. package/dist/internal/httpTransport.mjs +1 -1
  12. package/dist/internal/httpTransport.mjs.map +1 -1
  13. package/dist/internal/protocol.d.mts +1 -1
  14. package/dist/internal/protocols/2025-06-18.d.mts +1 -1
  15. package/dist/internal/protocols/2026-07-28.d.mts +1 -1
  16. package/dist/internal/protocols/2026-07-28.d.mts.map +1 -1
  17. package/dist/internal/protocols/2026-07-28.mjs +3 -2
  18. package/dist/internal/protocols/2026-07-28.mjs.map +1 -1
  19. package/dist/internal/rpc.d.mts +1 -1
  20. package/dist/internal/schema.d.mts +2 -2
  21. package/dist/internal/schema.mjs +39 -5
  22. package/dist/internal/schema.mjs.map +1 -1
  23. package/dist/internal/stdioTransport.d.mts +2 -2
  24. package/dist/internal/stdioTransport.mjs +1 -1
  25. package/dist/internal/stdioTransport.mjs.map +1 -1
  26. package/dist/{protocol-kjC3iAMb.d.mts → protocol-C3L9kaRW.d.mts} +3 -3
  27. package/dist/{protocol-kjC3iAMb.d.mts.map → protocol-C3L9kaRW.d.mts.map} +1 -1
  28. package/dist/{rpc-Bq95Lio9.d.mts → rpc-Bs2BCLha.d.mts} +2 -2
  29. package/dist/{rpc-Bq95Lio9.d.mts.map → rpc-Bs2BCLha.d.mts.map} +1 -1
  30. package/dist/{schema-D2oUNuo5.d.mts → schema-tDws3q4j.d.mts} +158 -11
  31. package/dist/schema-tDws3q4j.d.mts.map +1 -0
  32. package/package.json +7 -7
  33. package/src/Client.ts +2 -2
  34. package/src/Toolkit.test.ts +83 -0
  35. package/src/Toolkit.ts +65 -34
  36. package/src/internal/httpTransport.ts +1 -1
  37. package/src/internal/protocols/2026-07-28.ts +3 -2
  38. package/src/internal/schema.ts +44 -5
  39. package/src/internal/stdioTransport.ts +1 -1
  40. package/dist/auth-ZDvsxpCo.d.mts +0 -126
  41. package/dist/auth-ZDvsxpCo.d.mts.map +0 -1
  42. package/dist/schema-D2oUNuo5.d.mts.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { i as McpError } from "../McpError-DWg9BlW5.mjs";
2
- import { i as Transport } from "../rpc-Bq95Lio9.mjs";
2
+ import { i as Transport } from "../rpc-Bs2BCLha.mjs";
3
3
  import { Effect, Scope } from "effect";
4
- import { ChildProcessSpawner } from "effect/unstable/process";
4
+ import { ChildProcessSpawner } from "effect/process";
5
5
  //#region src/internal/stdioTransport.d.ts
6
6
  type StdioConfig = {
7
7
  readonly command: string;
@@ -1,7 +1,7 @@
1
1
  import { McpConnectFailed, McpTransportClosed } from "../McpError.mjs";
2
2
  import { Effect, Queue, Stream } from "effect";
3
3
  import * as JSONL from "@effect-uai/core/JSONL";
4
- import { ChildProcess } from "effect/unstable/process";
4
+ import { ChildProcess } from "effect/process";
5
5
  //#region src/internal/stdioTransport.ts
6
6
  /**
7
7
  * stdio `Transport`: spawn the server, frame stdout as JSONL (one JSON message
@@ -1 +1 @@
1
- {"version":3,"file":"stdioTransport.mjs","names":[],"sources":["../../src/internal/stdioTransport.ts"],"sourcesContent":["/**\n * stdio `Transport`: spawn the server, frame stdout as JSONL (one JSON message\n * per line). Scoped, so the child is killed on scope close. Era-blind; the\n * `meta.headers` a caller passes are HTTP-only and ignored here.\n */\nimport { Cause, Effect, Queue, type Scope, Stream } from \"effect\"\nimport { ChildProcess, ChildProcessSpawner } from \"effect/unstable/process\"\nimport * as JSONL from \"@effect-uai/core/JSONL\"\nimport { McpConnectFailed, type McpError, McpTransportClosed } from \"../McpError.js\"\nimport type { Transport } from \"./rpc.js\"\n\nexport type StdioConfig = {\n readonly command: string\n readonly args?: ReadonlyArray<string>\n readonly env?: Record<string, string>\n readonly cwd?: string\n}\n\nconst encoder = new TextEncoder()\n\nexport const make = (\n config: StdioConfig,\n): Effect.Effect<Transport, McpError, Scope.Scope | ChildProcessSpawner.ChildProcessSpawner> =>\n Effect.gen(function* () {\n // Outbound frames go through a queue rendered as the child's stdin stream;\n // `Queue.end` on scope close lets the child see a clean EOF.\n const outbound = yield* Queue.make<Uint8Array, Cause.Done>()\n const stdin = Stream.fromQueue(outbound)\n\n const handle = yield* ChildProcess.make(config.command, [...(config.args ?? [])], {\n ...(config.cwd !== undefined ? { cwd: config.cwd } : {}),\n ...(config.env !== undefined ? { env: config.env } : {}),\n stdin: { stream: stdin, endOnDone: true },\n stdout: \"pipe\",\n stderr: \"pipe\",\n killSignal: \"SIGTERM\",\n forceKillAfter: \"2 seconds\",\n }).pipe(\n Effect.mapError(\n (cause) =>\n new McpConnectFailed({ reason: `could not spawn ${config.command}`, raw: cause }),\n ),\n )\n yield* Effect.addFinalizer(() => Queue.end(outbound))\n\n const send = (frame: string): Effect.Effect<void, McpError> =>\n Queue.offer(outbound, encoder.encode(`${frame}\\n`)).pipe(\n Effect.mapError(\n (cause) =>\n new McpTransportClosed({ reason: \"server stdin closed\", raw: cause }) as McpError,\n ),\n Effect.asVoid,\n )\n\n const messages = handle.stdout.pipe(\n JSONL.fromBytes,\n Stream.mapError(\n (cause) => new McpTransportClosed({ reason: \"server stdout closed\", raw: cause }),\n ),\n )\n\n return { send, messages } satisfies Transport\n })\n"],"mappings":";;;;;;;;;;AAkBA,MAAM,UAAU,IAAI,YAAY;AAEhC,MAAa,QACX,WAEA,OAAO,IAAI,aAAa;CAGtB,MAAM,WAAW,OAAO,MAAM,KAA6B;CAC3D,MAAM,QAAQ,OAAO,UAAU,QAAQ;CAEvC,MAAM,SAAS,OAAO,aAAa,KAAK,OAAO,SAAS,CAAC,GAAI,OAAO,QAAQ,CAAC,CAAE,GAAG;EAChF,GAAI,OAAO,QAAQ,KAAA,IAAY,EAAE,KAAK,OAAO,IAAI,IAAI,CAAC;EACtD,GAAI,OAAO,QAAQ,KAAA,IAAY,EAAE,KAAK,OAAO,IAAI,IAAI,CAAC;EACtD,OAAO;GAAE,QAAQ;GAAO,WAAW;EAAK;EACxC,QAAQ;EACR,QAAQ;EACR,YAAY;EACZ,gBAAgB;CAClB,CAAC,CAAC,CAAC,KACD,OAAO,UACJ,UACC,IAAI,iBAAiB;EAAE,QAAQ,mBAAmB,OAAO;EAAW,KAAK;CAAM,CAAC,CACpF,CACF;CACA,OAAO,OAAO,mBAAmB,MAAM,IAAI,QAAQ,CAAC;CAEpD,MAAM,QAAQ,UACZ,MAAM,MAAM,UAAU,QAAQ,OAAO,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,KAClD,OAAO,UACJ,UACC,IAAI,mBAAmB;EAAE,QAAQ;EAAuB,KAAK;CAAM,CAAC,CACxE,GACA,OAAO,MACT;CASF,OAAO;EAAE;EAAM,UAPE,OAAO,OAAO,KAC7B,MAAM,WACN,OAAO,UACJ,UAAU,IAAI,mBAAmB;GAAE,QAAQ;GAAwB,KAAK;EAAM,CAAC,CAClF,CAGoB;CAAE;AAC1B,CAAC"}
1
+ {"version":3,"file":"stdioTransport.mjs","names":[],"sources":["../../src/internal/stdioTransport.ts"],"sourcesContent":["/**\n * stdio `Transport`: spawn the server, frame stdout as JSONL (one JSON message\n * per line). Scoped, so the child is killed on scope close. Era-blind; the\n * `meta.headers` a caller passes are HTTP-only and ignored here.\n */\nimport { Cause, Effect, Queue, type Scope, Stream } from \"effect\"\nimport { ChildProcess, ChildProcessSpawner } from \"effect/process\"\nimport * as JSONL from \"@effect-uai/core/JSONL\"\nimport { McpConnectFailed, type McpError, McpTransportClosed } from \"../McpError.js\"\nimport type { Transport } from \"./rpc.js\"\n\nexport type StdioConfig = {\n readonly command: string\n readonly args?: ReadonlyArray<string>\n readonly env?: Record<string, string>\n readonly cwd?: string\n}\n\nconst encoder = new TextEncoder()\n\nexport const make = (\n config: StdioConfig,\n): Effect.Effect<Transport, McpError, Scope.Scope | ChildProcessSpawner.ChildProcessSpawner> =>\n Effect.gen(function* () {\n // Outbound frames go through a queue rendered as the child's stdin stream;\n // `Queue.end` on scope close lets the child see a clean EOF.\n const outbound = yield* Queue.make<Uint8Array, Cause.Done>()\n const stdin = Stream.fromQueue(outbound)\n\n const handle = yield* ChildProcess.make(config.command, [...(config.args ?? [])], {\n ...(config.cwd !== undefined ? { cwd: config.cwd } : {}),\n ...(config.env !== undefined ? { env: config.env } : {}),\n stdin: { stream: stdin, endOnDone: true },\n stdout: \"pipe\",\n stderr: \"pipe\",\n killSignal: \"SIGTERM\",\n forceKillAfter: \"2 seconds\",\n }).pipe(\n Effect.mapError(\n (cause) =>\n new McpConnectFailed({ reason: `could not spawn ${config.command}`, raw: cause }),\n ),\n )\n yield* Effect.addFinalizer(() => Queue.end(outbound))\n\n const send = (frame: string): Effect.Effect<void, McpError> =>\n Queue.offer(outbound, encoder.encode(`${frame}\\n`)).pipe(\n Effect.mapError(\n (cause) =>\n new McpTransportClosed({ reason: \"server stdin closed\", raw: cause }) as McpError,\n ),\n Effect.asVoid,\n )\n\n const messages = handle.stdout.pipe(\n JSONL.fromBytes,\n Stream.mapError(\n (cause) => new McpTransportClosed({ reason: \"server stdout closed\", raw: cause }),\n ),\n )\n\n return { send, messages } satisfies Transport\n })\n"],"mappings":";;;;;;;;;;AAkBA,MAAM,UAAU,IAAI,YAAY;AAEhC,MAAa,QACX,WAEA,OAAO,IAAI,aAAa;CAGtB,MAAM,WAAW,OAAO,MAAM,KAA6B;CAC3D,MAAM,QAAQ,OAAO,UAAU,QAAQ;CAEvC,MAAM,SAAS,OAAO,aAAa,KAAK,OAAO,SAAS,CAAC,GAAI,OAAO,QAAQ,CAAC,CAAE,GAAG;EAChF,GAAI,OAAO,QAAQ,KAAA,IAAY,EAAE,KAAK,OAAO,IAAI,IAAI,CAAC;EACtD,GAAI,OAAO,QAAQ,KAAA,IAAY,EAAE,KAAK,OAAO,IAAI,IAAI,CAAC;EACtD,OAAO;GAAE,QAAQ;GAAO,WAAW;EAAK;EACxC,QAAQ;EACR,QAAQ;EACR,YAAY;EACZ,gBAAgB;CAClB,CAAC,CAAC,CAAC,KACD,OAAO,UACJ,UACC,IAAI,iBAAiB;EAAE,QAAQ,mBAAmB,OAAO;EAAW,KAAK;CAAM,CAAC,CACpF,CACF;CACA,OAAO,OAAO,mBAAmB,MAAM,IAAI,QAAQ,CAAC;CAEpD,MAAM,QAAQ,UACZ,MAAM,MAAM,UAAU,QAAQ,OAAO,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,KAClD,OAAO,UACJ,UACC,IAAI,mBAAmB;EAAE,QAAQ;EAAuB,KAAK;CAAM,CAAC,CACxE,GACA,OAAO,MACT;CASF,OAAO;EAAE;EAAM,UAPE,OAAO,OAAO,KAC7B,MAAM,WACN,OAAO,UACJ,UAAU,IAAI,mBAAmB;GAAE,QAAQ;GAAwB,KAAK;EAAM,CAAC,CAClF,CAGoB;CAAE;AAC1B,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { i as McpError } from "./McpError-DWg9BlW5.mjs";
2
- import { C as ProtocolVersion, T as ServerInfo, x as McpMethod } from "./schema-D2oUNuo5.mjs";
3
- import { n as McpConnection, r as SendMeta, t as Inbound } from "./rpc-Bq95Lio9.mjs";
2
+ import { A as ServerInfo, D as ProtocolVersion, T as McpMethod } from "./schema-tDws3q4j.mjs";
3
+ import { n as McpConnection, r as SendMeta, t as Inbound } from "./rpc-Bs2BCLha.mjs";
4
4
  import { Effect, Option, Scope } from "effect";
5
5
  //#region src/internal/protocol.d.ts
6
6
  type Protocol = {
@@ -32,4 +32,4 @@ type ModernRejection = {
32
32
  declare const modernRejection: (error: McpError) => Option.Option<ModernRejection>;
33
33
  //#endregion
34
34
  export { modernRejection as i, Protocol as n, ProtocolProbe as r, ModernRejection as t };
35
- //# sourceMappingURL=protocol-kjC3iAMb.d.mts.map
35
+ //# sourceMappingURL=protocol-C3L9kaRW.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"protocol-kjC3iAMb.d.mts","names":[],"sources":["../src/internal/protocol.ts"],"mappings":";;;;;KAqBY;WACD,SAAS;WACT,YAAY;;WAEZ,WAAW,QAAQ,WAAW;;WAE9B,OAAO,QAAQ,WAAW,oBAAoB;;WAE9C,YAAY,SAAS,YAAY,OAAO;;;;;;KAOvC,iBACV,YAAY,kBACT,OAAO,OAAO,OAAO,OAAO,WAAW,UAAU,MAAM;;;;;;;;KAWhD;WACD;WACA,WAAW;;cAGT,kBAAe,OAAW,aAAW,OAAO,OAAO"}
1
+ {"version":3,"file":"protocol-C3L9kaRW.d.mts","names":[],"sources":["../src/internal/protocol.ts"],"mappings":";;;;;KAqBY;WACD,SAAS;WACT,YAAY;;WAEZ,WAAW,QAAQ,WAAW;;WAE9B,OAAO,QAAQ,WAAW,oBAAoB;;WAE9C,YAAY,SAAS,YAAY,OAAO;;;;;;KAOvC,iBACV,YAAY,kBACT,OAAO,OAAO,OAAO,OAAO,WAAW,UAAU,MAAM;;;;;;;;KAWhD;WACD;WACA,WAAW;;cAGT,kBAAe,OAAW,aAAW,OAAO,OAAO"}
@@ -1,5 +1,5 @@
1
1
  import { i as McpError } from "./McpError-DWg9BlW5.mjs";
2
- import { f as JsonRpcId } from "./schema-D2oUNuo5.mjs";
2
+ import { h as JsonRpcId } from "./schema-tDws3q4j.mjs";
3
3
  import { Effect, Option, Scope, Stream } from "effect";
4
4
  //#region src/internal/rpc.d.ts
5
5
  /** Per-request transport hints (era headers on HTTP; stdio ignores them). */
@@ -31,4 +31,4 @@ type McpConnection = {
31
31
  declare const open: (transport: Transport, onInbound: (inbound: Inbound) => Effect.Effect<void>) => Effect.Effect<McpConnection, never, Scope.Scope>;
32
32
  //#endregion
33
33
  export { open as a, Transport as i, McpConnection as n, SendMeta as r, Inbound as t };
34
- //# sourceMappingURL=rpc-Bq95Lio9.d.mts.map
34
+ //# sourceMappingURL=rpc-Bs2BCLha.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rpc-Bq95Lio9.d.mts","names":[],"sources":["../src/internal/rpc.ts"],"mappings":";;;;;KAmBY;WACD,UAAU;;;KAIT;WACD,OAAO,eAAe,OAAO,aAAa,OAAO,aAAa;WAC9D,UAAU,OAAO,eAAe;;;KAI/B;WACD,IAAI,OAAO,OAAO;WAClB;WACA;;KAGC;WACD,UACP,gBACA,kBACA,OAAO,aACJ,OAAO,gBAAgB;WACnB,SACP,gBACA,kBACA,OAAO,aACJ,OAAO,aAAa;;WAEhB,UAAU,IAAI,WAAW,oBAAoB,OAAO,aAAa;WACjE,eACP,IAAI,WACJ,cACA,oBACG,OAAO,aAAa;;;;;;cAoBd,OAAI,WACJ,WAAS,YACR,SAAS,YAAY,OAAO,iBACvC,OAAO,OAAO,sBAAsB,MAAM"}
1
+ {"version":3,"file":"rpc-Bs2BCLha.d.mts","names":[],"sources":["../src/internal/rpc.ts"],"mappings":";;;;;KAmBY;WACD,UAAU;;;KAIT;WACD,OAAO,eAAe,OAAO,aAAa,OAAO,aAAa;WAC9D,UAAU,OAAO,eAAe;;;KAI/B;WACD,IAAI,OAAO,OAAO;WAClB;WACA;;KAGC;WACD,UACP,gBACA,kBACA,OAAO,aACJ,OAAO,gBAAgB;WACnB,SACP,gBACA,kBACA,OAAO,aACJ,OAAO,aAAa;;WAEhB,UAAU,IAAI,WAAW,oBAAoB,OAAO,aAAa;WACjE,eACP,IAAI,WACJ,cACA,oBACG,OAAO,aAAa;;;;;;cAoBd,OAAI,WACJ,WAAS,YACR,SAAS,YAAY,OAAO,iBACvC,OAAO,OAAO,sBAAsB,MAAM"}
@@ -114,30 +114,154 @@ declare const TextContent: Schema.Struct<{
114
114
  readonly text: Schema.String;
115
115
  }>;
116
116
  type TextContent = typeof TextContent.Type;
117
- /** Non-text blocks (image / audio / resource) are placeholder-summarized in v1. */
117
+ declare const ImageContent: Schema.Struct<{
118
+ readonly type: Schema.Literal<"image">;
119
+ readonly data: Schema.String;
120
+ readonly mimeType: Schema.String;
121
+ }>;
122
+ declare const AudioContent: Schema.Struct<{
123
+ readonly type: Schema.Literal<"audio">;
124
+ readonly data: Schema.String;
125
+ readonly mimeType: Schema.String;
126
+ }>;
127
+ declare const ResourceLink: Schema.Struct<{
128
+ readonly type: Schema.Literal<"resource_link">;
129
+ readonly uri: Schema.String;
130
+ readonly name: Schema.optional<Schema.String>;
131
+ readonly mimeType: Schema.optional<Schema.String>;
132
+ }>;
133
+ declare const EmbeddedResource: Schema.Struct<{
134
+ readonly type: Schema.Literal<"resource">;
135
+ readonly resource: Schema.Union<readonly [Schema.Struct<{
136
+ readonly uri: Schema.String;
137
+ readonly text: Schema.String;
138
+ }>, Schema.Struct<{
139
+ readonly uri: Schema.String;
140
+ readonly blob: Schema.String;
141
+ readonly mimeType: Schema.optional<Schema.String>;
142
+ }>]>;
143
+ }>;
144
+ /** A block type this client does not know; summarized for the model. */
118
145
  declare const OtherContent: Schema.Struct<{
119
146
  readonly type: Schema.String;
120
147
  }>;
121
- declare const ContentBlock: Schema.Union<readonly [Schema.Struct<{
148
+ declare const KnownContent: Schema.Union<readonly [Schema.Struct<{
122
149
  readonly type: Schema.Literal<"text">;
123
150
  readonly text: Schema.String;
124
151
  }>, Schema.Struct<{
125
- readonly type: Schema.String;
152
+ readonly type: Schema.Literal<"image">;
153
+ readonly data: Schema.String;
154
+ readonly mimeType: Schema.String;
155
+ }>, Schema.Struct<{
156
+ readonly type: Schema.Literal<"audio">;
157
+ readonly data: Schema.String;
158
+ readonly mimeType: Schema.String;
159
+ }>, Schema.Struct<{
160
+ readonly type: Schema.Literal<"resource_link">;
161
+ readonly uri: Schema.String;
162
+ readonly name: Schema.optional<Schema.String>;
163
+ readonly mimeType: Schema.optional<Schema.String>;
164
+ }>, Schema.Struct<{
165
+ readonly type: Schema.Literal<"resource">;
166
+ readonly resource: Schema.Union<readonly [Schema.Struct<{
167
+ readonly uri: Schema.String;
168
+ readonly text: Schema.String;
169
+ }>, Schema.Struct<{
170
+ readonly uri: Schema.String;
171
+ readonly blob: Schema.String;
172
+ readonly mimeType: Schema.optional<Schema.String>;
173
+ }>]>;
126
174
  }>]>;
127
- type ContentBlock = typeof ContentBlock.Type;
128
- declare const isTextContent: <I>(input: I) => input is I & {
175
+ type KnownContent = typeof KnownContent.Type;
176
+ declare const isKnownContent: <I>(input: I) => input is I & ({
129
177
  readonly type: "text";
130
178
  readonly text: string;
131
- };
179
+ } | {
180
+ readonly type: "image";
181
+ readonly data: string;
182
+ readonly mimeType: string;
183
+ } | {
184
+ readonly type: "audio";
185
+ readonly data: string;
186
+ readonly mimeType: string;
187
+ } | {
188
+ readonly type: "resource_link";
189
+ readonly uri: string;
190
+ readonly name?: string | undefined;
191
+ readonly mimeType?: string | undefined;
192
+ } | {
193
+ readonly type: "resource";
194
+ readonly resource: {
195
+ readonly uri: string;
196
+ readonly text: string;
197
+ } | {
198
+ readonly uri: string;
199
+ readonly blob: string;
200
+ readonly mimeType?: string | undefined;
201
+ };
202
+ });
203
+ declare const ContentBlock: Schema.Union<readonly [Schema.Union<readonly [Schema.Struct<{
204
+ readonly type: Schema.Literal<"text">;
205
+ readonly text: Schema.String;
206
+ }>, Schema.Struct<{
207
+ readonly type: Schema.Literal<"image">;
208
+ readonly data: Schema.String;
209
+ readonly mimeType: Schema.String;
210
+ }>, Schema.Struct<{
211
+ readonly type: Schema.Literal<"audio">;
212
+ readonly data: Schema.String;
213
+ readonly mimeType: Schema.String;
214
+ }>, Schema.Struct<{
215
+ readonly type: Schema.Literal<"resource_link">;
216
+ readonly uri: Schema.String;
217
+ readonly name: Schema.optional<Schema.String>;
218
+ readonly mimeType: Schema.optional<Schema.String>;
219
+ }>, Schema.Struct<{
220
+ readonly type: Schema.Literal<"resource">;
221
+ readonly resource: Schema.Union<readonly [Schema.Struct<{
222
+ readonly uri: Schema.String;
223
+ readonly text: Schema.String;
224
+ }>, Schema.Struct<{
225
+ readonly uri: Schema.String;
226
+ readonly blob: Schema.String;
227
+ readonly mimeType: Schema.optional<Schema.String>;
228
+ }>]>;
229
+ }>]>, Schema.Struct<{
230
+ readonly type: Schema.String;
231
+ }>]>;
232
+ type ContentBlock = typeof ContentBlock.Type;
132
233
  declare const CallToolResult: Schema.Struct<{
133
- readonly content: Schema.optionalKey<Schema.$Array<Schema.Union<readonly [Schema.Struct<{
234
+ readonly content: Schema.optionalKey<Schema.$Array<Schema.Union<readonly [Schema.Union<readonly [Schema.Struct<{
134
235
  readonly type: Schema.Literal<"text">;
135
236
  readonly text: Schema.String;
136
237
  }>, Schema.Struct<{
238
+ readonly type: Schema.Literal<"image">;
239
+ readonly data: Schema.String;
240
+ readonly mimeType: Schema.String;
241
+ }>, Schema.Struct<{
242
+ readonly type: Schema.Literal<"audio">;
243
+ readonly data: Schema.String;
244
+ readonly mimeType: Schema.String;
245
+ }>, Schema.Struct<{
246
+ readonly type: Schema.Literal<"resource_link">;
247
+ readonly uri: Schema.String;
248
+ readonly name: Schema.optional<Schema.String>;
249
+ readonly mimeType: Schema.optional<Schema.String>;
250
+ }>, Schema.Struct<{
251
+ readonly type: Schema.Literal<"resource">;
252
+ readonly resource: Schema.Union<readonly [Schema.Struct<{
253
+ readonly uri: Schema.String;
254
+ readonly text: Schema.String;
255
+ }>, Schema.Struct<{
256
+ readonly uri: Schema.String;
257
+ readonly blob: Schema.String;
258
+ readonly mimeType: Schema.optional<Schema.String>;
259
+ }>]>;
260
+ }>]>, Schema.Struct<{
137
261
  readonly type: Schema.String;
138
262
  }>]>>>;
139
263
  readonly isError: Schema.optional<Schema.Boolean>;
140
- readonly structuredContent: Schema.optional<Schema.Unknown>;
264
+ readonly structuredContent: Schema.optional<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
141
265
  readonly resultType: Schema.optional<Schema.String>;
142
266
  }>;
143
267
  type CallToolResult = typeof CallToolResult.Type;
@@ -145,11 +269,34 @@ declare const decodeCallToolResult: (input: unknown, options?: import("effect/Sc
145
269
  readonly content?: readonly ({
146
270
  readonly type: "text";
147
271
  readonly text: string;
272
+ } | {
273
+ readonly type: "image";
274
+ readonly data: string;
275
+ readonly mimeType: string;
276
+ } | {
277
+ readonly type: "audio";
278
+ readonly data: string;
279
+ readonly mimeType: string;
280
+ } | {
281
+ readonly type: "resource_link";
282
+ readonly uri: string;
283
+ readonly name?: string | undefined;
284
+ readonly mimeType?: string | undefined;
285
+ } | {
286
+ readonly type: "resource";
287
+ readonly resource: {
288
+ readonly uri: string;
289
+ readonly text: string;
290
+ } | {
291
+ readonly uri: string;
292
+ readonly blob: string;
293
+ readonly mimeType?: string | undefined;
294
+ };
148
295
  } | {
149
296
  readonly type: string;
150
297
  })[];
151
298
  readonly isError?: boolean | undefined;
152
- readonly structuredContent?: unknown;
299
+ readonly structuredContent?: Schema.Json | undefined;
153
300
  readonly resultType?: string | undefined;
154
301
  }, Schema.SchemaError, never>;
155
302
  declare const ServerInfo: Schema.Struct<{
@@ -241,5 +388,5 @@ declare const decodeInitializeResult: (input: unknown, options?: import("effect/
241
388
  readonly instructions?: string | undefined;
242
389
  }, Schema.SchemaError, never>;
243
390
  //#endregion
244
- export { decodeCallToolResult as A, resultFrame as B, ProtocolVersion as C, ToolInfo as D, TextContent as E, decodeUnsupportedVersionData as F, errorFrame as I, isTextContent as L, decodeInboundMessage as M, decodeInitializeResult as N, UnsupportedVersionData as O, decodeListToolsResult as P, notificationFrame as R, OtherContent as S, ServerInfo as T, META_CLIENT_INFO as _, CODE_UNSUPPORTED_PROTOCOL_VERSION as a, MODERN_ERROR_CODES as b, DiscoverResult as c, JsonRpcErrorObject as d, JsonRpcId as f, META_CLIENT_CAPABILITIES as g, ListToolsResult as h, CODE_MISSING_CLIENT_CAPABILITY as i, decodeDiscoverResult as j, asProtocolVersion as k, InboundMessage as l, LEGACY_VERSION as m, CODE_HEADER_MISMATCH as n, CallToolResult as o, LATEST_VERSION as p, CODE_METHOD_NOT_FOUND as r, ContentBlock as s, CLIENT_INFO as t, InitializeResult as u, META_PROTOCOL_VERSION as v, ResultMeta as w, McpMethod as x, META_SERVER_INFO as y, requestFrame as z };
245
- //# sourceMappingURL=schema-D2oUNuo5.d.mts.map
391
+ export { ServerInfo as A, decodeUnsupportedVersionData as B, META_SERVER_INFO as C, ProtocolVersion as D, OtherContent as E, decodeCallToolResult as F, resultFrame as G, isKnownContent as H, decodeDiscoverResult as I, decodeInboundMessage as L, ToolInfo as M, UnsupportedVersionData as N, ResourceLink as O, asProtocolVersion as P, decodeInitializeResult as R, META_PROTOCOL_VERSION as S, McpMethod as T, notificationFrame as U, errorFrame as V, requestFrame as W, LATEST_VERSION as _, CODE_MISSING_CLIENT_CAPABILITY as a, META_CLIENT_CAPABILITIES as b, ContentBlock as c, ImageContent as d, InboundMessage as f, KnownContent as g, JsonRpcId as h, CODE_METHOD_NOT_FOUND as i, TextContent as j, ResultMeta as k, DiscoverResult as l, JsonRpcErrorObject as m, CLIENT_INFO as n, CODE_UNSUPPORTED_PROTOCOL_VERSION as o, InitializeResult as p, CODE_HEADER_MISMATCH as r, CallToolResult as s, AudioContent as t, EmbeddedResource as u, LEGACY_VERSION as v, MODERN_ERROR_CODES as w, META_CLIENT_INFO as x, ListToolsResult as y, decodeListToolsResult as z };
392
+ //# sourceMappingURL=schema-tDws3q4j.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-tDws3q4j.d.mts","names":[],"sources":["../src/internal/schema.ts"],"mappings":";;;cAaa,iBAAe,OAAA;KAChB,yBAAyB,gBAAgB;;cAGxC;;cAGA;;cAKA,oBAAiB,gBAAkB,OAAO,OAAO;;;;;;cAQjD,WAAS,OAAA;KAMV,mBAAmB,UAAU;;cAG5B;cACA;cACA;;cAEA;cAEA;EAAgB;EAAyB;;;cAGzC;cACA;cACA;cACA;;;;;cAMA,oBAAoB;cAUpB,WAAS,OAAA,gBAAA,OAAA,QAAA,OAAA;KACV,mBAAmB,UAAU;cAE5B,oBAAkB,OAAA;;;;;KAKnB,4BAA4B,mBAAmB;;;;;;cAO9C,gBAAc,OAAA;;;;;;;;;;;KASf,wBAAwB,eAAe;cAEtC,uBAAoB,gBAAA,qCAAA,yCAAA;;;;;;;;;;GAA6C,OAAA;cAIjE,eAAY,IAAQ,WAAS,gBAAgB;cAG7C,oBAAiB,gBAAkB;cAKnC,cAAW,IAAQ,WAAS;cAG5B,aAAU,IAAQ,WAAS,cAAc;cAOzC,UAAQ,OAAA;;;;;KAKT,kBAAkB,SAAS;;;;;cAM1B,iBAAe,OAAA;;;;;;;;;;KAMhB,yBAAyB,gBAAgB;cAExC,wBAAqB,gBAAA,qCAAA,yCAAA;;;;;;;;;;;GAA8C,OAAA;cAInE,aAAW,OAAA;;;;KAIZ,qBAAqB,YAAY;cAEhC,cAAY,OAAA;;;;;cAMZ,cAAY,OAAA;;;;;cAMZ,cAAY,OAAA;;;;;;cAOZ,kBAAgB,OAAA;;;;;;;;;;;;cAahB,cAAY,OAAA;iBAAZ,OAAA;;cAIA,cAAY,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;KAOb,sBAAsB,aAAa;cAClC,iBAAc,GAAA,OAAA,MAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;cAEd,cAAY,OAAA,gBAAA,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KACb,sBAAsB,aAAa;cAElC,gBAAc,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAOf,wBAAwB,eAAe;cAEtC,uBAAoB,gBAAA,qCAAA,yCAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAApB,OAAA;;GAAiE,OAAA;cAMjE,YAAU,OAAA;;;;;KAKX,oBAAoB,WAAW;;cAG9B,YAAU,OAAA;;;;;;;KAGX,oBAAoB,WAAW;cAE9B,gBAAc,OAAA;;;;;;;;;;;;;;;;;KASf,wBAAwB,eAAe;cAEtC,uBAAoB,gBAAA,qCAAA,yCAAA;;;;;;;;;;;;;;;;;;GAA6C,OAAA;;;;;cAMjE,wBAAsB,OAAA;;;;KAIvB,gCAAgC,uBAAuB;cAEtD,+BAA4B,gBAAA,qCAAA,iBAAA,OAAA;;;;cAM5B,kBAAgB,OAAA;;;;;;;;;;KAMjB,0BAA0B,iBAAiB;cAE1C,yBAAsB,gBAAA,qCAAA,yCAAA;;;;;;;;;;;GAA+C,OAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-uai/mcp",
3
- "version": "0.17.0",
3
+ "version": "0.19.0-dev-20261109183959",
4
4
  "description": "Model Context Protocol client provider for @effect-uai/core.",
5
5
  "keywords": [
6
6
  "agents",
@@ -53,15 +53,15 @@
53
53
  },
54
54
  "dependencies": {},
55
55
  "devDependencies": {
56
- "@effect/platform-node": "4.0.0-rc.111",
57
- "@effect/vitest": "4.0.0-rc.111",
58
- "effect": "4.0.0-rc.111",
56
+ "@effect/platform-node": "4.1.2",
57
+ "@effect/vitest": "4.1.2",
58
+ "effect": "4.1.2",
59
59
  "typescript": "^7.0.2",
60
- "@effect-uai/core": "0.17.0"
60
+ "@effect-uai/core": "0.19.0-dev-20261109183959"
61
61
  },
62
62
  "peerDependencies": {
63
- "@effect-uai/core": ">=0.5.0 <1",
64
- "effect": ">=4.0.0-rc.111 <5.0.0"
63
+ "@effect-uai/core": "0.19.0-dev-20261109183959",
64
+ "effect": ">=4.0.1 <5.0.0"
65
65
  },
66
66
  "scripts": {
67
67
  "build": "tsdown",
package/src/Client.ts CHANGED
@@ -4,8 +4,8 @@
4
4
  * and fixed for the connection's lifetime. The client surface is era-uniform.
5
5
  */
6
6
  import { Context, Effect, Layer, Option, Ref, type Scope } from "effect"
7
- import type { HttpClient } from "effect/unstable/http"
8
- import type { ChildProcessSpawner } from "effect/unstable/process"
7
+ import type { HttpClient } from "effect/http"
8
+ import type { ChildProcessSpawner } from "effect/process"
9
9
  import { McpConnectFailed, type McpError, McpProtocolError } from "./McpError.js"
10
10
  import { Auth, type TokenSource } from "./internal/auth.js"
11
11
  import * as Handshake from "./internal/protocols/2025-06-18.js"
@@ -0,0 +1,83 @@
1
+ import { describe, expect, it } from "@effect/vitest"
2
+ import type { ToolCall } from "@effect-uai/core/Items"
3
+ import * as CoreToolkit from "@effect-uai/core/Toolkit"
4
+ import { Effect, Stream } from "effect"
5
+ import type { McpClient } from "./Client.js"
6
+ import { decodeCallToolResult } from "./internal/schema.js"
7
+ import { mcpToolkit } from "./Toolkit.js"
8
+
9
+ // A client whose `tools/call` results are raw wire JSON, decoded like the real one.
10
+ const clientReturning = (raw: unknown): McpClient => ({
11
+ serverInfo: { name: "fake", protocolVersion: "2026-07-28" },
12
+ listTools: Effect.succeed([{ name: "shot", inputSchema: { type: "object" } }]),
13
+ callTool: () => Effect.orDie(decodeCallToolResult(raw)),
14
+ })
15
+
16
+ const call: ToolCall = { type: "function_call", call_id: "c1", name: "shot", arguments: "{}" }
17
+
18
+ const historyOf = (raw: unknown) =>
19
+ Effect.gen(function* () {
20
+ const kit = yield* mcpToolkit(clientReturning(raw))
21
+ return yield* CoreToolkit.run(kit, [call]).pipe(
22
+ Stream.filter(CoreToolkit.isOutcome),
23
+ Stream.mapEffect(CoreToolkit.output(kit)),
24
+ Stream.runCollect,
25
+ )
26
+ })
27
+
28
+ describe("mcpToolkit", () => {
29
+ it.effect("keeps images, audio and resources as media parts instead of dropping them", () =>
30
+ Effect.gen(function* () {
31
+ const [item] = yield* historyOf({
32
+ content: [
33
+ { type: "text", text: "Home page" },
34
+ { type: "image", data: "AQID", mimeType: "image/png" },
35
+ { type: "audio", data: "AAAA", mimeType: "audio/wav" },
36
+ { type: "resource_link", uri: "https://x/a.pdf", name: "a.pdf" },
37
+ { type: "resource", resource: { uri: "mem://n", text: "a note" } },
38
+ { type: "hologram" },
39
+ ],
40
+ })
41
+ expect(item?.output).toEqual([
42
+ { type: "input_text", text: "Home page" },
43
+ {
44
+ type: "input_image",
45
+ source: { _tag: "base64", base64: "AQID", mimeType: "image/png" },
46
+ },
47
+ { type: "input_file", source: { _tag: "base64", base64: "AAAA", mimeType: "audio/wav" } },
48
+ { type: "input_file", source: { _tag: "url", url: "https://x/a.pdf" }, filename: "a.pdf" },
49
+ { type: "input_text", text: "a note" },
50
+ { type: "input_text", text: "[hologram content omitted]" },
51
+ ])
52
+ }),
53
+ )
54
+
55
+ it.effect("prefers structuredContent over its repeated text, keeping media", () =>
56
+ Effect.gen(function* () {
57
+ const [item] = yield* historyOf({
58
+ content: [
59
+ { type: "text", text: '{"hits":2}' },
60
+ { type: "image", data: "AQID", mimeType: "image/png" },
61
+ ],
62
+ structuredContent: { hits: 2 },
63
+ })
64
+ expect(item?.output.map((p) => (p.type === "input_json" ? p.value : p.type))).toEqual([
65
+ { hits: 2 },
66
+ "input_image",
67
+ ])
68
+ }),
69
+ )
70
+
71
+ it.effect("turns isError into the declared McpToolFailed the model reads", () =>
72
+ Effect.gen(function* () {
73
+ const [item] = yield* historyOf({
74
+ content: [{ type: "text", text: "rate limited" }],
75
+ isError: true,
76
+ })
77
+ expect(item).toMatchObject({
78
+ isError: true,
79
+ output: [{ type: "input_json", value: { _tag: "McpToolFailed", message: "rate limited" } }],
80
+ })
81
+ }),
82
+ )
83
+ })
package/src/Toolkit.ts CHANGED
@@ -1,17 +1,22 @@
1
1
  /**
2
- * Turn a connected MCP server into a core `Toolkit`, one `LocalTool` per MCP
2
+ * Turn a connected MCP server into a core `Toolkit`, one local tool per MCP
3
3
  * tool. Effectful because the tool list is fetched; composes with
4
4
  * `Toolkit.compose` like any other kit.
5
5
  */
6
- import { Effect, Option } from "effect"
6
+ import { Effect, Match, Option, Schema } from "effect"
7
+ import * as Content from "@effect-uai/core/Content"
8
+ import { fileBase64, fileUrl } from "@effect-uai/core/File"
9
+ import { imageBase64 } from "@effect-uai/core/Image"
7
10
  import * as Tool from "@effect-uai/core/Tool"
8
11
  import * as CoreToolkit from "@effect-uai/core/Toolkit"
12
+ import * as ToolOutput from "@effect-uai/core/ToolOutput"
9
13
  import type { McpClient } from "./Client.js"
10
14
  import { type McpError, McpProtocolError } from "./McpError.js"
11
15
  import {
12
16
  type CallToolResult,
13
17
  type ContentBlock,
14
- isTextContent,
18
+ isKnownContent,
19
+ type KnownContent,
15
20
  type ToolInfo,
16
21
  } from "./internal/schema.js"
17
22
 
@@ -20,6 +25,11 @@ export type McpToolkitOptions = {
20
25
  readonly prefix?: string
21
26
  }
22
27
 
28
+ /** The server reported an error (`isError`); the model sees its content as `message`. */
29
+ export class McpToolFailed extends Schema.TaggedError<McpToolFailed>(
30
+ "@betalyra/effect-uai/mcp/McpToolFailed",
31
+ )("McpToolFailed", { message: Schema.String }) {}
32
+
23
33
  /**
24
34
  * The server owns validation, so the input schema is a passthrough: it carries
25
35
  * the server's JSON Schema verbatim (the model sees the real thing) and
@@ -38,59 +48,80 @@ const passthroughSchema = (tool: ToolInfo): Tool.ToolInputSchema<unknown> => ({
38
48
  },
39
49
  })
40
50
 
41
- const textOf = (block: ContentBlock): string =>
42
- isTextContent(block) ? block.text : `[${block.type} content omitted]`
51
+ // A blob without a MIME type is opaque bytes.
52
+ const knownValue = Match.type<KnownContent>().pipe(
53
+ Match.discriminatorsExhaustive("type")({
54
+ text: (b): string | Content.Media => b.text,
55
+ image: (b) => Content.image(imageBase64(b.data, b.mimeType)),
56
+ audio: (b) => Content.file(fileBase64(b.data, b.mimeType)),
57
+ resource_link: (b) => Content.file(fileUrl(b.uri, b.mimeType), b.name),
58
+ resource: ({ resource }) =>
59
+ "text" in resource
60
+ ? resource.text
61
+ : Content.file(fileBase64(resource.blob, resource.mimeType ?? "application/octet-stream")),
62
+ }),
63
+ )
64
+
65
+ const valueOf = (block: ContentBlock): string | Content.Media =>
66
+ isKnownContent(block) ? knownValue(block) : `[${block.type} content omitted]`
43
67
 
44
68
  /**
45
- * Serialize a successful result for the model: `structuredContent` when the
46
- * server provides it, otherwise the joined text blocks. Non-text blocks become
47
- * a short placeholder until the loop grows a multimodal tool-output path.
69
+ * A result as the tool's value: `structuredContent` when the server provides it
70
+ * (its text blocks usually repeat it), plus any media; otherwise every block.
48
71
  */
49
- const outputOf = (result: CallToolResult): unknown =>
50
- result.structuredContent ?? (result.content ?? []).map(textOf).join("\n")
72
+ const outputOf = (result: CallToolResult): Content.Value => {
73
+ const values: ReadonlyArray<Schema.Json | Content.Media> = (result.content ?? []).map(valueOf)
74
+ return Option.match(Option.fromNullishOr(result.structuredContent), {
75
+ onNone: () => ToolOutput.single(values),
76
+ onSome: (structured) => ToolOutput.single([structured, ...values.filter(Content.isMedia)]),
77
+ })
78
+ }
79
+
80
+ const messageOf = (result: CallToolResult): string =>
81
+ (result.content ?? [])
82
+ .map(valueOf)
83
+ .filter((v): v is string => typeof v === "string")
84
+ .join("\n")
51
85
 
52
86
  /**
53
87
  * Two failure channels, deliberately: `isError` is the server telling the
54
- * model it went wrong, so it becomes a model-visible `ToolFailed`; an
88
+ * model it went wrong, so it becomes the declared `McpToolFailed`; an
55
89
  * `input_required` result is a protocol capability gap the model cannot fix,
56
- * so it stays a typed `McpError`.
90
+ * so it stays a typed `McpError` and fails the run.
57
91
  */
58
92
  const interpret = (
59
93
  name: string,
60
94
  result: CallToolResult,
61
- ): Effect.Effect<unknown, McpError | Tool.ToolFailed> => {
62
- if (result.isError === true) {
63
- return Tool.fail(String(outputOf(result)), { kind: "tool_failed" })
64
- }
65
- if (result.resultType === "input_required") {
66
- return Effect.fail(
67
- new McpProtocolError({
68
- method: "tools/call",
69
- reason: `${name} requires interactive input (MRTR), unsupported by this client`,
70
- }),
71
- )
72
- }
73
- return Effect.succeed(outputOf(result))
74
- }
75
-
76
- const runTool =
77
- (client: McpClient, name: string) =>
78
- (input: unknown): Effect.Effect<unknown, McpError | Tool.ToolFailed> =>
79
- client.callTool(name, input).pipe(Effect.flatMap((result) => interpret(name, result)))
95
+ ): Effect.Effect<Content.Value, McpError | McpToolFailed> =>
96
+ Match.value(result).pipe(
97
+ Match.when({ isError: true }, (r) => Effect.fail(new McpToolFailed({ message: messageOf(r) }))),
98
+ Match.when({ resultType: "input_required" }, () =>
99
+ Effect.fail(
100
+ new McpProtocolError({
101
+ method: "tools/call",
102
+ reason: `${name} requires interactive input (MRTR), unsupported by this client`,
103
+ }),
104
+ ),
105
+ ),
106
+ Match.orElse((r) => Effect.succeed(outputOf(r))),
107
+ )
80
108
 
81
109
  const toLocalTool = (client: McpClient, info: ToolInfo) =>
82
110
  Tool.make({
83
111
  name: info.name,
84
112
  description: info.description ?? info.name,
85
113
  inputSchema: passthroughSchema(info),
86
- run: runTool(client, info.name),
114
+ // Known only at runtime: any mix of text, JSON and media.
115
+ outputSchema: Content.Value,
116
+ failureSchema: McpToolFailed,
117
+ run: (input) =>
118
+ client.callTool(info.name, input).pipe(Effect.flatMap((r) => interpret(info.name, r))),
87
119
  strict: false,
88
120
  })
89
121
 
90
122
  /**
91
123
  * Build a `Toolkit` from the server's advertised tools. The list is snapshotted
92
- * here; a tool that later vanishes surfaces through `Toolkit.run`'s graceful
93
- * `unknown_tool` path.
124
+ * here; a tool that later vanishes surfaces as `Toolkit.run`'s `UnknownTool`.
94
125
  */
95
126
  export const mcpToolkit = (
96
127
  client: McpClient,
@@ -9,7 +9,7 @@
9
9
  * cancellation signal (closing the response stream).
10
10
  */
11
11
  import { Cause, Effect, Match, Option, Queue, Ref, type Scope, Stream } from "effect"
12
- import { HttpClient, HttpClientRequest, type HttpClientResponse } from "effect/unstable/http"
12
+ import { HttpClient, HttpClientRequest, type HttpClientResponse } from "effect/http"
13
13
  import * as SSE from "@effect-uai/core/SSE"
14
14
  import {
15
15
  McpAuthRequired,
@@ -7,7 +7,8 @@
7
7
  * Files here are named for the revision they implement. A later revision that
8
8
  * keeps this wire shape gets its own file re-exporting this one.
9
9
  */
10
- import { Effect, Encoding, Option, Predicate, Record, type Scope } from "effect"
10
+ import { Effect, Option, Predicate, Record, type Scope } from "effect"
11
+ import { Base64 } from "effect/encoding"
11
12
  import { type McpError, McpUnsupportedProtocol } from "../../McpError.js"
12
13
  import { modernRejection, type Protocol, type ProtocolProbe } from "../protocol.js"
13
14
  import type { McpConnection, SendMeta } from "../rpc.js"
@@ -56,7 +57,7 @@ const HEADER_SAFE = /^[\x21-\x7e]([\x20-\x7e]*[\x21-\x7e])?$/
56
57
  * for the marker) ride the spec's Base64 sentinel form.
57
58
  */
58
59
  export const headerValue = (raw: string): string =>
59
- HEADER_SAFE.test(raw) && !SENTINEL.test(raw) ? raw : `=?base64?${Encoding.encodeBase64(raw)}?=`
60
+ HEADER_SAFE.test(raw) && !SENTINEL.test(raw) ? raw : `=?base64?${Base64.encode(raw)}?=`
60
61
 
61
62
  const makeProtocol = (version: ProtocolVersion, serverInfo: ServerInfo): Protocol => ({
62
63
  version,