toolcraft 0.0.744 → 0.0.746

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. package/composition.json +3 -3
  2. package/dist/composition.json +3 -3
  3. package/node_modules/auth-store/dist/transaction-lock.js +2 -2
  4. package/node_modules/tiny-http-mcp-server/dist/composition.json +1 -1
  5. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/README.md +1 -1
  6. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/client-registration.js +1 -1
  7. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/loopback-authorization.d.ts +2 -9
  8. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/loopback-authorization.js +8 -48
  9. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/redirect-target.d.ts +10 -0
  10. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/redirect-target.js +42 -0
  11. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/resource-bound-store.js +2 -2
  12. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/session-transaction.js +4 -3
  13. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.browser.d.ts +6 -0
  14. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.browser.js +8 -0
  15. package/node_modules/tiny-http-mcp-server/package.json +1 -1
  16. package/node_modules/tiny-mcp-client/dist/index.browser.js +75 -11
  17. package/node_modules/tiny-mcp-client/dist/index.js +431 -424
  18. package/node_modules/tiny-stdio-mcp-server/dist/base64.js +6 -1
  19. package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
  20. package/node_modules/tiny-stdio-mcp-server/dist/headers.js +7 -6
  21. package/node_modules/toolcraft-schema/package.json +1 -1
  22. 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.113",
101
+ "version": "0.1.115",
102
102
  "license": "MIT"
103
103
  },
104
104
  {
@@ -113,7 +113,7 @@
113
113
  },
114
114
  {
115
115
  "name": "toolcraft",
116
- "version": "0.0.744",
116
+ "version": "0.0.746",
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.744",
126
+ "version": "0.0.746",
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.113",
101
+ "version": "0.1.115",
102
102
  "license": "MIT"
103
103
  },
104
104
  {
@@ -113,7 +113,7 @@
113
113
  },
114
114
  {
115
115
  "name": "toolcraft",
116
- "version": "0.0.744",
116
+ "version": "0.0.746",
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.744",
126
+ "version": "0.0.746",
127
127
  "license": "MIT"
128
128
  },
129
129
  {
@@ -4,8 +4,8 @@ import { hasOwnErrorCode } from "./error-codes.js";
4
4
  /** Filesystem bakery lock: unique claims allow dead-owner cleanup without deleting a replacement owner's lock. */
5
5
  export async function withSecretStoreFileLock(fs, lockDirectory, operation, options = {}) {
6
6
  options = { ...options, signal: options.signal, timeoutMs: options.timeoutMs };
7
- const timeoutMs = options.timeoutMs ?? 30_000;
8
- if (!Number.isFinite(timeoutMs) || timeoutMs < 0 || timeoutMs > 2_147_483_647)
7
+ const timeoutMs = options.timeoutMs ?? Infinity;
8
+ if (timeoutMs !== Infinity && (!Number.isFinite(timeoutMs) || timeoutMs < 0 || timeoutMs > 2_147_483_647))
9
9
  throw new Error("Invalid secret-store transaction lock timeout");
10
10
  options.signal?.throwIfAborted();
11
11
  const deadline = performance.now() + timeoutMs;
@@ -18,7 +18,7 @@
18
18
  },
19
19
  {
20
20
  "name": "tiny-http-mcp-server",
21
- "version": "0.1.113",
21
+ "version": "0.1.115",
22
22
  "license": "MIT"
23
23
  },
24
24
  {
@@ -47,7 +47,7 @@ alone do not establish either native error type.
47
47
  in both Node and Worker exports, and through `@poe-platform/safe-bash/mcp`.
48
48
  They use Web Crypto and host storage, with no filesystem, listener, browser opener
49
49
  or pending callback promise. Supply a statically registered client and trusted
50
- discovery. See [the public MCP example](../safe-bash-mcp/README.md#resumable-https-oauth)
50
+ discovery. See [the public MCP example](../safe-bash-command-mcp/README.md#resumable-https-oauth)
51
51
  for callback routing and the required atomic storage contract.
52
52
 
53
53
  ## Configuration
@@ -1,5 +1,5 @@
1
1
  import { copyBoundedOAuthJson } from "./bounded-json.js";
2
- import { loopbackTarget, validateHostedOAuthRedirect } from "./loopback-authorization.js";
2
+ import { loopbackTarget, validateHostedOAuthRedirect } from "./redirect-target.js";
3
3
  import { normalizeOAuthScope } from "./scope.js";
4
4
  import { normalizeOAuthTokenEndpointAuthMethod } from "./token-auth-method.js";
5
5
  /** Validate and copy a JSON DCR response without quoting credential input. */
@@ -1,5 +1,6 @@
1
1
  import http from "node:http";
2
2
  export { OAuthAuthorizationError } from "./authorization-error.js";
3
+ export { loopbackTarget, validateAuthorizationRedirect, validateHostedOAuthRedirect } from "./redirect-target.js";
3
4
  export interface OAuthLandingPage {
4
5
  title: string;
5
6
  body: string;
@@ -19,7 +20,7 @@ export interface LoopbackAuthorizationOptions {
19
20
  /** Exact registered HTTP loopback redirect, including its fixed port and query. */
20
21
  redirectUri?: string;
21
22
  signal?: AbortSignal;
22
- /** Bounds listener setup and authorization; defaults to two minutes. */
23
+ /** Bounds listener setup and authorization; defaults to unlimited. */
23
24
  timeoutMs?: number;
24
25
  }
25
26
  export interface LoopbackAuthorizationSession {
@@ -31,13 +32,5 @@ export interface LoopbackAuthorizationSession {
31
32
  export declare function snapshotLoopbackAuthorizationOptions(options: LoopbackAuthorizationOptions): LoopbackAuthorizationOptions;
32
33
  /** Authorization callback denial, retaining the provider diagnostic for host observers. */
33
34
  export declare function createLoopbackAuthorizationSession(options?: LoopbackAuthorizationOptions): Promise<LoopbackAuthorizationSession>;
34
- export declare function loopbackTarget(options: LoopbackAuthorizationOptions): {
35
- port: number;
36
- host: string;
37
- callbackPath: string;
38
- };
39
- /** Hosted redirects are explicit HTTPS URLs; local redirects retain loopback policy. */
40
- export declare function validateAuthorizationRedirect(options: LoopbackAuthorizationOptions): void;
41
- export declare function validateHostedOAuthRedirect(redirectUri: string, callbackPath?: string): void;
42
35
  export declare function extractCodeFromInput(input: string): string | null;
43
36
  export declare function buildSuccessPage(landingPage?: OAuthLandingPage): string;
@@ -2,7 +2,8 @@ import http from "node:http";
2
2
  import { parseAuthorizationState } from "./authorization-state.js";
3
3
  import { OAuthAuthorizationError } from "./authorization-error.js";
4
4
  export { OAuthAuthorizationError } from "./authorization-error.js";
5
- const oauthCallbackParameters = ["code", "state", "iss", "error", "error_description", "error_uri"];
5
+ import { oauthCallbackParameters, loopbackTarget, validateAuthorizationRedirect } from "./redirect-target.js";
6
+ export { loopbackTarget, validateAuthorizationRedirect, validateHostedOAuthRedirect } from "./redirect-target.js";
6
7
  /** Capture declared configuration regardless of property enumerability. */
7
8
  export function snapshotLoopbackAuthorizationOptions(options) {
8
9
  return { ...options, openBrowser: options.openBrowser?.bind(options), readLine: options.readLine?.bind(options), waitForCallback: options.waitForCallback?.bind(options), createServer: options.createServer?.bind(options),
@@ -13,8 +14,8 @@ export function snapshotLoopbackAuthorizationOptions(options) {
13
14
  export async function createLoopbackAuthorizationSession(options = {}) {
14
15
  options = snapshotLoopbackAuthorizationOptions(options);
15
16
  options.signal?.throwIfAborted();
16
- const timeoutMs = options.timeoutMs ?? 120_000;
17
- if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2_147_483_647)
17
+ const timeoutMs = options.timeoutMs ?? Infinity;
18
+ if (timeoutMs !== Infinity && (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2_147_483_647))
18
19
  throw new Error("OAuth authorization timeoutMs must be a positive supported timer interval");
19
20
  if (options.waitForCallback !== undefined)
20
21
  return createHostedAuthorizationSession(options, timeoutMs);
@@ -33,8 +34,8 @@ export async function createLoopbackAuthorizationSession(options = {}) {
33
34
  server.closeAllConnections?.();
34
35
  server.close();
35
36
  };
36
- const timer = setTimeout(() => controller.abort(new Error("OAuth authorization timed out")), timeoutMs);
37
- timer.unref?.();
37
+ const timer = timeoutMs === Infinity ? undefined : setTimeout(() => controller.abort(new Error("OAuth authorization timed out")), timeoutMs);
38
+ timer?.unref?.();
38
39
  controller.signal.addEventListener("abort", teardown, { once: true });
39
40
  options.signal?.addEventListener("abort", callerAbort, { once: true });
40
41
  if (options.signal?.aborted)
@@ -67,54 +68,13 @@ export async function createLoopbackAuthorizationSession(options = {}) {
67
68
  }
68
69
  };
69
70
  }
70
- export function loopbackTarget(options) {
71
- if (options.redirectUri !== undefined) {
72
- let url;
73
- try {
74
- url = new URL(options.redirectUri);
75
- }
76
- catch (cause) {
77
- throw new Error("Invalid OAuth loopback redirect URI", { cause });
78
- }
79
- if (url.protocol !== "http:" || !["localhost", "127.0.0.1", "[::1]"].includes(url.hostname)
80
- || url.username || url.password || url.href.includes("#") || url.port === "0"
81
- || oauthCallbackParameters.some(name => url.searchParams.has(name))
82
- || [...options.redirectUri].some(char => char.codePointAt(0) <= 32)
83
- || (options.callbackPath !== undefined && options.callbackPath !== url.pathname))
84
- throw new Error("Invalid OAuth loopback redirect URI");
85
- return { port: url.port ? Number(url.port) : 80, host: url.hostname === "[::1]" ? "::1" : url.hostname, callbackPath: url.pathname };
86
- }
87
- const callbackPath = options.callbackPath ?? "/callback";
88
- const parsed = new URL(callbackPath, "http://127.0.0.1");
89
- if (!callbackPath.startsWith("/") || parsed.origin !== "http://127.0.0.1" || parsed.pathname !== callbackPath || parsed.search || parsed.hash)
90
- throw new Error("Invalid OAuth loopback callback path");
91
- return { port: 0, host: "127.0.0.1", callbackPath };
92
- }
93
- /** Hosted redirects are explicit HTTPS URLs; local redirects retain loopback policy. */
94
- export function validateAuthorizationRedirect(options) {
95
- if (options.waitForCallback === undefined) {
96
- loopbackTarget(options);
97
- return;
98
- }
99
- if (typeof options.waitForCallback !== "function" || options.redirectUri === undefined)
100
- throw new Error("Hosted OAuth callbacks require redirectUri and waitForCallback");
101
- validateHostedOAuthRedirect(options.redirectUri, options.callbackPath);
102
- }
103
- export function validateHostedOAuthRedirect(redirectUri, callbackPath) {
104
- const url = new URL(redirectUri);
105
- if (url.protocol !== "https:" || url.username || url.password || url.href.includes("#") || url.port === "0"
106
- || oauthCallbackParameters.some(name => url.searchParams.has(name))
107
- || [...redirectUri].some(char => char.codePointAt(0) <= 32)
108
- || (callbackPath !== undefined && callbackPath !== url.pathname))
109
- throw new Error("Invalid hosted OAuth redirect URI");
110
- }
111
71
  function createHostedAuthorizationSession(options, timeoutMs) {
112
72
  validateAuthorizationRedirect(options);
113
73
  const redirectUri = options.redirectUri, controller = new AbortController();
114
74
  let used = false;
115
75
  const aborted = () => controller.abort(options.signal?.reason);
116
- const timer = setTimeout(() => controller.abort(new Error("OAuth authorization timed out")), timeoutMs);
117
- timer.unref?.();
76
+ const timer = timeoutMs === Infinity ? undefined : setTimeout(() => controller.abort(new Error("OAuth authorization timed out")), timeoutMs);
77
+ timer?.unref?.();
118
78
  const cleanup = () => { clearTimeout(timer); options.signal?.removeEventListener("abort", aborted); };
119
79
  controller.signal.addEventListener("abort", cleanup, { once: true });
120
80
  options.signal?.addEventListener("abort", aborted, { once: true });
@@ -0,0 +1,10 @@
1
+ import type { LoopbackAuthorizationOptions } from "./loopback-authorization.js";
2
+ export declare const oauthCallbackParameters: string[];
3
+ export declare function loopbackTarget(options: LoopbackAuthorizationOptions): {
4
+ port: number;
5
+ host: string;
6
+ callbackPath: string;
7
+ };
8
+ /** Hosted redirects are explicit HTTPS URLs; local redirects retain loopback policy. */
9
+ export declare function validateAuthorizationRedirect(options: LoopbackAuthorizationOptions): void;
10
+ export declare function validateHostedOAuthRedirect(redirectUri: string, callbackPath?: string): void;
@@ -0,0 +1,42 @@
1
+ export const oauthCallbackParameters = ["code", "state", "iss", "error", "error_description", "error_uri"];
2
+ export function loopbackTarget(options) {
3
+ if (options.redirectUri !== undefined) {
4
+ let url;
5
+ try {
6
+ url = new URL(options.redirectUri);
7
+ }
8
+ catch (cause) {
9
+ throw new Error("Invalid OAuth loopback redirect URI", { cause });
10
+ }
11
+ if (url.protocol !== "http:" || !["localhost", "127.0.0.1", "[::1]"].includes(url.hostname)
12
+ || url.username || url.password || url.href.includes("#") || url.port === "0"
13
+ || oauthCallbackParameters.some(name => url.searchParams.has(name))
14
+ || [...options.redirectUri].some(char => char.codePointAt(0) <= 32)
15
+ || (options.callbackPath !== undefined && options.callbackPath !== url.pathname))
16
+ throw new Error("Invalid OAuth loopback redirect URI");
17
+ return { port: url.port ? Number(url.port) : 80, host: url.hostname === "[::1]" ? "::1" : url.hostname, callbackPath: url.pathname };
18
+ }
19
+ const callbackPath = options.callbackPath ?? "/callback";
20
+ const parsed = new URL(callbackPath, "http://127.0.0.1");
21
+ if (!callbackPath.startsWith("/") || parsed.origin !== "http://127.0.0.1" || parsed.pathname !== callbackPath || parsed.search || parsed.hash)
22
+ throw new Error("Invalid OAuth loopback callback path");
23
+ return { port: 0, host: "127.0.0.1", callbackPath };
24
+ }
25
+ /** Hosted redirects are explicit HTTPS URLs; local redirects retain loopback policy. */
26
+ export function validateAuthorizationRedirect(options) {
27
+ if (options.waitForCallback === undefined) {
28
+ loopbackTarget(options);
29
+ return;
30
+ }
31
+ if (typeof options.waitForCallback !== "function" || options.redirectUri === undefined)
32
+ throw new Error("Hosted OAuth callbacks require redirectUri and waitForCallback");
33
+ validateHostedOAuthRedirect(options.redirectUri, options.callbackPath);
34
+ }
35
+ export function validateHostedOAuthRedirect(redirectUri, callbackPath) {
36
+ const url = new URL(redirectUri);
37
+ if (url.protocol !== "https:" || url.username || url.password || url.href.includes("#") || url.port === "0"
38
+ || oauthCallbackParameters.some(name => url.searchParams.has(name))
39
+ || [...redirectUri].some(char => char.codePointAt(0) <= 32)
40
+ || (callbackPath !== undefined && callbackPath !== url.pathname))
41
+ throw new Error("Invalid hosted OAuth redirect URI");
42
+ }
@@ -54,8 +54,8 @@ export function createResourceBoundOAuthStores(options, namespace, identity) {
54
54
  async function replace(record, options) {
55
55
  options = { ...options, signal: options.signal, timeoutMs: options.timeoutMs };
56
56
  options.signal?.throwIfAborted();
57
- const timeoutMs = options.timeoutMs ?? 30_000;
58
- if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2_147_483_647)
57
+ const timeoutMs = options.timeoutMs ?? Infinity;
58
+ if (timeoutMs !== Infinity && (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2_147_483_647))
59
59
  throw new Error("OAuth replacement timeoutMs must be a positive supported timer interval");
60
60
  if (store.withLock === undefined)
61
61
  throw new Error("OAuth resource identity backend must support transaction locks");
@@ -2,8 +2,8 @@ import { waitForOAuthOperation } from "./cancellable-operation.js";
2
2
  const queues = new WeakMap();
3
3
  /** Serialize the complete read/redeem/write operation, with independent cancellation for waiters. */
4
4
  export async function withOAuthSessionTransaction(store, resource, operation, options = {}) {
5
- const timeoutMs = options.timeoutMs ?? 30_000;
6
- if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2_147_483_647)
5
+ const timeoutMs = options.timeoutMs ?? Infinity;
6
+ if (timeoutMs !== Infinity && (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2_147_483_647))
7
7
  throw new Error("sessionLockTimeoutMs must be an integer from 1 to 2147483647 milliseconds");
8
8
  options.signal?.throwIfAborted();
9
9
  const started = performance.now();
@@ -21,7 +21,8 @@ export async function withOAuthSessionTransaction(store, resource, operation, op
21
21
  const waiting = new Promise((resolve, reject) => { rejectWait = reject; previous.then(resolve, reject); });
22
22
  const abort = () => rejectWait(options.signal?.reason);
23
23
  try {
24
- timer = setTimeout(() => rejectWait(new Error("Timed out waiting for OAuth session transaction lock")), timeoutMs);
24
+ if (timeoutMs !== Infinity)
25
+ timer = setTimeout(() => rejectWait(new Error("Timed out waiting for OAuth session transaction lock")), timeoutMs);
25
26
  options.signal?.addEventListener("abort", abort, { once: true });
26
27
  if (options.signal?.aborted)
27
28
  abort();
@@ -12,3 +12,9 @@ export declare function createDefaultOAuthClientProvider(): never;
12
12
  export declare function createAuthStoreSessionStore(): never;
13
13
  export { beginRemoteMcpAuthorization, completeRemoteMcpAuthorization } from "./client/resumable.js";
14
14
  export type { RemoteMcpAuthorizationTransaction, RemoteMcpAuthorizationStore, BeginRemoteMcpAuthorizationOptions, CompleteRemoteMcpAuthorizationOptions } from "./client/resumable.js";
15
+ export { normalizeOAuthScope } from "./client/scope.js";
16
+ export { waitForOAuthOperation } from "./client/cancellable-operation.js";
17
+ export { parseOAuthTokenGrant } from "./client/token-grant.js";
18
+ export { parseOAuthClientRegistration, normalizeStoredOAuthClient } from "./client/client-registration.js";
19
+ /** Native persistence needs a desktop host; portable callers provide atomic hooks. */
20
+ export declare function createResourceBoundOAuthStores(): never;
@@ -17,3 +17,11 @@ export function createAuthStoreSessionStore() {
17
17
  throw new Error("Desktop OAuth persistence is unavailable in Worker MCP");
18
18
  }
19
19
  export { beginRemoteMcpAuthorization, completeRemoteMcpAuthorization } from "./client/resumable.js";
20
+ export { normalizeOAuthScope } from "./client/scope.js";
21
+ export { waitForOAuthOperation } from "./client/cancellable-operation.js";
22
+ export { parseOAuthTokenGrant } from "./client/token-grant.js";
23
+ export { parseOAuthClientRegistration, normalizeStoredOAuthClient } from "./client/client-registration.js";
24
+ /** Native persistence needs a desktop host; portable callers provide atomic hooks. */
25
+ export function createResourceBoundOAuthStores() {
26
+ throw new Error("Desktop OAuth persistence is unavailable in Worker MCP; provide host-owned import/reset hooks");
27
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiny-http-mcp-server",
3
- "version": "0.1.113",
3
+ "version": "0.1.115",
4
4
  "description": "Minimal MCP server over HTTP built on tiny-stdio-mcp-server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -57,8 +57,63 @@ function assertHttpJsonBudget(text) {
57
57
  if (stack.length !== 0) throw new SyntaxError("Unterminated MCP HTTP JSON container");
58
58
  }
59
59
 
60
- // src/internal.ts
61
- import { PassThrough } from "node:stream";
60
+ // src/stream.browser.ts
61
+ var PassThrough = class {
62
+ destroyed = false;
63
+ writableEnded = false;
64
+ chunks = [];
65
+ wake;
66
+ failure;
67
+ listeners = /* @__PURE__ */ new Map();
68
+ once(event, listener) {
69
+ const listeners = this.listeners.get(event) ?? /* @__PURE__ */ new Set();
70
+ listeners.add(listener);
71
+ this.listeners.set(event, listeners);
72
+ return this;
73
+ }
74
+ write(value) {
75
+ if (this.destroyed || this.writableEnded) throw this.failure ?? new Error("HTTP message channel is closed");
76
+ this.chunks.push(typeof value === "string" ? new TextEncoder().encode(value) : value.slice());
77
+ this.wake?.();
78
+ return true;
79
+ }
80
+ end() {
81
+ this.writableEnded = true;
82
+ this.wake?.();
83
+ return this;
84
+ }
85
+ destroy(error) {
86
+ if (this.destroyed) return this;
87
+ this.destroyed = true;
88
+ this.failure = error;
89
+ this.chunks.length = 0;
90
+ this.wake?.();
91
+ const errors = this.listeners.get("error"), close = this.listeners.get("close");
92
+ this.listeners.clear();
93
+ if (error !== void 0) for (const listener of errors ?? []) listener(error);
94
+ for (const listener of close ?? []) listener();
95
+ return this;
96
+ }
97
+ async *[Symbol.asyncIterator]() {
98
+ try {
99
+ for (; ; ) {
100
+ if (this.failure !== void 0) throw this.failure;
101
+ const chunk = this.chunks.shift();
102
+ if (chunk !== void 0) {
103
+ yield chunk;
104
+ continue;
105
+ }
106
+ if (this.writableEnded || this.destroyed) return;
107
+ await new Promise((resolve) => {
108
+ this.wake = resolve;
109
+ });
110
+ this.wake = void 0;
111
+ }
112
+ } finally {
113
+ this.destroy();
114
+ }
115
+ }
116
+ };
62
117
 
63
118
  // src/http-message-validation.ts
64
119
  var subscriptionMethods = /* @__PURE__ */ new Set([
@@ -1255,7 +1310,11 @@ var JSON_RPC_ERROR_CODES = Object.freeze({
1255
1310
  function isBase64(value) {
1256
1311
  if (value.length % 4 !== 0)
1257
1312
  return false;
1258
- return Buffer.from(value, "base64").toString("base64") === value;
1313
+ try {
1314
+ return btoa(atob(value)) === value;
1315
+ } catch {
1316
+ return false;
1317
+ }
1259
1318
  }
1260
1319
 
1261
1320
  // ../tiny-stdio-mcp-server/dist/uri.js
@@ -3549,15 +3608,15 @@ function createOAuthClientProvider(options) {
3549
3608
 
3550
3609
  // ../tiny-stdio-mcp-server/dist/headers.js
3551
3610
  function encodeHeaderValue(value) {
3552
- const bytes = Buffer.from(value, "utf8");
3553
- if (bytes.toString("utf8") !== value)
3611
+ const bytes = new TextEncoder().encode(value);
3612
+ if (new TextDecoder("utf-8", { ignoreBOM: true }).decode(bytes) !== value)
3554
3613
  throw new Error("MCP header value must contain valid Unicode");
3555
3614
  const sentinel = value.startsWith("=?base64?") && value.endsWith("?=");
3556
3615
  const unsafe = value.trim() !== value || [...value].some((character) => {
3557
3616
  const code = character.charCodeAt(0);
3558
3617
  return code !== 9 && (code < 32 || code > 126);
3559
3618
  });
3560
- return sentinel || unsafe ? `=?base64?${bytes.toString("base64")}?=` : value;
3619
+ return sentinel || unsafe ? `=?base64?${btoa(Array.from(bytes, (byte) => String.fromCharCode(byte)).join(""))}?=` : value;
3561
3620
  }
3562
3621
  var schemaMaps = /* @__PURE__ */ new Set([
3563
3622
  "properties",
@@ -3993,6 +4052,10 @@ var OAuthMetadataDiscovery = class {
3993
4052
  );
3994
4053
  }
3995
4054
  };
4055
+ async function discoverOAuthMetadata(resourceUrl, options = {}) {
4056
+ const discovery = new OAuthMetadataDiscovery(options);
4057
+ return discovery.discover(resourceUrl, options);
4058
+ }
3996
4059
  function skipOptionalWhitespace(value, start) {
3997
4060
  let index = start;
3998
4061
  while (index < value.length && (value[index] === " " || value[index] === " ")) {
@@ -5187,7 +5250,7 @@ var HttpTransport = class {
5187
5250
  if (this.legacyProtocolVersion !== void 0) headers.set("MCP-Protocol-Version", this.legacyProtocolVersion);
5188
5251
  if (this.lastEventId !== void 0) {
5189
5252
  if (this.lastEventId === "") headers.delete("Last-Event-ID");
5190
- else headers.set("Last-Event-ID", Buffer.from(this.lastEventId, "utf8").toString("latin1"));
5253
+ else headers.set("Last-Event-ID", Array.from(new TextEncoder().encode(this.lastEventId), (byte) => String.fromCharCode(byte)).join(""));
5191
5254
  }
5192
5255
  return this.authorizeRequestHeaders(headers, signal);
5193
5256
  }
@@ -5616,7 +5679,7 @@ async function* readLines(stream, maxLineBytes = 16 * 1024 * 1024) {
5616
5679
  let bytes = 0;
5617
5680
  const decoder = new TextDecoder("utf-8", { fatal: true });
5618
5681
  const append = (part) => {
5619
- bytes += Buffer.byteLength(part, "utf8");
5682
+ bytes += new TextEncoder().encode(part).byteLength;
5620
5683
  if (bytes > maxLineBytes) throw new Error(`Stdio input line byte limit exceeded (${maxLineBytes} bytes)`);
5621
5684
  if (part.length > 0) parts.push(part);
5622
5685
  };
@@ -5677,7 +5740,7 @@ var SseParser = class {
5677
5740
  else if (index === this.buffer.length - 1) this.skipLf = true;
5678
5741
  }
5679
5742
  start = index + 1;
5680
- if (Buffer.byteLength(line, "utf8") > this.maxEventBytes)
5743
+ if (new TextEncoder().encode(line).byteLength > this.maxEventBytes)
5681
5744
  throw new Error(`SSE event exceeds ${this.maxEventBytes} bytes`);
5682
5745
  this.consumeLine(line, messages);
5683
5746
  this.assertEventSize("");
@@ -5709,7 +5772,7 @@ var SseParser = class {
5709
5772
  return;
5710
5773
  }
5711
5774
  if (field === "data") {
5712
- this.dataBytes += Buffer.byteLength(value, "utf8") + (this.dataLines.length > 0 ? 1 : 0);
5775
+ this.dataBytes += new TextEncoder().encode(value).byteLength + (this.dataLines.length > 0 ? 1 : 0);
5713
5776
  this.dataLines.push(value);
5714
5777
  return;
5715
5778
  }
@@ -5748,7 +5811,7 @@ var SseParser = class {
5748
5811
  this.dataBytes = 0;
5749
5812
  }
5750
5813
  assertEventSize(partialLine) {
5751
- if (this.dataBytes + Buffer.byteLength(this.eventType ?? "", "utf8") + Buffer.byteLength(this.eventId, "utf8") + Buffer.byteLength(partialLine, "utf8") > this.maxEventBytes)
5814
+ if (this.dataBytes + new TextEncoder().encode(this.eventType ?? "").byteLength + new TextEncoder().encode(this.eventId).byteLength + new TextEncoder().encode(partialLine).byteLength > this.maxEventBytes)
5752
5815
  throw new Error(`SSE event exceeds ${this.maxEventBytes} bytes`);
5753
5816
  }
5754
5817
  };
@@ -6466,6 +6529,7 @@ export {
6466
6529
  McpError,
6467
6530
  OAuthMetadataDiscovery,
6468
6531
  OAuthMetadataError,
6532
+ discoverOAuthMetadata,
6469
6533
  snapshotHttpTransportHeaders
6470
6534
  };
6471
6535
  /*!