toolcraft 0.0.345 → 0.0.347

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 CHANGED
@@ -98,7 +98,7 @@
98
98
  },
99
99
  {
100
100
  "name": "tiny-http-mcp-server",
101
- "version": "0.1.71",
101
+ "version": "0.1.75",
102
102
  "license": "MIT"
103
103
  },
104
104
  {
@@ -113,7 +113,7 @@
113
113
  },
114
114
  {
115
115
  "name": "toolcraft",
116
- "version": "0.0.345",
116
+ "version": "0.0.347",
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.345",
126
+ "version": "0.0.347",
127
127
  "license": "MIT"
128
128
  },
129
129
  {
@@ -98,7 +98,7 @@
98
98
  },
99
99
  {
100
100
  "name": "tiny-http-mcp-server",
101
- "version": "0.1.71",
101
+ "version": "0.1.75",
102
102
  "license": "MIT"
103
103
  },
104
104
  {
@@ -113,7 +113,7 @@
113
113
  },
114
114
  {
115
115
  "name": "toolcraft",
116
- "version": "0.0.345",
116
+ "version": "0.0.347",
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.345",
126
+ "version": "0.0.347",
127
127
  "license": "MIT"
128
128
  },
129
129
  {
@@ -18,7 +18,7 @@
18
18
  },
19
19
  {
20
20
  "name": "tiny-http-mcp-server",
21
- "version": "0.1.71",
21
+ "version": "0.1.75",
22
22
  "license": "MIT"
23
23
  },
24
24
  {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiny-http-mcp-server",
3
- "version": "0.1.71",
3
+ "version": "0.1.75",
4
4
  "description": "Minimal MCP server over HTTP built on tiny-stdio-mcp-server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -66,6 +66,10 @@ validation when preparing a registry before constructing transports.
66
66
  Receive streams may carry many bounded events and keepalive comments; the limit
67
67
  does not cap their lifetime bytes. Use request deadlines and cancellation to
68
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.
69
73
  HTTP failures expose `HttpTransportError.status` and `.method`, so callers can
70
74
  make transport decisions without parsing error messages. Legacy HTTP/SSE
71
75
  `connect()` also waits for the initialized notification POST to complete before
@@ -83,29 +87,36 @@ the same optional `closeReason` promise when closing requires asynchronous work.
83
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.
84
88
 
85
89
  ```ts
86
- import { HttpTransport, createDefaultOAuthClientProvider } from "tiny-mcp-client";
90
+ import { HttpTransport } from "tiny-mcp-client";
87
91
 
88
92
  const transport = new HttpTransport({
89
93
  url: "https://mcp.example.com/mcp",
90
94
  oauth: {
91
- provider: createDefaultOAuthClientProvider({
92
- client: {
93
- mode: "dynamic",
94
- metadata: {
95
- clientName: "tiny-client"
96
- }
97
- },
98
- browser: {
99
- openBrowser: async (url) => {
100
- console.log(`Open ${url}`);
101
- }
95
+ client: {
96
+ mode: "dynamic",
97
+ metadata: {
98
+ clientName: "tiny-client"
102
99
  }
103
- })
100
+ },
101
+ browser: {
102
+ openBrowser: async (url) => {
103
+ console.log(`Open ${url}`);
104
+ }
105
+ }
104
106
  }
105
107
  });
106
108
  ```
107
109
 
108
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
+ Protected-resource metadata HTTP failures also expose their numeric `status`.
114
+ Rejected metadata bodies start cleanup without delaying failure or caller
115
+ cancellation. Host cleanup may finish later; its rejection remains observed.
116
+ `OAuthMetadataError.is(value)` recognizes separately bundled native copies.
117
+ Bearer challenges with repeated case-insensitive parameters fail before OAuth
118
+ recovery, including identical repeats. Their rejected response bodies are released
119
+ without reflecting parameter values in the diagnostic.
109
120
 
110
121
  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.
111
122
 
@@ -304,6 +304,13 @@ 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
+ readonly status?: number | undefined;
311
+ static is(value: unknown): value is OAuthMetadataError;
312
+ constructor(phase: "protected-resource" | "authorization-server", message: string, status?: number | undefined);
313
+ }
307
314
  declare class OAuthMetadataDiscovery {
308
315
  private readonly fetchImpl;
309
316
  private readonly cache;
@@ -874,5 +881,5 @@ declare class JsonRpcMessageLayer {
874
881
  private handleCancellationNotification;
875
882
  }
876
883
 
877
- 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, snapshotHttpTransportHeaders };
884
+ 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 };
878
885
  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 };
@@ -6244,6 +6244,21 @@ function createParameterHeaders(definitions, argumentsValue) {
6244
6244
 
6245
6245
  // src/oauth-discovery.ts
6246
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, status) {
6250
+ super(message);
6251
+ this.phase = phase;
6252
+ this.status = status;
6253
+ this.name = "OAuthMetadataError";
6254
+ Object.defineProperty(this, metadataErrorBrand, { value: true });
6255
+ }
6256
+ phase;
6257
+ status;
6258
+ static is(value) {
6259
+ return value instanceof Error && Object.getOwnPropertyDescriptor(value, metadataErrorBrand)?.value === true;
6260
+ }
6261
+ };
6247
6262
  function defaultOAuthMetadataFetch(input, init) {
6248
6263
  return fetch(input, init);
6249
6264
  }
@@ -6281,7 +6296,8 @@ function validateProtectedResourceMetadata(value, resourceUrl) {
6281
6296
  }
6282
6297
  const normalizedResource = canonicalizeResourceIndicator(value.resource);
6283
6298
  if (normalizedResource !== resourceUrl) {
6284
- throw new Error(
6299
+ throw new OAuthMetadataError(
6300
+ "protected-resource",
6285
6301
  `Protected resource metadata resource mismatch: expected ${resourceUrl}, received ${value.resource}`
6286
6302
  );
6287
6303
  }
@@ -6303,7 +6319,8 @@ function validateAuthorizationServerMetadata(value, issuer) {
6303
6319
  throw new Error("Authorization server metadata must include issuer");
6304
6320
  }
6305
6321
  if (value.issuer !== issuer) {
6306
- throw new Error(
6322
+ throw new OAuthMetadataError(
6323
+ "authorization-server",
6307
6324
  `Authorization server metadata issuer mismatch: expected ${issuer}, received ${value.issuer}`
6308
6325
  );
6309
6326
  }
@@ -6345,11 +6362,12 @@ function validateAuthorizationServerMetadata(value, issuer) {
6345
6362
  }
6346
6363
  return value;
6347
6364
  }
6348
- async function readJsonResponse(response, label, signal) {
6365
+ async function readJsonResponse(response, phase, signal) {
6366
+ const label = phase === "protected-resource" ? "Protected resource metadata" : "Authorization server metadata";
6349
6367
  if (!response.ok) {
6350
- await response.body?.cancel().catch(() => void 0);
6368
+ void response.body?.cancel().catch(() => void 0);
6351
6369
  const statusDescriptor = `${response.status} ${response.statusText}`.trim();
6352
- throw new Error(`${label} request failed (${statusDescriptor})`);
6370
+ throw new OAuthMetadataError(phase, `${label} request failed (${statusDescriptor})`, response.status);
6353
6371
  }
6354
6372
  const text = await readBoundedResponseText(response, 1024 * 1024, void 0, signal);
6355
6373
  try {
@@ -6358,7 +6376,7 @@ async function readJsonResponse(response, label, signal) {
6358
6376
  throw new Error(`${label} response must be valid JSON`);
6359
6377
  }
6360
6378
  }
6361
- async function fetchMetadata(fetch2, location, label, parentSignal) {
6379
+ async function fetchMetadata(fetch2, location, phase, parentSignal) {
6362
6380
  parentSignal?.throwIfAborted();
6363
6381
  const deadline = AbortSignal.timeout(1e4);
6364
6382
  const signal = parentSignal === void 0 ? deadline : AbortSignal.any([deadline, parentSignal]);
@@ -6367,7 +6385,7 @@ async function fetchMetadata(fetch2, location, label, parentSignal) {
6367
6385
  headers: { Accept: "application/json" },
6368
6386
  signal
6369
6387
  });
6370
- return readJsonResponse(response, label, signal);
6388
+ return readJsonResponse(response, phase, signal);
6371
6389
  }
6372
6390
  function resolveWellKnownMetadataUrl(inputUrl, suffix) {
6373
6391
  const url = new URL(typeof inputUrl === "string" ? inputUrl : inputUrl.toString());
@@ -6486,7 +6504,7 @@ var OAuthMetadataDiscovery = class {
6486
6504
  for (const location of locations) {
6487
6505
  try {
6488
6506
  const metadata = validateProtectedResourceMetadata(
6489
- await fetchMetadata(this.fetchImpl, location, "Protected resource metadata", signal),
6507
+ await fetchMetadata(this.fetchImpl, location, "protected-resource", signal),
6490
6508
  resource
6491
6509
  );
6492
6510
  return { location, metadata };
@@ -6524,7 +6542,7 @@ var OAuthMetadataDiscovery = class {
6524
6542
  for (const authorizationServerMetadataUrl of metadataLocations) {
6525
6543
  try {
6526
6544
  const authorizationServerMetadata = validateAuthorizationServerMetadata(
6527
- await fetchMetadata(this.fetchImpl, authorizationServerMetadataUrl, "Authorization server metadata", signal),
6545
+ await fetchMetadata(this.fetchImpl, authorizationServerMetadataUrl, "authorization-server", signal),
6528
6546
  normalizedAuthorizationServer
6529
6547
  );
6530
6548
  const result = {
@@ -6546,7 +6564,8 @@ var OAuthMetadataDiscovery = class {
6546
6564
  }
6547
6565
  }
6548
6566
  }
6549
- throw new Error(
6567
+ throw new OAuthMetadataError(
6568
+ "authorization-server",
6550
6569
  `Unable to load authorization server metadata for ${cacheKey}: ${authorizationServerErrors.join(
6551
6570
  "; "
6552
6571
  )}`
@@ -6712,7 +6731,11 @@ function parseBearerWwwAuthenticateHeader(headerValue) {
6712
6731
  if (parsedParam === null) {
6713
6732
  break;
6714
6733
  }
6715
- params[parsedParam.name.toLowerCase()] = parsedParam.value;
6734
+ const parameterName = parsedParam.name.toLowerCase();
6735
+ if (scheme.token.toLowerCase() === "bearer" && Object.hasOwn(params, parameterName)) {
6736
+ throw new Error("Bearer challenge must not repeat authentication parameters");
6737
+ }
6738
+ params[parameterName] = parsedParam.value;
6716
6739
  index = skipOptionalWhitespace(headerValue, parsedParam.nextIndex);
6717
6740
  if (headerValue[index] !== ",") {
6718
6741
  break;
@@ -8004,7 +8027,8 @@ var HttpTransport = class {
8004
8027
  }
8005
8028
  if (this.legacyProtocolVersion !== void 0) headers.set("MCP-Protocol-Version", this.legacyProtocolVersion);
8006
8029
  if (this.lastEventId !== void 0) {
8007
- headers.set("Last-Event-ID", this.lastEventId);
8030
+ if (this.lastEventId === "") headers.delete("Last-Event-ID");
8031
+ else headers.set("Last-Event-ID", Buffer.from(this.lastEventId, "utf8").toString("latin1"));
8008
8032
  }
8009
8033
  return this.authorizeRequestHeaders(headers, signal);
8010
8034
  }
@@ -8177,9 +8201,9 @@ var HttpTransport = class {
8177
8201
  if (discoveryClient === void 0) {
8178
8202
  return false;
8179
8203
  }
8180
- const challenge = parseBearerWwwAuthenticateHeader(response.headers.get("WWW-Authenticate"));
8181
- const resourceMetadataUrl = challenge?.params.resource_metadata;
8182
8204
  try {
8205
+ const challenge = parseBearerWwwAuthenticateHeader(response.headers.get("WWW-Authenticate"));
8206
+ const resourceMetadataUrl = challenge?.params.resource_metadata;
8183
8207
  const discovery = await discoveryClient.discover(this.url, { resourceMetadataUrl, signal });
8184
8208
  const providerResponse = response.clone();
8185
8209
  let result;
@@ -8263,15 +8287,15 @@ var HttpTransport = class {
8263
8287
  void reader.cancel().catch(() => void 0);
8264
8288
  return;
8265
8289
  }
8266
- this.lastEventId = parser.lastEventId;
8290
+ this.lastEventId = parser.lastEventId ?? this.lastEventId;
8267
8291
  }
8268
8292
  const trailingChunk = decoder.decode();
8269
8293
  if (trailingChunk.length > 0) {
8270
8294
  this.writeSseMessages(parser.push(trailingChunk), context);
8271
- this.lastEventId = parser.lastEventId;
8295
+ this.lastEventId = parser.lastEventId ?? this.lastEventId;
8272
8296
  }
8273
8297
  this.writeSseMessages(parser.flush(), context);
8274
- this.lastEventId = parser.lastEventId;
8298
+ this.lastEventId = parser.lastEventId ?? this.lastEventId;
8275
8299
  if (context !== void 0 && !context.completed) throw new Error("MCP HTTP stream ended before its final response");
8276
8300
  } catch (error) {
8277
8301
  void reader.cancel().catch(() => void 0);
@@ -8361,10 +8385,12 @@ var HttpTransport = class {
8361
8385
  let attempt = await request();
8362
8386
  if (await this.maybeHandleUnauthorizedResponse(attempt.response, controller.signal, attempt.headers, attempt.tokens)) attempt = await request();
8363
8387
  const response = attempt.response;
8364
- const oauthError = this.oauthProvider === void 0 ? null : this.readOAuthChallengeError(response);
8365
- if (oauthError !== null) {
8388
+ try {
8389
+ const oauthError = this.oauthProvider === void 0 ? null : this.readOAuthChallengeError(response);
8390
+ if (oauthError !== null) throw oauthError;
8391
+ } catch (error) {
8366
8392
  void response.body?.cancel().catch(() => void 0);
8367
- throw oauthError;
8393
+ throw error;
8368
8394
  }
8369
8395
  return response;
8370
8396
  } finally {
@@ -9282,6 +9308,7 @@ export {
9282
9308
  McpClient,
9283
9309
  McpError,
9284
9310
  OAuthMetadataDiscovery,
9311
+ OAuthMetadataError,
9285
9312
  StdioTransport,
9286
9313
  createInMemoryTransportPair,
9287
9314
  createSdkTestPair,
@@ -8,7 +8,7 @@
8
8
  },
9
9
  {
10
10
  "name": "toolcraft-schema",
11
- "version": "0.0.345",
11
+ "version": "0.0.347",
12
12
  "license": "MIT"
13
13
  }
14
14
  ]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft-schema",
3
- "version": "0.0.345",
3
+ "version": "0.0.347",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft",
3
- "version": "0.0.345",
3
+ "version": "0.0.347",
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.345",
161
+ "toolcraft-schema": "0.0.347",
162
162
  "toolcraft-design": "*",
163
163
  "@poe-code/frontmatter": "*",
164
164
  "@poe-code/agent-mcp-config": "*",