@effect-uai/mcp 0.12.1

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 (71) hide show
  1. package/LICENSE +21 -0
  2. package/dist/Client.d.mts +55 -0
  3. package/dist/Client.d.mts.map +1 -0
  4. package/dist/Client.mjs +85 -0
  5. package/dist/Client.mjs.map +1 -0
  6. package/dist/McpError-DWg9BlW5.d.mts +83 -0
  7. package/dist/McpError-DWg9BlW5.d.mts.map +1 -0
  8. package/dist/McpError.d.mts +2 -0
  9. package/dist/McpError.mjs +61 -0
  10. package/dist/McpError.mjs.map +1 -0
  11. package/dist/Toolkit.d.mts +20 -0
  12. package/dist/Toolkit.d.mts.map +1 -0
  13. package/dist/Toolkit.mjs +70 -0
  14. package/dist/Toolkit.mjs.map +1 -0
  15. package/dist/auth-ZDvsxpCo.d.mts +126 -0
  16. package/dist/auth-ZDvsxpCo.d.mts.map +1 -0
  17. package/dist/index.d.mts +4 -0
  18. package/dist/index.mjs +4 -0
  19. package/dist/internal/auth.d.mts +2 -0
  20. package/dist/internal/auth.mjs +25 -0
  21. package/dist/internal/auth.mjs.map +1 -0
  22. package/dist/internal/httpTransport.d.mts +17 -0
  23. package/dist/internal/httpTransport.d.mts.map +1 -0
  24. package/dist/internal/httpTransport.mjs +88 -0
  25. package/dist/internal/httpTransport.mjs.map +1 -0
  26. package/dist/internal/protocol.d.mts +2 -0
  27. package/dist/internal/protocol.mjs +23 -0
  28. package/dist/internal/protocol.mjs.map +1 -0
  29. package/dist/internal/protocols/2025-06-18.d.mts +11 -0
  30. package/dist/internal/protocols/2025-06-18.d.mts.map +1 -0
  31. package/dist/internal/protocols/2025-06-18.mjs +70 -0
  32. package/dist/internal/protocols/2025-06-18.mjs.map +1 -0
  33. package/dist/internal/protocols/2025-11-25.d.mts +2 -0
  34. package/dist/internal/protocols/2025-11-25.mjs +2 -0
  35. package/dist/internal/protocols/2026-07-28.d.mts +16 -0
  36. package/dist/internal/protocols/2026-07-28.d.mts.map +1 -0
  37. package/dist/internal/protocols/2026-07-28.mjs +83 -0
  38. package/dist/internal/protocols/2026-07-28.mjs.map +1 -0
  39. package/dist/internal/rpc.d.mts +2 -0
  40. package/dist/internal/rpc.mjs +78 -0
  41. package/dist/internal/rpc.mjs.map +1 -0
  42. package/dist/internal/schema.d.mts +2 -0
  43. package/dist/internal/schema.mjs +168 -0
  44. package/dist/internal/schema.mjs.map +1 -0
  45. package/dist/internal/stdioTransport.d.mts +15 -0
  46. package/dist/internal/stdioTransport.d.mts.map +1 -0
  47. package/dist/internal/stdioTransport.mjs +46 -0
  48. package/dist/internal/stdioTransport.mjs.map +1 -0
  49. package/dist/protocol-kjC3iAMb.d.mts +35 -0
  50. package/dist/protocol-kjC3iAMb.d.mts.map +1 -0
  51. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  52. package/dist/rpc-Bq95Lio9.d.mts +34 -0
  53. package/dist/rpc-Bq95Lio9.d.mts.map +1 -0
  54. package/dist/schema-D2oUNuo5.d.mts +245 -0
  55. package/dist/schema-D2oUNuo5.d.mts.map +1 -0
  56. package/package.json +70 -0
  57. package/src/Client.ts +180 -0
  58. package/src/McpError.ts +93 -0
  59. package/src/Toolkit.ts +109 -0
  60. package/src/index.ts +3 -0
  61. package/src/internal/auth.ts +48 -0
  62. package/src/internal/httpTransport.ts +171 -0
  63. package/src/internal/protocol.ts +67 -0
  64. package/src/internal/protocols/2025-06-18.ts +110 -0
  65. package/src/internal/protocols/2025-11-25.ts +8 -0
  66. package/src/internal/protocols/2026-07-28.test.ts +131 -0
  67. package/src/internal/protocols/2026-07-28.ts +152 -0
  68. package/src/internal/rpc.test.ts +156 -0
  69. package/src/internal/rpc.ts +189 -0
  70. package/src/internal/schema.ts +223 -0
  71. package/src/internal/stdioTransport.ts +63 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Betalyra Sociedade Unipessoal Lda.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,55 @@
1
+ import { i as McpError } from "./McpError-DWg9BlW5.mjs";
2
+ import { n as TokenSource, t as Auth } from "./auth-ZDvsxpCo.mjs";
3
+ import { C as ProtocolVersion, D as ToolInfo, T as ServerInfo, o as CallToolResult } from "./schema-D2oUNuo5.mjs";
4
+ import { Context, Effect, Layer, Scope } from "effect";
5
+ import { HttpClient } from "effect/unstable/http";
6
+ import { ChildProcessSpawner } from "effect/unstable/process";
7
+ declare namespace Client_d_exports {
8
+ export { Auth, Mcp, McpClient, McpClientConfig, McpRequirements, McpServerInfo, ProtocolPin, TokenSource, connect, layer };
9
+ }
10
+ /** `auto` runs the probe; a pinned version skips detection. */
11
+ type ProtocolPin = "auto" | ProtocolVersion;
12
+ type McpClientConfig = {
13
+ readonly transport: "http";
14
+ readonly url: string;
15
+ /** Static, non-auth headers sent on every request. */
16
+ readonly headers?: Record<string, string>;
17
+ /** Omit for a public server. See `Auth` for the three shapes. */
18
+ readonly auth?: Auth;
19
+ readonly protocol?: ProtocolPin;
20
+ } | {
21
+ readonly transport: "stdio";
22
+ readonly command: string;
23
+ readonly args?: ReadonlyArray<string>;
24
+ readonly env?: Record<string, string>;
25
+ readonly cwd?: string;
26
+ readonly protocol?: ProtocolPin;
27
+ };
28
+ /**
29
+ * Services a client needs, by transport. Both are listed because the config is
30
+ * a runtime value: an app that only uses one still provides the other's layer,
31
+ * which is cheap (`FetchHttpClient.layer` / `NodeServices.layer`).
32
+ */
33
+ type McpRequirements = HttpClient.HttpClient | ChildProcessSpawner.ChildProcessSpawner;
34
+ type McpServerInfo = ServerInfo & {
35
+ /** The negotiated protocol version: the only era signal callers need. */
36
+ readonly protocolVersion: ProtocolVersion;
37
+ };
38
+ type McpClient = {
39
+ readonly listTools: Effect.Effect<ReadonlyArray<ToolInfo>, McpError>;
40
+ readonly callTool: (name: string, args: unknown) => Effect.Effect<CallToolResult, McpError>;
41
+ readonly serverInfo: McpServerInfo;
42
+ };
43
+ /**
44
+ * Open a scoped client. The connection lives exactly as long as the enclosing
45
+ * scope, so `Effect.scoped`, a `Layer`, or `Stream.scoped` all give correct
46
+ * teardown with no explicit close.
47
+ */
48
+ declare const connect: (config: McpClientConfig) => Effect.Effect<McpClient, McpError, Scope.Scope | McpRequirements>;
49
+ declare const Mcp_base: Context.ServiceClass<Mcp, "@betalyra/effect-uai/providers/mcp/Mcp", McpClient>;
50
+ declare class Mcp extends Mcp_base {}
51
+ /** DI convenience for the single-server app: connects when the layer builds. */
52
+ declare const layer: (config: McpClientConfig) => Layer.Layer<Mcp, McpError, McpRequirements>;
53
+ //#endregion
54
+ export { Auth, Mcp, McpClient, McpClientConfig, McpRequirements, McpServerInfo, ProtocolPin, type TokenSource, connect, layer, Client_d_exports as t };
55
+ //# sourceMappingURL=Client.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Client.d.mts","names":[],"sources":["../src/Client.ts"],"mappings":";;;;;;;;;;KA8BY,uBAAuB;KAEvB;WAEG;WACA;;WAEA,UAAU;;WAEV,OAAO;WACP,WAAW;;WAGX;WACA;WACA,OAAO;WACP,MAAM;WACN;WACA,WAAW;;;;;;;KAQd,kBAAkB,WAAW,aAAa,oBAAoB;KAE9D,gBAAgB;;WAEjB,iBAAiB;;KAGhB;WACD,WAAW,OAAO,OAAO,cAAc,WAAW;WAClD,WAAW,cAAc,kBAAkB,OAAO,OAAO,gBAAgB;WACzE,YAAY;;;;;;;cA8EV,UAAO,QACV,oBACP,OAAO,OAAO,WAAW,UAAU,MAAM,QAAQ;;cA2BvC,YAAY;;cAKZ,QAAK,QAAY,oBAAkB,MAAM,MAAM,KAAK,UAAU"}
@@ -0,0 +1,85 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { McpConnectFailed, McpProtocolError } from "./McpError.mjs";
3
+ import { Auth } from "./internal/auth.mjs";
4
+ import { decodeCallToolResult, decodeListToolsResult } from "./internal/schema.mjs";
5
+ import { probe } from "./internal/protocols/2025-06-18.mjs";
6
+ import { probe as probe$1 } from "./internal/protocols/2026-07-28.mjs";
7
+ import { open } from "./internal/rpc.mjs";
8
+ import { make } from "./internal/httpTransport.mjs";
9
+ import { make as make$1 } from "./internal/stdioTransport.mjs";
10
+ import { Context, Effect, Layer, Option, Ref } from "effect";
11
+ //#region src/Client.ts
12
+ /**
13
+ * `connect` opens a scoped MCP client: transport -> rpc core -> protocol probe.
14
+ * The protocol era is a property of the server, so it is negotiated once here
15
+ * and fixed for the connection's lifetime. The client surface is era-uniform.
16
+ */
17
+ var Client_exports = /* @__PURE__ */ __exportAll({
18
+ Auth: () => Auth,
19
+ Mcp: () => Mcp,
20
+ connect: () => connect,
21
+ layer: () => layer
22
+ });
23
+ const undecodable = (method, detail, raw) => new McpProtocolError({
24
+ method,
25
+ reason: `undecodable ${method} result${detail}`,
26
+ raw
27
+ });
28
+ const makeClient = (connection, protocol) => {
29
+ const request = (method, params) => connection.request(method, protocol.envelope(method, params), protocol.meta(method, params));
30
+ return {
31
+ listTools: request("tools/list", {}).pipe(Effect.flatMap((raw) => decodeListToolsResult(raw).pipe(Effect.mapError(() => undecodable("tools/list", "", raw)), Effect.map((result) => result.tools)))),
32
+ callTool: (name, args) => request("tools/call", {
33
+ name,
34
+ arguments: args ?? {}
35
+ }).pipe(Effect.flatMap((raw) => decodeCallToolResult(raw).pipe(Effect.mapError(() => undecodable("tools/call", ` for ${name}`, raw))))),
36
+ serverInfo: {
37
+ ...protocol.serverInfo,
38
+ protocolVersion: protocol.version
39
+ }
40
+ };
41
+ };
42
+ /**
43
+ * Try the stateless protocol first, then the handshake era. The stateless
44
+ * probe yields `None` only when the server proves it is not stateless (an
45
+ * error that is not one of the recognized modern codes), which is exactly the
46
+ * spec's era discriminator. A pinned `protocol` skips detection.
47
+ *
48
+ * The result is cached for the connection's lifetime by the caller: era is a
49
+ * property of the server, not of a request.
50
+ */
51
+ const negotiate = (connection, pin) => {
52
+ if (pin === "2026-07-28") return required(probe$1(connection));
53
+ if (pin !== "auto") return required(probe(connection));
54
+ return probe$1(connection).pipe(Effect.flatMap(Option.match({
55
+ onNone: () => required(probe(connection)),
56
+ onSome: Effect.succeed
57
+ })));
58
+ };
59
+ const required = (probe) => probe.pipe(Effect.flatMap(Option.match({
60
+ onNone: () => Effect.fail(new McpConnectFailed({ reason: "the server does not speak a supported MCP protocol" })),
61
+ onSome: Effect.succeed
62
+ })));
63
+ /**
64
+ * Open a scoped client. The connection lives exactly as long as the enclosing
65
+ * scope, so `Effect.scoped`, a `Layer`, or `Stream.scoped` all give correct
66
+ * teardown with no explicit close.
67
+ */
68
+ const connect = (config) => Effect.gen(function* () {
69
+ const transport = yield* config.transport === "http" ? make(config) : make$1(config);
70
+ const protocolRef = yield* Ref.make(Option.none());
71
+ const connection = yield* open(transport, (inbound) => Ref.get(protocolRef).pipe(Effect.flatMap(Option.match({
72
+ onNone: () => Effect.void,
73
+ onSome: (protocol) => protocol.onInbound(inbound)
74
+ }))));
75
+ const protocol = yield* negotiate(connection, config.protocol ?? "auto");
76
+ yield* Ref.set(protocolRef, Option.some(protocol));
77
+ return makeClient(connection, protocol);
78
+ });
79
+ var Mcp = class extends Context.Service()("@betalyra/effect-uai/providers/mcp/Mcp") {};
80
+ /** DI convenience for the single-server app: connects when the layer builds. */
81
+ const layer = (config) => Layer.effect(Mcp, connect(config));
82
+ //#endregion
83
+ export { Auth, Mcp, connect, layer, Client_exports as t };
84
+
85
+ //# sourceMappingURL=Client.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Client.mjs","names":["Stateless.probe","Handshake.probe","HttpTransport.make","StdioTransport.make"],"sources":["../src/Client.ts"],"sourcesContent":["/**\n * `connect` opens a scoped MCP client: transport -> rpc core -> protocol probe.\n * The protocol era is a property of the server, so it is negotiated once here\n * and fixed for the connection's lifetime. The client surface is era-uniform.\n */\nimport { Context, Effect, Layer, Option, Ref, type Scope } from \"effect\"\nimport type { HttpClient } from \"effect/unstable/http\"\nimport type { ChildProcessSpawner } from \"effect/unstable/process\"\nimport { McpConnectFailed, type McpError, McpProtocolError } from \"./McpError.js\"\nimport { Auth, type TokenSource } from \"./internal/auth.js\"\nimport * as Handshake from \"./internal/protocols/2025-06-18.js\"\nimport * as Stateless from \"./internal/protocols/2026-07-28.js\"\nimport type { Protocol } from \"./internal/protocol.js\"\nimport { type McpConnection, open } from \"./internal/rpc.js\"\nimport {\n type CallToolResult,\n decodeCallToolResult,\n decodeListToolsResult,\n LATEST_VERSION,\n type McpMethod,\n type ProtocolVersion,\n type ServerInfo,\n type ToolInfo,\n} from \"./internal/schema.js\"\nimport * as HttpTransport from \"./internal/httpTransport.js\"\nimport * as StdioTransport from \"./internal/stdioTransport.js\"\n\nexport { Auth, type TokenSource }\n\n/** `auto` runs the probe; a pinned version skips detection. */\nexport type ProtocolPin = \"auto\" | ProtocolVersion\n\nexport type McpClientConfig =\n | {\n readonly transport: \"http\"\n readonly url: string\n /** Static, non-auth headers sent on every request. */\n readonly headers?: Record<string, string>\n /** Omit for a public server. See `Auth` for the three shapes. */\n readonly auth?: Auth\n readonly protocol?: ProtocolPin\n }\n | {\n readonly transport: \"stdio\"\n readonly command: string\n readonly args?: ReadonlyArray<string>\n readonly env?: Record<string, string>\n readonly cwd?: string\n readonly protocol?: ProtocolPin\n }\n\n/**\n * Services a client needs, by transport. Both are listed because the config is\n * a runtime value: an app that only uses one still provides the other's layer,\n * which is cheap (`FetchHttpClient.layer` / `NodeServices.layer`).\n */\nexport type McpRequirements = HttpClient.HttpClient | ChildProcessSpawner.ChildProcessSpawner\n\nexport type McpServerInfo = ServerInfo & {\n /** The negotiated protocol version: the only era signal callers need. */\n readonly protocolVersion: ProtocolVersion\n}\n\nexport type McpClient = {\n readonly listTools: Effect.Effect<ReadonlyArray<ToolInfo>, McpError>\n readonly callTool: (name: string, args: unknown) => Effect.Effect<CallToolResult, McpError>\n readonly serverInfo: McpServerInfo\n}\n\nconst undecodable = (method: McpMethod, detail: string, raw: unknown): McpError =>\n new McpProtocolError({ method, reason: `undecodable ${method} result${detail}`, raw })\n\nconst makeClient = (connection: McpConnection, protocol: Protocol): McpClient => {\n const request = (method: McpMethod, params: unknown) =>\n connection.request(method, protocol.envelope(method, params), protocol.meta(method, params))\n\n return {\n listTools: request(\"tools/list\", {}).pipe(\n Effect.flatMap((raw) =>\n decodeListToolsResult(raw).pipe(\n Effect.mapError(() => undecodable(\"tools/list\", \"\", raw)),\n Effect.map((result) => result.tools),\n ),\n ),\n ),\n\n callTool: (name, args) =>\n request(\"tools/call\", { name, arguments: args ?? {} }).pipe(\n Effect.flatMap((raw) =>\n decodeCallToolResult(raw).pipe(\n Effect.mapError(() => undecodable(\"tools/call\", ` for ${name}`, raw)),\n ),\n ),\n ),\n\n serverInfo: { ...protocol.serverInfo, protocolVersion: protocol.version },\n }\n}\n\n/**\n * Try the stateless protocol first, then the handshake era. The stateless\n * probe yields `None` only when the server proves it is not stateless (an\n * error that is not one of the recognized modern codes), which is exactly the\n * spec's era discriminator. A pinned `protocol` skips detection.\n *\n * The result is cached for the connection's lifetime by the caller: era is a\n * property of the server, not of a request.\n */\nconst negotiate = (\n connection: McpConnection,\n pin: ProtocolPin,\n): Effect.Effect<Protocol, McpError, Scope.Scope> => {\n if (pin === LATEST_VERSION) return required(Stateless.probe(connection))\n if (pin !== \"auto\") return required(Handshake.probe(connection))\n return Stateless.probe(connection).pipe(\n Effect.flatMap(\n Option.match({\n onNone: () => required(Handshake.probe(connection)),\n onSome: Effect.succeed<Protocol>,\n }),\n ),\n )\n}\n\nconst required = (\n probe: Effect.Effect<Option.Option<Protocol>, McpError, Scope.Scope>,\n): Effect.Effect<Protocol, McpError, Scope.Scope> =>\n probe.pipe(\n Effect.flatMap(\n Option.match({\n onNone: () =>\n Effect.fail(\n new McpConnectFailed({ reason: \"the server does not speak a supported MCP protocol\" }),\n ),\n onSome: Effect.succeed<Protocol>,\n }),\n ),\n )\n\n/**\n * Open a scoped client. The connection lives exactly as long as the enclosing\n * scope, so `Effect.scoped`, a `Layer`, or `Stream.scoped` all give correct\n * teardown with no explicit close.\n */\nexport const connect = (\n config: McpClientConfig,\n): Effect.Effect<McpClient, McpError, Scope.Scope | McpRequirements> =>\n Effect.gen(function* () {\n const transport = yield* config.transport === \"http\"\n ? HttpTransport.make(config)\n : StdioTransport.make(config)\n\n // Inbound handling belongs to the protocol, which is not chosen until the\n // probe completes, so the reader reads it from a Ref that starts empty.\n // Frames arriving before then can only be legacy noise; ignore them.\n const protocolRef = yield* Ref.make(Option.none<Protocol>())\n const connection = yield* open(transport, (inbound) =>\n Ref.get(protocolRef).pipe(\n Effect.flatMap(\n Option.match({\n onNone: () => Effect.void,\n onSome: (protocol) => protocol.onInbound(inbound),\n }),\n ),\n ),\n )\n\n const protocol = yield* negotiate(connection, config.protocol ?? \"auto\")\n yield* Ref.set(protocolRef, Option.some(protocol))\n\n return makeClient(connection, protocol)\n })\n\nexport class Mcp extends Context.Service<Mcp, McpClient>()(\n \"@betalyra/effect-uai/providers/mcp/Mcp\",\n) {}\n\n/** DI convenience for the single-server app: connects when the layer builds. */\nexport const layer = (config: McpClientConfig): Layer.Layer<Mcp, McpError, McpRequirements> =>\n Layer.effect(Mcp, connect(config))\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAqEA,MAAM,eAAe,QAAmB,QAAgB,QACtD,IAAI,iBAAiB;CAAE;CAAQ,QAAQ,eAAe,OAAO,SAAS;CAAU;AAAI,CAAC;AAEvF,MAAM,cAAc,YAA2B,aAAkC;CAC/E,MAAM,WAAW,QAAmB,WAClC,WAAW,QAAQ,QAAQ,SAAS,SAAS,QAAQ,MAAM,GAAG,SAAS,KAAK,QAAQ,MAAM,CAAC;CAE7F,OAAO;EACL,WAAW,QAAQ,cAAc,CAAC,CAAC,CAAC,CAAC,KACnC,OAAO,SAAS,QACd,sBAAsB,GAAG,CAAC,CAAC,KACzB,OAAO,eAAe,YAAY,cAAc,IAAI,GAAG,CAAC,GACxD,OAAO,KAAK,WAAW,OAAO,KAAK,CACrC,CACF,CACF;EAEA,WAAW,MAAM,SACf,QAAQ,cAAc;GAAE;GAAM,WAAW,QAAQ,CAAC;EAAE,CAAC,CAAC,CAAC,KACrD,OAAO,SAAS,QACd,qBAAqB,GAAG,CAAC,CAAC,KACxB,OAAO,eAAe,YAAY,cAAc,QAAQ,QAAQ,GAAG,CAAC,CACtE,CACF,CACF;EAEF,YAAY;GAAE,GAAG,SAAS;GAAY,iBAAiB,SAAS;EAAQ;CAC1E;AACF;;;;;;;;;;AAWA,MAAM,aACJ,YACA,QACmD;CACnD,IAAI,QAAA,cAAwB,OAAO,SAASA,QAAgB,UAAU,CAAC;CACvE,IAAI,QAAQ,QAAQ,OAAO,SAASC,MAAgB,UAAU,CAAC;CAC/D,OAAOD,QAAgB,UAAU,CAAC,CAAC,KACjC,OAAO,QACL,OAAO,MAAM;EACX,cAAc,SAASC,MAAgB,UAAU,CAAC;EAClD,QAAQ,OAAO;CACjB,CAAC,CACH,CACF;AACF;AAEA,MAAM,YACJ,UAEA,MAAM,KACJ,OAAO,QACL,OAAO,MAAM;CACX,cACE,OAAO,KACL,IAAI,iBAAiB,EAAE,QAAQ,qDAAqD,CAAC,CACvF;CACF,QAAQ,OAAO;AACjB,CAAC,CACH,CACF;;;;;;AAOF,MAAa,WACX,WAEA,OAAO,IAAI,aAAa;CACtB,MAAM,YAAY,OAAO,OAAO,cAAc,SAC1CC,KAAmB,MAAM,IACzBC,OAAoB,MAAM;CAK9B,MAAM,cAAc,OAAO,IAAI,KAAK,OAAO,KAAe,CAAC;CAC3D,MAAM,aAAa,OAAO,KAAK,YAAY,YACzC,IAAI,IAAI,WAAW,CAAC,CAAC,KACnB,OAAO,QACL,OAAO,MAAM;EACX,cAAc,OAAO;EACrB,SAAS,aAAa,SAAS,UAAU,OAAO;CAClD,CAAC,CACH,CACF,CACF;CAEA,MAAM,WAAW,OAAO,UAAU,YAAY,OAAO,YAAY,MAAM;CACvE,OAAO,IAAI,IAAI,aAAa,OAAO,KAAK,QAAQ,CAAC;CAEjD,OAAO,WAAW,YAAY,QAAQ;AACxC,CAAC;AAEH,IAAa,MAAb,cAAyB,QAAQ,QAAwB,CAAC,CACxD,wCACF,CAAC,CAAC,CAAC;;AAGH,MAAa,SAAS,WACpB,MAAM,OAAO,KAAK,QAAQ,MAAM,CAAC"}
@@ -0,0 +1,83 @@
1
+ declare namespace McpError_d_exports {
2
+ export { McpAuthError, McpAuthRequired, McpConnectFailed, McpError, McpProtocolError, McpTransportClosed, McpUnsupportedProtocol, describe };
3
+ }
4
+ declare const McpConnectFailed_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
5
+ readonly _tag: "McpConnectFailed";
6
+ } & Readonly<A>;
7
+ /**
8
+ * Transport could not establish: DNS / TLS / non-2xx on the first request,
9
+ * a failed spawn, or a failed era probe. The raw transport error on `raw`.
10
+ */
11
+ declare class McpConnectFailed extends McpConnectFailed_base<{
12
+ reason?: string;
13
+ raw?: unknown;
14
+ }> {}
15
+ declare const McpUnsupportedProtocol_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
16
+ readonly _tag: "McpUnsupportedProtocol";
17
+ } & Readonly<A>;
18
+ /**
19
+ * Negotiation ended on a protocol version this client does not support
20
+ * (e.g. a 2025-03-26-only server, or a modern server whose `supported`
21
+ * list has no mutual version). `offered` is what the server proposed.
22
+ */
23
+ declare class McpUnsupportedProtocol extends McpUnsupportedProtocol_base<{
24
+ offered: ReadonlyArray<string>;
25
+ reason?: string;
26
+ }> {}
27
+ declare const McpProtocolError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
28
+ readonly _tag: "McpProtocolError";
29
+ } & Readonly<A>;
30
+ /**
31
+ * A JSON-RPC error reply (carrying its `code`), a reply that failed to
32
+ * decode, or an unsupported protocol feature (v1 fails `input_required`
33
+ * results here). `method` names the request that was in flight.
34
+ */
35
+ declare class McpProtocolError extends McpProtocolError_base<{
36
+ method?: string;
37
+ code?: number;
38
+ reason?: string;
39
+ raw?: unknown;
40
+ }> {}
41
+ declare const McpTransportClosed_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
42
+ readonly _tag: "McpTransportClosed";
43
+ } & Readonly<A>;
44
+ /**
45
+ * The connection dropped with a request in flight, or a legacy session
46
+ * expired (HTTP 404 on a known `Mcp-Session-Id`). Not resumable; the
47
+ * remedy is reconnecting and re-issuing the request.
48
+ */
49
+ declare class McpTransportClosed extends McpTransportClosed_base<{
50
+ method?: string;
51
+ reason?: string;
52
+ raw?: unknown;
53
+ }> {}
54
+ declare const McpAuthRequired_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
55
+ readonly _tag: "McpAuthRequired";
56
+ } & Readonly<A>;
57
+ /**
58
+ * The server answered `401`. `resourceMetadataUrl` is the RFC 9728
59
+ * Protected Resource Metadata pointer parsed from `WWW-Authenticate`,
60
+ * when present; an OAuth layer starts discovery there.
61
+ */
62
+ declare class McpAuthRequired extends McpAuthRequired_base<{
63
+ resourceMetadataUrl?: string;
64
+ wwwAuthenticate?: string;
65
+ }> {}
66
+ declare const McpAuthError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
67
+ readonly _tag: "McpAuthError";
68
+ } & Readonly<A>;
69
+ /** A `TokenSource` / OAuth grant failed to mint or refresh a token. */
70
+ declare class McpAuthError extends McpAuthError_base<{
71
+ reason?: string;
72
+ raw?: unknown;
73
+ }> {}
74
+ type McpError = McpConnectFailed | McpUnsupportedProtocol | McpProtocolError | McpTransportClosed | McpAuthRequired | McpAuthError;
75
+ /**
76
+ * Short human-readable description of an error, for logs and model-facing
77
+ * failure messages (`Toolkit.describeFailures(McpError.describe)`). Prose,
78
+ * not a contract; branch on `_tag` instead.
79
+ */
80
+ declare const describe: (e: McpError) => string;
81
+ //#endregion
82
+ export { McpError_d_exports as a, McpUnsupportedProtocol as c, McpError as i, describe as l, McpAuthRequired as n, McpProtocolError as o, McpConnectFailed as r, McpTransportClosed as s, McpAuthError as t };
83
+ //# sourceMappingURL=McpError-DWg9BlW5.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"McpError-DWg9BlW5.d.mts","names":[],"sources":["../src/McpError.ts"],"mappings":";;;;;;;;;;cAMa,yBAAyB;EACpC;EACA;;;;;;;;;;cAQW,+BAA+B;EAC1C,SAAS;EACT;;;;;;;;;;cAQW,yBAAyB;EACpC;EACA;EACA;EACA;;;;;;;;;;cAQW,2BAA2B;EACtC;EACA;EACA;;;;;;;;;;cAQW,wBAAwB;EACnC;EACA;;;;;;cAIW,qBAAqB;EAChC;EACA;;KAGU,WACR,mBACA,yBACA,mBACA,qBACA,kBACA;;;;;;cAUS,WAAW,GAAG"}
@@ -0,0 +1,2 @@
1
+ import { c as McpUnsupportedProtocol, i as McpError, l as describe, n as McpAuthRequired, o as McpProtocolError, r as McpConnectFailed, s as McpTransportClosed, t as McpAuthError } from "./McpError-DWg9BlW5.mjs";
2
+ export { McpAuthError, McpAuthRequired, McpConnectFailed, McpError, McpProtocolError, McpTransportClosed, McpUnsupportedProtocol, describe };
@@ -0,0 +1,61 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { Data, Match } from "effect";
3
+ //#region src/McpError.ts
4
+ var McpError_exports = /* @__PURE__ */ __exportAll({
5
+ McpAuthError: () => McpAuthError,
6
+ McpAuthRequired: () => McpAuthRequired,
7
+ McpConnectFailed: () => McpConnectFailed,
8
+ McpProtocolError: () => McpProtocolError,
9
+ McpTransportClosed: () => McpTransportClosed,
10
+ McpUnsupportedProtocol: () => McpUnsupportedProtocol,
11
+ describe: () => describe
12
+ });
13
+ /**
14
+ * Transport could not establish: DNS / TLS / non-2xx on the first request,
15
+ * a failed spawn, or a failed era probe. The raw transport error on `raw`.
16
+ */
17
+ var McpConnectFailed = class extends Data.TaggedError("McpConnectFailed") {};
18
+ /**
19
+ * Negotiation ended on a protocol version this client does not support
20
+ * (e.g. a 2025-03-26-only server, or a modern server whose `supported`
21
+ * list has no mutual version). `offered` is what the server proposed.
22
+ */
23
+ var McpUnsupportedProtocol = class extends Data.TaggedError("McpUnsupportedProtocol") {};
24
+ /**
25
+ * A JSON-RPC error reply (carrying its `code`), a reply that failed to
26
+ * decode, or an unsupported protocol feature (v1 fails `input_required`
27
+ * results here). `method` names the request that was in flight.
28
+ */
29
+ var McpProtocolError = class extends Data.TaggedError("McpProtocolError") {};
30
+ /**
31
+ * The connection dropped with a request in flight, or a legacy session
32
+ * expired (HTTP 404 on a known `Mcp-Session-Id`). Not resumable; the
33
+ * remedy is reconnecting and re-issuing the request.
34
+ */
35
+ var McpTransportClosed = class extends Data.TaggedError("McpTransportClosed") {};
36
+ /**
37
+ * The server answered `401`. `resourceMetadataUrl` is the RFC 9728
38
+ * Protected Resource Metadata pointer parsed from `WWW-Authenticate`,
39
+ * when present; an OAuth layer starts discovery there.
40
+ */
41
+ var McpAuthRequired = class extends Data.TaggedError("McpAuthRequired") {};
42
+ /** A `TokenSource` / OAuth grant failed to mint or refresh a token. */
43
+ var McpAuthError = class extends Data.TaggedError("McpAuthError") {};
44
+ const withReason = (base, reason) => reason === void 0 ? base : `${base}: ${reason}`;
45
+ /**
46
+ * Short human-readable description of an error, for logs and model-facing
47
+ * failure messages (`Toolkit.describeFailures(McpError.describe)`). Prose,
48
+ * not a contract; branch on `_tag` instead.
49
+ */
50
+ const describe = Match.type().pipe(Match.discriminatorsExhaustive("_tag")({
51
+ McpConnectFailed: (e) => withReason("could not connect to the MCP server", e.reason),
52
+ McpUnsupportedProtocol: (e) => `the MCP server only offers unsupported protocol versions (${e.offered.join(", ")})`,
53
+ McpProtocolError: (e) => withReason(`the MCP server rejected ${e.method ?? "the request"}${e.code === void 0 ? "" : ` (${e.code})`}`, e.reason),
54
+ McpTransportClosed: (e) => withReason("the MCP connection closed", e.reason),
55
+ McpAuthRequired: () => "the MCP server requires authentication",
56
+ McpAuthError: (e) => withReason("MCP authentication failed", e.reason)
57
+ }));
58
+ //#endregion
59
+ export { McpAuthError, McpAuthRequired, McpConnectFailed, McpProtocolError, McpTransportClosed, McpUnsupportedProtocol, describe, McpError_exports as t };
60
+
61
+ //# sourceMappingURL=McpError.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"McpError.mjs","names":[],"sources":["../src/McpError.ts"],"sourcesContent":["import { Data, Match } from \"effect\"\n\n/**\n * Transport could not establish: DNS / TLS / non-2xx on the first request,\n * a failed spawn, or a failed era probe. The raw transport error on `raw`.\n */\nexport class McpConnectFailed extends Data.TaggedError(\"McpConnectFailed\")<{\n reason?: string\n raw?: unknown\n}> {}\n\n/**\n * Negotiation ended on a protocol version this client does not support\n * (e.g. a 2025-03-26-only server, or a modern server whose `supported`\n * list has no mutual version). `offered` is what the server proposed.\n */\nexport class McpUnsupportedProtocol extends Data.TaggedError(\"McpUnsupportedProtocol\")<{\n offered: ReadonlyArray<string>\n reason?: string\n}> {}\n\n/**\n * A JSON-RPC error reply (carrying its `code`), a reply that failed to\n * decode, or an unsupported protocol feature (v1 fails `input_required`\n * results here). `method` names the request that was in flight.\n */\nexport class McpProtocolError extends Data.TaggedError(\"McpProtocolError\")<{\n method?: string\n code?: number\n reason?: string\n raw?: unknown\n}> {}\n\n/**\n * The connection dropped with a request in flight, or a legacy session\n * expired (HTTP 404 on a known `Mcp-Session-Id`). Not resumable; the\n * remedy is reconnecting and re-issuing the request.\n */\nexport class McpTransportClosed extends Data.TaggedError(\"McpTransportClosed\")<{\n method?: string\n reason?: string\n raw?: unknown\n}> {}\n\n/**\n * The server answered `401`. `resourceMetadataUrl` is the RFC 9728\n * Protected Resource Metadata pointer parsed from `WWW-Authenticate`,\n * when present; an OAuth layer starts discovery there.\n */\nexport class McpAuthRequired extends Data.TaggedError(\"McpAuthRequired\")<{\n resourceMetadataUrl?: string\n wwwAuthenticate?: string\n}> {}\n\n/** A `TokenSource` / OAuth grant failed to mint or refresh a token. */\nexport class McpAuthError extends Data.TaggedError(\"McpAuthError\")<{\n reason?: string\n raw?: unknown\n}> {}\n\nexport type McpError =\n | McpConnectFailed\n | McpUnsupportedProtocol\n | McpProtocolError\n | McpTransportClosed\n | McpAuthRequired\n | McpAuthError\n\nconst withReason = (base: string, reason: string | undefined): string =>\n reason === undefined ? base : `${base}: ${reason}`\n\n/**\n * Short human-readable description of an error, for logs and model-facing\n * failure messages (`Toolkit.describeFailures(McpError.describe)`). Prose,\n * not a contract; branch on `_tag` instead.\n */\nexport const describe: (e: McpError) => string = Match.type<McpError>().pipe(\n Match.discriminatorsExhaustive(\"_tag\")({\n McpConnectFailed: (e) => withReason(\"could not connect to the MCP server\", e.reason),\n McpUnsupportedProtocol: (e) =>\n `the MCP server only offers unsupported protocol versions (${e.offered.join(\", \")})`,\n McpProtocolError: (e) =>\n withReason(\n `the MCP server rejected ${e.method ?? \"the request\"}${\n e.code === undefined ? \"\" : ` (${e.code})`\n }`,\n e.reason,\n ),\n McpTransportClosed: (e) => withReason(\"the MCP connection closed\", e.reason),\n McpAuthRequired: () => \"the MCP server requires authentication\",\n McpAuthError: (e) => withReason(\"MCP authentication failed\", e.reason),\n }),\n)\n"],"mappings":";;;;;;;;;;;;;;;;AAMA,IAAa,mBAAb,cAAsC,KAAK,YAAY,kBAAkB,CAAC,CAGvE,CAAC;;;;;;AAOJ,IAAa,yBAAb,cAA4C,KAAK,YAAY,wBAAwB,CAAC,CAGnF,CAAC;;;;;;AAOJ,IAAa,mBAAb,cAAsC,KAAK,YAAY,kBAAkB,CAAC,CAKvE,CAAC;;;;;;AAOJ,IAAa,qBAAb,cAAwC,KAAK,YAAY,oBAAoB,CAAC,CAI3E,CAAC;;;;;;AAOJ,IAAa,kBAAb,cAAqC,KAAK,YAAY,iBAAiB,CAAC,CAGrE,CAAC;;AAGJ,IAAa,eAAb,cAAkC,KAAK,YAAY,cAAc,CAAC,CAG/D,CAAC;AAUJ,MAAM,cAAc,MAAc,WAChC,WAAW,KAAA,IAAY,OAAO,GAAG,KAAK,IAAI;;;;;;AAO5C,MAAa,WAAoC,MAAM,KAAe,CAAC,CAAC,KACtE,MAAM,yBAAyB,MAAM,CAAC,CAAC;CACrC,mBAAmB,MAAM,WAAW,uCAAuC,EAAE,MAAM;CACnF,yBAAyB,MACvB,6DAA6D,EAAE,QAAQ,KAAK,IAAI,EAAE;CACpF,mBAAmB,MACjB,WACE,2BAA2B,EAAE,UAAU,gBACrC,EAAE,SAAS,KAAA,IAAY,KAAK,KAAK,EAAE,KAAK,MAE1C,EAAE,MACJ;CACF,qBAAqB,MAAM,WAAW,6BAA6B,EAAE,MAAM;CAC3E,uBAAuB;CACvB,eAAe,MAAM,WAAW,6BAA6B,EAAE,MAAM;AACvE,CAAC,CACH"}
@@ -0,0 +1,20 @@
1
+ import { i as McpError } from "./McpError-DWg9BlW5.mjs";
2
+ import { McpClient } from "./Client.mjs";
3
+ import { Effect } from "effect";
4
+ import * as CoreToolkit from "@effect-uai/core/Toolkit";
5
+ declare namespace Toolkit_d_exports {
6
+ export { McpToolkitOptions, mcpToolkit };
7
+ }
8
+ type McpToolkitOptions = {
9
+ /** Namespace every tool as `<prefix>__<name>`; recommended when composing servers. */
10
+ readonly prefix?: string;
11
+ };
12
+ /**
13
+ * Build a `Toolkit` from the server's advertised tools. The list is snapshotted
14
+ * here; a tool that later vanishes surfaces through `Toolkit.run`'s graceful
15
+ * `unknown_tool` path.
16
+ */
17
+ declare const mcpToolkit: (client: McpClient, options?: McpToolkitOptions) => Effect.Effect<CoreToolkit.Toolkit, McpError>;
18
+ //#endregion
19
+ export { McpToolkitOptions, mcpToolkit, Toolkit_d_exports as t };
20
+ //# sourceMappingURL=Toolkit.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Toolkit.d.mts","names":[],"sources":["../src/Toolkit.ts"],"mappings":";;;;;;;KAiBY;;WAED;;;;;;;cA2EE,aAAU,QACb,WAAS,UACP,sBACT,OAAO,OAAO,YAAY,SAAS"}
@@ -0,0 +1,70 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { McpProtocolError } from "./McpError.mjs";
3
+ import { isTextContent } from "./internal/schema.mjs";
4
+ import { Effect, Option } from "effect";
5
+ import * as Tool from "@effect-uai/core/Tool";
6
+ import * as CoreToolkit from "@effect-uai/core/Toolkit";
7
+ //#region src/Toolkit.ts
8
+ /**
9
+ * Turn a connected MCP server into a core `Toolkit`, one `LocalTool` per MCP
10
+ * tool. Effectful because the tool list is fetched; composes with
11
+ * `Toolkit.compose` like any other kit.
12
+ */
13
+ var Toolkit_exports = /* @__PURE__ */ __exportAll({ mcpToolkit: () => mcpToolkit });
14
+ /**
15
+ * The server owns validation, so the input schema is a passthrough: it carries
16
+ * the server's JSON Schema verbatim (the model sees the real thing) and
17
+ * accepts whatever the model produced. `strict: false` because server schemas
18
+ * are not written to OpenAI strict rules.
19
+ */
20
+ const passthroughSchema = (tool) => ({ "~standard": {
21
+ version: 1,
22
+ vendor: "effect-uai-mcp",
23
+ validate: (value) => ({ value }),
24
+ jsonSchema: {
25
+ input: () => tool.inputSchema,
26
+ output: () => ({ type: "object" })
27
+ }
28
+ } });
29
+ const textOf = (block) => isTextContent(block) ? block.text : `[${block.type} content omitted]`;
30
+ /**
31
+ * Serialize a successful result for the model: `structuredContent` when the
32
+ * server provides it, otherwise the joined text blocks. Non-text blocks become
33
+ * a short placeholder until the loop grows a multimodal tool-output path.
34
+ */
35
+ const outputOf = (result) => result.structuredContent ?? (result.content ?? []).map(textOf).join("\n");
36
+ /**
37
+ * Two failure channels, deliberately: `isError` is the server telling the
38
+ * model it went wrong, so it becomes a model-visible `ToolFailed`; an
39
+ * `input_required` result is a protocol capability gap the model cannot fix,
40
+ * so it stays a typed `McpError`.
41
+ */
42
+ const interpret = (name, result) => {
43
+ if (result.isError === true) return Tool.fail(String(outputOf(result)), { kind: "tool_failed" });
44
+ if (result.resultType === "input_required") return Effect.fail(new McpProtocolError({
45
+ method: "tools/call",
46
+ reason: `${name} requires interactive input (MRTR), unsupported by this client`
47
+ }));
48
+ return Effect.succeed(outputOf(result));
49
+ };
50
+ const runTool = (client, name) => (input) => client.callTool(name, input).pipe(Effect.flatMap((result) => interpret(name, result)));
51
+ const toLocalTool = (client, info) => Tool.make({
52
+ name: info.name,
53
+ description: info.description ?? info.name,
54
+ inputSchema: passthroughSchema(info),
55
+ run: runTool(client, info.name),
56
+ strict: false
57
+ });
58
+ /**
59
+ * Build a `Toolkit` from the server's advertised tools. The list is snapshotted
60
+ * here; a tool that later vanishes surfaces through `Toolkit.run`'s graceful
61
+ * `unknown_tool` path.
62
+ */
63
+ const mcpToolkit = (client, options) => client.listTools.pipe(Effect.map((tools) => CoreToolkit.fromArray(tools.map((info) => toLocalTool(client, info)))), Effect.map((kit) => Option.fromNullishOr(options?.prefix).pipe(Option.match({
64
+ onNone: () => kit,
65
+ onSome: (prefix) => CoreToolkit.namespace(prefix, kit)
66
+ }))));
67
+ //#endregion
68
+ export { mcpToolkit, Toolkit_exports as t };
69
+
70
+ //# sourceMappingURL=Toolkit.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Toolkit.mjs","names":[],"sources":["../src/Toolkit.ts"],"sourcesContent":["/**\n * Turn a connected MCP server into a core `Toolkit`, one `LocalTool` per MCP\n * tool. Effectful because the tool list is fetched; composes with\n * `Toolkit.compose` like any other kit.\n */\nimport { Effect, Option } from \"effect\"\nimport * as Tool from \"@effect-uai/core/Tool\"\nimport * as CoreToolkit from \"@effect-uai/core/Toolkit\"\nimport type { McpClient } from \"./Client.js\"\nimport { type McpError, McpProtocolError } from \"./McpError.js\"\nimport {\n type CallToolResult,\n type ContentBlock,\n isTextContent,\n type ToolInfo,\n} from \"./internal/schema.js\"\n\nexport type McpToolkitOptions = {\n /** Namespace every tool as `<prefix>__<name>`; recommended when composing servers. */\n readonly prefix?: string\n}\n\n/**\n * The server owns validation, so the input schema is a passthrough: it carries\n * the server's JSON Schema verbatim (the model sees the real thing) and\n * accepts whatever the model produced. `strict: false` because server schemas\n * are not written to OpenAI strict rules.\n */\nconst passthroughSchema = (tool: ToolInfo): Tool.ToolInputSchema<unknown> => ({\n \"~standard\": {\n version: 1,\n vendor: \"effect-uai-mcp\",\n validate: (value: unknown) => ({ value }),\n jsonSchema: {\n input: () => tool.inputSchema,\n output: () => ({ type: \"object\" }),\n },\n },\n})\n\nconst textOf = (block: ContentBlock): string =>\n isTextContent(block) ? block.text : `[${block.type} content omitted]`\n\n/**\n * Serialize a successful result for the model: `structuredContent` when the\n * server provides it, otherwise the joined text blocks. Non-text blocks become\n * a short placeholder until the loop grows a multimodal tool-output path.\n */\nconst outputOf = (result: CallToolResult): unknown =>\n result.structuredContent ?? (result.content ?? []).map(textOf).join(\"\\n\")\n\n/**\n * Two failure channels, deliberately: `isError` is the server telling the\n * model it went wrong, so it becomes a model-visible `ToolFailed`; an\n * `input_required` result is a protocol capability gap the model cannot fix,\n * so it stays a typed `McpError`.\n */\nconst interpret = (\n name: string,\n result: CallToolResult,\n): Effect.Effect<unknown, McpError | Tool.ToolFailed> => {\n if (result.isError === true) {\n return Tool.fail(String(outputOf(result)), { kind: \"tool_failed\" })\n }\n if (result.resultType === \"input_required\") {\n return Effect.fail(\n new McpProtocolError({\n method: \"tools/call\",\n reason: `${name} requires interactive input (MRTR), unsupported by this client`,\n }),\n )\n }\n return Effect.succeed(outputOf(result))\n}\n\nconst runTool =\n (client: McpClient, name: string) =>\n (input: unknown): Effect.Effect<unknown, McpError | Tool.ToolFailed> =>\n client.callTool(name, input).pipe(Effect.flatMap((result) => interpret(name, result)))\n\nconst toLocalTool = (client: McpClient, info: ToolInfo) =>\n Tool.make({\n name: info.name,\n description: info.description ?? info.name,\n inputSchema: passthroughSchema(info),\n run: runTool(client, info.name),\n strict: false,\n })\n\n/**\n * Build a `Toolkit` from the server's advertised tools. The list is snapshotted\n * here; a tool that later vanishes surfaces through `Toolkit.run`'s graceful\n * `unknown_tool` path.\n */\nexport const mcpToolkit = (\n client: McpClient,\n options?: McpToolkitOptions,\n): Effect.Effect<CoreToolkit.Toolkit, McpError> =>\n client.listTools.pipe(\n Effect.map((tools) => CoreToolkit.fromArray(tools.map((info) => toLocalTool(client, info)))),\n Effect.map((kit) =>\n Option.fromNullishOr(options?.prefix).pipe(\n Option.match({\n onNone: () => kit,\n onSome: (prefix) => CoreToolkit.namespace(prefix, kit),\n }),\n ),\n ),\n )\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA4BA,MAAM,qBAAqB,UAAmD,EAC5E,aAAa;CACX,SAAS;CACT,QAAQ;CACR,WAAW,WAAoB,EAAE,MAAM;CACvC,YAAY;EACV,aAAa,KAAK;EAClB,eAAe,EAAE,MAAM,SAAS;CAClC;AACF,EACF;AAEA,MAAM,UAAU,UACd,cAAc,KAAK,IAAI,MAAM,OAAO,IAAI,MAAM,KAAK;;;;;;AAOrD,MAAM,YAAY,WAChB,OAAO,sBAAsB,OAAO,WAAW,CAAC,EAAA,CAAG,IAAI,MAAM,CAAC,CAAC,KAAK,IAAI;;;;;;;AAQ1E,MAAM,aACJ,MACA,WACuD;CACvD,IAAI,OAAO,YAAY,MACrB,OAAO,KAAK,KAAK,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE,MAAM,cAAc,CAAC;CAEpE,IAAI,OAAO,eAAe,kBACxB,OAAO,OAAO,KACZ,IAAI,iBAAiB;EACnB,QAAQ;EACR,QAAQ,GAAG,KAAK;CAClB,CAAC,CACH;CAEF,OAAO,OAAO,QAAQ,SAAS,MAAM,CAAC;AACxC;AAEA,MAAM,WACH,QAAmB,UACnB,UACC,OAAO,SAAS,MAAM,KAAK,CAAC,CAAC,KAAK,OAAO,SAAS,WAAW,UAAU,MAAM,MAAM,CAAC,CAAC;AAEzF,MAAM,eAAe,QAAmB,SACtC,KAAK,KAAK;CACR,MAAM,KAAK;CACX,aAAa,KAAK,eAAe,KAAK;CACtC,aAAa,kBAAkB,IAAI;CACnC,KAAK,QAAQ,QAAQ,KAAK,IAAI;CAC9B,QAAQ;AACV,CAAC;;;;;;AAOH,MAAa,cACX,QACA,YAEA,OAAO,UAAU,KACf,OAAO,KAAK,UAAU,YAAY,UAAU,MAAM,KAAK,SAAS,YAAY,QAAQ,IAAI,CAAC,CAAC,CAAC,GAC3F,OAAO,KAAK,QACV,OAAO,cAAc,SAAS,MAAM,CAAC,CAAC,KACpC,OAAO,MAAM;CACX,cAAc;CACd,SAAS,WAAW,YAAY,UAAU,QAAQ,GAAG;AACvD,CAAC,CACH,CACF,CACF"}
@@ -0,0 +1,126 @@
1
+ import { t as McpAuthError } from "./McpError-DWg9BlW5.mjs";
2
+ import { Data, Effect, Option, Redacted } from "effect";
3
+ //#region src/internal/auth.d.ts
4
+ /**
5
+ * An Effect yielding a bearer token. Any external OAuth, vault, or secret
6
+ * manager plugs in here. Read per request, so a producer minting expensive
7
+ * tokens caches internally (as `OAuth.clientCredentials` does).
8
+ */
9
+ type TokenSource = Effect.Effect<Redacted.Redacted<string>, McpAuthError>;
10
+ type Auth = Data.TaggedEnum<{
11
+ /** A fixed token: the turnkey case for a token-gated server. */
12
+ Static: {
13
+ readonly token: Redacted.Redacted<string>;
14
+ readonly scheme?: string;
15
+ };
16
+ /** Bring your own, re-read before every request. */
17
+ TokenSource: {
18
+ readonly token: TokenSource;
19
+ readonly scheme?: string;
20
+ };
21
+ /** Built-in OAuth 2.1, which produces a `TokenSource` and rides the same seam. */
22
+ OAuth: {
23
+ readonly source: TokenSource;
24
+ readonly scheme?: string;
25
+ };
26
+ }>;
27
+ declare const Auth: {
28
+ readonly $is: <Tag extends "OAuth" | "Static" | "TokenSource">(tag: Tag) => (u: unknown) => u is Extract<{
29
+ readonly _tag: "OAuth";
30
+ readonly source: TokenSource;
31
+ readonly scheme?: string;
32
+ }, {
33
+ readonly _tag: Tag;
34
+ }> | Extract<{
35
+ readonly _tag: "Static";
36
+ readonly token: Redacted.Redacted<string>;
37
+ readonly scheme?: string;
38
+ }, {
39
+ readonly _tag: Tag;
40
+ }> | Extract<{
41
+ readonly _tag: "TokenSource";
42
+ readonly token: TokenSource;
43
+ readonly scheme?: string;
44
+ }, {
45
+ readonly _tag: Tag;
46
+ }>;
47
+ readonly $match: {
48
+ <Cases extends { readonly [Tag in "OAuth" | "Static" | "TokenSource"]: (args: Extract<{
49
+ readonly _tag: "OAuth";
50
+ readonly source: TokenSource;
51
+ readonly scheme?: string;
52
+ } | {
53
+ readonly _tag: "Static";
54
+ readonly token: Redacted.Redacted<string>;
55
+ readonly scheme?: string;
56
+ } | {
57
+ readonly _tag: "TokenSource";
58
+ readonly token: TokenSource;
59
+ readonly scheme?: string;
60
+ }, {
61
+ readonly _tag: Tag;
62
+ }>) => any; }>(cases: Cases): (value: {
63
+ readonly _tag: "OAuth";
64
+ readonly source: TokenSource;
65
+ readonly scheme?: string;
66
+ } | {
67
+ readonly _tag: "Static";
68
+ readonly token: Redacted.Redacted<string>;
69
+ readonly scheme?: string;
70
+ } | {
71
+ readonly _tag: "TokenSource";
72
+ readonly token: TokenSource;
73
+ readonly scheme?: string;
74
+ }) => import("effect/Unify").Unify<ReturnType<Cases["OAuth" | "Static" | "TokenSource"]>>;
75
+ <Cases extends { readonly [Tag in "OAuth" | "Static" | "TokenSource"]: (args: Extract<{
76
+ readonly _tag: "OAuth";
77
+ readonly source: TokenSource;
78
+ readonly scheme?: string;
79
+ } | {
80
+ readonly _tag: "Static";
81
+ readonly token: Redacted.Redacted<string>;
82
+ readonly scheme?: string;
83
+ } | {
84
+ readonly _tag: "TokenSource";
85
+ readonly token: TokenSource;
86
+ readonly scheme?: string;
87
+ }, {
88
+ readonly _tag: Tag;
89
+ }>) => any; }>(value: {
90
+ readonly _tag: "OAuth";
91
+ readonly source: TokenSource;
92
+ readonly scheme?: string;
93
+ } | {
94
+ readonly _tag: "Static";
95
+ readonly token: Redacted.Redacted<string>;
96
+ readonly scheme?: string;
97
+ } | {
98
+ readonly _tag: "TokenSource";
99
+ readonly token: TokenSource;
100
+ readonly scheme?: string;
101
+ }, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["OAuth" | "Static" | "TokenSource"]>>;
102
+ };
103
+ readonly OAuth: Data.TaggedEnum.ConstructorFrom<{
104
+ readonly _tag: "OAuth";
105
+ readonly source: TokenSource;
106
+ readonly scheme?: string;
107
+ }, "_tag">;
108
+ readonly Static: Data.TaggedEnum.ConstructorFrom<{
109
+ readonly _tag: "Static";
110
+ readonly token: Redacted.Redacted<string>;
111
+ readonly scheme?: string;
112
+ }, "_tag">;
113
+ readonly TokenSource: Data.TaggedEnum.ConstructorFrom<{
114
+ readonly _tag: "TokenSource";
115
+ readonly token: TokenSource;
116
+ readonly scheme?: string;
117
+ }, "_tag">;
118
+ };
119
+ /**
120
+ * The `Authorization` header for one request, or none for a public server.
121
+ * `Redacted.value` is unwrapped only here.
122
+ */
123
+ declare const authHeaders: (auth: Option.Option<Auth>) => Effect.Effect<Record<string, string>, McpAuthError>;
124
+ //#endregion
125
+ export { TokenSource as n, authHeaders as r, Auth as t };
126
+ //# sourceMappingURL=auth-ZDvsxpCo.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-ZDvsxpCo.d.mts","names":[],"sources":["../src/internal/auth.ts"],"mappings":";;;;;;;;KAaY,cAAc,OAAO,OAAO,SAAS,kBAAkB;KAEvD,OAAO,KAAK;;EAEtB;aAAmB,OAAO,SAAS;aAA2B;;;EAE9D;aAAwB,OAAO;aAAsB;;;EAErD;aAAkB,QAAQ;aAAsB;;;cAGrC;;;aAHe,QAAA;aAA+B;;;;;aAJ/B,OAAA,SAAS;aAAoC;;;;;aAExC,OAAA;aAA+B;;;;;;;eAEpC,QAAA;eAA+B;;;eAJ/B,OAAA,SAAS;eAAoC;;;eAExC,OAAA;eAA+B;;;;;;;;;;;;;;;;;;eAEpC,QAAA;eAA+B;;;eAJ/B,OAAA,SAAS;eAAoC;;;eAExC,OAAA;eAA+B;;;;;;;;;;;;;;;;;;;aAEpC,QAAA;aAA+B;;;;aAJ/B,OAAA,SAAS;aAAoC;;;;aAExC,OAAA;aAA+B;;;;;;;cAiBnD,cAAW,MAChB,OAAO,OAAO,UACnB,OAAO,OAAO,wBAAwB"}
@@ -0,0 +1,4 @@
1
+ import { a as McpError_d_exports } from "./McpError-DWg9BlW5.mjs";
2
+ import { t as Client_d_exports } from "./Client.mjs";
3
+ import { t as Toolkit_d_exports } from "./Toolkit.mjs";
4
+ export { Client_d_exports as Client, McpError_d_exports as McpError, Toolkit_d_exports as Toolkit };
package/dist/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ import { t as McpError_exports } from "./McpError.mjs";
2
+ import { t as Client_exports } from "./Client.mjs";
3
+ import { t as Toolkit_exports } from "./Toolkit.mjs";
4
+ export { Client_exports as Client, McpError_exports as McpError, Toolkit_exports as Toolkit };
@@ -0,0 +1,2 @@
1
+ import { n as TokenSource, r as authHeaders, t as Auth } from "../auth-ZDvsxpCo.mjs";
2
+ export { Auth, TokenSource, authHeaders };
@@ -0,0 +1,25 @@
1
+ import { Data, Effect, Option, Redacted } from "effect";
2
+ //#region src/internal/auth.ts
3
+ /**
4
+ * Auth resolution for the HTTP transport. Era-blind: the stateless and
5
+ * handshake protocols both ride an OAuth 2.1 resource server, and neither
6
+ * knows this file exists. Tokens stay `Redacted` until the wire boundary.
7
+ */
8
+ const Auth = Data.taggedEnum();
9
+ const tokenOf = Auth.$match({
10
+ Static: ({ token }) => Effect.succeed(token),
11
+ TokenSource: ({ token }) => token,
12
+ OAuth: ({ source }) => source
13
+ });
14
+ /**
15
+ * The `Authorization` header for one request, or none for a public server.
16
+ * `Redacted.value` is unwrapped only here.
17
+ */
18
+ const authHeaders = (auth) => Option.match(auth, {
19
+ onNone: () => Effect.succeed({}),
20
+ onSome: (a) => tokenOf(a).pipe(Effect.map((token) => ({ authorization: `${a.scheme ?? "Bearer"} ${Redacted.value(token)}` })))
21
+ });
22
+ //#endregion
23
+ export { Auth, authHeaders };
24
+
25
+ //# sourceMappingURL=auth.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.mjs","names":[],"sources":["../../src/internal/auth.ts"],"sourcesContent":["/**\n * Auth resolution for the HTTP transport. Era-blind: the stateless and\n * handshake protocols both ride an OAuth 2.1 resource server, and neither\n * knows this file exists. Tokens stay `Redacted` until the wire boundary.\n */\nimport { Data, Effect, Option, Redacted } from \"effect\"\nimport type { McpAuthError } from \"../McpError.js\"\n\n/**\n * An Effect yielding a bearer token. Any external OAuth, vault, or secret\n * manager plugs in here. Read per request, so a producer minting expensive\n * tokens caches internally (as `OAuth.clientCredentials` does).\n */\nexport type TokenSource = Effect.Effect<Redacted.Redacted<string>, McpAuthError>\n\nexport type Auth = Data.TaggedEnum<{\n /** A fixed token: the turnkey case for a token-gated server. */\n Static: { readonly token: Redacted.Redacted<string>; readonly scheme?: string }\n /** Bring your own, re-read before every request. */\n TokenSource: { readonly token: TokenSource; readonly scheme?: string }\n /** Built-in OAuth 2.1, which produces a `TokenSource` and rides the same seam. */\n OAuth: { readonly source: TokenSource; readonly scheme?: string }\n}>\n\nexport const Auth = Data.taggedEnum<Auth>()\n\nconst tokenOf: (auth: Auth) => TokenSource = Auth.$match({\n Static: ({ token }) => Effect.succeed(token),\n TokenSource: ({ token }) => token,\n OAuth: ({ source }) => source,\n})\n\n/**\n * The `Authorization` header for one request, or none for a public server.\n * `Redacted.value` is unwrapped only here.\n */\nexport const authHeaders = (\n auth: Option.Option<Auth>,\n): Effect.Effect<Record<string, string>, McpAuthError> =>\n Option.match(auth, {\n onNone: () => Effect.succeed({}),\n onSome: (a) =>\n tokenOf(a).pipe(\n Effect.map((token) => ({\n authorization: `${a.scheme ?? \"Bearer\"} ${Redacted.value(token)}`,\n })),\n ),\n })\n"],"mappings":";;;;;;;AAwBA,MAAa,OAAO,KAAK,WAAiB;AAE1C,MAAM,UAAuC,KAAK,OAAO;CACvD,SAAS,EAAE,YAAY,OAAO,QAAQ,KAAK;CAC3C,cAAc,EAAE,YAAY;CAC5B,QAAQ,EAAE,aAAa;AACzB,CAAC;;;;;AAMD,MAAa,eACX,SAEA,OAAO,MAAM,MAAM;CACjB,cAAc,OAAO,QAAQ,CAAC,CAAC;CAC/B,SAAS,MACP,QAAQ,CAAC,CAAC,CAAC,KACT,OAAO,KAAK,WAAW,EACrB,eAAe,GAAG,EAAE,UAAU,SAAS,GAAG,SAAS,MAAM,KAAK,IAChE,EAAE,CACJ;AACJ,CAAC"}