@fractaal/pi-mcp 0.86.9 → 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.
- package/CHANGELOG.md +53 -0
- package/README.md +60 -5
- package/dist/auth-provider.d.ts +4 -0
- package/dist/auth-provider.d.ts.map +1 -1
- package/dist/auth-provider.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +9 -5
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +117 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +153 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/input-schema.d.ts +7 -0
- package/dist/input-schema.d.ts.map +1 -0
- package/dist/input-schema.js +13 -0
- package/dist/input-schema.js.map +1 -0
- package/dist/naming.d.ts +18 -0
- package/dist/naming.d.ts.map +1 -0
- package/dist/naming.js +29 -0
- package/dist/naming.js.map +1 -0
- package/dist/oauth/auth-provider.d.ts +52 -0
- package/dist/oauth/auth-provider.d.ts.map +1 -0
- package/dist/oauth/auth-provider.js +97 -0
- package/dist/oauth/auth-provider.js.map +1 -0
- package/dist/oauth/callback.d.ts.map +1 -1
- package/dist/oauth/callback.js +5 -1
- package/dist/oauth/callback.js.map +1 -1
- package/dist/oauth/client-settings.d.ts +107 -0
- package/dist/oauth/client-settings.d.ts.map +1 -0
- package/dist/oauth/client-settings.js +114 -0
- package/dist/oauth/client-settings.js.map +1 -0
- package/dist/oauth/discovery.d.ts +3 -0
- package/dist/oauth/discovery.d.ts.map +1 -1
- package/dist/oauth/discovery.js +9 -6
- package/dist/oauth/discovery.js.map +1 -1
- package/dist/oauth/errors.d.ts +10 -2
- package/dist/oauth/errors.d.ts.map +1 -1
- package/dist/oauth/errors.js +13 -1
- package/dist/oauth/errors.js.map +1 -1
- package/dist/oauth/flow.d.ts +14 -0
- package/dist/oauth/flow.d.ts.map +1 -1
- package/dist/oauth/flow.js +52 -15
- package/dist/oauth/flow.js.map +1 -1
- package/dist/oauth/index.d.ts +5 -1
- package/dist/oauth/index.d.ts.map +1 -1
- package/dist/oauth/index.js +4 -1
- package/dist/oauth/index.js.map +1 -1
- package/dist/oauth/loopback.d.ts +51 -0
- package/dist/oauth/loopback.d.ts.map +1 -0
- package/dist/oauth/loopback.js +98 -0
- package/dist/oauth/loopback.js.map +1 -0
- package/dist/oauth/sign-in.d.ts +73 -0
- package/dist/oauth/sign-in.d.ts.map +1 -0
- package/dist/oauth/sign-in.js +182 -0
- package/dist/oauth/sign-in.js.map +1 -0
- package/dist/oauth/types.d.ts +1 -0
- package/dist/oauth/types.d.ts.map +1 -1
- package/dist/oauth/types.js +9 -5
- package/dist/oauth/types.js.map +1 -1
- package/dist/output-limit.d.ts +24 -0
- package/dist/output-limit.d.ts.map +1 -0
- package/dist/output-limit.js +58 -0
- package/dist/output-limit.js.map +1 -0
- package/dist/protocol/content.d.ts +2 -1
- package/dist/protocol/content.d.ts.map +1 -1
- package/dist/protocol/content.js.map +1 -1
- package/dist/transports/lines.d.ts +34 -0
- package/dist/transports/lines.d.ts.map +1 -0
- package/dist/transports/lines.js +85 -0
- package/dist/transports/lines.js.map +1 -0
- package/dist/transports/pipe.d.ts +49 -0
- package/dist/transports/pipe.d.ts.map +1 -0
- package/dist/transports/pipe.js +96 -0
- package/dist/transports/pipe.js.map +1 -0
- package/dist/transports/stdio.d.ts +1 -4
- package/dist/transports/stdio.d.ts.map +1 -1
- package/dist/transports/stdio.js +20 -51
- package/dist/transports/stdio.js.map +1 -1
- package/dist/transports/streamable-http.d.ts +9 -2
- package/dist/transports/streamable-http.d.ts.map +1 -1
- package/dist/transports/streamable-http.js +105 -30
- package/dist/transports/streamable-http.js.map +1 -1
- package/dist/transports/transport.d.ts +9 -1
- package/dist/transports/transport.d.ts.map +1 -1
- package/dist/transports/transport.js.map +1 -1
- package/package.json +4 -5
- /package/{LICENSES → THIRD_PARTY_NOTICES}/modelcontextprotocol-typescript-sdk.txt +0 -0
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,9 +1,14 @@
|
|
|
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
|
-
export type { McpTransport, McpTransportCloseListener, McpTransportErrorListener, McpTransportMessageListener, } from "./transports/transport.ts";
|
|
13
|
+
export type { McpTransport, McpTransportCloseListener, McpTransportErrorListener, McpTransportMessageListener, McpTransportSendOptions, } from "./transports/transport.ts";
|
|
9
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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} 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"]}
|
package/dist/naming.d.ts
ADDED
|
@@ -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"]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The auth provider for connections to an OAuth-protected MCP server: it sends the stored access token
|
|
3
|
+
* and refreshes it, but never starts a browser flow. When the user has to sign in, it fails with
|
|
4
|
+
* `McpOAuthAuthorizationRequiredError` and the application runs `startMcpSignIn`.
|
|
5
|
+
*/
|
|
6
|
+
import type { AuthProvider, McpFetch } from "../auth-provider.ts";
|
|
7
|
+
import { type McpOAuthClientOptions, type McpOAuthSettings } from "./client-settings.ts";
|
|
8
|
+
import { type OAuthFlowResult } from "./flow.ts";
|
|
9
|
+
import type { McpOAuthState } from "./provider.ts";
|
|
10
|
+
import type { OAuthChallenge } from "./types.ts";
|
|
11
|
+
/** An `AuthProvider` that can say when its refreshes have finished. */
|
|
12
|
+
export interface McpAuthProvider extends AuthProvider {
|
|
13
|
+
/** Resolves when no refresh is running, so shutdown does not drop rotated tokens before they are saved. */
|
|
14
|
+
settled(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Auth provider for MCP connections: sends the stored access token and refreshes it when it is about
|
|
18
|
+
* to expire or after a 401. Throws `McpOAuthAuthorizationRequiredError` when the user has to sign in,
|
|
19
|
+
* including when the server asks for more scope (`insufficient_scope`). `onChallenge` receives the
|
|
20
|
+
* server's `WWW-Authenticate` challenge so sign-in can use its resource metadata URL and scope.
|
|
21
|
+
* `settings` is only called when a refresh is needed, so a secret that fails to resolve fails the
|
|
22
|
+
* refresh instead of the whole connection setup.
|
|
23
|
+
*
|
|
24
|
+
* Many servers rotate refresh tokens, so two refreshes with the same refresh token lose the grant.
|
|
25
|
+
* Requests in this process share one refresh, and other processes are kept out by the store's
|
|
26
|
+
* refresh lock, held from reading the tokens to saving new ones. Tokens that changed meanwhile
|
|
27
|
+
* (another process refreshed them, or the user signed in) are used without refreshing.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createMcpAuthProvider(options: Omit<McpOAuthClientOptions, "settings"> & {
|
|
30
|
+
settings: () => McpOAuthSettings;
|
|
31
|
+
onChallenge: (challenge: OAuthChallenge) => void;
|
|
32
|
+
/**
|
|
33
|
+
* A redirect URI the application signs in with, such as its https callback route. Refreshing tokens
|
|
34
|
+
* from a sign-in with a Client ID Metadata Document needs a redirect URI the documents list; a
|
|
35
|
+
* refresh never redirects, so any listed one works. Default: the loopback redirect URI of `settings`.
|
|
36
|
+
*/
|
|
37
|
+
redirectUrl?: string;
|
|
38
|
+
}): McpAuthProvider;
|
|
39
|
+
/**
|
|
40
|
+
* Refresh the tokens of `state` with the stored client (or the document it signed in with), or report
|
|
41
|
+
* `REDIRECT` when that is not possible. `options.redirectUrl` is a redirect URI the application signs in
|
|
42
|
+
* with, tried before the loopback ones of the settings and the stored client. Never registers a client or
|
|
43
|
+
* prepares an authorization request, so a pending sign-in stays usable. Call while holding the store's
|
|
44
|
+
* refresh lock. Not cancellable: a refresh the server answered may have rotated the refresh token, so its
|
|
45
|
+
* answer must be saved.
|
|
46
|
+
*/
|
|
47
|
+
export declare function refreshStoredTokens(client: McpOAuthClientOptions, state: McpOAuthState, options?: {
|
|
48
|
+
fetch?: McpFetch;
|
|
49
|
+
challenge?: OAuthChallenge;
|
|
50
|
+
redirectUrl?: string;
|
|
51
|
+
}): Promise<OAuthFlowResult>;
|
|
52
|
+
//# sourceMappingURL=auth-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-provider.d.ts","sourceRoot":"","sources":["../../src/oauth/auth-provider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAIN,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EAGrB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,uEAAuE;AACvE,MAAM,WAAW,eAAgB,SAAQ,YAAY;IACpD,2GAA2G;IAC3G,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAKD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,IAAI,CAAC,qBAAqB,EAAE,UAAU,CAAC,GAAG;IAClD,QAAQ,EAAE,MAAM,gBAAgB,CAAC;IACjC,WAAW,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAC;IACjD;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB,GACC,eAAe,CA2CjB;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACxC,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,aAAa,EACpB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,QAAQ,CAAC;IAAC,SAAS,CAAC,EAAE,cAAc,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GAClF,OAAO,CAAC,eAAe,CAAC,CAiB1B"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The auth provider for connections to an OAuth-protected MCP server: it sends the stored access token
|
|
3
|
+
* and refreshes it, but never starts a browser flow. When the user has to sign in, it fails with
|
|
4
|
+
* `McpOAuthAuthorizationRequiredError` and the application runs `startMcpSignIn`.
|
|
5
|
+
*/
|
|
6
|
+
import { createOAuthClientProvider, DEFAULT_REDIRECT_URL, loopbackSettings, registeredRedirectUrls, timedFetch, } from "./client-settings.js";
|
|
7
|
+
import { parseWwwAuthenticate } from "./discovery.js";
|
|
8
|
+
import { McpOAuthAuthorizationRequiredError } from "./errors.js";
|
|
9
|
+
import { authorizeMcp } from "./flow.js";
|
|
10
|
+
/** Access tokens this close to expiry are refreshed before they are sent. */
|
|
11
|
+
const REFRESH_SKEW_MS = 30_000;
|
|
12
|
+
/**
|
|
13
|
+
* Auth provider for MCP connections: sends the stored access token and refreshes it when it is about
|
|
14
|
+
* to expire or after a 401. Throws `McpOAuthAuthorizationRequiredError` when the user has to sign in,
|
|
15
|
+
* including when the server asks for more scope (`insufficient_scope`). `onChallenge` receives the
|
|
16
|
+
* server's `WWW-Authenticate` challenge so sign-in can use its resource metadata URL and scope.
|
|
17
|
+
* `settings` is only called when a refresh is needed, so a secret that fails to resolve fails the
|
|
18
|
+
* refresh instead of the whole connection setup.
|
|
19
|
+
*
|
|
20
|
+
* Many servers rotate refresh tokens, so two refreshes with the same refresh token lose the grant.
|
|
21
|
+
* Requests in this process share one refresh, and other processes are kept out by the store's
|
|
22
|
+
* refresh lock, held from reading the tokens to saving new ones. Tokens that changed meanwhile
|
|
23
|
+
* (another process refreshed them, or the user signed in) are used without refreshing.
|
|
24
|
+
*/
|
|
25
|
+
export function createMcpAuthProvider(options) {
|
|
26
|
+
const { store, redirectUrl } = options;
|
|
27
|
+
let refreshing;
|
|
28
|
+
/** Replace `staleToken`, the access token that expired or was rejected. */
|
|
29
|
+
const refresh = (staleToken, fetch = globalThis.fetch, challenge) => {
|
|
30
|
+
refreshing ??= store
|
|
31
|
+
.withRefreshLock(async () => {
|
|
32
|
+
const state = await store.load();
|
|
33
|
+
if (state?.tokens?.access_token !== staleToken)
|
|
34
|
+
return;
|
|
35
|
+
if (!state?.tokens?.refresh_token)
|
|
36
|
+
throw new McpOAuthAuthorizationRequiredError();
|
|
37
|
+
const client = { ...options, settings: options.settings() };
|
|
38
|
+
const result = await refreshStoredTokens(client, state, { fetch, challenge, redirectUrl });
|
|
39
|
+
if (result === "REDIRECT")
|
|
40
|
+
throw new McpOAuthAuthorizationRequiredError();
|
|
41
|
+
})
|
|
42
|
+
.finally(() => {
|
|
43
|
+
refreshing = undefined;
|
|
44
|
+
});
|
|
45
|
+
return refreshing;
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
token: async () => {
|
|
49
|
+
await refreshing?.catch(() => undefined);
|
|
50
|
+
const state = await store.load();
|
|
51
|
+
const token = state?.tokens?.access_token;
|
|
52
|
+
const expired = state?.tokensExpireAt !== undefined && state.tokensExpireAt - REFRESH_SKEW_MS <= Date.now();
|
|
53
|
+
if (!expired || !state?.tokens?.refresh_token)
|
|
54
|
+
return token;
|
|
55
|
+
// Failures fall through: the request goes out with the old token and a 401 decides what happens.
|
|
56
|
+
await refresh(token).catch(() => undefined);
|
|
57
|
+
return (await store.load())?.tokens?.access_token;
|
|
58
|
+
},
|
|
59
|
+
onUnauthorized: async (context) => {
|
|
60
|
+
const challenge = parseWwwAuthenticate(context.response.headers.get("www-authenticate"));
|
|
61
|
+
options.onChallenge(challenge);
|
|
62
|
+
// A refresh keeps the granted scope, so more scope needs a new sign-in.
|
|
63
|
+
if (challenge.error === "insufficient_scope")
|
|
64
|
+
throw new McpOAuthAuthorizationRequiredError();
|
|
65
|
+
await refresh(context.token, context.fetch, challenge);
|
|
66
|
+
},
|
|
67
|
+
settled: async () => {
|
|
68
|
+
await refreshing?.catch(() => undefined);
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Refresh the tokens of `state` with the stored client (or the document it signed in with), or report
|
|
74
|
+
* `REDIRECT` when that is not possible. `options.redirectUrl` is a redirect URI the application signs in
|
|
75
|
+
* with, tried before the loopback ones of the settings and the stored client. Never registers a client or
|
|
76
|
+
* prepares an authorization request, so a pending sign-in stays usable. Call while holding the store's
|
|
77
|
+
* refresh lock. Not cancellable: a refresh the server answered may have rotated the refresh token, so its
|
|
78
|
+
* answer must be saved.
|
|
79
|
+
*/
|
|
80
|
+
export async function refreshStoredTokens(client, state, options = {}) {
|
|
81
|
+
// Refreshing never redirects the user; the redirect URI only decides whether tokens without a stored
|
|
82
|
+
// client can be refreshed with a Client ID Metadata Document, so any URI the documents list works.
|
|
83
|
+
const candidates = [
|
|
84
|
+
options.redirectUrl,
|
|
85
|
+
loopbackSettings(client.settings).fixedRedirectUrl,
|
|
86
|
+
registeredRedirectUrls(state.clientInformation)[0],
|
|
87
|
+
].filter((url) => url !== undefined);
|
|
88
|
+
const redirectUrl = candidates.find((url) => client.clientMetadataDocuments?.listsRedirectUrl(url)) ?? DEFAULT_REDIRECT_URL;
|
|
89
|
+
return authorizeMcp(createOAuthClientProvider(client, redirectUrl), {
|
|
90
|
+
serverUrl: client.serverUrl,
|
|
91
|
+
resourceMetadataUrl: options.challenge?.resourceMetadataUrl,
|
|
92
|
+
authorizationServerMetadataUrl: client.settings.authServerMetadataUrl,
|
|
93
|
+
fetch: timedFetch(options.fetch),
|
|
94
|
+
refreshOnly: true,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=auth-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-provider.js","sourceRoot":"","sources":["../../src/oauth/auth-provider.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The auth provider for connections to an OAuth-protected MCP server: it sends the stored access token\n * and refreshes it, but never starts a browser flow. When the user has to sign in, it fails with\n * `McpOAuthAuthorizationRequiredError` and the application runs `startMcpSignIn`.\n */\n\nimport type { AuthProvider, McpFetch } from \"../auth-provider.ts\";\nimport {\n\tcreateOAuthClientProvider,\n\tDEFAULT_REDIRECT_URL,\n\tloopbackSettings,\n\ttype McpOAuthClientOptions,\n\ttype McpOAuthSettings,\n\tregisteredRedirectUrls,\n\ttimedFetch,\n} from \"./client-settings.ts\";\nimport { parseWwwAuthenticate } from \"./discovery.ts\";\nimport { McpOAuthAuthorizationRequiredError } from \"./errors.ts\";\nimport { authorizeMcp, type OAuthFlowResult } from \"./flow.ts\";\nimport type { McpOAuthState } from \"./provider.ts\";\nimport type { OAuthChallenge } from \"./types.ts\";\n\n/** An `AuthProvider` that can say when its refreshes have finished. */\nexport interface McpAuthProvider extends AuthProvider {\n\t/** Resolves when no refresh is running, so shutdown does not drop rotated tokens before they are saved. */\n\tsettled(): Promise<void>;\n}\n\n/** Access tokens this close to expiry are refreshed before they are sent. */\nconst REFRESH_SKEW_MS = 30_000;\n\n/**\n * Auth provider for MCP connections: sends the stored access token and refreshes it when it is about\n * to expire or after a 401. Throws `McpOAuthAuthorizationRequiredError` when the user has to sign in,\n * including when the server asks for more scope (`insufficient_scope`). `onChallenge` receives the\n * server's `WWW-Authenticate` challenge so sign-in can use its resource metadata URL and scope.\n * `settings` is only called when a refresh is needed, so a secret that fails to resolve fails the\n * refresh instead of the whole connection setup.\n *\n * Many servers rotate refresh tokens, so two refreshes with the same refresh token lose the grant.\n * Requests in this process share one refresh, and other processes are kept out by the store's\n * refresh lock, held from reading the tokens to saving new ones. Tokens that changed meanwhile\n * (another process refreshed them, or the user signed in) are used without refreshing.\n */\nexport function createMcpAuthProvider(\n\toptions: Omit<McpOAuthClientOptions, \"settings\"> & {\n\t\tsettings: () => McpOAuthSettings;\n\t\tonChallenge: (challenge: OAuthChallenge) => void;\n\t\t/**\n\t\t * A redirect URI the application signs in with, such as its https callback route. Refreshing tokens\n\t\t * from a sign-in with a Client ID Metadata Document needs a redirect URI the documents list; a\n\t\t * refresh never redirects, so any listed one works. Default: the loopback redirect URI of `settings`.\n\t\t */\n\t\tredirectUrl?: string;\n\t},\n): McpAuthProvider {\n\tconst { store, redirectUrl } = options;\n\tlet refreshing: Promise<void> | undefined;\n\n\t/** Replace `staleToken`, the access token that expired or was rejected. */\n\tconst refresh = (staleToken: string | undefined, fetch: McpFetch = globalThis.fetch, challenge?: OAuthChallenge) => {\n\t\trefreshing ??= store\n\t\t\t.withRefreshLock(async () => {\n\t\t\t\tconst state = await store.load();\n\t\t\t\tif (state?.tokens?.access_token !== staleToken) return;\n\t\t\t\tif (!state?.tokens?.refresh_token) throw new McpOAuthAuthorizationRequiredError();\n\t\t\t\tconst client = { ...options, settings: options.settings() };\n\t\t\t\tconst result = await refreshStoredTokens(client, state, { fetch, challenge, redirectUrl });\n\t\t\t\tif (result === \"REDIRECT\") throw new McpOAuthAuthorizationRequiredError();\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\trefreshing = undefined;\n\t\t\t});\n\t\treturn refreshing;\n\t};\n\n\treturn {\n\t\ttoken: async () => {\n\t\t\tawait refreshing?.catch(() => undefined);\n\t\t\tconst state = await store.load();\n\t\t\tconst token = state?.tokens?.access_token;\n\t\t\tconst expired = state?.tokensExpireAt !== undefined && state.tokensExpireAt - REFRESH_SKEW_MS <= Date.now();\n\t\t\tif (!expired || !state?.tokens?.refresh_token) return token;\n\t\t\t// Failures fall through: the request goes out with the old token and a 401 decides what happens.\n\t\t\tawait refresh(token).catch(() => undefined);\n\t\t\treturn (await store.load())?.tokens?.access_token;\n\t\t},\n\t\tonUnauthorized: async (context) => {\n\t\t\tconst challenge = parseWwwAuthenticate(context.response.headers.get(\"www-authenticate\"));\n\t\t\toptions.onChallenge(challenge);\n\t\t\t// A refresh keeps the granted scope, so more scope needs a new sign-in.\n\t\t\tif (challenge.error === \"insufficient_scope\") throw new McpOAuthAuthorizationRequiredError();\n\t\t\tawait refresh(context.token, context.fetch, challenge);\n\t\t},\n\t\tsettled: async () => {\n\t\t\tawait refreshing?.catch(() => undefined);\n\t\t},\n\t};\n}\n\n/**\n * Refresh the tokens of `state` with the stored client (or the document it signed in with), or report\n * `REDIRECT` when that is not possible. `options.redirectUrl` is a redirect URI the application signs in\n * with, tried before the loopback ones of the settings and the stored client. Never registers a client or\n * prepares an authorization request, so a pending sign-in stays usable. Call while holding the store's\n * refresh lock. Not cancellable: a refresh the server answered may have rotated the refresh token, so its\n * answer must be saved.\n */\nexport async function refreshStoredTokens(\n\tclient: McpOAuthClientOptions,\n\tstate: McpOAuthState,\n\toptions: { fetch?: McpFetch; challenge?: OAuthChallenge; redirectUrl?: string } = {},\n): Promise<OAuthFlowResult> {\n\t// Refreshing never redirects the user; the redirect URI only decides whether tokens without a stored\n\t// client can be refreshed with a Client ID Metadata Document, so any URI the documents list works.\n\tconst candidates = [\n\t\toptions.redirectUrl,\n\t\tloopbackSettings(client.settings).fixedRedirectUrl,\n\t\tregisteredRedirectUrls(state.clientInformation)[0],\n\t].filter((url) => url !== undefined);\n\tconst redirectUrl =\n\t\tcandidates.find((url) => client.clientMetadataDocuments?.listsRedirectUrl(url)) ?? DEFAULT_REDIRECT_URL;\n\treturn authorizeMcp(createOAuthClientProvider(client, redirectUrl), {\n\t\tserverUrl: client.serverUrl,\n\t\tresourceMetadataUrl: options.challenge?.resourceMetadataUrl,\n\t\tauthorizationServerMetadataUrl: client.settings.authServerMetadataUrl,\n\t\tfetch: timedFetch(options.fetch),\n\t\trefreshOnly: true,\n\t});\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"callback.d.ts","sourceRoot":"","sources":["../../src/oauth/callback.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,4DAA4D;AAC5D,MAAM,MAAM,iBAAiB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhG,MAAM,WAAW,0BAA0B;IAC1C,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kGAAkG;IAClG,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,MAAM,CAAC;CACjD;AAOD,qBAAa,mBAAmB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAoD;IACtE,OAAO,CAAC,OAAO,CAQX;IAEJ,OAAO,eAYN;IAED,OAAa,MAAM,CAAC,OAAO,GAAE,0BAA+B,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuB1F;IAED;;;OAGG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CASpE;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"callback.d.ts","sourceRoot":"","sources":["../../src/oauth/callback.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,4DAA4D;AAC5D,MAAM,MAAM,iBAAiB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhG,MAAM,WAAW,0BAA0B;IAC1C,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kGAAkG;IAClG,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,MAAM,CAAC;CACjD;AAOD,qBAAa,mBAAmB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAoD;IACtE,OAAO,CAAC,OAAO,CAQX;IAEJ,OAAO,eAYN;IAED,OAAa,MAAM,CAAC,OAAO,GAAE,0BAA+B,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuB1F;IAED;;;OAGG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CASpE;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAa3B;IAED,OAAO,CAAC,KAAK;IASb,OAAO,CAAC,MAAM;CAwCd"}
|
package/dist/oauth/callback.js
CHANGED
|
@@ -58,9 +58,13 @@ export class OAuthCallbackServer {
|
|
|
58
58
|
pending.reject(new Error("OAuth callback server closed"));
|
|
59
59
|
}
|
|
60
60
|
this.pending.clear();
|
|
61
|
-
|
|
61
|
+
const closed = new Promise((resolve, reject) => {
|
|
62
62
|
this.server.close((error) => (error ? reject(error) : resolve()));
|
|
63
63
|
});
|
|
64
|
+
// `close()` waits for every connection, and a browser may keep an unused one open (a preconnect or a
|
|
65
|
+
// spare keep-alive socket) for minutes. Responses have been sent by now, so drop them.
|
|
66
|
+
this.server.closeAllConnections();
|
|
67
|
+
await closed;
|
|
64
68
|
}
|
|
65
69
|
reply(response, status, page) {
|
|
66
70
|
if (this.renderPage) {
|