@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.
- package/dist/Client.d.mts +4 -4
- package/dist/Client.mjs.map +1 -1
- package/dist/Toolkit.d.mts +9 -5
- package/dist/Toolkit.d.mts.map +1 -1
- package/dist/Toolkit.mjs +42 -24
- package/dist/Toolkit.mjs.map +1 -1
- package/dist/auth-CRIPfiPB.d.mts +47 -0
- package/dist/auth-CRIPfiPB.d.mts.map +1 -0
- package/dist/internal/auth.d.mts +1 -1
- package/dist/internal/httpTransport.d.mts +3 -3
- package/dist/internal/httpTransport.mjs +1 -1
- package/dist/internal/httpTransport.mjs.map +1 -1
- package/dist/internal/protocol.d.mts +1 -1
- package/dist/internal/protocols/2025-06-18.d.mts +1 -1
- package/dist/internal/protocols/2026-07-28.d.mts +1 -1
- package/dist/internal/protocols/2026-07-28.d.mts.map +1 -1
- package/dist/internal/protocols/2026-07-28.mjs +3 -2
- package/dist/internal/protocols/2026-07-28.mjs.map +1 -1
- package/dist/internal/rpc.d.mts +1 -1
- package/dist/internal/schema.d.mts +2 -2
- package/dist/internal/schema.mjs +39 -5
- package/dist/internal/schema.mjs.map +1 -1
- package/dist/internal/stdioTransport.d.mts +2 -2
- package/dist/internal/stdioTransport.mjs +1 -1
- package/dist/internal/stdioTransport.mjs.map +1 -1
- package/dist/{protocol-kjC3iAMb.d.mts → protocol-C3L9kaRW.d.mts} +3 -3
- package/dist/{protocol-kjC3iAMb.d.mts.map → protocol-C3L9kaRW.d.mts.map} +1 -1
- package/dist/{rpc-Bq95Lio9.d.mts → rpc-Bs2BCLha.d.mts} +2 -2
- package/dist/{rpc-Bq95Lio9.d.mts.map → rpc-Bs2BCLha.d.mts.map} +1 -1
- package/dist/{schema-D2oUNuo5.d.mts → schema-tDws3q4j.d.mts} +158 -11
- package/dist/schema-tDws3q4j.d.mts.map +1 -0
- package/package.json +7 -7
- package/src/Client.ts +2 -2
- package/src/Toolkit.test.ts +83 -0
- package/src/Toolkit.ts +65 -34
- package/src/internal/httpTransport.ts +1 -1
- package/src/internal/protocols/2026-07-28.ts +3 -2
- package/src/internal/schema.ts +44 -5
- package/src/internal/stdioTransport.ts +1 -1
- package/dist/auth-ZDvsxpCo.d.mts +0 -126
- package/dist/auth-ZDvsxpCo.d.mts.map +0 -1
- package/dist/schema-D2oUNuo5.d.mts.map +0 -1
package/dist/Client.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { i as McpError } from "./McpError-DWg9BlW5.mjs";
|
|
2
|
-
import { n as TokenSource, t as Auth } from "./auth-
|
|
3
|
-
import {
|
|
2
|
+
import { n as TokenSource, t as Auth } from "./auth-CRIPfiPB.mjs";
|
|
3
|
+
import { A as ServerInfo, D as ProtocolVersion, M as ToolInfo, s as CallToolResult } from "./schema-tDws3q4j.mjs";
|
|
4
4
|
import { Context, Effect, Layer, Scope } from "effect";
|
|
5
|
-
import { HttpClient } from "effect/
|
|
6
|
-
import { ChildProcessSpawner } from "effect/
|
|
5
|
+
import { HttpClient } from "effect/http";
|
|
6
|
+
import { ChildProcessSpawner } from "effect/process";
|
|
7
7
|
declare namespace Client_d_exports {
|
|
8
8
|
export { Auth, Mcp, McpClient, McpClientConfig, McpRequirements, McpServerInfo, ProtocolPin, TokenSource, connect, layer };
|
|
9
9
|
}
|
package/dist/Client.mjs.map
CHANGED
|
@@ -1 +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/
|
|
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/http\"\nimport type { ChildProcessSpawner } from \"effect/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"}
|
package/dist/Toolkit.d.mts
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import { i as McpError } from "./McpError-DWg9BlW5.mjs";
|
|
2
2
|
import { McpClient } from "./Client.mjs";
|
|
3
|
-
import { Effect } from "effect";
|
|
3
|
+
import { Effect, Schema } from "effect";
|
|
4
4
|
import * as CoreToolkit from "@effect-uai/core/Toolkit";
|
|
5
5
|
declare namespace Toolkit_d_exports {
|
|
6
|
-
export { McpToolkitOptions, mcpToolkit };
|
|
6
|
+
export { McpToolFailed, McpToolkitOptions, mcpToolkit };
|
|
7
7
|
}
|
|
8
8
|
type McpToolkitOptions = {
|
|
9
9
|
/** Namespace every tool as `<prefix>__<name>`; recommended when composing servers. */
|
|
10
10
|
readonly prefix?: string;
|
|
11
11
|
};
|
|
12
|
+
declare const McpToolFailed_base: Schema.Class<McpToolFailed, Schema.TaggedStruct<"McpToolFailed", {
|
|
13
|
+
readonly message: Schema.String;
|
|
14
|
+
}>, import("effect/Cause").YieldableError>;
|
|
15
|
+
/** The server reported an error (`isError`); the model sees its content as `message`. */
|
|
16
|
+
declare class McpToolFailed extends McpToolFailed_base {}
|
|
12
17
|
/**
|
|
13
18
|
* Build a `Toolkit` from the server's advertised tools. The list is snapshotted
|
|
14
|
-
* here; a tool that later vanishes surfaces
|
|
15
|
-
* `unknown_tool` path.
|
|
19
|
+
* here; a tool that later vanishes surfaces as `Toolkit.run`'s `UnknownTool`.
|
|
16
20
|
*/
|
|
17
21
|
declare const mcpToolkit: (client: McpClient, options?: McpToolkitOptions) => Effect.Effect<CoreToolkit.Toolkit, McpError>;
|
|
18
22
|
//#endregion
|
|
19
|
-
export { McpToolkitOptions, mcpToolkit, Toolkit_d_exports as t };
|
|
23
|
+
export { McpToolFailed, McpToolkitOptions, mcpToolkit, Toolkit_d_exports as t };
|
|
20
24
|
//# sourceMappingURL=Toolkit.d.mts.map
|
package/dist/Toolkit.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toolkit.d.mts","names":[],"sources":["../src/Toolkit.ts"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"Toolkit.d.mts","names":[],"sources":["../src/Toolkit.ts"],"mappings":";;;;;;;KAsBY;;WAED;;;;;;cAIE,sBAAsB;;;;;cAiGtB,aAAU,QACb,WAAS,UACP,sBACT,OAAO,OAAO,YAAY,SAAS"}
|
package/dist/Toolkit.mjs
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
2
|
import { McpProtocolError } from "./McpError.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { Effect, Option } from "effect";
|
|
3
|
+
import { isKnownContent } from "./internal/schema.mjs";
|
|
4
|
+
import { Effect, Match, Option, Schema } from "effect";
|
|
5
|
+
import * as Content from "@effect-uai/core/Content";
|
|
6
|
+
import { fileBase64, fileUrl } from "@effect-uai/core/File";
|
|
7
|
+
import { imageBase64 } from "@effect-uai/core/Image";
|
|
5
8
|
import * as Tool from "@effect-uai/core/Tool";
|
|
6
9
|
import * as CoreToolkit from "@effect-uai/core/Toolkit";
|
|
10
|
+
import * as ToolOutput from "@effect-uai/core/ToolOutput";
|
|
7
11
|
//#region src/Toolkit.ts
|
|
8
12
|
/**
|
|
9
|
-
* Turn a connected MCP server into a core `Toolkit`, one
|
|
13
|
+
* Turn a connected MCP server into a core `Toolkit`, one local tool per MCP
|
|
10
14
|
* tool. Effectful because the tool list is fetched; composes with
|
|
11
15
|
* `Toolkit.compose` like any other kit.
|
|
12
16
|
*/
|
|
13
|
-
var Toolkit_exports = /* @__PURE__ */ __exportAll({
|
|
17
|
+
var Toolkit_exports = /* @__PURE__ */ __exportAll({
|
|
18
|
+
McpToolFailed: () => McpToolFailed,
|
|
19
|
+
mcpToolkit: () => mcpToolkit
|
|
20
|
+
});
|
|
21
|
+
/** The server reported an error (`isError`); the model sees its content as `message`. */
|
|
22
|
+
var McpToolFailed = class extends Schema.TaggedError("@betalyra/effect-uai/mcp/McpToolFailed")("McpToolFailed", { message: Schema.String }) {};
|
|
14
23
|
/**
|
|
15
24
|
* The server owns validation, so the input schema is a passthrough: it carries
|
|
16
25
|
* the server's JSON Schema verbatim (the model sees the real thing) and
|
|
@@ -26,45 +35,54 @@ const passthroughSchema = (tool) => ({ "~standard": {
|
|
|
26
35
|
output: () => ({ type: "object" })
|
|
27
36
|
}
|
|
28
37
|
} });
|
|
29
|
-
const
|
|
38
|
+
const knownValue = Match.type().pipe(Match.discriminatorsExhaustive("type")({
|
|
39
|
+
text: (b) => b.text,
|
|
40
|
+
image: (b) => Content.image(imageBase64(b.data, b.mimeType)),
|
|
41
|
+
audio: (b) => Content.file(fileBase64(b.data, b.mimeType)),
|
|
42
|
+
resource_link: (b) => Content.file(fileUrl(b.uri, b.mimeType), b.name),
|
|
43
|
+
resource: ({ resource }) => "text" in resource ? resource.text : Content.file(fileBase64(resource.blob, resource.mimeType ?? "application/octet-stream"))
|
|
44
|
+
}));
|
|
45
|
+
const valueOf = (block) => isKnownContent(block) ? knownValue(block) : `[${block.type} content omitted]`;
|
|
30
46
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* a short placeholder until the loop grows a multimodal tool-output path.
|
|
47
|
+
* A result as the tool's value: `structuredContent` when the server provides it
|
|
48
|
+
* (its text blocks usually repeat it), plus any media; otherwise every block.
|
|
34
49
|
*/
|
|
35
|
-
const outputOf = (result) =>
|
|
50
|
+
const outputOf = (result) => {
|
|
51
|
+
const values = (result.content ?? []).map(valueOf);
|
|
52
|
+
return Option.match(Option.fromNullishOr(result.structuredContent), {
|
|
53
|
+
onNone: () => ToolOutput.single(values),
|
|
54
|
+
onSome: (structured) => ToolOutput.single([structured, ...values.filter(Content.isMedia)])
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
const messageOf = (result) => (result.content ?? []).map(valueOf).filter((v) => typeof v === "string").join("\n");
|
|
36
58
|
/**
|
|
37
59
|
* Two failure channels, deliberately: `isError` is the server telling the
|
|
38
|
-
* model it went wrong, so it becomes
|
|
60
|
+
* model it went wrong, so it becomes the declared `McpToolFailed`; an
|
|
39
61
|
* `input_required` result is a protocol capability gap the model cannot fix,
|
|
40
|
-
* so it stays a typed `McpError
|
|
62
|
+
* so it stays a typed `McpError` and fails the run.
|
|
41
63
|
*/
|
|
42
|
-
const interpret = (name, result) => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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)));
|
|
64
|
+
const interpret = (name, result) => Match.value(result).pipe(Match.when({ isError: true }, (r) => Effect.fail(new McpToolFailed({ message: messageOf(r) }))), Match.when({ resultType: "input_required" }, () => Effect.fail(new McpProtocolError({
|
|
65
|
+
method: "tools/call",
|
|
66
|
+
reason: `${name} requires interactive input (MRTR), unsupported by this client`
|
|
67
|
+
}))), Match.orElse((r) => Effect.succeed(outputOf(r))));
|
|
51
68
|
const toLocalTool = (client, info) => Tool.make({
|
|
52
69
|
name: info.name,
|
|
53
70
|
description: info.description ?? info.name,
|
|
54
71
|
inputSchema: passthroughSchema(info),
|
|
55
|
-
|
|
72
|
+
outputSchema: Content.Value,
|
|
73
|
+
failureSchema: McpToolFailed,
|
|
74
|
+
run: (input) => client.callTool(info.name, input).pipe(Effect.flatMap((r) => interpret(info.name, r))),
|
|
56
75
|
strict: false
|
|
57
76
|
});
|
|
58
77
|
/**
|
|
59
78
|
* Build a `Toolkit` from the server's advertised tools. The list is snapshotted
|
|
60
|
-
* here; a tool that later vanishes surfaces
|
|
61
|
-
* `unknown_tool` path.
|
|
79
|
+
* here; a tool that later vanishes surfaces as `Toolkit.run`'s `UnknownTool`.
|
|
62
80
|
*/
|
|
63
81
|
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
82
|
onNone: () => kit,
|
|
65
83
|
onSome: (prefix) => CoreToolkit.namespace(prefix, kit)
|
|
66
84
|
}))));
|
|
67
85
|
//#endregion
|
|
68
|
-
export { mcpToolkit, Toolkit_exports as t };
|
|
86
|
+
export { McpToolFailed, mcpToolkit, Toolkit_exports as t };
|
|
69
87
|
|
|
70
88
|
//# sourceMappingURL=Toolkit.mjs.map
|
package/dist/Toolkit.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toolkit.mjs","names":[],"sources":["../src/Toolkit.ts"],"sourcesContent":["/**\n * Turn a connected MCP server into a core `Toolkit`, one
|
|
1
|
+
{"version":3,"file":"Toolkit.mjs","names":[],"sources":["../src/Toolkit.ts"],"sourcesContent":["/**\n * Turn a connected MCP server into a core `Toolkit`, one local tool per MCP\n * tool. Effectful because the tool list is fetched; composes with\n * `Toolkit.compose` like any other kit.\n */\nimport { Effect, Match, Option, Schema } from \"effect\"\nimport * as Content from \"@effect-uai/core/Content\"\nimport { fileBase64, fileUrl } from \"@effect-uai/core/File\"\nimport { imageBase64 } from \"@effect-uai/core/Image\"\nimport * as Tool from \"@effect-uai/core/Tool\"\nimport * as CoreToolkit from \"@effect-uai/core/Toolkit\"\nimport * as ToolOutput from \"@effect-uai/core/ToolOutput\"\nimport type { McpClient } from \"./Client.js\"\nimport { type McpError, McpProtocolError } from \"./McpError.js\"\nimport {\n type CallToolResult,\n type ContentBlock,\n isKnownContent,\n type KnownContent,\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/** The server reported an error (`isError`); the model sees its content as `message`. */\nexport class McpToolFailed extends Schema.TaggedError<McpToolFailed>(\n \"@betalyra/effect-uai/mcp/McpToolFailed\",\n)(\"McpToolFailed\", { message: Schema.String }) {}\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\n// A blob without a MIME type is opaque bytes.\nconst knownValue = Match.type<KnownContent>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n text: (b): string | Content.Media => b.text,\n image: (b) => Content.image(imageBase64(b.data, b.mimeType)),\n audio: (b) => Content.file(fileBase64(b.data, b.mimeType)),\n resource_link: (b) => Content.file(fileUrl(b.uri, b.mimeType), b.name),\n resource: ({ resource }) =>\n \"text\" in resource\n ? resource.text\n : Content.file(fileBase64(resource.blob, resource.mimeType ?? \"application/octet-stream\")),\n }),\n)\n\nconst valueOf = (block: ContentBlock): string | Content.Media =>\n isKnownContent(block) ? knownValue(block) : `[${block.type} content omitted]`\n\n/**\n * A result as the tool's value: `structuredContent` when the server provides it\n * (its text blocks usually repeat it), plus any media; otherwise every block.\n */\nconst outputOf = (result: CallToolResult): Content.Value => {\n const values: ReadonlyArray<Schema.Json | Content.Media> = (result.content ?? []).map(valueOf)\n return Option.match(Option.fromNullishOr(result.structuredContent), {\n onNone: () => ToolOutput.single(values),\n onSome: (structured) => ToolOutput.single([structured, ...values.filter(Content.isMedia)]),\n })\n}\n\nconst messageOf = (result: CallToolResult): string =>\n (result.content ?? [])\n .map(valueOf)\n .filter((v): v is string => typeof v === \"string\")\n .join(\"\\n\")\n\n/**\n * Two failure channels, deliberately: `isError` is the server telling the\n * model it went wrong, so it becomes the declared `McpToolFailed`; an\n * `input_required` result is a protocol capability gap the model cannot fix,\n * so it stays a typed `McpError` and fails the run.\n */\nconst interpret = (\n name: string,\n result: CallToolResult,\n): Effect.Effect<Content.Value, McpError | McpToolFailed> =>\n Match.value(result).pipe(\n Match.when({ isError: true }, (r) => Effect.fail(new McpToolFailed({ message: messageOf(r) }))),\n Match.when({ resultType: \"input_required\" }, () =>\n Effect.fail(\n new McpProtocolError({\n method: \"tools/call\",\n reason: `${name} requires interactive input (MRTR), unsupported by this client`,\n }),\n ),\n ),\n Match.orElse((r) => Effect.succeed(outputOf(r))),\n )\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 // Known only at runtime: any mix of text, JSON and media.\n outputSchema: Content.Value,\n failureSchema: McpToolFailed,\n run: (input) =>\n client.callTool(info.name, input).pipe(Effect.flatMap((r) => interpret(info.name, r))),\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 as `Toolkit.run`'s `UnknownTool`.\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,IAAa,gBAAb,cAAmC,OAAO,YACxC,wCACF,CAAC,CAAC,iBAAiB,EAAE,SAAS,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;;;;;;;AAQhD,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;AAGA,MAAM,aAAa,MAAM,KAAmB,CAAC,CAAC,KAC5C,MAAM,yBAAyB,MAAM,CAAC,CAAC;CACrC,OAAO,MAA8B,EAAE;CACvC,QAAQ,MAAM,QAAQ,MAAM,YAAY,EAAE,MAAM,EAAE,QAAQ,CAAC;CAC3D,QAAQ,MAAM,QAAQ,KAAK,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC;CACzD,gBAAgB,MAAM,QAAQ,KAAK,QAAQ,EAAE,KAAK,EAAE,QAAQ,GAAG,EAAE,IAAI;CACrE,WAAW,EAAE,eACX,UAAU,WACN,SAAS,OACT,QAAQ,KAAK,WAAW,SAAS,MAAM,SAAS,YAAY,0BAA0B,CAAC;AAC/F,CAAC,CACH;AAEA,MAAM,WAAW,UACf,eAAe,KAAK,IAAI,WAAW,KAAK,IAAI,IAAI,MAAM,KAAK;;;;;AAM7D,MAAM,YAAY,WAA0C;CAC1D,MAAM,UAAsD,OAAO,WAAW,CAAC,EAAA,CAAG,IAAI,OAAO;CAC7F,OAAO,OAAO,MAAM,OAAO,cAAc,OAAO,iBAAiB,GAAG;EAClE,cAAc,WAAW,OAAO,MAAM;EACtC,SAAS,eAAe,WAAW,OAAO,CAAC,YAAY,GAAG,OAAO,OAAO,QAAQ,OAAO,CAAC,CAAC;CAC3F,CAAC;AACH;AAEA,MAAM,aAAa,YAChB,OAAO,WAAW,CAAC,EAAA,CACjB,IAAI,OAAO,CAAC,CACZ,QAAQ,MAAmB,OAAO,MAAM,QAAQ,CAAC,CACjD,KAAK,IAAI;;;;;;;AAQd,MAAM,aACJ,MACA,WAEA,MAAM,MAAM,MAAM,CAAC,CAAC,KAClB,MAAM,KAAK,EAAE,SAAS,KAAK,IAAI,MAAM,OAAO,KAAK,IAAI,cAAc,EAAE,SAAS,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,GAC9F,MAAM,KAAK,EAAE,YAAY,iBAAiB,SACxC,OAAO,KACL,IAAI,iBAAiB;CACnB,QAAQ;CACR,QAAQ,GAAG,KAAK;AAClB,CAAC,CACH,CACF,GACA,MAAM,QAAQ,MAAM,OAAO,QAAQ,SAAS,CAAC,CAAC,CAAC,CACjD;AAEF,MAAM,eAAe,QAAmB,SACtC,KAAK,KAAK;CACR,MAAM,KAAK;CACX,aAAa,KAAK,eAAe,KAAK;CACtC,aAAa,kBAAkB,IAAI;CAEnC,cAAc,QAAQ;CACtB,eAAe;CACf,MAAM,UACJ,OAAO,SAAS,KAAK,MAAM,KAAK,CAAC,CAAC,KAAK,OAAO,SAAS,MAAM,UAAU,KAAK,MAAM,CAAC,CAAC,CAAC;CACvF,QAAQ;AACV,CAAC;;;;;AAMH,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,47 @@
|
|
|
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: Data.TaggedEnum.Constructor<{
|
|
28
|
+
readonly _tag: "OAuth";
|
|
29
|
+
readonly source: TokenSource;
|
|
30
|
+
readonly scheme?: string;
|
|
31
|
+
} | {
|
|
32
|
+
readonly _tag: "Static";
|
|
33
|
+
readonly token: Redacted.Redacted<string>;
|
|
34
|
+
readonly scheme?: string;
|
|
35
|
+
} | {
|
|
36
|
+
readonly _tag: "TokenSource";
|
|
37
|
+
readonly token: TokenSource;
|
|
38
|
+
readonly scheme?: string;
|
|
39
|
+
}>;
|
|
40
|
+
/**
|
|
41
|
+
* The `Authorization` header for one request, or none for a public server.
|
|
42
|
+
* `Redacted.value` is unwrapped only here.
|
|
43
|
+
*/
|
|
44
|
+
declare const authHeaders: (auth: Option.Option<Auth>) => Effect.Effect<Record<string, string>, McpAuthError>;
|
|
45
|
+
//#endregion
|
|
46
|
+
export { TokenSource as n, authHeaders as r, Auth as t };
|
|
47
|
+
//# sourceMappingURL=auth-CRIPfiPB.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-CRIPfiPB.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,MAAI,KAAA,WAAA;;WAHW,QAAA;WAA+B;;;WAJ/B,OAAA,SAAS;WAAoC;;;WAExC,OAAA;WAA+B;;;;;;cAiBnD,cAAW,MAChB,OAAO,OAAO,UACnB,OAAO,OAAO,wBAAwB"}
|
package/dist/internal/auth.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as TokenSource, r as authHeaders, t as Auth } from "../auth-
|
|
1
|
+
import { n as TokenSource, r as authHeaders, t as Auth } from "../auth-CRIPfiPB.mjs";
|
|
2
2
|
export { Auth, TokenSource, authHeaders };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { i as McpError } from "../McpError-DWg9BlW5.mjs";
|
|
2
|
-
import { t as Auth } from "../auth-
|
|
3
|
-
import { i as Transport } from "../rpc-
|
|
2
|
+
import { t as Auth } from "../auth-CRIPfiPB.mjs";
|
|
3
|
+
import { i as Transport } from "../rpc-Bs2BCLha.mjs";
|
|
4
4
|
import { Effect, Scope } from "effect";
|
|
5
|
-
import { HttpClient } from "effect/
|
|
5
|
+
import { HttpClient } from "effect/http";
|
|
6
6
|
//#region src/internal/httpTransport.d.ts
|
|
7
7
|
type HttpConfig = {
|
|
8
8
|
readonly url: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { McpAuthRequired, McpConnectFailed, McpProtocolError, McpTransportClosed } from "../McpError.mjs";
|
|
2
2
|
import { authHeaders } from "./auth.mjs";
|
|
3
3
|
import { Effect, Match, Option, Queue, Ref, Stream } from "effect";
|
|
4
|
-
import { HttpClient, HttpClientRequest } from "effect/
|
|
4
|
+
import { HttpClient, HttpClientRequest } from "effect/http";
|
|
5
5
|
import * as SSE from "@effect-uai/core/SSE";
|
|
6
6
|
//#region src/internal/httpTransport.ts
|
|
7
7
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"httpTransport.mjs","names":[],"sources":["../../src/internal/httpTransport.ts"],"sourcesContent":["/**\n * Streamable HTTP `Transport`. Each client message is its own POST; the server\n * answers with either one JSON object or a request-scoped SSE stream ending in\n * the JSON-RPC response, and a client MUST accept both. Frames from every\n * in-flight POST demux into one `messages` stream, which the rpc core\n * correlates by id.\n *\n * Interrupting a request aborts its POST, which is exactly the spec's\n * cancellation signal (closing the response stream).\n */\nimport { Cause, Effect, Match, Option, Queue, Ref, type Scope, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest, type HttpClientResponse } from \"effect/
|
|
1
|
+
{"version":3,"file":"httpTransport.mjs","names":[],"sources":["../../src/internal/httpTransport.ts"],"sourcesContent":["/**\n * Streamable HTTP `Transport`. Each client message is its own POST; the server\n * answers with either one JSON object or a request-scoped SSE stream ending in\n * the JSON-RPC response, and a client MUST accept both. Frames from every\n * in-flight POST demux into one `messages` stream, which the rpc core\n * correlates by id.\n *\n * Interrupting a request aborts its POST, which is exactly the spec's\n * cancellation signal (closing the response stream).\n */\nimport { Cause, Effect, Match, Option, Queue, Ref, type Scope, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest, type HttpClientResponse } from \"effect/http\"\nimport * as SSE from \"@effect-uai/core/SSE\"\nimport {\n McpAuthRequired,\n McpConnectFailed,\n type McpError,\n McpProtocolError,\n McpTransportClosed,\n} from \"../McpError.js\"\nimport { type Auth, authHeaders } from \"./auth.js\"\nimport type { SendMeta, Transport } from \"./rpc.js\"\n\nexport type HttpConfig = {\n readonly url: string\n /** Static, non-auth headers applied to every request. */\n readonly headers?: Record<string, string>\n /** Omit for a public server. Resolved per request, so a rotating source works. */\n readonly auth?: Auth\n}\n\n/** RFC 9728: the `resource_metadata` pointer on a 401's `WWW-Authenticate`. */\nconst resourceMetadataUrl = (wwwAuthenticate: string): string | undefined =>\n /resource_metadata=\"([^\"]+)\"/.exec(wwwAuthenticate)?.[1]\n\nconst unauthorized = (response: HttpClientResponse.HttpClientResponse): McpError => {\n const header = response.headers[\"www-authenticate\"]\n const pointer = header === undefined ? undefined : resourceMetadataUrl(header)\n return new McpAuthRequired({\n ...(pointer !== undefined ? { resourceMetadataUrl: pointer } : {}),\n ...(header !== undefined ? { wwwAuthenticate: header } : {}),\n })\n}\n\n/**\n * A non-2xx that is not a 401. The body still matters: a modern server answers\n * `400` for `-32022` / `-32021` / `-32020` and `404` for `-32601`, and era\n * detection reads those codes off the JSON-RPC body. So the body is forwarded\n * into `messages` whenever it parses as JSON, and only a bodyless failure\n * becomes a transport error.\n */\nconst isJsonRpcBody = (body: string): boolean => body.trimStart().startsWith(\"{\")\n\nexport const make = (\n config: HttpConfig,\n): Effect.Effect<Transport, McpError, Scope.Scope | HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const inbox = yield* Queue.make<string, Cause.Done>()\n yield* Effect.addFinalizer(() => Queue.end(inbox))\n\n // The legacy era's `Mcp-Session-Id` is a transport concern, not a protocol\n // one: capture whatever the server mints and echo it from then on, so the\n // protocol implementations stay transport-agnostic. Stateless servers\n // never send one, and this stays empty.\n const session = yield* Ref.make(Option.none<string>())\n\n const post = (frame: string, meta?: SendMeta): Effect.Effect<void, McpError> =>\n Effect.gen(function* () {\n const sessionId = yield* Ref.get(session)\n const auth = yield* authHeaders(Option.fromNullishOr(config.auth))\n const request = HttpClientRequest.post(config.url).pipe(\n HttpClientRequest.setHeaders({\n \"content-type\": \"application/json\",\n // Both response shapes are legal for any request.\n accept: \"application/json, text/event-stream\",\n ...config.headers,\n ...auth,\n ...meta?.headers,\n ...Option.match(sessionId, {\n onNone: () => ({}),\n onSome: (id) => ({ \"Mcp-Session-Id\": id }),\n }),\n }),\n // The content type must be set on the body: it overrides any header.\n HttpClientRequest.bodyText(frame, \"application/json\"),\n )\n const response = yield* client\n .execute(request)\n .pipe(\n Effect.mapError(\n (cause) => new McpConnectFailed({ reason: \"MCP request failed\", raw: cause }),\n ),\n )\n yield* captureSession(response)\n yield* ingest(response)\n })\n\n const captureSession = (\n response: HttpClientResponse.HttpClientResponse,\n ): Effect.Effect<void> => {\n const minted = response.headers[\"mcp-session-id\"]\n return minted === undefined ? Effect.void : Ref.set(session, Option.some(minted))\n }\n\n const ingest = (\n response: HttpClientResponse.HttpClientResponse,\n ): Effect.Effect<void, McpError> =>\n Match.value(response.status).pipe(\n Match.when(401, () => Effect.fail(unauthorized(response))),\n Match.when(404, () => expiredOrBody(response)),\n // 202 answers a notification and carries no body.\n Match.when(202, () => Effect.void),\n Match.orElse(() => readFrames(response)),\n )\n\n // A 404 against a session we hold means the server dropped it; the caller\n // reconnects rather than retrying into a dead session.\n const expiredOrBody = (\n response: HttpClientResponse.HttpClientResponse,\n ): Effect.Effect<void, McpError> =>\n Ref.get(session).pipe(\n Effect.flatMap((held) =>\n Option.isSome(held)\n ? Effect.fail(new McpTransportClosed({ reason: \"the server expired this session\" }))\n : readBody(response),\n ),\n )\n\n const readFrames = (\n response: HttpClientResponse.HttpClientResponse,\n ): Effect.Effect<void, McpError> =>\n (response.headers[\"content-type\"] ?? \"\").includes(\"text/event-stream\")\n ? readSse(response)\n : readBody(response)\n\n const readSse = (\n response: HttpClientResponse.HttpClientResponse,\n ): Effect.Effect<void, McpError> =>\n response.stream.pipe(\n SSE.fromBytes,\n Stream.runForEach((event) => Queue.offer(inbox, event.data)),\n Effect.mapError(\n (cause) => new McpTransportClosed({ reason: \"SSE stream failed\", raw: cause }),\n ),\n )\n\n const readBody = (\n response: HttpClientResponse.HttpClientResponse,\n ): Effect.Effect<void, McpError> =>\n response.text.pipe(\n Effect.mapError(\n (cause) => new McpTransportClosed({ reason: \"could not read response\", raw: cause }),\n ),\n Effect.flatMap((body) =>\n isJsonRpcBody(body)\n ? Effect.asVoid(Queue.offer(inbox, body))\n : Effect.fail(\n new McpProtocolError({\n code: response.status,\n reason: `HTTP ${response.status} with a non-JSON-RPC body`,\n raw: body.slice(0, 500),\n }),\n ),\n ),\n )\n\n // The POST runs on the caller's fiber so interruption aborts it, which is\n // the spec's cancellation signal.\n return { send: post, messages: Stream.fromQueue(inbox) } satisfies Transport\n })\n"],"mappings":";;;;;;;;;;;;;;;;;AAgCA,MAAM,uBAAuB,oBAC3B,8BAA8B,KAAK,eAAe,CAAC,GAAG;AAExD,MAAM,gBAAgB,aAA8D;CAClF,MAAM,SAAS,SAAS,QAAQ;CAChC,MAAM,UAAU,WAAW,KAAA,IAAY,KAAA,IAAY,oBAAoB,MAAM;CAC7E,OAAO,IAAI,gBAAgB;EACzB,GAAI,YAAY,KAAA,IAAY,EAAE,qBAAqB,QAAQ,IAAI,CAAC;EAChE,GAAI,WAAW,KAAA,IAAY,EAAE,iBAAiB,OAAO,IAAI,CAAC;CAC5D,CAAC;AACH;;;;;;;;AASA,MAAM,iBAAiB,SAA0B,KAAK,UAAU,CAAC,CAAC,WAAW,GAAG;AAEhF,MAAa,QACX,WAEA,OAAO,IAAI,aAAa;CACtB,MAAM,SAAS,OAAO,WAAW;CACjC,MAAM,QAAQ,OAAO,MAAM,KAAyB;CACpD,OAAO,OAAO,mBAAmB,MAAM,IAAI,KAAK,CAAC;CAMjD,MAAM,UAAU,OAAO,IAAI,KAAK,OAAO,KAAa,CAAC;CAErD,MAAM,QAAQ,OAAe,SAC3B,OAAO,IAAI,aAAa;EACtB,MAAM,YAAY,OAAO,IAAI,IAAI,OAAO;EACxC,MAAM,OAAO,OAAO,YAAY,OAAO,cAAc,OAAO,IAAI,CAAC;EACjE,MAAM,UAAU,kBAAkB,KAAK,OAAO,GAAG,CAAC,CAAC,KACjD,kBAAkB,WAAW;GAC3B,gBAAgB;GAEhB,QAAQ;GACR,GAAG,OAAO;GACV,GAAG;GACH,GAAG,MAAM;GACT,GAAG,OAAO,MAAM,WAAW;IACzB,eAAe,CAAC;IAChB,SAAS,QAAQ,EAAE,kBAAkB,GAAG;GAC1C,CAAC;EACH,CAAC,GAED,kBAAkB,SAAS,OAAO,kBAAkB,CACtD;EACA,MAAM,WAAW,OAAO,OACrB,QAAQ,OAAO,CAAC,CAChB,KACC,OAAO,UACJ,UAAU,IAAI,iBAAiB;GAAE,QAAQ;GAAsB,KAAK;EAAM,CAAC,CAC9E,CACF;EACF,OAAO,eAAe,QAAQ;EAC9B,OAAO,OAAO,QAAQ;CACxB,CAAC;CAEH,MAAM,kBACJ,aACwB;EACxB,MAAM,SAAS,SAAS,QAAQ;EAChC,OAAO,WAAW,KAAA,IAAY,OAAO,OAAO,IAAI,IAAI,SAAS,OAAO,KAAK,MAAM,CAAC;CAClF;CAEA,MAAM,UACJ,aAEA,MAAM,MAAM,SAAS,MAAM,CAAC,CAAC,KAC3B,MAAM,KAAK,WAAW,OAAO,KAAK,aAAa,QAAQ,CAAC,CAAC,GACzD,MAAM,KAAK,WAAW,cAAc,QAAQ,CAAC,GAE7C,MAAM,KAAK,WAAW,OAAO,IAAI,GACjC,MAAM,aAAa,WAAW,QAAQ,CAAC,CACzC;CAIF,MAAM,iBACJ,aAEA,IAAI,IAAI,OAAO,CAAC,CAAC,KACf,OAAO,SAAS,SACd,OAAO,OAAO,IAAI,IACd,OAAO,KAAK,IAAI,mBAAmB,EAAE,QAAQ,kCAAkC,CAAC,CAAC,IACjF,SAAS,QAAQ,CACvB,CACF;CAEF,MAAM,cACJ,cAEC,SAAS,QAAQ,mBAAmB,GAAA,CAAI,SAAS,mBAAmB,IACjE,QAAQ,QAAQ,IAChB,SAAS,QAAQ;CAEvB,MAAM,WACJ,aAEA,SAAS,OAAO,KACd,IAAI,WACJ,OAAO,YAAY,UAAU,MAAM,MAAM,OAAO,MAAM,IAAI,CAAC,GAC3D,OAAO,UACJ,UAAU,IAAI,mBAAmB;EAAE,QAAQ;EAAqB,KAAK;CAAM,CAAC,CAC/E,CACF;CAEF,MAAM,YACJ,aAEA,SAAS,KAAK,KACZ,OAAO,UACJ,UAAU,IAAI,mBAAmB;EAAE,QAAQ;EAA2B,KAAK;CAAM,CAAC,CACrF,GACA,OAAO,SAAS,SACd,cAAc,IAAI,IACd,OAAO,OAAO,MAAM,MAAM,OAAO,IAAI,CAAC,IACtC,OAAO,KACL,IAAI,iBAAiB;EACnB,MAAM,SAAS;EACf,QAAQ,QAAQ,SAAS,OAAO;EAChC,KAAK,KAAK,MAAM,GAAG,GAAG;CACxB,CAAC,CACH,CACN,CACF;CAIF,OAAO;EAAE,MAAM;EAAM,UAAU,OAAO,UAAU,KAAK;CAAE;AACzD,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as modernRejection, n as Protocol, r as ProtocolProbe, t as ModernRejection } from "../protocol-
|
|
1
|
+
import { i as modernRejection, n as Protocol, r as ProtocolProbe, t as ModernRejection } from "../protocol-C3L9kaRW.mjs";
|
|
2
2
|
export { ModernRejection, Protocol, ProtocolProbe, modernRejection };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"2026-07-28.d.mts","names":[],"sources":["../../../src/internal/protocols/2026-07-28.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"2026-07-28.d.mts","names":[],"sources":["../../../src/internal/protocols/2026-07-28.ts"],"mappings":";;;;;;cA0Da,cAAW;;;;;;cAqCX,OAAO"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { McpUnsupportedProtocol } from "../../McpError.mjs";
|
|
2
2
|
import { CLIENT_INFO, LATEST_VERSION, META_CLIENT_CAPABILITIES, META_CLIENT_INFO, META_PROTOCOL_VERSION, asProtocolVersion, decodeDiscoverResult } from "../schema.mjs";
|
|
3
3
|
import { modernRejection } from "../protocol.mjs";
|
|
4
|
-
import { Effect,
|
|
4
|
+
import { Effect, Option, Predicate, Record } from "effect";
|
|
5
|
+
import { Base64 } from "effect/encoding";
|
|
5
6
|
//#region src/internal/protocols/2026-07-28.ts
|
|
6
7
|
/**
|
|
7
8
|
* Protocol revision 2026-07-28: the stateless era. No handshake and no
|
|
@@ -31,7 +32,7 @@ const HEADER_SAFE = /^[\x21-\x7e]([\x20-\x7e]*[\x21-\x7e])?$/;
|
|
|
31
32
|
* Header values outside the safe set (and any literal that would be mistaken
|
|
32
33
|
* for the marker) ride the spec's Base64 sentinel form.
|
|
33
34
|
*/
|
|
34
|
-
const headerValue = (raw) => HEADER_SAFE.test(raw) && !SENTINEL.test(raw) ? raw : `=?base64?${
|
|
35
|
+
const headerValue = (raw) => HEADER_SAFE.test(raw) && !SENTINEL.test(raw) ? raw : `=?base64?${Base64.encode(raw)}?=`;
|
|
35
36
|
const makeProtocol = (version, serverInfo) => ({
|
|
36
37
|
version,
|
|
37
38
|
serverInfo,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"2026-07-28.mjs","names":[],"sources":["../../../src/internal/protocols/2026-07-28.ts"],"sourcesContent":["/**\n * Protocol revision 2026-07-28: the stateless era. No handshake and no\n * session; every request self-describes via `_meta`, mirrored into the HTTP\n * request headers. Servers never send requests back, so `onInbound` has\n * nothing to answer.\n *\n * Files here are named for the revision they implement. A later revision that\n * keeps this wire shape gets its own file re-exporting this one.\n */\nimport { Effect,
|
|
1
|
+
{"version":3,"file":"2026-07-28.mjs","names":[],"sources":["../../../src/internal/protocols/2026-07-28.ts"],"sourcesContent":["/**\n * Protocol revision 2026-07-28: the stateless era. No handshake and no\n * session; every request self-describes via `_meta`, mirrored into the HTTP\n * request headers. Servers never send requests back, so `onInbound` has\n * nothing to answer.\n *\n * Files here are named for the revision they implement. A later revision that\n * keeps this wire shape gets its own file re-exporting this one.\n */\nimport { Effect, Option, Predicate, Record, type Scope } from \"effect\"\nimport { Base64 } from \"effect/encoding\"\nimport { type McpError, McpUnsupportedProtocol } from \"../../McpError.js\"\nimport { modernRejection, type Protocol, type ProtocolProbe } from \"../protocol.js\"\nimport type { McpConnection, SendMeta } from \"../rpc.js\"\nimport {\n asProtocolVersion,\n CLIENT_INFO,\n decodeDiscoverResult,\n META_CLIENT_CAPABILITIES,\n META_CLIENT_INFO,\n META_SERVER_INFO,\n META_PROTOCOL_VERSION,\n type McpMethod,\n LATEST_VERSION,\n type ProtocolVersion,\n type ServerInfo,\n} from \"../schema.js\"\n\nconst UNKNOWN_SERVER: ServerInfo = { name: \"unknown\" }\n\n/**\n * Which param field carries the subject name mirrored into `Mcp-Name`, per\n * method. Keyed by string rather than `McpMethod` so the methods a later\n * revision adds need no cast here.\n */\nconst NAME_FIELD: Record<string, string> = {\n \"tools/call\": \"name\",\n \"prompts/get\": \"name\",\n \"resources/read\": \"uri\",\n}\n\nconst subjectName = (method: McpMethod, params: unknown): Option.Option<string> =>\n Record.get(NAME_FIELD, method).pipe(\n Option.flatMap((field) => Record.get(asRecord(params), field)),\n Option.filter(Predicate.isString),\n )\n\nconst asRecord = (value: unknown): Record<string, unknown> =>\n Predicate.isObject(value) ? value : {}\n\nconst SENTINEL = /^=\\?base64\\?.*\\?=$/\n// Visible ASCII only, no leading or trailing space (RFC 9110 field values).\nconst HEADER_SAFE = /^[\\x21-\\x7e]([\\x20-\\x7e]*[\\x21-\\x7e])?$/\n\n/**\n * Header values outside the safe set (and any literal that would be mistaken\n * for the marker) ride the spec's Base64 sentinel form.\n */\nexport const headerValue = (raw: string): string =>\n HEADER_SAFE.test(raw) && !SENTINEL.test(raw) ? raw : `=?base64?${Base64.encode(raw)}?=`\n\nconst makeProtocol = (version: ProtocolVersion, serverInfo: ServerInfo): Protocol => ({\n version,\n serverInfo,\n\n envelope: (_method, params) => ({\n ...asRecord(params),\n _meta: {\n [META_PROTOCOL_VERSION]: version,\n [META_CLIENT_INFO]: CLIENT_INFO,\n [META_CLIENT_CAPABILITIES]: {},\n },\n }),\n\n // `MCP-Protocol-Version` MUST equal the `_meta` version or the server\n // answers -32020, so both are minted from the same `version`.\n meta: (method, params): SendMeta => ({\n headers: {\n \"MCP-Protocol-Version\": version,\n \"Mcp-Method\": method,\n ...subjectName(method, params).pipe(\n Option.map((name) => ({ \"Mcp-Name\": headerValue(name) })),\n Option.getOrElse(() => ({})),\n ),\n },\n }),\n\n onInbound: () => Effect.void,\n})\n\n/**\n * Probe with `server/discover`. A `DiscoverResult` means modern. A modern\n * error code means modern-but-rejected: on `-32022` retry with a mutual\n * version. Anything else yields `None`, i.e. \"try the legacy handshake\".\n */\nexport const probe: ProtocolProbe = (connection) =>\n discover(connection, LATEST_VERSION).pipe(\n Effect.map(Option.some),\n Effect.catch((error) =>\n Option.match(modernRejection(error), {\n // Not a modern error shape, so this is not a modern server.\n onNone: () => Effect.succeedNone,\n onSome: ({ supported }) => retryOnMutualVersion(connection, supported),\n }),\n ),\n )\n\nconst retryOnMutualVersion = (\n connection: McpConnection,\n offered: ReadonlyArray<string>,\n): Effect.Effect<Option.Option<Protocol>, McpError, Scope.Scope> =>\n Option.match(Option.firstSomeOf(offered.map(asProtocolVersion)), {\n onNone: () =>\n Effect.fail(\n new McpUnsupportedProtocol({\n offered,\n reason: \"the server is modern but shares no protocol version with this client\",\n }),\n ),\n onSome: (version) => discover(connection, version).pipe(Effect.map(Option.some)),\n })\n\nconst discover = (\n connection: McpConnection,\n version: ProtocolVersion,\n): Effect.Effect<Protocol, McpError, Scope.Scope> =>\n Effect.gen(function* () {\n const probing = makeProtocol(version, UNKNOWN_SERVER)\n const raw = yield* connection.request(\n \"server/discover\",\n probing.envelope(\"server/discover\", {}),\n probing.meta(\"server/discover\", {}),\n )\n const result = yield* decodeDiscoverResult(raw).pipe(\n Effect.mapError(\n () =>\n new McpUnsupportedProtocol({\n offered: [],\n reason: \"server/discover returned an undecodable result\",\n }),\n ),\n )\n // The server answered on `version`, so it supports it; prefer it, and only\n // fall to its list if it somehow disagrees.\n const agreed = result.supportedVersions.includes(version)\n ? version\n : Option.getOrElse(\n Option.firstSomeOf(result.supportedVersions.map(asProtocolVersion)),\n () => version,\n )\n const identity = result._meta?.[META_SERVER_INFO] ?? result.serverInfo ?? UNKNOWN_SERVER\n return makeProtocol(agreed, identity)\n })\n"],"mappings":";;;;;;;;;;;;;;;AA4BA,MAAM,iBAA6B,EAAE,MAAM,UAAU;;;;;;AAOrD,MAAM,aAAqC;CACzC,cAAc;CACd,eAAe;CACf,kBAAkB;AACpB;AAEA,MAAM,eAAe,QAAmB,WACtC,OAAO,IAAI,YAAY,MAAM,CAAC,CAAC,KAC7B,OAAO,SAAS,UAAU,OAAO,IAAI,SAAS,MAAM,GAAG,KAAK,CAAC,GAC7D,OAAO,OAAO,UAAU,QAAQ,CAClC;AAEF,MAAM,YAAY,UAChB,UAAU,SAAS,KAAK,IAAI,QAAQ,CAAC;AAEvC,MAAM,WAAW;AAEjB,MAAM,cAAc;;;;;AAMpB,MAAa,eAAe,QAC1B,YAAY,KAAK,GAAG,KAAK,CAAC,SAAS,KAAK,GAAG,IAAI,MAAM,YAAY,OAAO,OAAO,GAAG,EAAE;AAEtF,MAAM,gBAAgB,SAA0B,gBAAsC;CACpF;CACA;CAEA,WAAW,SAAS,YAAY;EAC9B,GAAG,SAAS,MAAM;EAClB,OAAO;IACJ,wBAAwB;IACxB,mBAAmB;IACnB,2BAA2B,CAAC;EAC/B;CACF;CAIA,OAAO,QAAQ,YAAsB,EACnC,SAAS;EACP,wBAAwB;EACxB,cAAc;EACd,GAAG,YAAY,QAAQ,MAAM,CAAC,CAAC,KAC7B,OAAO,KAAK,UAAU,EAAE,YAAY,YAAY,IAAI,EAAE,EAAE,GACxD,OAAO,iBAAiB,CAAC,EAAE,CAC7B;CACF,EACF;CAEA,iBAAiB,OAAO;AAC1B;;;;;;AAOA,MAAa,SAAwB,eACnC,SAAS,YAAY,cAAc,CAAC,CAAC,KACnC,OAAO,IAAI,OAAO,IAAI,GACtB,OAAO,OAAO,UACZ,OAAO,MAAM,gBAAgB,KAAK,GAAG;CAEnC,cAAc,OAAO;CACrB,SAAS,EAAE,gBAAgB,qBAAqB,YAAY,SAAS;AACvE,CAAC,CACH,CACF;AAEF,MAAM,wBACJ,YACA,YAEA,OAAO,MAAM,OAAO,YAAY,QAAQ,IAAI,iBAAiB,CAAC,GAAG;CAC/D,cACE,OAAO,KACL,IAAI,uBAAuB;EACzB;EACA,QAAQ;CACV,CAAC,CACH;CACF,SAAS,YAAY,SAAS,YAAY,OAAO,CAAC,CAAC,KAAK,OAAO,IAAI,OAAO,IAAI,CAAC;AACjF,CAAC;AAEH,MAAM,YACJ,YACA,YAEA,OAAO,IAAI,aAAa;CACtB,MAAM,UAAU,aAAa,SAAS,cAAc;CACpD,MAAM,MAAM,OAAO,WAAW,QAC5B,mBACA,QAAQ,SAAS,mBAAmB,CAAC,CAAC,GACtC,QAAQ,KAAK,mBAAmB,CAAC,CAAC,CACpC;CACA,MAAM,SAAS,OAAO,qBAAqB,GAAG,CAAC,CAAC,KAC9C,OAAO,eAEH,IAAI,uBAAuB;EACzB,SAAS,CAAC;EACV,QAAQ;CACV,CAAC,CACL,CACF;CAGA,MAAM,SAAS,OAAO,kBAAkB,SAAS,OAAO,IACpD,UACA,OAAO,UACL,OAAO,YAAY,OAAO,kBAAkB,IAAI,iBAAiB,CAAC,SAC5D,OACR;CACJ,MAAM,WAAW,OAAO,QAAA,yCAA6B,OAAO,cAAc;CAC1E,OAAO,aAAa,QAAQ,QAAQ;AACtC,CAAC"}
|
package/dist/internal/rpc.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as open, i as Transport, n as McpConnection, r as SendMeta, t as Inbound } from "../rpc-
|
|
1
|
+
import { a as open, i as Transport, n as McpConnection, r as SendMeta, t as Inbound } from "../rpc-Bs2BCLha.mjs";
|
|
2
2
|
export { Inbound, McpConnection, SendMeta, Transport, open };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
export { CLIENT_INFO, CODE_HEADER_MISMATCH, CODE_METHOD_NOT_FOUND, CODE_MISSING_CLIENT_CAPABILITY, CODE_UNSUPPORTED_PROTOCOL_VERSION, CallToolResult, ContentBlock, DiscoverResult, InboundMessage, InitializeResult, JsonRpcErrorObject, JsonRpcId, LATEST_VERSION, LEGACY_VERSION, ListToolsResult, META_CLIENT_CAPABILITIES, META_CLIENT_INFO, META_PROTOCOL_VERSION, META_SERVER_INFO, MODERN_ERROR_CODES, McpMethod, OtherContent, ProtocolVersion, ResultMeta, ServerInfo, TextContent, ToolInfo, UnsupportedVersionData, asProtocolVersion, decodeCallToolResult, decodeDiscoverResult, decodeInboundMessage, decodeInitializeResult, decodeListToolsResult, decodeUnsupportedVersionData, errorFrame,
|
|
1
|
+
import { A as ServerInfo, B as decodeUnsupportedVersionData, C as META_SERVER_INFO, D as ProtocolVersion, E as OtherContent, F as decodeCallToolResult, G as resultFrame, H as isKnownContent, I as decodeDiscoverResult, L as decodeInboundMessage, M as ToolInfo, N as UnsupportedVersionData, O as ResourceLink, P as asProtocolVersion, R as decodeInitializeResult, S as META_PROTOCOL_VERSION, T as McpMethod, U as notificationFrame, V as errorFrame, W as requestFrame, _ as LATEST_VERSION, a as CODE_MISSING_CLIENT_CAPABILITY, b as META_CLIENT_CAPABILITIES, c as ContentBlock, d as ImageContent, f as InboundMessage, g as KnownContent, h as JsonRpcId, i as CODE_METHOD_NOT_FOUND, j as TextContent, k as ResultMeta, l as DiscoverResult, m as JsonRpcErrorObject, n as CLIENT_INFO, o as CODE_UNSUPPORTED_PROTOCOL_VERSION, p as InitializeResult, r as CODE_HEADER_MISMATCH, s as CallToolResult, t as AudioContent, u as EmbeddedResource, v as LEGACY_VERSION, w as MODERN_ERROR_CODES, x as META_CLIENT_INFO, y as ListToolsResult, z as decodeListToolsResult } from "../schema-tDws3q4j.mjs";
|
|
2
|
+
export { AudioContent, CLIENT_INFO, CODE_HEADER_MISMATCH, CODE_METHOD_NOT_FOUND, CODE_MISSING_CLIENT_CAPABILITY, CODE_UNSUPPORTED_PROTOCOL_VERSION, CallToolResult, ContentBlock, DiscoverResult, EmbeddedResource, ImageContent, InboundMessage, InitializeResult, JsonRpcErrorObject, JsonRpcId, KnownContent, LATEST_VERSION, LEGACY_VERSION, ListToolsResult, META_CLIENT_CAPABILITIES, META_CLIENT_INFO, META_PROTOCOL_VERSION, META_SERVER_INFO, MODERN_ERROR_CODES, McpMethod, OtherContent, ProtocolVersion, ResourceLink, ResultMeta, ServerInfo, TextContent, ToolInfo, UnsupportedVersionData, asProtocolVersion, decodeCallToolResult, decodeDiscoverResult, decodeInboundMessage, decodeInitializeResult, decodeListToolsResult, decodeUnsupportedVersionData, errorFrame, isKnownContent, notificationFrame, requestFrame, resultFrame };
|
package/dist/internal/schema.mjs
CHANGED
|
@@ -120,14 +120,48 @@ const TextContent = Schema.Struct({
|
|
|
120
120
|
type: Schema.Literal("text"),
|
|
121
121
|
text: Schema.String
|
|
122
122
|
});
|
|
123
|
-
|
|
123
|
+
const ImageContent = Schema.Struct({
|
|
124
|
+
type: Schema.Literal("image"),
|
|
125
|
+
data: Schema.String,
|
|
126
|
+
mimeType: Schema.String
|
|
127
|
+
});
|
|
128
|
+
const AudioContent = Schema.Struct({
|
|
129
|
+
type: Schema.Literal("audio"),
|
|
130
|
+
data: Schema.String,
|
|
131
|
+
mimeType: Schema.String
|
|
132
|
+
});
|
|
133
|
+
const ResourceLink = Schema.Struct({
|
|
134
|
+
type: Schema.Literal("resource_link"),
|
|
135
|
+
uri: Schema.String,
|
|
136
|
+
name: Schema.optional(Schema.String),
|
|
137
|
+
mimeType: Schema.optional(Schema.String)
|
|
138
|
+
});
|
|
139
|
+
const EmbeddedResource = Schema.Struct({
|
|
140
|
+
type: Schema.Literal("resource"),
|
|
141
|
+
resource: Schema.Union([Schema.Struct({
|
|
142
|
+
uri: Schema.String,
|
|
143
|
+
text: Schema.String
|
|
144
|
+
}), Schema.Struct({
|
|
145
|
+
uri: Schema.String,
|
|
146
|
+
blob: Schema.String,
|
|
147
|
+
mimeType: Schema.optional(Schema.String)
|
|
148
|
+
})])
|
|
149
|
+
});
|
|
150
|
+
/** A block type this client does not know; summarized for the model. */
|
|
124
151
|
const OtherContent = Schema.Struct({ type: Schema.String });
|
|
125
|
-
const
|
|
126
|
-
|
|
152
|
+
const KnownContent = Schema.Union([
|
|
153
|
+
TextContent,
|
|
154
|
+
ImageContent,
|
|
155
|
+
AudioContent,
|
|
156
|
+
ResourceLink,
|
|
157
|
+
EmbeddedResource
|
|
158
|
+
]);
|
|
159
|
+
const isKnownContent = Schema.is(KnownContent);
|
|
160
|
+
const ContentBlock = Schema.Union([KnownContent, OtherContent]);
|
|
127
161
|
const CallToolResult = Schema.Struct({
|
|
128
162
|
content: Schema.optionalKey(Schema.Array(ContentBlock)),
|
|
129
163
|
isError: Schema.optional(Schema.Boolean),
|
|
130
|
-
structuredContent: Schema.optional(Schema.
|
|
164
|
+
structuredContent: Schema.optional(Schema.Json),
|
|
131
165
|
resultType: Schema.optional(Schema.String)
|
|
132
166
|
});
|
|
133
167
|
const decodeCallToolResult = Schema.decodeUnknownEffect(CallToolResult);
|
|
@@ -163,6 +197,6 @@ const InitializeResult = Schema.Struct({
|
|
|
163
197
|
});
|
|
164
198
|
const decodeInitializeResult = Schema.decodeUnknownEffect(InitializeResult);
|
|
165
199
|
//#endregion
|
|
166
|
-
export { CLIENT_INFO, CODE_HEADER_MISMATCH, CODE_METHOD_NOT_FOUND, CODE_MISSING_CLIENT_CAPABILITY, CODE_UNSUPPORTED_PROTOCOL_VERSION, CallToolResult, ContentBlock, DiscoverResult, InboundMessage, InitializeResult, JsonRpcErrorObject, JsonRpcId, LATEST_VERSION, LEGACY_VERSION, ListToolsResult, META_CLIENT_CAPABILITIES, META_CLIENT_INFO, META_PROTOCOL_VERSION, META_SERVER_INFO, MODERN_ERROR_CODES, McpMethod, OtherContent, ProtocolVersion, ResultMeta, ServerInfo, TextContent, ToolInfo, UnsupportedVersionData, asProtocolVersion, decodeCallToolResult, decodeDiscoverResult, decodeInboundMessage, decodeInitializeResult, decodeListToolsResult, decodeUnsupportedVersionData, errorFrame,
|
|
200
|
+
export { AudioContent, CLIENT_INFO, CODE_HEADER_MISMATCH, CODE_METHOD_NOT_FOUND, CODE_MISSING_CLIENT_CAPABILITY, CODE_UNSUPPORTED_PROTOCOL_VERSION, CallToolResult, ContentBlock, DiscoverResult, EmbeddedResource, ImageContent, InboundMessage, InitializeResult, JsonRpcErrorObject, JsonRpcId, KnownContent, LATEST_VERSION, LEGACY_VERSION, ListToolsResult, META_CLIENT_CAPABILITIES, META_CLIENT_INFO, META_PROTOCOL_VERSION, META_SERVER_INFO, MODERN_ERROR_CODES, McpMethod, OtherContent, ProtocolVersion, ResourceLink, ResultMeta, ServerInfo, TextContent, ToolInfo, UnsupportedVersionData, asProtocolVersion, decodeCallToolResult, decodeDiscoverResult, decodeInboundMessage, decodeInitializeResult, decodeListToolsResult, decodeUnsupportedVersionData, errorFrame, isKnownContent, notificationFrame, requestFrame, resultFrame };
|
|
167
201
|
|
|
168
202
|
//# sourceMappingURL=schema.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.mjs","names":[],"sources":["../../src/internal/schema.ts"],"sourcesContent":["/**\n * Self-contained MCP wire schemas, both protocol eras. The single source of\n * truth for the wire format; nothing MCP-shaped is imported from effect.\n * Decodes are deliberately loose (unknown extra fields pass through) so\n * spec-compliant servers with richer payloads never fail the client.\n */\nimport { Option, Schema } from \"effect\"\n\n// ---------------------------------------------------------------------------\n// Protocol versions\n// ---------------------------------------------------------------------------\n\n/** Every protocol version this client can speak. */\nexport const ProtocolVersion = Schema.Literals([\"2026-07-28\", \"2025-11-25\", \"2025-06-18\"])\nexport type ProtocolVersion = typeof ProtocolVersion.Type\n\n/** Newest version we speak: the stateless protocol, and what the probe tries first. */\nexport const LATEST_VERSION = \"2026-07-28\" satisfies ProtocolVersion\n\n/** The legacy handshake era we negotiate; 2025-11-25 is wire-compatible. */\nexport const LEGACY_VERSION = \"2025-06-18\" satisfies ProtocolVersion\n\nconst isProtocolVersion = Schema.is(ProtocolVersion)\n\n/** Narrow a server-offered version string to one we support. */\nexport const asProtocolVersion = (raw: string): Option.Option<ProtocolVersion> =>\n isProtocolVersion(raw) ? Option.some(raw) : Option.none()\n\n/**\n * The JSON-RPC methods this client issues. Not to be confused with an HTTP\n * method: on Streamable HTTP every one of these travels as a POST, and the\n * name is mirrored into the `Mcp-Method` header.\n */\nexport const McpMethod = Schema.Literals([\n \"server/discover\",\n \"initialize\",\n \"tools/list\",\n \"tools/call\",\n])\nexport type McpMethod = typeof McpMethod.Type\n\n/** Modern `_meta` keys (SEP-2575): every request self-describes its protocol. */\nexport const META_PROTOCOL_VERSION = \"io.modelcontextprotocol/protocolVersion\"\nexport const META_CLIENT_INFO = \"io.modelcontextprotocol/clientInfo\"\nexport const META_CLIENT_CAPABILITIES = \"io.modelcontextprotocol/clientCapabilities\"\n/** Servers identify themselves in each *result's* `_meta`, not at the top level. */\nexport const META_SERVER_INFO = \"io.modelcontextprotocol/serverInfo\"\n\nexport const CLIENT_INFO = { name: \"@effect-uai/mcp\", version: \"0.12.1\" }\n\n/** Modern JSON-RPC error codes. */\nexport const CODE_HEADER_MISMATCH = -32020\nexport const CODE_MISSING_CLIENT_CAPABILITY = -32021\nexport const CODE_UNSUPPORTED_PROTOCOL_VERSION = -32022\nexport const CODE_METHOD_NOT_FOUND = -32601\n\n/**\n * A reply carrying one of these proves the server speaks a modern version, so\n * era detection corrects and retries instead of falling back to `initialize`.\n */\nexport const MODERN_ERROR_CODES: ReadonlySet<number> = new Set([\n CODE_HEADER_MISMATCH,\n CODE_MISSING_CLIENT_CAPABILITY,\n CODE_UNSUPPORTED_PROTOCOL_VERSION,\n])\n\n// ---------------------------------------------------------------------------\n// JSON-RPC envelope\n// ---------------------------------------------------------------------------\n\nexport const JsonRpcId = Schema.Union([Schema.Number, Schema.String])\nexport type JsonRpcId = typeof JsonRpcId.Type\n\nexport const JsonRpcErrorObject = Schema.Struct({\n code: Schema.Number,\n message: Schema.optional(Schema.String),\n data: Schema.optional(Schema.Unknown),\n})\nexport type JsonRpcErrorObject = typeof JsonRpcErrorObject.Type\n\n/**\n * Any inbound frame: a reply (`id` + `result`/`error`), a server-initiated\n * request (`method` + `id`), or a notification (`method`, no `id`). One\n * loose struct so the reader can classify without a union decode.\n */\nexport const InboundMessage = Schema.Struct({\n // Null is legal and meaningful: JSON-RPC uses it for errors raised before a\n // request could be attributed. Rejecting it would drop the frame entirely.\n id: Schema.optional(Schema.NullOr(JsonRpcId)),\n result: Schema.optional(Schema.Unknown),\n error: Schema.optional(JsonRpcErrorObject),\n method: Schema.optional(Schema.String),\n params: Schema.optional(Schema.Unknown),\n})\nexport type InboundMessage = typeof InboundMessage.Type\n\nexport const decodeInboundMessage = Schema.decodeUnknownEffect(InboundMessage)\n\n// --- outbound frame builders -----------------------------------------------\n\nexport const requestFrame = (id: JsonRpcId, method: string, params: unknown): string =>\n JSON.stringify({ jsonrpc: \"2.0\", id, method, params: params ?? {} })\n\nexport const notificationFrame = (method: string, params?: unknown): string =>\n JSON.stringify(\n params === undefined ? { jsonrpc: \"2.0\", method } : { jsonrpc: \"2.0\", method, params },\n )\n\nexport const resultFrame = (id: JsonRpcId, result: unknown): string =>\n JSON.stringify({ jsonrpc: \"2.0\", id, result })\n\nexport const errorFrame = (id: JsonRpcId, code: number, message: string): string =>\n JSON.stringify({ jsonrpc: \"2.0\", id, error: { code, message } })\n\n// ---------------------------------------------------------------------------\n// Shared: tools\n// ---------------------------------------------------------------------------\n\nexport const ToolInfo = Schema.Struct({\n name: Schema.String,\n description: Schema.optional(Schema.String),\n inputSchema: Schema.Record(Schema.String, Schema.Unknown),\n})\nexport type ToolInfo = typeof ToolInfo.Type\n\n/**\n * Modern list results may carry cache metadata (`ttlMs` / `cacheScope`);\n * v1 reads but does not act on them (input for a later refresh feature).\n */\nexport const ListToolsResult = Schema.Struct({\n tools: Schema.Array(ToolInfo),\n nextCursor: Schema.optional(Schema.String),\n ttlMs: Schema.optional(Schema.Number),\n cacheScope: Schema.optional(Schema.String),\n})\nexport type ListToolsResult = typeof ListToolsResult.Type\n\nexport const decodeListToolsResult = Schema.decodeUnknownEffect(ListToolsResult)\n\n// --- content blocks ---------------------------------------------------------\n\nexport const TextContent = Schema.Struct({\n type: Schema.Literal(\"text\"),\n text: Schema.String,\n})\nexport type TextContent = typeof TextContent.Type\n\n/** Non-text blocks (image / audio / resource) are placeholder-summarized in v1. */\nexport const OtherContent = Schema.Struct({\n type: Schema.String,\n})\n\nexport const ContentBlock = Schema.Union([TextContent, OtherContent])\nexport type ContentBlock = typeof ContentBlock.Type\n\nexport const isTextContent = Schema.is(TextContent)\n\nexport const CallToolResult = Schema.Struct({\n content: Schema.optionalKey(Schema.Array(ContentBlock)),\n isError: Schema.optional(Schema.Boolean),\n structuredContent: Schema.optional(Schema.Unknown),\n // Required on modern results; absent on legacy ones.\n resultType: Schema.optional(Schema.String),\n})\nexport type CallToolResult = typeof CallToolResult.Type\n\nexport const decodeCallToolResult = Schema.decodeUnknownEffect(CallToolResult)\n\n// ---------------------------------------------------------------------------\n// Modern era (2026-07-28)\n// ---------------------------------------------------------------------------\n\nexport const ServerInfo = Schema.Struct({\n name: Schema.String,\n version: Schema.optional(Schema.String),\n title: Schema.optional(Schema.String),\n})\nexport type ServerInfo = typeof ServerInfo.Type\n\n/** The subset of a result's `_meta` we read. */\nexport const ResultMeta = Schema.Struct({\n [META_SERVER_INFO]: Schema.optional(ServerInfo),\n})\nexport type ResultMeta = typeof ResultMeta.Type\n\nexport const DiscoverResult = Schema.Struct({\n supportedVersions: Schema.Array(Schema.String),\n capabilities: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),\n // Spec-conformant servers put identity in `_meta`; a top-level `serverInfo`\n // is accepted too, since that is where the legacy handshake carries it.\n _meta: Schema.optional(ResultMeta),\n serverInfo: Schema.optional(ServerInfo),\n instructions: Schema.optional(Schema.String),\n})\nexport type DiscoverResult = typeof DiscoverResult.Type\n\nexport const decodeDiscoverResult = Schema.decodeUnknownEffect(DiscoverResult)\n\n/**\n * `data` of a `-32022` UnsupportedProtocolVersionError reply. Decoded, not\n * hand-parsed: the shape is server-controlled and may carry extra fields.\n */\nexport const UnsupportedVersionData = Schema.Struct({\n supported: Schema.optional(Schema.Array(Schema.String)),\n requested: Schema.optional(Schema.NullOr(Schema.String)),\n})\nexport type UnsupportedVersionData = typeof UnsupportedVersionData.Type\n\nexport const decodeUnsupportedVersionData = Schema.decodeUnknownOption(UnsupportedVersionData)\n\n// ---------------------------------------------------------------------------\n// Legacy era (2025-06-18 / 2025-11-25)\n// ---------------------------------------------------------------------------\n\nexport const InitializeResult = Schema.Struct({\n protocolVersion: Schema.String,\n capabilities: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),\n serverInfo: Schema.optional(ServerInfo),\n instructions: Schema.optional(Schema.String),\n})\nexport type InitializeResult = typeof InitializeResult.Type\n\nexport const decodeInitializeResult = Schema.decodeUnknownEffect(InitializeResult)\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"schema.mjs","names":[],"sources":["../../src/internal/schema.ts"],"sourcesContent":["/**\n * Self-contained MCP wire schemas, both protocol eras. The single source of\n * truth for the wire format; nothing MCP-shaped is imported from effect.\n * Decodes are deliberately loose (unknown extra fields pass through) so\n * spec-compliant servers with richer payloads never fail the client.\n */\nimport { Option, Schema } from \"effect\"\n\n// ---------------------------------------------------------------------------\n// Protocol versions\n// ---------------------------------------------------------------------------\n\n/** Every protocol version this client can speak. */\nexport const ProtocolVersion = Schema.Literals([\"2026-07-28\", \"2025-11-25\", \"2025-06-18\"])\nexport type ProtocolVersion = typeof ProtocolVersion.Type\n\n/** Newest version we speak: the stateless protocol, and what the probe tries first. */\nexport const LATEST_VERSION = \"2026-07-28\" satisfies ProtocolVersion\n\n/** The legacy handshake era we negotiate; 2025-11-25 is wire-compatible. */\nexport const LEGACY_VERSION = \"2025-06-18\" satisfies ProtocolVersion\n\nconst isProtocolVersion = Schema.is(ProtocolVersion)\n\n/** Narrow a server-offered version string to one we support. */\nexport const asProtocolVersion = (raw: string): Option.Option<ProtocolVersion> =>\n isProtocolVersion(raw) ? Option.some(raw) : Option.none()\n\n/**\n * The JSON-RPC methods this client issues. Not to be confused with an HTTP\n * method: on Streamable HTTP every one of these travels as a POST, and the\n * name is mirrored into the `Mcp-Method` header.\n */\nexport const McpMethod = Schema.Literals([\n \"server/discover\",\n \"initialize\",\n \"tools/list\",\n \"tools/call\",\n])\nexport type McpMethod = typeof McpMethod.Type\n\n/** Modern `_meta` keys (SEP-2575): every request self-describes its protocol. */\nexport const META_PROTOCOL_VERSION = \"io.modelcontextprotocol/protocolVersion\"\nexport const META_CLIENT_INFO = \"io.modelcontextprotocol/clientInfo\"\nexport const META_CLIENT_CAPABILITIES = \"io.modelcontextprotocol/clientCapabilities\"\n/** Servers identify themselves in each *result's* `_meta`, not at the top level. */\nexport const META_SERVER_INFO = \"io.modelcontextprotocol/serverInfo\"\n\nexport const CLIENT_INFO = { name: \"@effect-uai/mcp\", version: \"0.12.1\" }\n\n/** Modern JSON-RPC error codes. */\nexport const CODE_HEADER_MISMATCH = -32020\nexport const CODE_MISSING_CLIENT_CAPABILITY = -32021\nexport const CODE_UNSUPPORTED_PROTOCOL_VERSION = -32022\nexport const CODE_METHOD_NOT_FOUND = -32601\n\n/**\n * A reply carrying one of these proves the server speaks a modern version, so\n * era detection corrects and retries instead of falling back to `initialize`.\n */\nexport const MODERN_ERROR_CODES: ReadonlySet<number> = new Set([\n CODE_HEADER_MISMATCH,\n CODE_MISSING_CLIENT_CAPABILITY,\n CODE_UNSUPPORTED_PROTOCOL_VERSION,\n])\n\n// ---------------------------------------------------------------------------\n// JSON-RPC envelope\n// ---------------------------------------------------------------------------\n\nexport const JsonRpcId = Schema.Union([Schema.Number, Schema.String])\nexport type JsonRpcId = typeof JsonRpcId.Type\n\nexport const JsonRpcErrorObject = Schema.Struct({\n code: Schema.Number,\n message: Schema.optional(Schema.String),\n data: Schema.optional(Schema.Unknown),\n})\nexport type JsonRpcErrorObject = typeof JsonRpcErrorObject.Type\n\n/**\n * Any inbound frame: a reply (`id` + `result`/`error`), a server-initiated\n * request (`method` + `id`), or a notification (`method`, no `id`). One\n * loose struct so the reader can classify without a union decode.\n */\nexport const InboundMessage = Schema.Struct({\n // Null is legal and meaningful: JSON-RPC uses it for errors raised before a\n // request could be attributed. Rejecting it would drop the frame entirely.\n id: Schema.optional(Schema.NullOr(JsonRpcId)),\n result: Schema.optional(Schema.Unknown),\n error: Schema.optional(JsonRpcErrorObject),\n method: Schema.optional(Schema.String),\n params: Schema.optional(Schema.Unknown),\n})\nexport type InboundMessage = typeof InboundMessage.Type\n\nexport const decodeInboundMessage = Schema.decodeUnknownEffect(InboundMessage)\n\n// --- outbound frame builders -----------------------------------------------\n\nexport const requestFrame = (id: JsonRpcId, method: string, params: unknown): string =>\n JSON.stringify({ jsonrpc: \"2.0\", id, method, params: params ?? {} })\n\nexport const notificationFrame = (method: string, params?: unknown): string =>\n JSON.stringify(\n params === undefined ? { jsonrpc: \"2.0\", method } : { jsonrpc: \"2.0\", method, params },\n )\n\nexport const resultFrame = (id: JsonRpcId, result: unknown): string =>\n JSON.stringify({ jsonrpc: \"2.0\", id, result })\n\nexport const errorFrame = (id: JsonRpcId, code: number, message: string): string =>\n JSON.stringify({ jsonrpc: \"2.0\", id, error: { code, message } })\n\n// ---------------------------------------------------------------------------\n// Shared: tools\n// ---------------------------------------------------------------------------\n\nexport const ToolInfo = Schema.Struct({\n name: Schema.String,\n description: Schema.optional(Schema.String),\n inputSchema: Schema.Record(Schema.String, Schema.Unknown),\n})\nexport type ToolInfo = typeof ToolInfo.Type\n\n/**\n * Modern list results may carry cache metadata (`ttlMs` / `cacheScope`);\n * v1 reads but does not act on them (input for a later refresh feature).\n */\nexport const ListToolsResult = Schema.Struct({\n tools: Schema.Array(ToolInfo),\n nextCursor: Schema.optional(Schema.String),\n ttlMs: Schema.optional(Schema.Number),\n cacheScope: Schema.optional(Schema.String),\n})\nexport type ListToolsResult = typeof ListToolsResult.Type\n\nexport const decodeListToolsResult = Schema.decodeUnknownEffect(ListToolsResult)\n\n// --- content blocks ---------------------------------------------------------\n\nexport const TextContent = Schema.Struct({\n type: Schema.Literal(\"text\"),\n text: Schema.String,\n})\nexport type TextContent = typeof TextContent.Type\n\nexport const ImageContent = Schema.Struct({\n type: Schema.Literal(\"image\"),\n data: Schema.String,\n mimeType: Schema.String,\n})\n\nexport const AudioContent = Schema.Struct({\n type: Schema.Literal(\"audio\"),\n data: Schema.String,\n mimeType: Schema.String,\n})\n\nexport const ResourceLink = Schema.Struct({\n type: Schema.Literal(\"resource_link\"),\n uri: Schema.String,\n name: Schema.optional(Schema.String),\n mimeType: Schema.optional(Schema.String),\n})\n\nexport const EmbeddedResource = Schema.Struct({\n type: Schema.Literal(\"resource\"),\n resource: Schema.Union([\n Schema.Struct({ uri: Schema.String, text: Schema.String }),\n Schema.Struct({\n uri: Schema.String,\n blob: Schema.String,\n mimeType: Schema.optional(Schema.String),\n }),\n ]),\n})\n\n/** A block type this client does not know; summarized for the model. */\nexport const OtherContent = Schema.Struct({\n type: Schema.String,\n})\n\nexport const KnownContent = Schema.Union([\n TextContent,\n ImageContent,\n AudioContent,\n ResourceLink,\n EmbeddedResource,\n])\nexport type KnownContent = typeof KnownContent.Type\nexport const isKnownContent = Schema.is(KnownContent)\n\nexport const ContentBlock = Schema.Union([KnownContent, OtherContent])\nexport type ContentBlock = typeof ContentBlock.Type\n\nexport const CallToolResult = Schema.Struct({\n content: Schema.optionalKey(Schema.Array(ContentBlock)),\n isError: Schema.optional(Schema.Boolean),\n structuredContent: Schema.optional(Schema.Json),\n // Required on modern results; absent on legacy ones.\n resultType: Schema.optional(Schema.String),\n})\nexport type CallToolResult = typeof CallToolResult.Type\n\nexport const decodeCallToolResult = Schema.decodeUnknownEffect(CallToolResult)\n\n// ---------------------------------------------------------------------------\n// Modern era (2026-07-28)\n// ---------------------------------------------------------------------------\n\nexport const ServerInfo = Schema.Struct({\n name: Schema.String,\n version: Schema.optional(Schema.String),\n title: Schema.optional(Schema.String),\n})\nexport type ServerInfo = typeof ServerInfo.Type\n\n/** The subset of a result's `_meta` we read. */\nexport const ResultMeta = Schema.Struct({\n [META_SERVER_INFO]: Schema.optional(ServerInfo),\n})\nexport type ResultMeta = typeof ResultMeta.Type\n\nexport const DiscoverResult = Schema.Struct({\n supportedVersions: Schema.Array(Schema.String),\n capabilities: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),\n // Spec-conformant servers put identity in `_meta`; a top-level `serverInfo`\n // is accepted too, since that is where the legacy handshake carries it.\n _meta: Schema.optional(ResultMeta),\n serverInfo: Schema.optional(ServerInfo),\n instructions: Schema.optional(Schema.String),\n})\nexport type DiscoverResult = typeof DiscoverResult.Type\n\nexport const decodeDiscoverResult = Schema.decodeUnknownEffect(DiscoverResult)\n\n/**\n * `data` of a `-32022` UnsupportedProtocolVersionError reply. Decoded, not\n * hand-parsed: the shape is server-controlled and may carry extra fields.\n */\nexport const UnsupportedVersionData = Schema.Struct({\n supported: Schema.optional(Schema.Array(Schema.String)),\n requested: Schema.optional(Schema.NullOr(Schema.String)),\n})\nexport type UnsupportedVersionData = typeof UnsupportedVersionData.Type\n\nexport const decodeUnsupportedVersionData = Schema.decodeUnknownOption(UnsupportedVersionData)\n\n// ---------------------------------------------------------------------------\n// Legacy era (2025-06-18 / 2025-11-25)\n// ---------------------------------------------------------------------------\n\nexport const InitializeResult = Schema.Struct({\n protocolVersion: Schema.String,\n capabilities: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),\n serverInfo: Schema.optional(ServerInfo),\n instructions: Schema.optional(Schema.String),\n})\nexport type InitializeResult = typeof InitializeResult.Type\n\nexport const decodeInitializeResult = Schema.decodeUnknownEffect(InitializeResult)\n"],"mappings":"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"}
|