toolcraft 0.0.344 → 0.0.346
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/composition.json +3 -3
- package/dist/composition.json +3 -3
- package/node_modules/tiny-http-mcp-server/dist/composition.json +1 -1
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/README.md +12 -3
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/loopback-authorization.js +25 -16
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-endpoint.js +2 -0
- package/node_modules/tiny-http-mcp-server/package.json +1 -1
- package/node_modules/tiny-mcp-client/README.md +22 -13
- package/node_modules/tiny-mcp-client/dist/index.d.ts +9 -1
- package/node_modules/tiny-mcp-client/dist/index.js +65 -27
- package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
- package/node_modules/toolcraft-schema/package.json +1 -1
- package/package.json +2 -2
package/composition.json
CHANGED
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
},
|
|
99
99
|
{
|
|
100
100
|
"name": "tiny-http-mcp-server",
|
|
101
|
-
"version": "0.1.
|
|
101
|
+
"version": "0.1.74",
|
|
102
102
|
"license": "MIT"
|
|
103
103
|
},
|
|
104
104
|
{
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"name": "toolcraft",
|
|
116
|
-
"version": "0.0.
|
|
116
|
+
"version": "0.0.346",
|
|
117
117
|
"license": "MIT"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"name": "toolcraft-schema",
|
|
126
|
-
"version": "0.0.
|
|
126
|
+
"version": "0.0.346",
|
|
127
127
|
"license": "MIT"
|
|
128
128
|
},
|
|
129
129
|
{
|
package/dist/composition.json
CHANGED
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
},
|
|
99
99
|
{
|
|
100
100
|
"name": "tiny-http-mcp-server",
|
|
101
|
-
"version": "0.1.
|
|
101
|
+
"version": "0.1.74",
|
|
102
102
|
"license": "MIT"
|
|
103
103
|
},
|
|
104
104
|
{
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"name": "toolcraft",
|
|
116
|
-
"version": "0.0.
|
|
116
|
+
"version": "0.0.346",
|
|
117
117
|
"license": "MIT"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"name": "toolcraft-schema",
|
|
126
|
-
"version": "0.0.
|
|
126
|
+
"version": "0.0.346",
|
|
127
127
|
"license": "MIT"
|
|
128
128
|
},
|
|
129
129
|
{
|
|
@@ -91,14 +91,21 @@ later environment changes cannot move a transaction between file and Keychain.
|
|
|
91
91
|
Fixed redirects support `localhost`, `127.0.0.1`, and `::1` over HTTP. Their
|
|
92
92
|
exact spelling, port, path and query are preserved through registration,
|
|
93
93
|
authorization and code exchange. Credentials, fragments, port zero and reserved
|
|
94
|
-
OAuth callback query parameters are rejected before binding a listener.
|
|
94
|
+
OAuth callback query parameters, including `error_uri`, are rejected before binding a listener.
|
|
95
95
|
`createDefaultOAuthClientProvider` also checks the configured redirect before
|
|
96
96
|
creating the provider. Imported tokens that cannot be sent as HTTP header
|
|
97
97
|
values fail with diagnostics that omit their contents. Omit
|
|
98
98
|
`redirectUri` to allocate a random loopback port. Standalone callback sessions
|
|
99
|
-
accept the same `redirectUri`, `signal` and `timeoutMs` options.
|
|
99
|
+
accept the same `redirectUri`, `signal` and `timeoutMs` options. They capture configuration,
|
|
100
|
+
landing-page data and selected callbacks before invoking a host listener factory;
|
|
101
|
+
original signals and selected callback receivers/live method state remain active. Cancellation,
|
|
100
102
|
timeout and explicit close settle pending code waits and release listeners.
|
|
101
|
-
Always close a
|
|
103
|
+
Always close a standalone session in `finally`. Failed callback responses declare
|
|
104
|
+
plain UTF-8 text with content sniffing disabled; provider markup remains literal
|
|
105
|
+
text. Successful landing pages remain HTML with escaped custom title/body. Recognized
|
|
106
|
+
OAuth callback parameters must occur only once in HTTP callbacks or pasted URLs;
|
|
107
|
+
duplicates fail without reflecting values and code extraction returns null.
|
|
108
|
+
Unrecognized extension parameters remain ignored, including repeated fields.
|
|
102
109
|
|
|
103
110
|
Native provider calls capture request option handles before host work. Unauthorized
|
|
104
111
|
handling also owns complete discovery metadata, presented grant values, rejected
|
|
@@ -266,6 +273,8 @@ Before sending a refresh request, the provider persists a tokenless session with
|
|
|
266
273
|
A successful response replaces it with the rotated grant. A crash, cancellation,
|
|
267
274
|
network disconnect or incomplete response leaves the marker, so another process
|
|
268
275
|
cannot replay a possibly consumed refresh token or revive the initial import.
|
|
276
|
+
An explicit `refresh_token` must be a nonempty string; malformed fields also
|
|
277
|
+
retain the pending marker. Omitting it preserves the previous refresh token.
|
|
269
278
|
Such a session requires fresh authorization. Interactive unauthorized handling
|
|
270
279
|
can recover it; headless requests fail with an explicit unknown-outcome error.
|
|
271
280
|
Only complete OAuth error responses establish a rejected request and allow a
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import http from "node:http";
|
|
2
2
|
import { parseAuthorizationState } from "./authorization-state.js";
|
|
3
3
|
const authorizationErrorBrand = Symbol.for("poe-platform.mcp-oauth.OAuthAuthorizationError");
|
|
4
|
+
const oauthCallbackParameters = ["code", "state", "iss", "error", "error_description", "error_uri"];
|
|
4
5
|
/** Authorization callback denial, retaining the provider diagnostic for host observers. */
|
|
5
6
|
export class OAuthAuthorizationError extends Error {
|
|
6
7
|
error;
|
|
@@ -18,6 +19,10 @@ export class OAuthAuthorizationError extends Error {
|
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
export async function createLoopbackAuthorizationSession(options = {}) {
|
|
22
|
+
const selected = { ...options };
|
|
23
|
+
options = { ...selected, createServer: selected.createServer?.bind(options),
|
|
24
|
+
openBrowser: selected.openBrowser?.bind(options), readLine: selected.readLine?.bind(options),
|
|
25
|
+
landingPage: selected.landingPage === undefined ? undefined : { ...selected.landingPage } };
|
|
21
26
|
options.signal?.throwIfAborted();
|
|
22
27
|
const timeoutMs = options.timeoutMs ?? 120_000;
|
|
23
28
|
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2_147_483_647)
|
|
@@ -80,10 +85,9 @@ export function loopbackTarget(options) {
|
|
|
80
85
|
catch (cause) {
|
|
81
86
|
throw new Error("Invalid OAuth loopback redirect URI", { cause });
|
|
82
87
|
}
|
|
83
|
-
const forbiddenQuery = ["code", "state", "error", "error_description", "iss"];
|
|
84
88
|
if (url.protocol !== "http:" || !["localhost", "127.0.0.1", "[::1]"].includes(url.hostname)
|
|
85
89
|
|| url.username || url.password || url.hash || url.port === "0"
|
|
86
|
-
||
|
|
90
|
+
|| oauthCallbackParameters.some(name => url.searchParams.has(name))
|
|
87
91
|
|| [...options.redirectUri].some(char => char.codePointAt(0) <= 32)
|
|
88
92
|
|| (options.callbackPath !== undefined && options.callbackPath !== url.pathname))
|
|
89
93
|
throw new Error("Invalid OAuth loopback redirect URI");
|
|
@@ -154,11 +158,8 @@ function waitForAuthorizationCode(server, authorizationUrl, options, callbackPat
|
|
|
154
158
|
res.end("Not found");
|
|
155
159
|
return;
|
|
156
160
|
}
|
|
157
|
-
const callbackParameters = {
|
|
158
|
-
code: url.searchParams.get("code"), error: url.searchParams.get("error"),
|
|
159
|
-
errorDescription: url.searchParams.get("error_description"), state: url.searchParams.get("state"), iss: url.searchParams.get("iss")
|
|
160
|
-
};
|
|
161
161
|
try {
|
|
162
|
+
const callbackParameters = readAuthorizationCallbackParameters(url);
|
|
162
163
|
validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
|
|
163
164
|
if (callbackParameters.error !== null)
|
|
164
165
|
throw new OAuthAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
|
|
@@ -168,7 +169,7 @@ function waitForAuthorizationCode(server, authorizationUrl, options, callbackPat
|
|
|
168
169
|
settle(() => resolve(code));
|
|
169
170
|
}
|
|
170
171
|
catch (error) {
|
|
171
|
-
res.writeHead(400);
|
|
172
|
+
res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8", "X-Content-Type-Options": "nosniff" });
|
|
172
173
|
res.end(error instanceof Error ? error.message : "Invalid OAuth callback");
|
|
173
174
|
settle(() => reject(error));
|
|
174
175
|
}
|
|
@@ -196,22 +197,21 @@ function waitForAuthorizationCode(server, authorizationUrl, options, callbackPat
|
|
|
196
197
|
});
|
|
197
198
|
}
|
|
198
199
|
export function extractCodeFromInput(input) {
|
|
199
|
-
|
|
200
|
+
try {
|
|
201
|
+
return extractCallbackParametersFromInput(input)?.code ?? null;
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
200
206
|
}
|
|
201
207
|
function extractCallbackParametersFromInput(input) {
|
|
202
208
|
const trimmed = input.replaceAll("\r", "").replaceAll("\n", "").trim();
|
|
203
209
|
if (trimmed.length === 0) {
|
|
204
210
|
return null;
|
|
205
211
|
}
|
|
212
|
+
let url;
|
|
206
213
|
try {
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
code: url.searchParams.get("code"),
|
|
210
|
-
error: url.searchParams.get("error"),
|
|
211
|
-
errorDescription: url.searchParams.get("error_description"),
|
|
212
|
-
state: url.searchParams.get("state"),
|
|
213
|
-
iss: url.searchParams.get("iss")
|
|
214
|
-
};
|
|
214
|
+
url = new URL(trimmed);
|
|
215
215
|
}
|
|
216
216
|
catch {
|
|
217
217
|
return {
|
|
@@ -222,6 +222,15 @@ function extractCallbackParametersFromInput(input) {
|
|
|
222
222
|
iss: null
|
|
223
223
|
};
|
|
224
224
|
}
|
|
225
|
+
return readAuthorizationCallbackParameters(url);
|
|
226
|
+
}
|
|
227
|
+
function readAuthorizationCallbackParameters(url) {
|
|
228
|
+
for (const parameter of oauthCallbackParameters) {
|
|
229
|
+
if (url.searchParams.getAll(parameter).length > 1)
|
|
230
|
+
throw new Error(`OAuth callback parameter '${parameter}' must occur only once`);
|
|
231
|
+
}
|
|
232
|
+
return { code: url.searchParams.get("code"), error: url.searchParams.get("error"),
|
|
233
|
+
errorDescription: url.searchParams.get("error_description"), state: url.searchParams.get("state"), iss: url.searchParams.get("iss") };
|
|
225
234
|
}
|
|
226
235
|
function readExpectedAuthorizationCallback(authorizationUrl) {
|
|
227
236
|
const url = new URL(authorizationUrl);
|
package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-endpoint.js
CHANGED
|
@@ -138,6 +138,8 @@ async function requestTokens(input) {
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
const refreshToken = getOwnEntry(payload, "refresh_token");
|
|
141
|
+
if (refreshToken !== undefined && (typeof refreshToken !== "string" || refreshToken.trim().length === 0))
|
|
142
|
+
throw new Error("OAuth token response has invalid refresh_token");
|
|
141
143
|
const scope = getOwnEntry(payload, "scope");
|
|
142
144
|
const normalizedRefreshToken = typeof refreshToken === "string" && refreshToken.trim().length > 0
|
|
143
145
|
? refreshToken.trim()
|
|
@@ -58,10 +58,18 @@ Legacy SSE mode opens a GET stream and posts messages to the endpoint announced
|
|
|
58
58
|
by the server. Announced endpoints must remain on the original origin without
|
|
59
59
|
embedded credentials or fragments; endpoint changes close the connection.
|
|
60
60
|
Both transports accept the same headers, OAuth provider and response limits.
|
|
61
|
+
Static HTTP headers are copied and validated when the transport is constructed,
|
|
62
|
+
before OAuth callbacks run. Invalid headers fail without reflecting their values.
|
|
63
|
+
Use `snapshotHttpTransportHeaders(headers)` to apply the same ownership and safe
|
|
64
|
+
validation when preparing a registry before constructing transports.
|
|
61
65
|
`maxResponseBytes` bounds a complete JSON body or each SSE event (default 16 MiB).
|
|
62
66
|
Receive streams may carry many bounded events and keepalive comments; the limit
|
|
63
67
|
does not cap their lifetime bytes. Use request deadlines and cancellation to
|
|
64
68
|
bound pending operations.
|
|
69
|
+
Receive reconnections retain the last completed event ID through events and
|
|
70
|
+
keepalives without IDs. An explicit empty `id:` clears the resume header,
|
|
71
|
+
including any initially configured value.
|
|
72
|
+
Unicode event IDs use UTF-8 bytes in `Last-Event-ID` headers.
|
|
65
73
|
HTTP failures expose `HttpTransportError.status` and `.method`, so callers can
|
|
66
74
|
make transport decisions without parsing error messages. Legacy HTTP/SSE
|
|
67
75
|
`connect()` also waits for the initialized notification POST to complete before
|
|
@@ -79,29 +87,30 @@ the same optional `closeReason` promise when closing requires asynchronous work.
|
|
|
79
87
|
`HttpTransport` accepts `oauth` options from `mcp-oauth`. When a protected server returns a Bearer `WWW-Authenticate` challenge, the transport discovers protected-resource metadata, loads authorization-server metadata, lets the OAuth provider handle authorization, and retries the request when credentials are available.
|
|
80
88
|
|
|
81
89
|
```ts
|
|
82
|
-
import { HttpTransport
|
|
90
|
+
import { HttpTransport } from "tiny-mcp-client";
|
|
83
91
|
|
|
84
92
|
const transport = new HttpTransport({
|
|
85
93
|
url: "https://mcp.example.com/mcp",
|
|
86
94
|
oauth: {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
clientName: "tiny-client"
|
|
92
|
-
}
|
|
93
|
-
},
|
|
94
|
-
browser: {
|
|
95
|
-
openBrowser: async (url) => {
|
|
96
|
-
console.log(`Open ${url}`);
|
|
97
|
-
}
|
|
95
|
+
client: {
|
|
96
|
+
mode: "dynamic",
|
|
97
|
+
metadata: {
|
|
98
|
+
clientName: "tiny-client"
|
|
98
99
|
}
|
|
99
|
-
}
|
|
100
|
+
},
|
|
101
|
+
browser: {
|
|
102
|
+
openBrowser: async (url) => {
|
|
103
|
+
console.log(`Open ${url}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
100
106
|
}
|
|
101
107
|
});
|
|
102
108
|
```
|
|
103
109
|
|
|
104
110
|
You can also call `discoverOAuthMetadata(resourceUrl, options)` directly, or instantiate `OAuthMetadataDiscovery` with a custom `fetch` implementation and shared cache. An explicit `resourceMetadataUrl` requires fresh network discovery without reading either cache; validated results still populate both caches. Lookup options accept `signal`; cancellation stops metadata fetches and body reads without trying another discovery candidate. It also settles while shared-cache reads, writes or eviction wait. Host cache work may finish afterward; its late rejection remains observed, and canceled discovery does not start another candidate.
|
|
111
|
+
Resource/issuer binding mismatches and exhausted authorization-server discovery
|
|
112
|
+
expose `OAuthMetadataError.phase` while retaining complete SDK messages.
|
|
113
|
+
`OAuthMetadataError.is(value)` recognizes separately bundled native copies.
|
|
105
114
|
|
|
106
115
|
OAuth provider inputs receive the originating request's `signal`, covering header authorization and unauthorized handling. Modern request cancellation stops its OAuth work while leaving other requests usable; transport disposal cancels all pending OAuth operations. The default provider propagates this signal to callback, registration and token work. Custom providers must observe it for their own operations.
|
|
107
116
|
|
|
@@ -304,6 +304,12 @@ interface OAuthMetadataLookupOptions {
|
|
|
304
304
|
resourceMetadataUrl?: string | URL;
|
|
305
305
|
signal?: AbortSignal;
|
|
306
306
|
}
|
|
307
|
+
/** Native metadata failure with complete SDK diagnostics and a safe phase. */
|
|
308
|
+
declare class OAuthMetadataError extends Error {
|
|
309
|
+
readonly phase: "protected-resource" | "authorization-server";
|
|
310
|
+
static is(value: unknown): value is OAuthMetadataError;
|
|
311
|
+
constructor(phase: "protected-resource" | "authorization-server", message: string);
|
|
312
|
+
}
|
|
307
313
|
declare class OAuthMetadataDiscovery {
|
|
308
314
|
private readonly fetchImpl;
|
|
309
315
|
private readonly cache;
|
|
@@ -725,6 +731,8 @@ declare class StdioTransport implements McpTransport {
|
|
|
725
731
|
private appendStderrOutput;
|
|
726
732
|
dispose(reason?: Error): void;
|
|
727
733
|
}
|
|
734
|
+
/** Own and validate static HTTP headers without reflecting malformed values. */
|
|
735
|
+
declare function snapshotHttpTransportHeaders(headers?: RequestInit["headers"]): Headers;
|
|
728
736
|
declare class HttpTransport implements McpTransport {
|
|
729
737
|
readonly readable: Readable;
|
|
730
738
|
readonly writable: Writable;
|
|
@@ -872,5 +880,5 @@ declare class JsonRpcMessageLayer {
|
|
|
872
880
|
private handleCancellationNotification;
|
|
873
881
|
}
|
|
874
882
|
|
|
875
|
-
export { ERROR_INTERNAL, ERROR_INVALID_PARAMS, ERROR_INVALID_REQUEST, ERROR_METHOD_NOT_FOUND, ERROR_PARSE, HttpTransport, HttpTransportError, JsonRpcMessageLayer, MCP_PROTOCOL_VERSIONS, McpClient, McpError, OAuthMetadataDiscovery, StdioTransport, createInMemoryTransportPair, createSdkTestPair, createTestPair, discoverOAuthMetadata, fetchMcpResponse };
|
|
883
|
+
export { ERROR_INTERNAL, ERROR_INVALID_PARAMS, ERROR_INVALID_REQUEST, ERROR_METHOD_NOT_FOUND, ERROR_PARSE, HttpTransport, HttpTransportError, JsonRpcMessageLayer, MCP_PROTOCOL_VERSIONS, McpClient, McpError, OAuthMetadataDiscovery, OAuthMetadataError, StdioTransport, createInMemoryTransportPair, createSdkTestPair, createTestPair, discoverOAuthMetadata, fetchMcpResponse, snapshotHttpTransportHeaders };
|
|
876
884
|
export type { AudioContent, BlobResourceContents, CacheableResultMetadata, CallToolOptions, CallToolParams, CallToolResult, ClientCapabilities, CompleteArgument, CompleteParams, CompleteResult, Completion, ConnectResult, ContentItem, CreateMessageParams, CreateMessageResult, ElicitationParams, ElicitationResult, EmbeddedResource, GetPromptParams, GetPromptResult, HttpTransportFetch, HttpTransportOptions, ImageContent, InMemoryTransportPair, IncludeContext, InitializeParams, InitializeResult, JsonRpcErrorObject, JsonRpcErrorResponse, JsonRpcMessage, JsonRpcNotification, JsonRpcRequest, JsonRpcRequestOptions, JsonRpcResponse, JsonRpcSuccessResponse, LogLevel, LogMessage, McpClientConnection, McpClientOptions, McpProtocolVersion, McpRequestContext, McpSubscription, McpTransport, McpTransportClosedEvent, ModelHint, ModelPreferences, NotificationFilter, OAuthAuthorizationServerMetadata, OAuthClientProvider, OAuthClientProviderOptions, OAuthDiscoveryCache, OAuthDiscoveryResult, OAuthMetadataFetch, OAuthProtectedResourceMetadata, OAuthSessionStore, OAuthUnauthorizedChallenge, PaginatedParams, PaginatedResult, ProgressParams, ProgressToken, PromptMessage, PromptReference, ReadResourceParams, RequestId, ResourceContents, ResourceReference, ResultMetadata, Root, SamplingContent, SamplingMessage, SdkTestPair, ServerCapabilities, StdioSpawn, StdioTransportOptions, StoredOAuthSession, SubscriptionOptions, TextContent, TextResourceContents, Tool, ToolResultContent, ToolUseContent };
|
|
@@ -3517,6 +3517,7 @@ function getOwnEntry(record2, key2) {
|
|
|
3517
3517
|
|
|
3518
3518
|
// ../mcp-oauth/dist/client/loopback-authorization.js
|
|
3519
3519
|
var authorizationErrorBrand = /* @__PURE__ */ Symbol.for("poe-platform.mcp-oauth.OAuthAuthorizationError");
|
|
3520
|
+
var oauthCallbackParameters = ["code", "state", "iss", "error", "error_description", "error_uri"];
|
|
3520
3521
|
var OAuthAuthorizationError = class extends Error {
|
|
3521
3522
|
error;
|
|
3522
3523
|
errorDescription;
|
|
@@ -3533,6 +3534,14 @@ var OAuthAuthorizationError = class extends Error {
|
|
|
3533
3534
|
}
|
|
3534
3535
|
};
|
|
3535
3536
|
async function createLoopbackAuthorizationSession(options = {}) {
|
|
3537
|
+
const selected = { ...options };
|
|
3538
|
+
options = {
|
|
3539
|
+
...selected,
|
|
3540
|
+
createServer: selected.createServer?.bind(options),
|
|
3541
|
+
openBrowser: selected.openBrowser?.bind(options),
|
|
3542
|
+
readLine: selected.readLine?.bind(options),
|
|
3543
|
+
landingPage: selected.landingPage === void 0 ? void 0 : { ...selected.landingPage }
|
|
3544
|
+
};
|
|
3536
3545
|
options.signal?.throwIfAborted();
|
|
3537
3546
|
const timeoutMs = options.timeoutMs ?? 12e4;
|
|
3538
3547
|
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647)
|
|
@@ -3592,8 +3601,7 @@ function loopbackTarget(options) {
|
|
|
3592
3601
|
} catch (cause) {
|
|
3593
3602
|
throw new Error("Invalid OAuth loopback redirect URI", { cause });
|
|
3594
3603
|
}
|
|
3595
|
-
|
|
3596
|
-
if (url.protocol !== "http:" || !["localhost", "127.0.0.1", "[::1]"].includes(url.hostname) || url.username || url.password || url.hash || url.port === "0" || forbiddenQuery.some((name) => url.searchParams.has(name)) || [...options.redirectUri].some((char) => char.codePointAt(0) <= 32) || options.callbackPath !== void 0 && options.callbackPath !== url.pathname)
|
|
3604
|
+
if (url.protocol !== "http:" || !["localhost", "127.0.0.1", "[::1]"].includes(url.hostname) || url.username || url.password || url.hash || url.port === "0" || oauthCallbackParameters.some((name) => url.searchParams.has(name)) || [...options.redirectUri].some((char) => char.codePointAt(0) <= 32) || options.callbackPath !== void 0 && options.callbackPath !== url.pathname)
|
|
3597
3605
|
throw new Error("Invalid OAuth loopback redirect URI");
|
|
3598
3606
|
return { port: url.port ? Number(url.port) : 80, host: url.hostname === "[::1]" ? "::1" : url.hostname, callbackPath: url.pathname };
|
|
3599
3607
|
}
|
|
@@ -3669,14 +3677,8 @@ function waitForAuthorizationCode(server, authorizationUrl, options, callbackPat
|
|
|
3669
3677
|
res.end("Not found");
|
|
3670
3678
|
return;
|
|
3671
3679
|
}
|
|
3672
|
-
const callbackParameters = {
|
|
3673
|
-
code: url.searchParams.get("code"),
|
|
3674
|
-
error: url.searchParams.get("error"),
|
|
3675
|
-
errorDescription: url.searchParams.get("error_description"),
|
|
3676
|
-
state: url.searchParams.get("state"),
|
|
3677
|
-
iss: url.searchParams.get("iss")
|
|
3678
|
-
};
|
|
3679
3680
|
try {
|
|
3681
|
+
const callbackParameters = readAuthorizationCallbackParameters(url);
|
|
3680
3682
|
validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
|
|
3681
3683
|
if (callbackParameters.error !== null)
|
|
3682
3684
|
throw new OAuthAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
|
|
@@ -3685,7 +3687,7 @@ function waitForAuthorizationCode(server, authorizationUrl, options, callbackPat
|
|
|
3685
3687
|
res.end(buildSuccessPage(options.landingPage));
|
|
3686
3688
|
settle(() => resolve(code));
|
|
3687
3689
|
} catch (error) {
|
|
3688
|
-
res.writeHead(400);
|
|
3690
|
+
res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8", "X-Content-Type-Options": "nosniff" });
|
|
3689
3691
|
res.end(error instanceof Error ? error.message : "Invalid OAuth callback");
|
|
3690
3692
|
settle(() => reject(error));
|
|
3691
3693
|
}
|
|
@@ -3716,15 +3718,9 @@ function extractCallbackParametersFromInput(input) {
|
|
|
3716
3718
|
if (trimmed.length === 0) {
|
|
3717
3719
|
return null;
|
|
3718
3720
|
}
|
|
3721
|
+
let url;
|
|
3719
3722
|
try {
|
|
3720
|
-
|
|
3721
|
-
return {
|
|
3722
|
-
code: url.searchParams.get("code"),
|
|
3723
|
-
error: url.searchParams.get("error"),
|
|
3724
|
-
errorDescription: url.searchParams.get("error_description"),
|
|
3725
|
-
state: url.searchParams.get("state"),
|
|
3726
|
-
iss: url.searchParams.get("iss")
|
|
3727
|
-
};
|
|
3723
|
+
url = new URL(trimmed);
|
|
3728
3724
|
} catch {
|
|
3729
3725
|
return {
|
|
3730
3726
|
code: trimmed,
|
|
@@ -3734,6 +3730,20 @@ function extractCallbackParametersFromInput(input) {
|
|
|
3734
3730
|
iss: null
|
|
3735
3731
|
};
|
|
3736
3732
|
}
|
|
3733
|
+
return readAuthorizationCallbackParameters(url);
|
|
3734
|
+
}
|
|
3735
|
+
function readAuthorizationCallbackParameters(url) {
|
|
3736
|
+
for (const parameter of oauthCallbackParameters) {
|
|
3737
|
+
if (url.searchParams.getAll(parameter).length > 1)
|
|
3738
|
+
throw new Error(`OAuth callback parameter '${parameter}' must occur only once`);
|
|
3739
|
+
}
|
|
3740
|
+
return {
|
|
3741
|
+
code: url.searchParams.get("code"),
|
|
3742
|
+
error: url.searchParams.get("error"),
|
|
3743
|
+
errorDescription: url.searchParams.get("error_description"),
|
|
3744
|
+
state: url.searchParams.get("state"),
|
|
3745
|
+
iss: url.searchParams.get("iss")
|
|
3746
|
+
};
|
|
3737
3747
|
}
|
|
3738
3748
|
function readExpectedAuthorizationCallback(authorizationUrl) {
|
|
3739
3749
|
const url = new URL(authorizationUrl);
|
|
@@ -5174,6 +5184,8 @@ async function requestTokens(input) {
|
|
|
5174
5184
|
}
|
|
5175
5185
|
}
|
|
5176
5186
|
const refreshToken = getOwnEntry5(payload, "refresh_token");
|
|
5187
|
+
if (refreshToken !== void 0 && (typeof refreshToken !== "string" || refreshToken.trim().length === 0))
|
|
5188
|
+
throw new Error("OAuth token response has invalid refresh_token");
|
|
5177
5189
|
const scope = getOwnEntry5(payload, "scope");
|
|
5178
5190
|
const normalizedRefreshToken = typeof refreshToken === "string" && refreshToken.trim().length > 0 ? refreshToken.trim() : void 0;
|
|
5179
5191
|
const normalizedScope = normalizeOAuthScope(scope);
|
|
@@ -6232,6 +6244,19 @@ function createParameterHeaders(definitions, argumentsValue) {
|
|
|
6232
6244
|
|
|
6233
6245
|
// src/oauth-discovery.ts
|
|
6234
6246
|
import { isIP as isIP2 } from "node:net";
|
|
6247
|
+
var metadataErrorBrand = /* @__PURE__ */ Symbol.for("poe-platform.tiny-mcp-client.OAuthMetadataError");
|
|
6248
|
+
var OAuthMetadataError = class extends Error {
|
|
6249
|
+
constructor(phase, message) {
|
|
6250
|
+
super(message);
|
|
6251
|
+
this.phase = phase;
|
|
6252
|
+
this.name = "OAuthMetadataError";
|
|
6253
|
+
Object.defineProperty(this, metadataErrorBrand, { value: true });
|
|
6254
|
+
}
|
|
6255
|
+
phase;
|
|
6256
|
+
static is(value) {
|
|
6257
|
+
return value instanceof Error && Object.getOwnPropertyDescriptor(value, metadataErrorBrand)?.value === true;
|
|
6258
|
+
}
|
|
6259
|
+
};
|
|
6235
6260
|
function defaultOAuthMetadataFetch(input, init) {
|
|
6236
6261
|
return fetch(input, init);
|
|
6237
6262
|
}
|
|
@@ -6269,7 +6294,8 @@ function validateProtectedResourceMetadata(value, resourceUrl) {
|
|
|
6269
6294
|
}
|
|
6270
6295
|
const normalizedResource = canonicalizeResourceIndicator(value.resource);
|
|
6271
6296
|
if (normalizedResource !== resourceUrl) {
|
|
6272
|
-
throw new
|
|
6297
|
+
throw new OAuthMetadataError(
|
|
6298
|
+
"protected-resource",
|
|
6273
6299
|
`Protected resource metadata resource mismatch: expected ${resourceUrl}, received ${value.resource}`
|
|
6274
6300
|
);
|
|
6275
6301
|
}
|
|
@@ -6291,7 +6317,8 @@ function validateAuthorizationServerMetadata(value, issuer) {
|
|
|
6291
6317
|
throw new Error("Authorization server metadata must include issuer");
|
|
6292
6318
|
}
|
|
6293
6319
|
if (value.issuer !== issuer) {
|
|
6294
|
-
throw new
|
|
6320
|
+
throw new OAuthMetadataError(
|
|
6321
|
+
"authorization-server",
|
|
6295
6322
|
`Authorization server metadata issuer mismatch: expected ${issuer}, received ${value.issuer}`
|
|
6296
6323
|
);
|
|
6297
6324
|
}
|
|
@@ -6534,7 +6561,8 @@ var OAuthMetadataDiscovery = class {
|
|
|
6534
6561
|
}
|
|
6535
6562
|
}
|
|
6536
6563
|
}
|
|
6537
|
-
throw new
|
|
6564
|
+
throw new OAuthMetadataError(
|
|
6565
|
+
"authorization-server",
|
|
6538
6566
|
`Unable to load authorization server metadata for ${cacheKey}: ${authorizationServerErrors.join(
|
|
6539
6567
|
"; "
|
|
6540
6568
|
)}`
|
|
@@ -7659,6 +7687,13 @@ var StdioTransport = class _StdioTransport {
|
|
|
7659
7687
|
}
|
|
7660
7688
|
}
|
|
7661
7689
|
};
|
|
7690
|
+
function snapshotHttpTransportHeaders(headers) {
|
|
7691
|
+
try {
|
|
7692
|
+
return new Headers(headers);
|
|
7693
|
+
} catch {
|
|
7694
|
+
throw new Error("Invalid HTTP transport headers");
|
|
7695
|
+
}
|
|
7696
|
+
}
|
|
7662
7697
|
var HttpTransport = class {
|
|
7663
7698
|
readable;
|
|
7664
7699
|
writable;
|
|
@@ -7708,7 +7743,7 @@ var HttpTransport = class {
|
|
|
7708
7743
|
this.maxResponseBytes = maxResponseBytes;
|
|
7709
7744
|
this.url = url;
|
|
7710
7745
|
this.mode = mode;
|
|
7711
|
-
this.headers = headers;
|
|
7746
|
+
this.headers = snapshotHttpTransportHeaders(headers);
|
|
7712
7747
|
this.fetchImpl = fetchImpl;
|
|
7713
7748
|
this.onWarning = onWarning;
|
|
7714
7749
|
this.oauthProvider = oauth === void 0 ? void 0 : createOAuthClientProvider(oauth);
|
|
@@ -7985,7 +8020,8 @@ var HttpTransport = class {
|
|
|
7985
8020
|
}
|
|
7986
8021
|
if (this.legacyProtocolVersion !== void 0) headers.set("MCP-Protocol-Version", this.legacyProtocolVersion);
|
|
7987
8022
|
if (this.lastEventId !== void 0) {
|
|
7988
|
-
headers.
|
|
8023
|
+
if (this.lastEventId === "") headers.delete("Last-Event-ID");
|
|
8024
|
+
else headers.set("Last-Event-ID", Buffer.from(this.lastEventId, "utf8").toString("latin1"));
|
|
7989
8025
|
}
|
|
7990
8026
|
return this.authorizeRequestHeaders(headers, signal);
|
|
7991
8027
|
}
|
|
@@ -8244,15 +8280,15 @@ var HttpTransport = class {
|
|
|
8244
8280
|
void reader.cancel().catch(() => void 0);
|
|
8245
8281
|
return;
|
|
8246
8282
|
}
|
|
8247
|
-
this.lastEventId = parser.lastEventId;
|
|
8283
|
+
this.lastEventId = parser.lastEventId ?? this.lastEventId;
|
|
8248
8284
|
}
|
|
8249
8285
|
const trailingChunk = decoder.decode();
|
|
8250
8286
|
if (trailingChunk.length > 0) {
|
|
8251
8287
|
this.writeSseMessages(parser.push(trailingChunk), context);
|
|
8252
|
-
this.lastEventId = parser.lastEventId;
|
|
8288
|
+
this.lastEventId = parser.lastEventId ?? this.lastEventId;
|
|
8253
8289
|
}
|
|
8254
8290
|
this.writeSseMessages(parser.flush(), context);
|
|
8255
|
-
this.lastEventId = parser.lastEventId;
|
|
8291
|
+
this.lastEventId = parser.lastEventId ?? this.lastEventId;
|
|
8256
8292
|
if (context !== void 0 && !context.completed) throw new Error("MCP HTTP stream ended before its final response");
|
|
8257
8293
|
} catch (error) {
|
|
8258
8294
|
void reader.cancel().catch(() => void 0);
|
|
@@ -9263,12 +9299,14 @@ export {
|
|
|
9263
9299
|
McpClient,
|
|
9264
9300
|
McpError,
|
|
9265
9301
|
OAuthMetadataDiscovery,
|
|
9302
|
+
OAuthMetadataError,
|
|
9266
9303
|
StdioTransport,
|
|
9267
9304
|
createInMemoryTransportPair,
|
|
9268
9305
|
createSdkTestPair,
|
|
9269
9306
|
createTestPair,
|
|
9270
9307
|
discoverOAuthMetadata,
|
|
9271
|
-
fetchMcpResponse
|
|
9308
|
+
fetchMcpResponse,
|
|
9309
|
+
snapshotHttpTransportHeaders
|
|
9272
9310
|
};
|
|
9273
9311
|
/*!
|
|
9274
9312
|
The MCP project is undergoing a licensing transition from the MIT License to the Apache License, Version 2.0 ("Apache-2.0"). All new code and specification contributions to the project are licensed under Apache-2.0. Documentation contributions (excluding specifications) are licensed under CC-BY-4.0.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.346",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
"yaml"
|
|
159
159
|
],
|
|
160
160
|
"optionalDependencies": {
|
|
161
|
-
"toolcraft-schema": "0.0.
|
|
161
|
+
"toolcraft-schema": "0.0.346",
|
|
162
162
|
"toolcraft-design": "*",
|
|
163
163
|
"@poe-code/frontmatter": "*",
|
|
164
164
|
"@poe-code/agent-mcp-config": "*",
|