@fractaal/pi-mcp 1.4.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +60 -5
  3. package/dist/config.d.ts +117 -0
  4. package/dist/config.d.ts.map +1 -0
  5. package/dist/config.js +153 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/index.d.ts +5 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +5 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/input-schema.d.ts +7 -0
  12. package/dist/input-schema.d.ts.map +1 -0
  13. package/dist/input-schema.js +13 -0
  14. package/dist/input-schema.js.map +1 -0
  15. package/dist/naming.d.ts +18 -0
  16. package/dist/naming.d.ts.map +1 -0
  17. package/dist/naming.js +29 -0
  18. package/dist/naming.js.map +1 -0
  19. package/dist/oauth/auth-provider.d.ts +52 -0
  20. package/dist/oauth/auth-provider.d.ts.map +1 -0
  21. package/dist/oauth/auth-provider.js +97 -0
  22. package/dist/oauth/auth-provider.js.map +1 -0
  23. package/dist/oauth/callback.d.ts.map +1 -1
  24. package/dist/oauth/callback.js +5 -1
  25. package/dist/oauth/callback.js.map +1 -1
  26. package/dist/oauth/client-settings.d.ts +107 -0
  27. package/dist/oauth/client-settings.d.ts.map +1 -0
  28. package/dist/oauth/client-settings.js +114 -0
  29. package/dist/oauth/client-settings.js.map +1 -0
  30. package/dist/oauth/discovery.js +1 -1
  31. package/dist/oauth/discovery.js.map +1 -1
  32. package/dist/oauth/errors.d.ts +7 -0
  33. package/dist/oauth/errors.d.ts.map +1 -1
  34. package/dist/oauth/errors.js +10 -0
  35. package/dist/oauth/errors.js.map +1 -1
  36. package/dist/oauth/flow.d.ts +10 -0
  37. package/dist/oauth/flow.d.ts.map +1 -1
  38. package/dist/oauth/flow.js +31 -8
  39. package/dist/oauth/flow.js.map +1 -1
  40. package/dist/oauth/index.d.ts +5 -1
  41. package/dist/oauth/index.d.ts.map +1 -1
  42. package/dist/oauth/index.js +4 -1
  43. package/dist/oauth/index.js.map +1 -1
  44. package/dist/oauth/loopback.d.ts +51 -0
  45. package/dist/oauth/loopback.d.ts.map +1 -0
  46. package/dist/oauth/loopback.js +98 -0
  47. package/dist/oauth/loopback.js.map +1 -0
  48. package/dist/oauth/sign-in.d.ts +73 -0
  49. package/dist/oauth/sign-in.d.ts.map +1 -0
  50. package/dist/oauth/sign-in.js +182 -0
  51. package/dist/oauth/sign-in.js.map +1 -0
  52. package/dist/oauth/types.js +1 -1
  53. package/dist/oauth/types.js.map +1 -1
  54. package/dist/output-limit.d.ts +24 -0
  55. package/dist/output-limit.d.ts.map +1 -0
  56. package/dist/output-limit.js +58 -0
  57. package/dist/output-limit.js.map +1 -0
  58. package/dist/transports/lines.d.ts +34 -0
  59. package/dist/transports/lines.d.ts.map +1 -0
  60. package/dist/transports/lines.js +85 -0
  61. package/dist/transports/lines.js.map +1 -0
  62. package/dist/transports/pipe.d.ts +49 -0
  63. package/dist/transports/pipe.d.ts.map +1 -0
  64. package/dist/transports/pipe.js +96 -0
  65. package/dist/transports/pipe.js.map +1 -0
  66. package/dist/transports/stdio.d.ts +1 -4
  67. package/dist/transports/stdio.d.ts.map +1 -1
  68. package/dist/transports/stdio.js +20 -51
  69. package/dist/transports/stdio.js.map +1 -1
  70. package/dist/transports/streamable-http.d.ts.map +1 -1
  71. package/dist/transports/streamable-http.js +77 -12
  72. package/dist/transports/streamable-http.js.map +1 -1
  73. package/package.json +3 -4
  74. /package/{LICENSES → THIRD_PARTY_NOTICES}/modelcontextprotocol-typescript-sdk.txt +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.6.0] - 2026-11-06
4
+
5
+ ### Added
6
+
7
+ - Added `PipeTransport`, MCP's stdio transport over a program started by the caller through an `McpPipe` (`write`, `closeStdin`, `close`, `exited`), for example `@earendil-works/pi-env`'s `openProcess()` on another machine. `StdioTransport` and `PipeTransport` share the newline-delimited framing and the stderr tail.
8
+ - Added MCP OAuth sign-in in two halves (`@earendil-works/pi-mcp/oauth`), moved from pi's coding agent: `startMcpSignIn()` refreshes the stored tokens or returns the authorization URL, and `completeMcpSignIn()` exchanges the code later, possibly in another request with the same state store. The code comes from a loopback callback server (`listenForLoopbackSignIn()`, `waitForLoopbackSignIn()`), the application's own callback route, or a pasted redirect URL (`responseFromRedirectUrl()`). Client ID Metadata Documents (`clientMetadataDocuments`) and the client name are the caller's. Aborting a sign-in's `signal` also stops `startMcpSignIn()` while it waits for the refresh lock; a store's `withRefreshLock()` throws `McpRefreshLockBusyError` when another process holds the lock too long, which makes `startMcpSignIn()` skip the refresh, while any other lock failure fails the sign-in. Also added `createMcpAuthProvider()`, which refreshes tokens once across requests and, through `McpOAuthServerStore.withRefreshLock()`, across processes; its `redirectUrl` option names a redirect URI the application signs in with, so tokens from a Client ID Metadata Document that lists only an https callback route can be refreshed. Also added the `refreshOnly` option of `authorizeMcp()`, which refreshes without registering a client or replacing a pending authorization's `state` and PKCE verifier.
9
+ - Added helpers that let applications name and configure MCP tools like pi's coding agent, moved from it: `createMcpToolName()` (`mcp__<server>__<tool>`, sanitized, with a hash suffix for long or colliding names) and `mcpNamespace()`; `normalizeMcpInputSchema()`, which makes an input schema an object schema; `limitMcpContent()` and `MCP_OUTPUT_MAX_BYTES`, which cut model-facing text over 20KB in the middle and optionally save the full text through a caller's `McpOutputSaver`; and `validateMcpServerConfig()`, which validates one entry of the `mcpServers` configuration shape (stdio, Streamable HTTP, `oauth`, `enabled`, `description`, `timeout`), returns the entry tagged with its transport, and keeps the application's own fields for it to check afterwards. `isMcpServerOverride()` recognizes entries that only override a server defined elsewhere.
10
+
11
+ ### Fixed
12
+
13
+ - The third-party licenses are in `THIRD_PARTY_NOTICES/`, renamed from `LICENSES/`, which Vite's license notices read as the package's license file, failing browser builds that bundle the package.
14
+ - Fixed Streamable HTTP `application/json` responses bypassing `maxMessageBytes`; oversized JSON responses are now cancelled and rejected like oversized SSE events ([#11335](https://github.com/earendil-works/pi/issues/11335))
15
+ - Fixed Streamable HTTP errors reading the whole HTTP error body; only the first 8 KiB kept in the error is read, and the rest is cancelled
16
+ - Fixed `OAuthCallbackServer.close()` waiting for minutes when the browser kept an unused connection open to the callback port, for example a preconnect. It now closes the remaining connections ([#11311](https://github.com/earendil-works/pi/issues/11311))
17
+
18
+ ## [1.5.0] - 2026-11-02
19
+
20
+ ### Changed
21
+
22
+ - README: the `AgentTool` wrapping example passes the MCP input schema as plain JSON Schema instead of through TypeBox's `Type.Unsafe`.
23
+
3
24
  ## [1.4.1] - 2026-10-24
4
25
 
5
26
  ### Fixed
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A small, standalone Model Context Protocol client. It does not depend on the official MCP SDK or other pi packages.
4
4
 
5
- The package provides a transport-neutral client core, stdio and Streamable HTTP transports, and an in-memory testing transport.
5
+ The package provides a transport-neutral client core, stdio and Streamable HTTP transports, a stdio transport over a pipe to a program started elsewhere, and an in-memory testing transport.
6
6
 
7
7
  ## Usage
8
8
 
@@ -27,6 +27,24 @@ await client.close();
27
27
 
28
28
  For a remote server, use `new StreamableHttpTransport({ url, headers })`. Fetch can be injected for proxying or custom networking.
29
29
 
30
+ An MCP transport owns framing and I/O. It delivers individual JSON-RPC messages to `McpClient`; the client owns request correlation, initialization, timeouts, cancellation, server requests, and protocol-level helpers.
31
+
32
+ ### Stdio servers started elsewhere
33
+
34
+ `PipeTransport` speaks MCP's stdio framing over a program that something else starts, for example on another machine. `open` starts the program, connects its stdout and stderr to the given handlers, and returns an `McpPipe` (`write`, `closeStdin`, `close` to kill, and an `exited` promise). `@earendil-works/pi-env`'s `openProcess()` returns one, without an adapter:
35
+
36
+ ```typescript
37
+ import { openProcess } from "@earendil-works/pi-env";
38
+ import { PipeTransport } from "@earendil-works/pi-mcp";
39
+
40
+ const transport = new PipeTransport({
41
+ open: (handlers) => openProcess(connection, { cwd, argv, ...handlers }),
42
+ });
43
+ await client.connect(transport);
44
+ ```
45
+
46
+ Closing the transport closes the program's stdin and kills it if it has not exited after `closeTimeoutMs` (default 2000). The transport closes when the program ends.
47
+
30
48
  ### Tools for an LLM
31
49
 
32
50
  `toLlmContent(result)` converts a `CallToolResult` to text and image content for a model, in the shape of `@earendil-works/pi-ai`'s `TextContent` and `ImageContent`. Text and images pass through, embedded text and image resources are unwrapped, and audio, resource links, and binary resources become short text placeholders. A result without content blocks but with `structuredContent` becomes its JSON.
@@ -36,7 +54,6 @@ Wrapping an MCP tool as a `pi-agent-core` `AgentTool`:
36
54
  ```typescript
37
55
  import type { AgentTool } from "@earendil-works/pi-agent-core";
38
56
  import { toLlmContent } from "@earendil-works/pi-mcp";
39
- import { Type } from "typebox";
40
57
 
41
58
  const tools: AgentTool[] = (await client.listTools()).map((tool) => ({
42
59
  // Providers allow at most 64 characters of [A-Za-z0-9_-].
@@ -44,7 +61,7 @@ const tools: AgentTool[] = (await client.listTools()).map((tool) => ({
44
61
  label: tool.title ?? tool.name,
45
62
  description: tool.description ?? tool.name,
46
63
  // Providers require an object schema, and some reject one without `properties`.
47
- parameters: Type.Unsafe({ ...tool.inputSchema, type: "object", properties: tool.inputSchema.properties ?? {} }),
64
+ parameters: { ...tool.inputSchema, type: "object", properties: tool.inputSchema.properties ?? {} },
48
65
  execute: async (_toolCallId, params, signal) => {
49
66
  const result = await client.callTool(tool.name, params as Record<string, unknown>, { signal });
50
67
  // MCP reports tool failures in the result instead of as a protocol error.
@@ -53,6 +70,8 @@ const tools: AgentTool[] = (await client.listTools()).map((tool) => ({
53
70
  }));
54
71
  ```
55
72
 
73
+ For applications that name tools like pi's coding agent: `createMcpToolName(server, tool, isTaken)` builds `mcp__<server>__<tool>`, limited to the characters and the 64-character length providers accept, with a hash suffix for long names and for names `isTaken` reports as used by another tool. `mcpNamespace(server)` is the prefix of a server's tool names. `normalizeMcpInputSchema()` turns an input schema into an object schema with `properties`, and `limitMcpContent(content, saveOutput)` keeps model-facing text within `MCP_OUTPUT_MAX_BYTES` (20KB) by cutting its middle; `saveOutput`, when given, stores the full text and returns the path the model can read it from.
74
+
56
75
  The [mcp-codemode example](https://github.com/earendil-works/pi/tree/main/packages/agent/examples/mcp-codemode) also forwards progress, passes `structuredContent` through, and lets `@earendil-works/pi-codemode` scripts call the tools.
57
76
 
58
77
  ### OAuth
@@ -109,9 +128,44 @@ await connected;
109
128
 
110
129
  Inject `McpOAuthStateStore` into `McpOAuthProvider` for durable credentials. The package does not open a browser or choose where credentials are stored.
111
130
 
112
- The OAuth implementation is adapted from the MIT-licensed Model Context Protocol TypeScript SDK v1.29.0. Its license is included under `LICENSES/`.
131
+ #### Sign-in in two halves
113
132
 
114
- An MCP transport owns framing and I/O. It delivers individual JSON-RPC messages to `McpClient`; the client owns request correlation, initialization, timeouts, cancellation, server requests, and protocol-level helpers.
133
+ `startMcpSignIn()` and `completeMcpSignIn()` run the sign-in pi uses, split so the second half can run in another request with the same state store (an `McpOAuthServerStore`: a state store with a refresh lock). The caller picks the redirect URI: a loopback one, or an https callback route of its own. `startMcpSignIn()` first tries to refresh the stored tokens under the store's refresh lock, so never call it while holding that lock; its `redirect` result is plain data the application can persist until the response arrives. Pass `completeMcpSignIn()` the same options, including the redirect URI you chose (not `started.redirectUrl`, which may be a variant specific to the MCP server).
134
+
135
+ ```typescript
136
+ import { completeMcpSignIn, responseFromRedirectUrl, startMcpSignIn } from "@earendil-works/pi-mcp/oauth";
137
+
138
+ const signIn = { serverUrl, store, settings: {}, redirectUrl: "https://app.example/mcp/callback", clientName: "My app" };
139
+ const started = await startMcpSignIn(signIn);
140
+ if (started.kind === "redirect") {
141
+ // Send the browser to started.authorizationUrl; keep `started` until the response arrives.
142
+ // Later, a callback route builds { code, state, iss } from its query; a pasted URL goes through:
143
+ const response = responseFromRedirectUrl(pastedUrl, started);
144
+ await completeMcpSignIn({ ...signIn, authorization: started, response });
145
+ }
146
+ ```
147
+
148
+ For a browser on the same machine, `listenForLoopbackSignIn()` starts a loopback callback server and returns its redirect URI, and `waitForLoopbackSignIn()` waits for the response on it or for a pasted redirect URL. `clientMetadataDocuments` lets authorization servers that support Client ID Metadata Documents identify the application by a document it publishes instead of registering a client. `createMcpAuthProvider()` is the matching `AuthProvider` for connections: it refreshes the stored tokens and throws `McpOAuthAuthorizationRequiredError` when the user has to sign in.
149
+
150
+ The OAuth implementation is adapted from the MIT-licensed Model Context Protocol TypeScript SDK v1.29.0. Its license is included under `THIRD_PARTY_NOTICES/`.
151
+
152
+ ### Configuration
153
+
154
+ `validateMcpServerConfig(name, entry, options)` validates one entry of the `mcpServers` shape that MCP clients share: stdio servers (`command`, `args`, `env`, `cwd`), Streamable HTTP servers (`url`, `headers`, `oauth`), and `enabled`, `description`, and `timeout`. It returns `{ kind: "stdio" | "http", config }` or `{ kind: "invalid", error }`. Fields it does not know stay in `config`, typed `unknown`, for the application to check afterwards, depending on `kind` where it matters. Resolving `${VAR}` references and relative paths is left to the application. `oauth.clientRegistration: "cimd"` is accepted only with `options.checkMetadataDocumentCallback`, which checks that the application's Client ID Metadata Documents list the callback.
155
+
156
+ ```typescript
157
+ import { validateMcpServerConfig } from "@earendil-works/pi-mcp";
158
+
159
+ for (const [name, entry] of Object.entries(file.mcpServers)) {
160
+ const result = validateMcpServerConfig(name, entry);
161
+ if (result.kind === "invalid") errors.push(result.error);
162
+ else if (result.config.shared !== undefined && typeof result.config.shared !== "boolean") {
163
+ errors.push(`server "${name}": shared must be a boolean`);
164
+ } else servers.set(name, result);
165
+ }
166
+ ```
167
+
168
+ `isMcpServerOverride(entry)` is true for entries without `command`, `url`, or `type`, which applications treat as overriding a few fields of a server defined in another file.
115
169
 
116
170
  ## Supported protocol surface
117
171
 
@@ -124,6 +178,7 @@ An MCP transport owns framing and I/O. It delivers individual JSON-RPC messages
124
178
  - request cancellation
125
179
  - Streamable HTTP sessions, the server-to-client GET stream with reconnection, and resumption of dropped response streams with `Last-Event-ID`
126
180
  - stdio shutdown per the spec (close stdin, then SIGTERM, then SIGKILL), applied to the server's whole process group
181
+ - stdio over a caller-provided pipe (`PipeTransport`)
127
182
  - server `ping` and `roots/list` requests
128
183
  - logging and tool-list-change notifications through the generic notification API
129
184
  - OAuth protected-resource and authorization-server discovery
@@ -0,0 +1,117 @@
1
+ /**
2
+ * The `mcpServers` configuration shape that MCP clients share, so configurations can be copied between
3
+ * them: stdio servers (`command`, `args`, `env`, `cwd`), Streamable HTTP servers (`url`, `headers`,
4
+ * `oauth`), and `enabled`, `description` and `timeout` for both.
5
+ *
6
+ * ```json
7
+ * {
8
+ * "mcpServers": {
9
+ * "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "."] },
10
+ * "docs": { "url": "https://example.com/mcp", "headers": { "Authorization": "Bearer ${DOCS_TOKEN}" } }
11
+ * }
12
+ * }
13
+ * ```
14
+ *
15
+ * Applications add fields of their own: {@link validateMcpServerConfig} keeps fields it does not know
16
+ * for the caller to check afterwards.
17
+ */
18
+ /** Fields every server entry may have. */
19
+ interface McpServerConfigBase {
20
+ /** What the server offers, in a sentence. */
21
+ description?: string;
22
+ /** Set to false to keep the entry without connecting. Default: true. */
23
+ enabled?: boolean;
24
+ /** Per-request timeout in seconds. */
25
+ timeout?: number;
26
+ }
27
+ /** A server started as a local program that speaks MCP over stdin and stdout. */
28
+ export interface McpStdioServerConfig extends McpServerConfigBase {
29
+ type?: "stdio";
30
+ command: string;
31
+ args?: string[];
32
+ /** Added to the environment. The application decides whether values may reference variables (`${NAME}`). */
33
+ env?: Record<string, string>;
34
+ /** The application decides what relative paths resolve against. */
35
+ cwd?: string;
36
+ }
37
+ /** OAuth client settings for servers that do not support dynamic client registration. */
38
+ export interface McpOAuthConfig {
39
+ /** Pre-registered client id. Without it, the application registers a client with the authorization server. */
40
+ clientId?: string;
41
+ /** As configured; the application decides whether it may reference variables. */
42
+ clientSecret?: string;
43
+ /**
44
+ * Port of the loopback callback server, for clients registered with a fixed redirect URI. Without
45
+ * `callbackUrl`, the redirect URI is `http://127.0.0.1:<port>/callback`.
46
+ */
47
+ callbackPort?: number;
48
+ /**
49
+ * Redirect URI registered for `clientId`, for example `http://localhost:8080/oauth/callback`. It must
50
+ * be an `http` URI on `localhost`, `127.0.0.1`, or `[::1]`. Without a port, the callback server
51
+ * listens on `callbackPort` or a free port, which is added to the URI (RFC 8252).
52
+ */
53
+ callbackUrl?: string;
54
+ /** Scopes to request, separated by spaces. Default: the scopes the server advertises. */
55
+ scope?: string;
56
+ /** `client_name` sent with dynamic client registration. Default: the application's name. */
57
+ clientName?: string;
58
+ /**
59
+ * How the application identifies itself without `clientId`. `auto` (default): its Client ID Metadata
60
+ * Document when the authorization server supports them for public clients, otherwise dynamic client
61
+ * registration. `cimd`: always the document, and sign-in fails without support. `dcr`: always
62
+ * dynamic client registration.
63
+ */
64
+ clientRegistration?: "auto" | "dcr" | "cimd";
65
+ /**
66
+ * Authorization server metadata document (RFC 8414 or OpenID Connect discovery) to use instead of
67
+ * discovery through the server, for servers that advertise a wrong authorization server or none.
68
+ * The document is trusted as configured. Must use https, except on loopback hosts.
69
+ */
70
+ authServerMetadataUrl?: string;
71
+ }
72
+ /** A server reached over Streamable HTTP. */
73
+ export interface McpHttpServerConfig extends McpServerConfigBase {
74
+ type?: "http";
75
+ url: string;
76
+ /** Sent with every request. The application decides whether values may reference variables. */
77
+ headers?: Record<string, string>;
78
+ oauth?: McpOAuthConfig;
79
+ }
80
+ /** One entry of the `mcpServers` shape: a stdio or a Streamable HTTP server. */
81
+ export type McpServerConfig = McpStdioServerConfig | McpHttpServerConfig;
82
+ /**
83
+ * A validated server entry, tagged with its transport, or why it is invalid. The config is the entry
84
+ * as given: fields the validator does not know are kept, unchecked, for the caller to check.
85
+ */
86
+ export type McpServerConfigResult = {
87
+ kind: "stdio";
88
+ config: McpStdioServerConfig & Record<string, unknown>;
89
+ } | {
90
+ kind: "http";
91
+ config: McpHttpServerConfig & Record<string, unknown>;
92
+ } | {
93
+ kind: "invalid";
94
+ error: string;
95
+ };
96
+ /** What the application adds to the validation of server entries. */
97
+ export interface McpServerConfigOptions {
98
+ /**
99
+ * Checks the `oauth.callbackUrl` (undefined: none configured) of an entry with
100
+ * `oauth.clientRegistration: "cimd"`: the application's Client ID Metadata Documents must list it.
101
+ * Returns an error message. Without this check, `"cimd"` is refused.
102
+ */
103
+ checkMetadataDocumentCallback?(callbackUrl: string | undefined): string | undefined;
104
+ }
105
+ /**
106
+ * Validate one entry of the `mcpServers` shape (configuration files are a trust boundary). Server
107
+ * names are letters, digits, `_` and `-`, which tool names and namespaces are built from.
108
+ */
109
+ export declare function validateMcpServerConfig(name: string, raw: unknown, options?: McpServerConfigOptions): McpServerConfigResult;
110
+ /**
111
+ * Whether an entry overrides a server defined elsewhere instead of defining one: it has no `command`,
112
+ * `url` or `type`. Applications let a less trusted file (a project's) override a few fields, such as
113
+ * `enabled`, of a server a trusted file defines; which fields is theirs to decide.
114
+ */
115
+ export declare function isMcpServerOverride(entry: Record<string, unknown>): boolean;
116
+ export {};
117
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,0CAA0C;AAC1C,UAAU,mBAAmB;IAC5B,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,iFAAiF;AACjF,MAAM,WAAW,oBAAqB,SAAQ,mBAAmB;IAChE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,4GAA4G;IAC5G,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,mEAAmE;IACnE,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,yFAAyF;AACzF,MAAM,WAAW,cAAc;IAC9B,8GAA8G;IAC9G,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yFAAyF;IACzF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4FAA4F;IAC5F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;IAC7C;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,6CAA6C;AAC7C,MAAM,WAAW,mBAAoB,SAAQ,mBAAmB;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,cAAc,CAAC;CACvB;AAED,gFAAgF;AAChF,MAAM,MAAM,eAAe,GAAG,oBAAoB,GAAG,mBAAmB,CAAC;AAEzE;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAC9B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACzE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACvE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC,qEAAqE;AACrE,MAAM,WAAW,sBAAsB;IACtC;;;;OAIG;IACH,6BAA6B,CAAC,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;CACpF;AAID;;;GAGG;AACH,wBAAgB,uBAAuB,CACtC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,sBAA2B,GAClC,qBAAqB,CAsBvB;AAiGD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAE3E"}
package/dist/config.js ADDED
@@ -0,0 +1,153 @@
1
+ /**
2
+ * The `mcpServers` configuration shape that MCP clients share, so configurations can be copied between
3
+ * them: stdio servers (`command`, `args`, `env`, `cwd`), Streamable HTTP servers (`url`, `headers`,
4
+ * `oauth`), and `enabled`, `description` and `timeout` for both.
5
+ *
6
+ * ```json
7
+ * {
8
+ * "mcpServers": {
9
+ * "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "."] },
10
+ * "docs": { "url": "https://example.com/mcp", "headers": { "Authorization": "Bearer ${DOCS_TOKEN}" } }
11
+ * }
12
+ * }
13
+ * ```
14
+ *
15
+ * Applications add fields of their own: {@link validateMcpServerConfig} keeps fields it does not know
16
+ * for the caller to check afterwards.
17
+ */
18
+ const SERVER_NAME = /^[A-Za-z0-9_-]+$/;
19
+ /**
20
+ * Validate one entry of the `mcpServers` shape (configuration files are a trust boundary). Server
21
+ * names are letters, digits, `_` and `-`, which tool names and namespaces are built from.
22
+ */
23
+ export function validateMcpServerConfig(name, raw, options = {}) {
24
+ const invalid = (error) => ({ kind: "invalid", error });
25
+ if (!SERVER_NAME.test(name))
26
+ return invalid(`invalid server name "${name}" (use letters, digits, "_" and "-")`);
27
+ if (!isRecord(raw))
28
+ return invalid(`server "${name}" must be an object`);
29
+ const fieldsError = validateBaseFields(raw);
30
+ if (fieldsError)
31
+ return invalid(`server "${name}": ${fieldsError}`);
32
+ const { type, url, command } = raw;
33
+ if (type === "sse") {
34
+ return invalid(`server "${name}": legacy SSE transport is not supported; use the streamable HTTP URL`);
35
+ }
36
+ // The casts below hold because the fields were checked one by one, which the types cannot express.
37
+ if (typeof url === "string" && (type === undefined || type === "http" || type === "streamable-http")) {
38
+ const error = validateHttpFields(raw, url, options);
39
+ if (error)
40
+ return invalid(`server "${name}": ${error}`);
41
+ return { kind: "http", config: raw };
42
+ }
43
+ if (typeof command === "string" && (type === undefined || type === "stdio")) {
44
+ const error = validateStdioFields(raw);
45
+ if (error)
46
+ return invalid(`server "${name}": ${error}`);
47
+ return { kind: "stdio", config: raw };
48
+ }
49
+ return invalid(`server "${name}" needs either "command" (stdio) or "url" (streamable HTTP)`);
50
+ }
51
+ function isRecord(value) {
52
+ return typeof value === "object" && value !== null && !Array.isArray(value);
53
+ }
54
+ function validateBaseFields(entry) {
55
+ const { enabled, timeout, description } = entry;
56
+ if (enabled !== undefined && typeof enabled !== "boolean")
57
+ return "enabled must be a boolean";
58
+ if (description !== undefined && typeof description !== "string")
59
+ return "description must be a string";
60
+ if (timeout !== undefined && (typeof timeout !== "number" || !(timeout > 0))) {
61
+ return "timeout must be a positive number of seconds";
62
+ }
63
+ return undefined;
64
+ }
65
+ function validateHttpFields(entry, url, options) {
66
+ if (!URL.canParse(url) || !/^https?:$/.test(new URL(url).protocol))
67
+ return "url must be an http or https URL";
68
+ if (entry.headers !== undefined && !isStringRecord(entry.headers))
69
+ return "headers must map names to strings";
70
+ return validateOAuth(entry.oauth, options);
71
+ }
72
+ function validateStdioFields(entry) {
73
+ const { args, env, cwd } = entry;
74
+ if (args !== undefined && !(Array.isArray(args) && args.every((arg) => typeof arg === "string"))) {
75
+ return "args must be an array of strings";
76
+ }
77
+ if (env !== undefined && !isStringRecord(env))
78
+ return "env must map names to strings";
79
+ if (cwd !== undefined && typeof cwd !== "string")
80
+ return "cwd must be a string";
81
+ return undefined;
82
+ }
83
+ function isStringRecord(value) {
84
+ return isRecord(value) && Object.values(value).every((entry) => typeof entry === "string");
85
+ }
86
+ const LOOPBACK_HOSTS = ["localhost", "127.0.0.1", "[::1]"];
87
+ function validateOAuth(value, options) {
88
+ if (value === undefined)
89
+ return undefined;
90
+ if (!isRecord(value))
91
+ return "oauth must be an object";
92
+ if (value.clientId !== undefined && typeof value.clientId !== "string")
93
+ return "oauth.clientId must be a string";
94
+ if (value.clientSecret !== undefined && typeof value.clientSecret !== "string") {
95
+ return "oauth.clientSecret must be a string";
96
+ }
97
+ const port = value.callbackPort;
98
+ if (port !== undefined && (typeof port !== "number" || !Number.isInteger(port) || port < 1 || port > 65535)) {
99
+ return "oauth.callbackPort must be a port number";
100
+ }
101
+ if (value.callbackUrl !== undefined) {
102
+ if (typeof value.callbackUrl !== "string" || !isLoopbackRedirectUri(value.callbackUrl)) {
103
+ return "oauth.callbackUrl must be an http URI on localhost, 127.0.0.1, or [::1] without query or fragment";
104
+ }
105
+ const urlPort = new URL(value.callbackUrl).port;
106
+ if (urlPort && port !== undefined && Number(urlPort) !== port) {
107
+ return "oauth.callbackUrl and oauth.callbackPort name different ports";
108
+ }
109
+ }
110
+ if (value.scope !== undefined && typeof value.scope !== "string")
111
+ return "oauth.scope must be a string";
112
+ if (value.clientName !== undefined && (typeof value.clientName !== "string" || !value.clientName.trim())) {
113
+ return "oauth.clientName must be a non-empty string";
114
+ }
115
+ const registration = value.clientRegistration;
116
+ if (registration !== undefined && registration !== "auto" && registration !== "dcr") {
117
+ if (registration !== "cimd")
118
+ return 'oauth.clientRegistration must be "auto", "dcr", or "cimd"';
119
+ if (value.clientId !== undefined || value.clientName !== undefined) {
120
+ return 'oauth.clientRegistration "cimd" cannot be combined with oauth.clientId or oauth.clientName';
121
+ }
122
+ if (!options.checkMetadataDocumentCallback) {
123
+ return 'oauth.clientRegistration "cimd" is not supported: there are no Client ID Metadata Documents';
124
+ }
125
+ const callbackError = options.checkMetadataDocumentCallback(typeof value.callbackUrl === "string" ? value.callbackUrl : undefined);
126
+ if (callbackError)
127
+ return callbackError;
128
+ }
129
+ const metadataUrl = value.authServerMetadataUrl;
130
+ if (metadataUrl !== undefined) {
131
+ const url = typeof metadataUrl === "string" && URL.canParse(metadataUrl) ? new URL(metadataUrl) : undefined;
132
+ if (!url || !(url.protocol === "https:" || (url.protocol === "http:" && LOOPBACK_HOSTS.includes(url.hostname)))) {
133
+ return "oauth.authServerMetadataUrl must be an https URL, or http on localhost, 127.0.0.1, or [::1]";
134
+ }
135
+ }
136
+ return undefined;
137
+ }
138
+ /** Whether a redirect URI can be served by a loopback callback server. */
139
+ function isLoopbackRedirectUri(value) {
140
+ if (!URL.canParse(value))
141
+ return false;
142
+ const url = new URL(value);
143
+ return url.protocol === "http:" && LOOPBACK_HOSTS.includes(url.hostname) && url.search === "" && url.hash === "";
144
+ }
145
+ /**
146
+ * Whether an entry overrides a server defined elsewhere instead of defining one: it has no `command`,
147
+ * `url` or `type`. Applications let a less trusted file (a project's) override a few fields, such as
148
+ * `enabled`, of a server a trusted file defines; which fields is theirs to decide.
149
+ */
150
+ export function isMcpServerOverride(entry) {
151
+ return entry.command === undefined && entry.url === undefined && entry.type === undefined;
152
+ }
153
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The `mcpServers` configuration shape that MCP clients share, so configurations can be copied between\n * them: stdio servers (`command`, `args`, `env`, `cwd`), Streamable HTTP servers (`url`, `headers`,\n * `oauth`), and `enabled`, `description` and `timeout` for both.\n *\n * ```json\n * {\n * \"mcpServers\": {\n * \"filesystem\": { \"command\": \"npx\", \"args\": [\"-y\", \"@modelcontextprotocol/server-filesystem\", \".\"] },\n * \"docs\": { \"url\": \"https://example.com/mcp\", \"headers\": { \"Authorization\": \"Bearer ${DOCS_TOKEN}\" } }\n * }\n * }\n * ```\n *\n * Applications add fields of their own: {@link validateMcpServerConfig} keeps fields it does not know\n * for the caller to check afterwards.\n */\n\n/** Fields every server entry may have. */\ninterface McpServerConfigBase {\n\t/** What the server offers, in a sentence. */\n\tdescription?: string;\n\t/** Set to false to keep the entry without connecting. Default: true. */\n\tenabled?: boolean;\n\t/** Per-request timeout in seconds. */\n\ttimeout?: number;\n}\n\n/** A server started as a local program that speaks MCP over stdin and stdout. */\nexport interface McpStdioServerConfig extends McpServerConfigBase {\n\ttype?: \"stdio\";\n\tcommand: string;\n\targs?: string[];\n\t/** Added to the environment. The application decides whether values may reference variables (`${NAME}`). */\n\tenv?: Record<string, string>;\n\t/** The application decides what relative paths resolve against. */\n\tcwd?: string;\n}\n\n/** OAuth client settings for servers that do not support dynamic client registration. */\nexport interface McpOAuthConfig {\n\t/** Pre-registered client id. Without it, the application registers a client with the authorization server. */\n\tclientId?: string;\n\t/** As configured; the application decides whether it may reference variables. */\n\tclientSecret?: string;\n\t/**\n\t * Port of the loopback callback server, for clients registered with a fixed redirect URI. Without\n\t * `callbackUrl`, the redirect URI is `http://127.0.0.1:<port>/callback`.\n\t */\n\tcallbackPort?: number;\n\t/**\n\t * Redirect URI registered for `clientId`, for example `http://localhost:8080/oauth/callback`. It must\n\t * be an `http` URI on `localhost`, `127.0.0.1`, or `[::1]`. Without a port, the callback server\n\t * listens on `callbackPort` or a free port, which is added to the URI (RFC 8252).\n\t */\n\tcallbackUrl?: string;\n\t/** Scopes to request, separated by spaces. Default: the scopes the server advertises. */\n\tscope?: string;\n\t/** `client_name` sent with dynamic client registration. Default: the application's name. */\n\tclientName?: string;\n\t/**\n\t * How the application identifies itself without `clientId`. `auto` (default): its Client ID Metadata\n\t * Document when the authorization server supports them for public clients, otherwise dynamic client\n\t * registration. `cimd`: always the document, and sign-in fails without support. `dcr`: always\n\t * dynamic client registration.\n\t */\n\tclientRegistration?: \"auto\" | \"dcr\" | \"cimd\";\n\t/**\n\t * Authorization server metadata document (RFC 8414 or OpenID Connect discovery) to use instead of\n\t * discovery through the server, for servers that advertise a wrong authorization server or none.\n\t * The document is trusted as configured. Must use https, except on loopback hosts.\n\t */\n\tauthServerMetadataUrl?: string;\n}\n\n/** A server reached over Streamable HTTP. */\nexport interface McpHttpServerConfig extends McpServerConfigBase {\n\ttype?: \"http\";\n\turl: string;\n\t/** Sent with every request. The application decides whether values may reference variables. */\n\theaders?: Record<string, string>;\n\toauth?: McpOAuthConfig;\n}\n\n/** One entry of the `mcpServers` shape: a stdio or a Streamable HTTP server. */\nexport type McpServerConfig = McpStdioServerConfig | McpHttpServerConfig;\n\n/**\n * A validated server entry, tagged with its transport, or why it is invalid. The config is the entry\n * as given: fields the validator does not know are kept, unchecked, for the caller to check.\n */\nexport type McpServerConfigResult =\n\t| { kind: \"stdio\"; config: McpStdioServerConfig & Record<string, unknown> }\n\t| { kind: \"http\"; config: McpHttpServerConfig & Record<string, unknown> }\n\t| { kind: \"invalid\"; error: string };\n\n/** What the application adds to the validation of server entries. */\nexport interface McpServerConfigOptions {\n\t/**\n\t * Checks the `oauth.callbackUrl` (undefined: none configured) of an entry with\n\t * `oauth.clientRegistration: \"cimd\"`: the application's Client ID Metadata Documents must list it.\n\t * Returns an error message. Without this check, `\"cimd\"` is refused.\n\t */\n\tcheckMetadataDocumentCallback?(callbackUrl: string | undefined): string | undefined;\n}\n\nconst SERVER_NAME = /^[A-Za-z0-9_-]+$/;\n\n/**\n * Validate one entry of the `mcpServers` shape (configuration files are a trust boundary). Server\n * names are letters, digits, `_` and `-`, which tool names and namespaces are built from.\n */\nexport function validateMcpServerConfig(\n\tname: string,\n\traw: unknown,\n\toptions: McpServerConfigOptions = {},\n): McpServerConfigResult {\n\tconst invalid = (error: string): McpServerConfigResult => ({ kind: \"invalid\", error });\n\tif (!SERVER_NAME.test(name)) return invalid(`invalid server name \"${name}\" (use letters, digits, \"_\" and \"-\")`);\n\tif (!isRecord(raw)) return invalid(`server \"${name}\" must be an object`);\n\tconst fieldsError = validateBaseFields(raw);\n\tif (fieldsError) return invalid(`server \"${name}\": ${fieldsError}`);\n\tconst { type, url, command } = raw;\n\tif (type === \"sse\") {\n\t\treturn invalid(`server \"${name}\": legacy SSE transport is not supported; use the streamable HTTP URL`);\n\t}\n\t// The casts below hold because the fields were checked one by one, which the types cannot express.\n\tif (typeof url === \"string\" && (type === undefined || type === \"http\" || type === \"streamable-http\")) {\n\t\tconst error = validateHttpFields(raw, url, options);\n\t\tif (error) return invalid(`server \"${name}\": ${error}`);\n\t\treturn { kind: \"http\", config: raw as McpHttpServerConfig & Record<string, unknown> };\n\t}\n\tif (typeof command === \"string\" && (type === undefined || type === \"stdio\")) {\n\t\tconst error = validateStdioFields(raw);\n\t\tif (error) return invalid(`server \"${name}\": ${error}`);\n\t\treturn { kind: \"stdio\", config: raw as McpStdioServerConfig & Record<string, unknown> };\n\t}\n\treturn invalid(`server \"${name}\" needs either \"command\" (stdio) or \"url\" (streamable HTTP)`);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction validateBaseFields(entry: Record<string, unknown>): string | undefined {\n\tconst { enabled, timeout, description } = entry;\n\tif (enabled !== undefined && typeof enabled !== \"boolean\") return \"enabled must be a boolean\";\n\tif (description !== undefined && typeof description !== \"string\") return \"description must be a string\";\n\tif (timeout !== undefined && (typeof timeout !== \"number\" || !(timeout > 0))) {\n\t\treturn \"timeout must be a positive number of seconds\";\n\t}\n\treturn undefined;\n}\n\nfunction validateHttpFields(\n\tentry: Record<string, unknown>,\n\turl: string,\n\toptions: McpServerConfigOptions,\n): string | undefined {\n\tif (!URL.canParse(url) || !/^https?:$/.test(new URL(url).protocol)) return \"url must be an http or https URL\";\n\tif (entry.headers !== undefined && !isStringRecord(entry.headers)) return \"headers must map names to strings\";\n\treturn validateOAuth(entry.oauth, options);\n}\n\nfunction validateStdioFields(entry: Record<string, unknown>): string | undefined {\n\tconst { args, env, cwd } = entry;\n\tif (args !== undefined && !(Array.isArray(args) && args.every((arg) => typeof arg === \"string\"))) {\n\t\treturn \"args must be an array of strings\";\n\t}\n\tif (env !== undefined && !isStringRecord(env)) return \"env must map names to strings\";\n\tif (cwd !== undefined && typeof cwd !== \"string\") return \"cwd must be a string\";\n\treturn undefined;\n}\n\nfunction isStringRecord(value: unknown): value is Record<string, string> {\n\treturn isRecord(value) && Object.values(value).every((entry) => typeof entry === \"string\");\n}\n\nconst LOOPBACK_HOSTS = [\"localhost\", \"127.0.0.1\", \"[::1]\"];\n\nfunction validateOAuth(value: unknown, options: McpServerConfigOptions): string | undefined {\n\tif (value === undefined) return undefined;\n\tif (!isRecord(value)) return \"oauth must be an object\";\n\tif (value.clientId !== undefined && typeof value.clientId !== \"string\") return \"oauth.clientId must be a string\";\n\tif (value.clientSecret !== undefined && typeof value.clientSecret !== \"string\") {\n\t\treturn \"oauth.clientSecret must be a string\";\n\t}\n\tconst port = value.callbackPort;\n\tif (port !== undefined && (typeof port !== \"number\" || !Number.isInteger(port) || port < 1 || port > 65535)) {\n\t\treturn \"oauth.callbackPort must be a port number\";\n\t}\n\tif (value.callbackUrl !== undefined) {\n\t\tif (typeof value.callbackUrl !== \"string\" || !isLoopbackRedirectUri(value.callbackUrl)) {\n\t\t\treturn \"oauth.callbackUrl must be an http URI on localhost, 127.0.0.1, or [::1] without query or fragment\";\n\t\t}\n\t\tconst urlPort = new URL(value.callbackUrl).port;\n\t\tif (urlPort && port !== undefined && Number(urlPort) !== port) {\n\t\t\treturn \"oauth.callbackUrl and oauth.callbackPort name different ports\";\n\t\t}\n\t}\n\tif (value.scope !== undefined && typeof value.scope !== \"string\") return \"oauth.scope must be a string\";\n\tif (value.clientName !== undefined && (typeof value.clientName !== \"string\" || !value.clientName.trim())) {\n\t\treturn \"oauth.clientName must be a non-empty string\";\n\t}\n\tconst registration = value.clientRegistration;\n\tif (registration !== undefined && registration !== \"auto\" && registration !== \"dcr\") {\n\t\tif (registration !== \"cimd\") return 'oauth.clientRegistration must be \"auto\", \"dcr\", or \"cimd\"';\n\t\tif (value.clientId !== undefined || value.clientName !== undefined) {\n\t\t\treturn 'oauth.clientRegistration \"cimd\" cannot be combined with oauth.clientId or oauth.clientName';\n\t\t}\n\t\tif (!options.checkMetadataDocumentCallback) {\n\t\t\treturn 'oauth.clientRegistration \"cimd\" is not supported: there are no Client ID Metadata Documents';\n\t\t}\n\t\tconst callbackError = options.checkMetadataDocumentCallback(\n\t\t\ttypeof value.callbackUrl === \"string\" ? value.callbackUrl : undefined,\n\t\t);\n\t\tif (callbackError) return callbackError;\n\t}\n\tconst metadataUrl = value.authServerMetadataUrl;\n\tif (metadataUrl !== undefined) {\n\t\tconst url = typeof metadataUrl === \"string\" && URL.canParse(metadataUrl) ? new URL(metadataUrl) : undefined;\n\t\tif (!url || !(url.protocol === \"https:\" || (url.protocol === \"http:\" && LOOPBACK_HOSTS.includes(url.hostname)))) {\n\t\t\treturn \"oauth.authServerMetadataUrl must be an https URL, or http on localhost, 127.0.0.1, or [::1]\";\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/** Whether a redirect URI can be served by a loopback callback server. */\nfunction isLoopbackRedirectUri(value: string): boolean {\n\tif (!URL.canParse(value)) return false;\n\tconst url = new URL(value);\n\treturn url.protocol === \"http:\" && LOOPBACK_HOSTS.includes(url.hostname) && url.search === \"\" && url.hash === \"\";\n}\n\n/**\n * Whether an entry overrides a server defined elsewhere instead of defining one: it has no `command`,\n * `url` or `type`. Applications let a less trusted file (a project's) override a few fields, such as\n * `enabled`, of a server a trusted file defines; which fields is theirs to decide.\n */\nexport function isMcpServerOverride(entry: Record<string, unknown>): boolean {\n\treturn entry.command === undefined && entry.url === undefined && entry.type === undefined;\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,8 +1,13 @@
1
1
  export type { AuthProvider, McpFetch, UnauthorizedContext } from "./auth-provider.ts";
2
2
  export { McpClient, type McpClientOptions, type McpRequestOptions } from "./client.ts";
3
+ export { isMcpServerOverride, type McpHttpServerConfig, type McpOAuthConfig, type McpServerConfig, type McpServerConfigOptions, type McpServerConfigResult, type McpStdioServerConfig, validateMcpServerConfig, } from "./config.ts";
4
+ export { normalizeMcpInputSchema } from "./input-schema.ts";
5
+ export { createMcpToolName, mcpNamespace } from "./naming.ts";
6
+ export { type LimitedMcpContent, limitMcpContent, MCP_OUTPUT_MAX_BYTES, type McpOutputSaver, } from "./output-limit.ts";
3
7
  export { type AudioContent, type BlobResourceContents, type CallToolResult, type ContentAnnotations, type ContentBlock, type EmbeddedResourceContent, type ImageContent, type LlmContent, type ResourceLinkContent, type TextContent, type TextResourceContents, toLlmContent, } from "./protocol/content.ts";
4
8
  export { isJsonRpcNotification, isJsonRpcRequest, isJsonRpcResponse, JSON_RPC_ERROR_CODES, type JsonRpcErrorObject, type JsonRpcErrorResponse, type JsonRpcId, type JsonRpcMessage, type JsonRpcNotification, type JsonRpcRequest, type JsonRpcResponse, type JsonRpcSuccessResponse, McpAbortError, McpConnectionClosedError, McpError, McpTimeoutError, parseJsonRpcMessage, } from "./protocol/jsonrpc.ts";
5
9
  export { type CancelledNotification, type ClientCapabilities, type Implementation, type InitializeParams, type InitializeResult, LATEST_PROTOCOL_VERSION, type ListResourcesResult, type ListResourceTemplatesResult, type ListToolsResult, type ProgressNotification, type ReadResourceResult, type Resource, type ResourceTemplate, type Root, type ServerCapabilities, SUPPORTED_PROTOCOL_VERSIONS, type SupportedProtocolVersion, type Tool, type ToolAnnotations, type ToolExecution, } from "./protocol/types.ts";
10
+ export { type McpPipe, type McpPipeHandlers, PipeTransport, type PipeTransportOptions, } from "./transports/pipe.ts";
6
11
  export { StdioTransport, type StdioTransportOptions } from "./transports/stdio.ts";
7
12
  export { McpAuthRequiredError, McpHttpError, McpSessionExpiredError, StreamableHttpTransport, type StreamableHttpTransportOptions, } from "./transports/streamable-http.ts";
8
13
  export type { McpTransport, McpTransportCloseListener, McpTransportErrorListener, McpTransportMessageListener, McpTransportSendOptions, } from "./transports/transport.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACtF,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACvF,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,YAAY,GACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,aAAa,EACb,wBAAwB,EACxB,QAAQ,EACR,eAAe,EACf,mBAAmB,GACnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,uBAAuB,EACvB,KAAK,mBAAmB,EACxB,KAAK,2BAA2B,EAChC,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,KAAK,IAAI,EACT,KAAK,kBAAkB,EACvB,2BAA2B,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,IAAI,EACT,KAAK,eAAe,EACpB,KAAK,aAAa,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EACN,oBAAoB,EACpB,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,EACvB,KAAK,8BAA8B,GACnC,MAAM,iCAAiC,CAAC;AACzC,YAAY,EACX,YAAY,EACZ,yBAAyB,EACzB,yBAAyB,EACzB,2BAA2B,EAC3B,uBAAuB,GACvB,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACtF,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACvF,OAAO,EACN,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,uBAAuB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EACN,KAAK,iBAAiB,EACtB,eAAe,EACf,oBAAoB,EACpB,KAAK,cAAc,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,YAAY,GACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,aAAa,EACb,wBAAwB,EACxB,QAAQ,EACR,eAAe,EACf,mBAAmB,GACnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,uBAAuB,EACvB,KAAK,mBAAmB,EACxB,KAAK,2BAA2B,EAChC,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,KAAK,IAAI,EACT,KAAK,kBAAkB,EACvB,2BAA2B,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,IAAI,EACT,KAAK,eAAe,EACpB,KAAK,aAAa,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,eAAe,EACpB,aAAa,EACb,KAAK,oBAAoB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EACN,oBAAoB,EACpB,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,EACvB,KAAK,8BAA8B,GACnC,MAAM,iCAAiC,CAAC;AACzC,YAAY,EACX,YAAY,EACZ,yBAAyB,EACzB,yBAAyB,EACzB,2BAA2B,EAC3B,uBAAuB,GACvB,MAAM,2BAA2B,CAAC"}
package/dist/index.js CHANGED
@@ -1,7 +1,12 @@
1
1
  export { McpClient } from "./client.js";
2
+ export { isMcpServerOverride, validateMcpServerConfig, } from "./config.js";
3
+ export { normalizeMcpInputSchema } from "./input-schema.js";
4
+ export { createMcpToolName, mcpNamespace } from "./naming.js";
5
+ export { limitMcpContent, MCP_OUTPUT_MAX_BYTES, } from "./output-limit.js";
2
6
  export { toLlmContent, } from "./protocol/content.js";
3
7
  export { isJsonRpcNotification, isJsonRpcRequest, isJsonRpcResponse, JSON_RPC_ERROR_CODES, McpAbortError, McpConnectionClosedError, McpError, McpTimeoutError, parseJsonRpcMessage, } from "./protocol/jsonrpc.js";
4
8
  export { LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, } from "./protocol/types.js";
9
+ export { PipeTransport, } from "./transports/pipe.js";
5
10
  export { StdioTransport } from "./transports/stdio.js";
6
11
  export { McpAuthRequiredError, McpHttpError, McpSessionExpiredError, StreamableHttpTransport, } from "./transports/streamable-http.js";
7
12
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAiD,MAAM,aAAa,CAAC;AACvF,OAAO,EAYN,YAAY,GACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EASpB,aAAa,EACb,wBAAwB,EACxB,QAAQ,EACR,eAAe,EACf,mBAAmB,GACnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAMN,uBAAuB,EAUvB,2BAA2B,GAK3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,cAAc,EAA8B,MAAM,uBAAuB,CAAC;AACnF,OAAO,EACN,oBAAoB,EACpB,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,GAEvB,MAAM,iCAAiC,CAAC","sourcesContent":["export type { AuthProvider, McpFetch, UnauthorizedContext } from \"./auth-provider.ts\";\nexport { McpClient, type McpClientOptions, type McpRequestOptions } from \"./client.ts\";\nexport {\n\ttype AudioContent,\n\ttype BlobResourceContents,\n\ttype CallToolResult,\n\ttype ContentAnnotations,\n\ttype ContentBlock,\n\ttype EmbeddedResourceContent,\n\ttype ImageContent,\n\ttype LlmContent,\n\ttype ResourceLinkContent,\n\ttype TextContent,\n\ttype TextResourceContents,\n\ttoLlmContent,\n} from \"./protocol/content.ts\";\nexport {\n\tisJsonRpcNotification,\n\tisJsonRpcRequest,\n\tisJsonRpcResponse,\n\tJSON_RPC_ERROR_CODES,\n\ttype JsonRpcErrorObject,\n\ttype JsonRpcErrorResponse,\n\ttype JsonRpcId,\n\ttype JsonRpcMessage,\n\ttype JsonRpcNotification,\n\ttype JsonRpcRequest,\n\ttype JsonRpcResponse,\n\ttype JsonRpcSuccessResponse,\n\tMcpAbortError,\n\tMcpConnectionClosedError,\n\tMcpError,\n\tMcpTimeoutError,\n\tparseJsonRpcMessage,\n} from \"./protocol/jsonrpc.ts\";\nexport {\n\ttype CancelledNotification,\n\ttype ClientCapabilities,\n\ttype Implementation,\n\ttype InitializeParams,\n\ttype InitializeResult,\n\tLATEST_PROTOCOL_VERSION,\n\ttype ListResourcesResult,\n\ttype ListResourceTemplatesResult,\n\ttype ListToolsResult,\n\ttype ProgressNotification,\n\ttype ReadResourceResult,\n\ttype Resource,\n\ttype ResourceTemplate,\n\ttype Root,\n\ttype ServerCapabilities,\n\tSUPPORTED_PROTOCOL_VERSIONS,\n\ttype SupportedProtocolVersion,\n\ttype Tool,\n\ttype ToolAnnotations,\n\ttype ToolExecution,\n} from \"./protocol/types.ts\";\nexport { StdioTransport, type StdioTransportOptions } from \"./transports/stdio.ts\";\nexport {\n\tMcpAuthRequiredError,\n\tMcpHttpError,\n\tMcpSessionExpiredError,\n\tStreamableHttpTransport,\n\ttype StreamableHttpTransportOptions,\n} from \"./transports/streamable-http.ts\";\nexport type {\n\tMcpTransport,\n\tMcpTransportCloseListener,\n\tMcpTransportErrorListener,\n\tMcpTransportMessageListener,\n\tMcpTransportSendOptions,\n} from \"./transports/transport.ts\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAiD,MAAM,aAAa,CAAC;AACvF,OAAO,EACN,mBAAmB,EAOnB,uBAAuB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAEN,eAAe,EACf,oBAAoB,GAEpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAYN,YAAY,GACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EASpB,aAAa,EACb,wBAAwB,EACxB,QAAQ,EACR,eAAe,EACf,mBAAmB,GACnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAMN,uBAAuB,EAUvB,2BAA2B,GAK3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAGN,aAAa,GAEb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,cAAc,EAA8B,MAAM,uBAAuB,CAAC;AACnF,OAAO,EACN,oBAAoB,EACpB,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,GAEvB,MAAM,iCAAiC,CAAC","sourcesContent":["export type { AuthProvider, McpFetch, UnauthorizedContext } from \"./auth-provider.ts\";\nexport { McpClient, type McpClientOptions, type McpRequestOptions } from \"./client.ts\";\nexport {\n\tisMcpServerOverride,\n\ttype McpHttpServerConfig,\n\ttype McpOAuthConfig,\n\ttype McpServerConfig,\n\ttype McpServerConfigOptions,\n\ttype McpServerConfigResult,\n\ttype McpStdioServerConfig,\n\tvalidateMcpServerConfig,\n} from \"./config.ts\";\nexport { normalizeMcpInputSchema } from \"./input-schema.ts\";\nexport { createMcpToolName, mcpNamespace } from \"./naming.ts\";\nexport {\n\ttype LimitedMcpContent,\n\tlimitMcpContent,\n\tMCP_OUTPUT_MAX_BYTES,\n\ttype McpOutputSaver,\n} from \"./output-limit.ts\";\nexport {\n\ttype AudioContent,\n\ttype BlobResourceContents,\n\ttype CallToolResult,\n\ttype ContentAnnotations,\n\ttype ContentBlock,\n\ttype EmbeddedResourceContent,\n\ttype ImageContent,\n\ttype LlmContent,\n\ttype ResourceLinkContent,\n\ttype TextContent,\n\ttype TextResourceContents,\n\ttoLlmContent,\n} from \"./protocol/content.ts\";\nexport {\n\tisJsonRpcNotification,\n\tisJsonRpcRequest,\n\tisJsonRpcResponse,\n\tJSON_RPC_ERROR_CODES,\n\ttype JsonRpcErrorObject,\n\ttype JsonRpcErrorResponse,\n\ttype JsonRpcId,\n\ttype JsonRpcMessage,\n\ttype JsonRpcNotification,\n\ttype JsonRpcRequest,\n\ttype JsonRpcResponse,\n\ttype JsonRpcSuccessResponse,\n\tMcpAbortError,\n\tMcpConnectionClosedError,\n\tMcpError,\n\tMcpTimeoutError,\n\tparseJsonRpcMessage,\n} from \"./protocol/jsonrpc.ts\";\nexport {\n\ttype CancelledNotification,\n\ttype ClientCapabilities,\n\ttype Implementation,\n\ttype InitializeParams,\n\ttype InitializeResult,\n\tLATEST_PROTOCOL_VERSION,\n\ttype ListResourcesResult,\n\ttype ListResourceTemplatesResult,\n\ttype ListToolsResult,\n\ttype ProgressNotification,\n\ttype ReadResourceResult,\n\ttype Resource,\n\ttype ResourceTemplate,\n\ttype Root,\n\ttype ServerCapabilities,\n\tSUPPORTED_PROTOCOL_VERSIONS,\n\ttype SupportedProtocolVersion,\n\ttype Tool,\n\ttype ToolAnnotations,\n\ttype ToolExecution,\n} from \"./protocol/types.ts\";\nexport {\n\ttype McpPipe,\n\ttype McpPipeHandlers,\n\tPipeTransport,\n\ttype PipeTransportOptions,\n} from \"./transports/pipe.ts\";\nexport { StdioTransport, type StdioTransportOptions } from \"./transports/stdio.ts\";\nexport {\n\tMcpAuthRequiredError,\n\tMcpHttpError,\n\tMcpSessionExpiredError,\n\tStreamableHttpTransport,\n\ttype StreamableHttpTransportOptions,\n} from \"./transports/streamable-http.ts\";\nexport type {\n\tMcpTransport,\n\tMcpTransportCloseListener,\n\tMcpTransportErrorListener,\n\tMcpTransportMessageListener,\n\tMcpTransportSendOptions,\n} from \"./transports/transport.ts\";\n"]}
@@ -0,0 +1,7 @@
1
+ /** An MCP tool's input schema in the shape LLM tool APIs accept. */
2
+ /**
3
+ * The tool's input schema as an object schema. Tool parameters must be objects, MCP servers may omit
4
+ * `type`, and some providers reject object schemas without `properties`. Other keywords are kept.
5
+ */
6
+ export declare function normalizeMcpInputSchema(schema: Record<string, unknown>): Record<string, unknown>;
7
+ //# sourceMappingURL=input-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-schema.d.ts","sourceRoot":"","sources":["../src/input-schema.ts"],"names":[],"mappings":"AAAA,oEAAoE;AAEpE;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMhG"}
@@ -0,0 +1,13 @@
1
+ /** An MCP tool's input schema in the shape LLM tool APIs accept. */
2
+ /**
3
+ * The tool's input schema as an object schema. Tool parameters must be objects, MCP servers may omit
4
+ * `type`, and some providers reject object schemas without `properties`. Other keywords are kept.
5
+ */
6
+ export function normalizeMcpInputSchema(schema) {
7
+ return {
8
+ ...schema,
9
+ type: schema.type ?? "object",
10
+ ...(schema.properties === undefined ? { properties: {} } : {}),
11
+ };
12
+ }
13
+ //# sourceMappingURL=input-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-schema.js","sourceRoot":"","sources":["../src/input-schema.ts"],"names":[],"mappings":"AAAA,oEAAoE;AAEpE;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAA+B;IACtE,OAAO;QACN,GAAG,MAAM;QACT,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,QAAQ;QAC7B,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9D,CAAC;AACH,CAAC","sourcesContent":["/** An MCP tool's input schema in the shape LLM tool APIs accept. */\n\n/**\n * The tool's input schema as an object schema. Tool parameters must be objects, MCP servers may omit\n * `type`, and some providers reject object schemas without `properties`. Other keywords are kept.\n */\nexport function normalizeMcpInputSchema(schema: Record<string, unknown>): Record<string, unknown> {\n\treturn {\n\t\t...schema,\n\t\ttype: schema.type ?? \"object\",\n\t\t...(schema.properties === undefined ? { properties: {} } : {}),\n\t};\n}\n"]}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Names of MCP tools and of their servers' namespaces as models and code mode scripts see them.
3
+ * Applications that name tools the same way rank and group them the same way in tool search and
4
+ * code mode.
5
+ */
6
+ /**
7
+ * `mcp__<server>__<tool>`, sanitized and shortened with a hash suffix when too long. Like Codex,
8
+ * everything but `[A-Za-z0-9_]` becomes `_`, so the name is also the identifier code mode scripts
9
+ * call it by. `isTaken` reports names used by a different MCP tool: sanitizing can map two tools to
10
+ * one name (`a-b` and `a_b`), which then get the hash suffix.
11
+ */
12
+ export declare function createMcpToolName(server: string, tool: string, isTaken?: (name: string) => boolean): string;
13
+ /**
14
+ * Namespace of a server's tools: `mcp__<server>` with `-` replaced by `_`, like the tool names. Server
15
+ * names that differ only in `-` and `_` share a namespace, so a configuration must not have both.
16
+ */
17
+ export declare function mcpNamespace(server: string): string;
18
+ //# sourceMappingURL=naming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../src/naming.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAChC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAqB,GAC9C,MAAM,CAKR;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnD"}
package/dist/naming.js ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Names of MCP tools and of their servers' namespaces as models and code mode scripts see them.
3
+ * Applications that name tools the same way rank and group them the same way in tool search and
4
+ * code mode.
5
+ */
6
+ import { createHash } from "node:crypto";
7
+ /** Provider tool names are limited to 64 characters of `[A-Za-z0-9_-]`. */
8
+ const MAX_TOOL_NAME_LENGTH = 64;
9
+ /**
10
+ * `mcp__<server>__<tool>`, sanitized and shortened with a hash suffix when too long. Like Codex,
11
+ * everything but `[A-Za-z0-9_]` becomes `_`, so the name is also the identifier code mode scripts
12
+ * call it by. `isTaken` reports names used by a different MCP tool: sanitizing can map two tools to
13
+ * one name (`a-b` and `a_b`), which then get the hash suffix.
14
+ */
15
+ export function createMcpToolName(server, tool, isTaken = () => false) {
16
+ const name = `mcp__${server}__${tool}`.replace(/[^A-Za-z0-9_]/g, "_");
17
+ if (name.length <= MAX_TOOL_NAME_LENGTH && !isTaken(name))
18
+ return name;
19
+ const hash = createHash("sha256").update(`${server}\0${tool}`).digest("hex").slice(0, 8);
20
+ return `${name.slice(0, MAX_TOOL_NAME_LENGTH - hash.length - 1)}_${hash}`;
21
+ }
22
+ /**
23
+ * Namespace of a server's tools: `mcp__<server>` with `-` replaced by `_`, like the tool names. Server
24
+ * names that differ only in `-` and `_` share a namespace, so a configuration must not have both.
25
+ */
26
+ export function mcpNamespace(server) {
27
+ return `mcp__${server.replace(/-/g, "_")}`;
28
+ }
29
+ //# sourceMappingURL=naming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"naming.js","sourceRoot":"","sources":["../src/naming.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,2EAA2E;AAC3E,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAChC,MAAc,EACd,IAAY,EACZ,OAAO,GAA8B,GAAG,EAAE,CAAC,KAAK;IAEhD,MAAM,IAAI,GAAG,QAAQ,MAAM,KAAK,IAAI,EAAE,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;IACtE,IAAI,IAAI,CAAC,MAAM,IAAI,oBAAoB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACvE,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACzF,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;AAC3E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc;IAC1C,OAAO,QAAQ,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;AAC5C,CAAC","sourcesContent":["/**\n * Names of MCP tools and of their servers' namespaces as models and code mode scripts see them.\n * Applications that name tools the same way rank and group them the same way in tool search and\n * code mode.\n */\n\nimport { createHash } from \"node:crypto\";\n\n/** Provider tool names are limited to 64 characters of `[A-Za-z0-9_-]`. */\nconst MAX_TOOL_NAME_LENGTH = 64;\n\n/**\n * `mcp__<server>__<tool>`, sanitized and shortened with a hash suffix when too long. Like Codex,\n * everything but `[A-Za-z0-9_]` becomes `_`, so the name is also the identifier code mode scripts\n * call it by. `isTaken` reports names used by a different MCP tool: sanitizing can map two tools to\n * one name (`a-b` and `a_b`), which then get the hash suffix.\n */\nexport function createMcpToolName(\n\tserver: string,\n\ttool: string,\n\tisTaken: (name: string) => boolean = () => false,\n): string {\n\tconst name = `mcp__${server}__${tool}`.replace(/[^A-Za-z0-9_]/g, \"_\");\n\tif (name.length <= MAX_TOOL_NAME_LENGTH && !isTaken(name)) return name;\n\tconst hash = createHash(\"sha256\").update(`${server}\\0${tool}`).digest(\"hex\").slice(0, 8);\n\treturn `${name.slice(0, MAX_TOOL_NAME_LENGTH - hash.length - 1)}_${hash}`;\n}\n\n/**\n * Namespace of a server's tools: `mcp__<server>` with `-` replaced by `_`, like the tool names. Server\n * names that differ only in `-` and `_` share a namespace, so a configuration must not have both.\n */\nexport function mcpNamespace(server: string): string {\n\treturn `mcp__${server.replace(/-/g, \"_\")}`;\n}\n"]}