tiny-http-mcp-server 0.1.43 → 0.1.45
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/dist/composition.json +1 -1
- package/node_modules/mcp-oauth/README.md +10 -1
- package/node_modules/mcp-oauth/dist/client/token-endpoint.js +11 -7
- package/node_modules/mcp-oauth/dist/http-fetch.js +43 -6
- package/node_modules/mcp-oauth/dist/index.d.ts +2 -1
- package/node_modules/mcp-oauth/dist/index.js +2 -1
- package/node_modules/tiny-mcp-client/dist/index.js +62 -23
- package/package.json +1 -1
package/dist/composition.json
CHANGED
|
@@ -29,9 +29,11 @@ const verifier = createJwksTokenVerifier({
|
|
|
29
29
|
- `createAuthStoreSessionStore(options)`: persisted OAuth session store backed by `auth-store`.
|
|
30
30
|
- `createLoopbackAuthorizationSession(options)`: local callback server for browser authorization.
|
|
31
31
|
- `generateCodeVerifier()` and `generateCodeChallenge(...)`: PKCE helpers.
|
|
32
|
+
- `normalizeStoredOAuthClient(value)`: normalize a saved client identity, full registration and ownership marker.
|
|
33
|
+
- `normalizeOAuthScope(value)`: validate scope syntax and normalize its case-sensitive set.
|
|
32
34
|
- `canonicalizeResourceIndicator(value)`: resource indicator canonicalization.
|
|
33
35
|
- `createJwksTokenVerifier(options)`: JWKS-backed access-token verifier for MCP servers.
|
|
34
|
-
- `OAuthError`:
|
|
36
|
+
- `OAuthError`: OAuth HTTP error type with status, retryability and known-outcome fields.
|
|
35
37
|
|
|
36
38
|
## Configuration
|
|
37
39
|
|
|
@@ -242,3 +244,10 @@ cannot disable this policy.
|
|
|
242
244
|
|
|
243
245
|
This package exposes no direct environment variables. When `authStore` is used,
|
|
244
246
|
`auth-store` honors its own backend environment variables.
|
|
247
|
+
|
|
248
|
+
Malformed OAuth HTTP errors retain their numeric HTTP status without echoing
|
|
249
|
+
response bodies. Raw/incomplete client-error responses (including registration
|
|
250
|
+
HTTP 403) are nonretryable `invalid_response` errors; authorization fails without
|
|
251
|
+
waiting for consent that never started. Raw server errors remain transient.
|
|
252
|
+
`outcomeKnown: false` still withholds an uncertain rotating refresh family: a
|
|
253
|
+
nonretryable HTTP status alone does not prove a refresh token was unconsumed.
|
|
@@ -16,13 +16,15 @@ export class OAuthError extends Error {
|
|
|
16
16
|
/** True only when a complete OAuth error response establishes rejection. */
|
|
17
17
|
outcomeKnown;
|
|
18
18
|
constructor(shape, status, outcomeKnown = true) {
|
|
19
|
-
|
|
19
|
+
const description = Object.hasOwn(shape, "error_description") ? shape.error_description : undefined;
|
|
20
|
+
const uri = Object.hasOwn(shape, "error_uri") ? shape.error_uri : undefined;
|
|
21
|
+
super(description ?? (outcomeKnown ? shape.error : `OAuth HTTP response did not contain a valid error (HTTP ${status})`));
|
|
20
22
|
this.name = "OAuthError";
|
|
21
23
|
this.error = shape.error;
|
|
22
|
-
this.errorDescription =
|
|
23
|
-
this.errorUri =
|
|
24
|
-
this.error_description =
|
|
25
|
-
this.error_uri =
|
|
24
|
+
this.errorDescription = description;
|
|
25
|
+
this.errorUri = uri;
|
|
26
|
+
this.error_description = description;
|
|
27
|
+
this.error_uri = uri;
|
|
26
28
|
this.status = status;
|
|
27
29
|
this.retryable = isRetryableOAuthError(this);
|
|
28
30
|
this.terminal = !this.retryable;
|
|
@@ -164,7 +166,9 @@ export async function readOAuthJsonObjectResponse(response, signal) {
|
|
|
164
166
|
const record = payload;
|
|
165
167
|
if (!response.ok) {
|
|
166
168
|
const error = getOwnEntry(record, "error");
|
|
167
|
-
|
|
169
|
+
if (typeof error !== "string" || error.trim() === "")
|
|
170
|
+
throw fallbackError;
|
|
171
|
+
throw new OAuthError(readOAuthError(record), response.status);
|
|
168
172
|
}
|
|
169
173
|
return record;
|
|
170
174
|
}
|
|
@@ -182,7 +186,7 @@ function getOwnEntry(record, key) {
|
|
|
182
186
|
return Object.prototype.hasOwnProperty.call(record, key) ? record[key] : undefined;
|
|
183
187
|
}
|
|
184
188
|
function createFallbackOAuthError(status) {
|
|
185
|
-
const error = status === 503 ? "temporarily_unavailable" : "server_error";
|
|
189
|
+
const error = status === 503 ? "temporarily_unavailable" : status >= 500 ? "server_error" : "invalid_response";
|
|
186
190
|
return new OAuthError({ error }, status, false);
|
|
187
191
|
}
|
|
188
192
|
function normalizeBearerTokenType(value) {
|
|
@@ -1,8 +1,45 @@
|
|
|
1
1
|
export async function fetchMcpResponse(fetchImplementation, input, init = {}) {
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
const signal = init.signal;
|
|
3
|
+
signal?.throwIfAborted();
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
let settled = false;
|
|
6
|
+
const finish = (error, response) => {
|
|
7
|
+
if (settled)
|
|
8
|
+
return;
|
|
9
|
+
settled = true;
|
|
10
|
+
signal?.removeEventListener("abort", abort);
|
|
11
|
+
if (response === undefined)
|
|
12
|
+
reject(error);
|
|
13
|
+
else
|
|
14
|
+
resolve(response);
|
|
15
|
+
};
|
|
16
|
+
const abort = () => finish(signal?.reason);
|
|
17
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
18
|
+
if (signal?.aborted) {
|
|
19
|
+
abort();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
let pending;
|
|
23
|
+
try {
|
|
24
|
+
pending = fetchImplementation(input, { ...init, redirect: "error" });
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
finish(error);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
void pending.then(response => {
|
|
31
|
+
if (settled || signal?.aborted) {
|
|
32
|
+
void response.body?.cancel().catch(() => undefined);
|
|
33
|
+
if (!settled)
|
|
34
|
+
abort();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (response.redirected || response.type === "opaqueredirect") {
|
|
38
|
+
void response.body?.cancel().catch(() => undefined);
|
|
39
|
+
finish(new Error("MCP HTTP redirects are not allowed"));
|
|
40
|
+
}
|
|
41
|
+
else
|
|
42
|
+
finish(undefined, response);
|
|
43
|
+
}, error => finish(error));
|
|
44
|
+
});
|
|
8
45
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
export { normalizeOAuthScope } from "./client/scope.js";
|
|
1
2
|
export { parseOAuthTokenGrant } from "./client/token-grant.js";
|
|
2
3
|
export type { OAuthTokenGrantImportOptions } from "./client/token-grant.js";
|
|
3
4
|
export { createAuthStoreSessionStore, } from "./client/auth-store-session-store.js";
|
|
4
5
|
export { createResourceBoundOAuthStores } from "./client/resource-bound-store.js";
|
|
5
6
|
export type { ResourceBoundOAuthStores } from "./client/resource-bound-store.js";
|
|
6
|
-
export { parseOAuthClientRegistration } from "./client/client-registration.js";
|
|
7
|
+
export { parseOAuthClientRegistration, normalizeStoredOAuthClient } from "./client/client-registration.js";
|
|
7
8
|
export { createDefaultOAuthClientProvider, createOAuthClientProvider, } from "./client/default-oauth-client-provider.js";
|
|
8
9
|
export { buildSuccessPage, createLoopbackAuthorizationSession, extractCodeFromInput, } from "./client/loopback-authorization.js";
|
|
9
10
|
export { generateCodeChallenge, generateCodeVerifier, } from "./client/pkce.js";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
export { normalizeOAuthScope } from "./client/scope.js";
|
|
1
2
|
export { parseOAuthTokenGrant } from "./client/token-grant.js";
|
|
2
3
|
export { createAuthStoreSessionStore, } from "./client/auth-store-session-store.js";
|
|
3
4
|
export { createResourceBoundOAuthStores } from "./client/resource-bound-store.js";
|
|
4
|
-
export { parseOAuthClientRegistration } from "./client/client-registration.js";
|
|
5
|
+
export { parseOAuthClientRegistration, normalizeStoredOAuthClient } from "./client/client-registration.js";
|
|
5
6
|
export { createDefaultOAuthClientProvider, createOAuthClientProvider, } from "./client/default-oauth-client-provider.js";
|
|
6
7
|
export { buildSuccessPage, createLoopbackAuthorizationSession, extractCodeFromInput, } from "./client/loopback-authorization.js";
|
|
7
8
|
export { generateCodeChallenge, generateCodeVerifier, } from "./client/pkce.js";
|
|
@@ -3395,6 +3395,16 @@ var SubscriptionManager = class {
|
|
|
3395
3395
|
}
|
|
3396
3396
|
};
|
|
3397
3397
|
|
|
3398
|
+
// ../mcp-oauth/dist/client/scope.js
|
|
3399
|
+
function normalizeOAuthScope(scope) {
|
|
3400
|
+
if (scope === void 0)
|
|
3401
|
+
return void 0;
|
|
3402
|
+
if (typeof scope !== "string" || [...scope].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) > 126 || char === '"' || char === "\\"))
|
|
3403
|
+
throw new Error("Invalid OAuth scope syntax");
|
|
3404
|
+
const normalized = [...new Set(scope.split(" ").filter(Boolean))].sort().join(" ");
|
|
3405
|
+
return normalized || void 0;
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3398
3408
|
// ../mcp-oauth/dist/client/bounded-json.js
|
|
3399
3409
|
function copyBoundedOAuthJson(value, message) {
|
|
3400
3410
|
const invalid = () => new Error(message);
|
|
@@ -3441,16 +3451,6 @@ function copyBoundedOAuthJson(value, message) {
|
|
|
3441
3451
|
return result;
|
|
3442
3452
|
}
|
|
3443
3453
|
|
|
3444
|
-
// ../mcp-oauth/dist/client/scope.js
|
|
3445
|
-
function normalizeOAuthScope(scope) {
|
|
3446
|
-
if (scope === void 0)
|
|
3447
|
-
return void 0;
|
|
3448
|
-
if (typeof scope !== "string" || [...scope].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) > 126 || char === '"' || char === "\\"))
|
|
3449
|
-
throw new Error("Invalid OAuth scope syntax");
|
|
3450
|
-
const normalized = [...new Set(scope.split(" ").filter(Boolean))].sort().join(" ");
|
|
3451
|
-
return normalized || void 0;
|
|
3452
|
-
}
|
|
3453
|
-
|
|
3454
3454
|
// ../mcp-oauth/dist/client/loopback-authorization.js
|
|
3455
3455
|
import http from "node:http";
|
|
3456
3456
|
|
|
@@ -4898,12 +4898,47 @@ import { isIP } from "node:net";
|
|
|
4898
4898
|
|
|
4899
4899
|
// ../mcp-oauth/dist/http-fetch.js
|
|
4900
4900
|
async function fetchMcpResponse(fetchImplementation, input, init = {}) {
|
|
4901
|
-
const
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4901
|
+
const signal = init.signal;
|
|
4902
|
+
signal?.throwIfAborted();
|
|
4903
|
+
return new Promise((resolve, reject) => {
|
|
4904
|
+
let settled = false;
|
|
4905
|
+
const finish = (error, response) => {
|
|
4906
|
+
if (settled)
|
|
4907
|
+
return;
|
|
4908
|
+
settled = true;
|
|
4909
|
+
signal?.removeEventListener("abort", abort);
|
|
4910
|
+
if (response === void 0)
|
|
4911
|
+
reject(error);
|
|
4912
|
+
else
|
|
4913
|
+
resolve(response);
|
|
4914
|
+
};
|
|
4915
|
+
const abort = () => finish(signal?.reason);
|
|
4916
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
4917
|
+
if (signal?.aborted) {
|
|
4918
|
+
abort();
|
|
4919
|
+
return;
|
|
4920
|
+
}
|
|
4921
|
+
let pending;
|
|
4922
|
+
try {
|
|
4923
|
+
pending = fetchImplementation(input, { ...init, redirect: "error" });
|
|
4924
|
+
} catch (error) {
|
|
4925
|
+
finish(error);
|
|
4926
|
+
return;
|
|
4927
|
+
}
|
|
4928
|
+
void pending.then((response) => {
|
|
4929
|
+
if (settled || signal?.aborted) {
|
|
4930
|
+
void response.body?.cancel().catch(() => void 0);
|
|
4931
|
+
if (!settled)
|
|
4932
|
+
abort();
|
|
4933
|
+
return;
|
|
4934
|
+
}
|
|
4935
|
+
if (response.redirected || response.type === "opaqueredirect") {
|
|
4936
|
+
void response.body?.cancel().catch(() => void 0);
|
|
4937
|
+
finish(new Error("MCP HTTP redirects are not allowed"));
|
|
4938
|
+
} else
|
|
4939
|
+
finish(void 0, response);
|
|
4940
|
+
}, (error) => finish(error));
|
|
4941
|
+
});
|
|
4907
4942
|
}
|
|
4908
4943
|
|
|
4909
4944
|
// ../mcp-oauth/dist/client/default-oauth-client-provider.js
|
|
@@ -4979,13 +5014,15 @@ var OAuthError = class extends Error {
|
|
|
4979
5014
|
/** True only when a complete OAuth error response establishes rejection. */
|
|
4980
5015
|
outcomeKnown;
|
|
4981
5016
|
constructor(shape, status, outcomeKnown = true) {
|
|
4982
|
-
|
|
5017
|
+
const description = Object.hasOwn(shape, "error_description") ? shape.error_description : void 0;
|
|
5018
|
+
const uri = Object.hasOwn(shape, "error_uri") ? shape.error_uri : void 0;
|
|
5019
|
+
super(description ?? (outcomeKnown ? shape.error : `OAuth HTTP response did not contain a valid error (HTTP ${status})`));
|
|
4983
5020
|
this.name = "OAuthError";
|
|
4984
5021
|
this.error = shape.error;
|
|
4985
|
-
this.errorDescription =
|
|
4986
|
-
this.errorUri =
|
|
4987
|
-
this.error_description =
|
|
4988
|
-
this.error_uri =
|
|
5022
|
+
this.errorDescription = description;
|
|
5023
|
+
this.errorUri = uri;
|
|
5024
|
+
this.error_description = description;
|
|
5025
|
+
this.error_uri = uri;
|
|
4989
5026
|
this.status = status;
|
|
4990
5027
|
this.retryable = isRetryableOAuthError(this);
|
|
4991
5028
|
this.terminal = !this.retryable;
|
|
@@ -5114,7 +5151,9 @@ async function readOAuthJsonObjectResponse(response, signal) {
|
|
|
5114
5151
|
const record2 = payload;
|
|
5115
5152
|
if (!response.ok) {
|
|
5116
5153
|
const error = getOwnEntry5(record2, "error");
|
|
5117
|
-
|
|
5154
|
+
if (typeof error !== "string" || error.trim() === "")
|
|
5155
|
+
throw fallbackError;
|
|
5156
|
+
throw new OAuthError(readOAuthError(record2), response.status);
|
|
5118
5157
|
}
|
|
5119
5158
|
return record2;
|
|
5120
5159
|
}
|
|
@@ -5132,7 +5171,7 @@ function getOwnEntry5(record2, key2) {
|
|
|
5132
5171
|
return Object.prototype.hasOwnProperty.call(record2, key2) ? record2[key2] : void 0;
|
|
5133
5172
|
}
|
|
5134
5173
|
function createFallbackOAuthError(status) {
|
|
5135
|
-
const error = status === 503 ? "temporarily_unavailable" : "server_error";
|
|
5174
|
+
const error = status === 503 ? "temporarily_unavailable" : status >= 500 ? "server_error" : "invalid_response";
|
|
5136
5175
|
return new OAuthError({ error }, status, false);
|
|
5137
5176
|
}
|
|
5138
5177
|
function normalizeBearerTokenType(value) {
|