tiny-http-mcp-server 0.1.59 → 0.1.61
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/auth-store/README.md +4 -0
- package/node_modules/auth-store/dist/create-secret-store.d.ts +3 -1
- package/node_modules/auth-store/dist/create-secret-store.js +3 -2
- package/node_modules/auth-store/dist/index.d.ts +1 -1
- package/node_modules/auth-store/dist/index.js +1 -1
- package/node_modules/mcp-oauth/README.md +7 -1
- package/node_modules/mcp-oauth/dist/client/auth-store-session-store.js +2 -2
- package/node_modules/mcp-oauth/dist/client/cancellable-operation.d.ts +2 -0
- package/node_modules/mcp-oauth/dist/client/cancellable-operation.js +18 -0
- package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +4 -2
- package/node_modules/mcp-oauth/dist/client/session-transaction.js +10 -3
- package/node_modules/tiny-mcp-client/dist/index.js +32 -7
- package/package.json +1 -1
package/dist/composition.json
CHANGED
|
@@ -25,6 +25,10 @@ const value = await store.get(); // "secret-value"
|
|
|
25
25
|
await store.delete();
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
Use `resolveSecretStoreBackend(options)` to select and validate the backend
|
|
29
|
+
without constructing a store or reading credentials. Pass the returned backend
|
|
30
|
+
explicitly when deferred operations must keep the same environment selection.
|
|
31
|
+
|
|
28
32
|
Both built-in backends expose `store.withLock(operation, { signal, timeoutMs })`
|
|
29
33
|
for transactions spanning a read, external operation and write. Independent
|
|
30
34
|
instances and processes serialize the same encrypted file or Keychain
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
import type { CreateSecretStoreInput, CreateSecretStoreResult } from "./types.js";
|
|
1
|
+
import type { CreateSecretStoreInput, CreateSecretStoreResult, StoreBackend } from "./types.js";
|
|
2
2
|
export declare function createSecretStore(input: CreateSecretStoreInput): CreateSecretStoreResult;
|
|
3
|
+
/** Select and validate a backend without constructing a store or accessing credentials. */
|
|
4
|
+
export declare function resolveSecretStoreBackend(input: CreateSecretStoreInput): StoreBackend;
|
|
@@ -17,7 +17,7 @@ const storeFactories = {
|
|
|
17
17
|
}
|
|
18
18
|
};
|
|
19
19
|
export function createSecretStore(input) {
|
|
20
|
-
const backend =
|
|
20
|
+
const backend = resolveSecretStoreBackend(input);
|
|
21
21
|
const platform = input.platform ?? process.platform;
|
|
22
22
|
if (backend === "keychain" && platform !== MACOS_PLATFORM) {
|
|
23
23
|
throw new Error(`Keychain backend is only supported on macOS. Current platform: ${platform}`);
|
|
@@ -25,7 +25,8 @@ export function createSecretStore(input) {
|
|
|
25
25
|
const store = storeFactories[backend](input);
|
|
26
26
|
return { backend, store };
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
/** Select and validate a backend without constructing a store or accessing credentials. */
|
|
29
|
+
export function resolveSecretStoreBackend(input) {
|
|
29
30
|
const envVar = input.backendEnvVar ?? DEFAULT_BACKEND_ENV_VAR;
|
|
30
31
|
const configuredBackend = input.backend ?? getOwnEnvValue(input.env, envVar) ?? getOwnEnvValue(process.env, envVar);
|
|
31
32
|
const backend = configuredBackend?.trim();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { createSecretStore } from "./create-secret-store.js";
|
|
1
|
+
export { createSecretStore, resolveSecretStoreBackend } from "./create-secret-store.js";
|
|
2
2
|
export { EncryptedFileStore } from "./encrypted-file-store.js";
|
|
3
3
|
export { KeychainStore } from "./keychain-store.js";
|
|
4
4
|
export type { SecretStoreLockOptions, SecretStoreLockFileSystem } from "./transaction-lock.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { createSecretStore } from "./create-secret-store.js";
|
|
1
|
+
export { createSecretStore, resolveSecretStoreBackend } from "./create-secret-store.js";
|
|
2
2
|
export { EncryptedFileStore } from "./encrypted-file-store.js";
|
|
3
3
|
export { KeychainStore } from "./keychain-store.js";
|
|
4
4
|
export { key, MigratingSecretStore } from "./provider-store.js";
|
|
@@ -64,6 +64,8 @@ aborting the original signal still cancels authorization.
|
|
|
64
64
|
Native session and client persistence factories also capture file paths, salts,
|
|
65
65
|
Keychain identities, lock locations and selected filesystem/command handles.
|
|
66
66
|
Mutating these settings cannot redirect a later read or write.
|
|
67
|
+
The factories resolve the selected backend environment variable once at creation;
|
|
68
|
+
later environment changes cannot move a transaction between file and Keychain.
|
|
67
69
|
|
|
68
70
|
`createJwksTokenVerifier(options)` accepts:
|
|
69
71
|
|
|
@@ -93,7 +95,11 @@ Always close a successful standalone session in `finally`.
|
|
|
93
95
|
|
|
94
96
|
Provider request inputs accept an optional `signal`. It reaches callback waits,
|
|
95
97
|
registration, token requests and bounded token-body reads. Cancellation retains
|
|
96
|
-
its original reason and does not retry authorization.
|
|
98
|
+
its original reason and does not retry authorization. Native provider calls
|
|
99
|
+
also settle cancellation while host persistence or lazy discovery callbacks are
|
|
100
|
+
waiting. An unfinished transaction keeps its lease until its host work finishes;
|
|
101
|
+
following callers must wait or reach their own lock-acquisition limit. This
|
|
102
|
+
prevents overlap with a pending refresh-intent write. Custom providers should
|
|
97
103
|
observe the supplied signal and pass it to any work they start.
|
|
98
104
|
|
|
99
105
|
`authorizeRequest` may return an owned token snapshot for the request it
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { normalizeStoredOAuthClient } from "./client-registration.js";
|
|
2
2
|
import crypto from "node:crypto";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { createSecretStore } from "auth-store";
|
|
4
|
+
import { createSecretStore, resolveSecretStoreBackend } from "auth-store";
|
|
5
5
|
import { canonicalizeResourceIndicator } from "../resource-indicator.js";
|
|
6
6
|
const DEFAULT_FILE_SALT = "poe-code:mcp-oauth:v1";
|
|
7
7
|
const DEFAULT_FILE_DIRECTORY = ".poe-code/mcp-oauth";
|
|
@@ -83,7 +83,7 @@ export function createAuthStoreClientStore(options, namespace) {
|
|
|
83
83
|
}
|
|
84
84
|
function snapshotPersistenceOptions(options) {
|
|
85
85
|
return {
|
|
86
|
-
...options,
|
|
86
|
+
...options, backend: resolveSecretStoreBackend(options),
|
|
87
87
|
...(options.fileStore === undefined ? {} : { fileStore: { ...options.fileStore } }),
|
|
88
88
|
...(options.keychainStore === undefined ? {} : { keychainStore: { ...options.keychainStore,
|
|
89
89
|
...(options.keychainStore.lock === undefined ? {} : { lock: { ...options.keychainStore.lock } }) } })
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Settle the caller on cancellation without abandoning observation of host completion. */
|
|
2
|
+
export async function waitForOAuthOperation(operation, signal) {
|
|
3
|
+
if (signal === undefined)
|
|
4
|
+
return operation;
|
|
5
|
+
let abort;
|
|
6
|
+
try {
|
|
7
|
+
return await new Promise((resolve, reject) => {
|
|
8
|
+
abort = () => reject(signal.reason);
|
|
9
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
10
|
+
operation.then(resolve, reject);
|
|
11
|
+
if (signal.aborted)
|
|
12
|
+
abort();
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
finally {
|
|
16
|
+
signal.removeEventListener("abort", abort);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -12,6 +12,7 @@ import { generateCodeChallenge, generateCodeVerifier } from "./pkce.js";
|
|
|
12
12
|
import { exchangeAuthorizationCode, OAuthError, refreshAccessToken, isRetryableOAuthError, readOAuthJsonObjectResponse } from "./token-endpoint.js";
|
|
13
13
|
import { canonicalizeResourceIndicator } from "../resource-indicator.js";
|
|
14
14
|
import { withOAuthSessionTransaction } from "./session-transaction.js";
|
|
15
|
+
import { waitForOAuthOperation } from "./cancellable-operation.js";
|
|
15
16
|
const MAX_JS_DATE_MS = 8_640_000_000_000_000;
|
|
16
17
|
export function createOAuthClientProvider(options) {
|
|
17
18
|
if (isProviderOptions(options)) {
|
|
@@ -83,7 +84,7 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
83
84
|
return { ...initialGrant.tokens };
|
|
84
85
|
if (input.discover === undefined)
|
|
85
86
|
return;
|
|
86
|
-
const discovery = await input.discover();
|
|
87
|
+
const discovery = await waitForOAuthOperation(input.discover(), input.signal);
|
|
87
88
|
input.signal?.throwIfAborted();
|
|
88
89
|
assertRequestMatchesResource(resource, canonicalizeResourceIndicator(discovery.resource));
|
|
89
90
|
session = await ensureAuthorizedSession(resource, discovery, input.fetch, true, false, input.signal);
|
|
@@ -113,11 +114,12 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
113
114
|
},
|
|
114
115
|
async handleUnauthorized(input) {
|
|
115
116
|
try {
|
|
117
|
+
input.signal?.throwIfAborted();
|
|
116
118
|
assertNoAccessTokenInUrl(input.requestUrl, "Protected resource request URL");
|
|
117
119
|
const requestUrl = canonicalizeResourceIndicator(input.requestUrl);
|
|
118
120
|
const resource = canonicalizeResourceIndicator(input.discovery.resource);
|
|
119
121
|
assertRequestMatchesResource(requestUrl, resource);
|
|
120
|
-
const cached = await loadSession(resource);
|
|
122
|
+
const cached = await waitForOAuthOperation(loadSession(resource), input.signal);
|
|
121
123
|
const currentTokens = cached?.tokens ?? (!initialGrantConsumed && initialGrant?.resource === resource ? initialGrant.tokens : undefined);
|
|
122
124
|
let rejectedCurrentGrant = hasCachedAccessToken(cached) || (!initialGrantConsumed && initialGrant?.resource === resource);
|
|
123
125
|
let presentedTokens = input.presentedTokens;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { waitForOAuthOperation } from "./cancellable-operation.js";
|
|
1
2
|
const queues = new WeakMap();
|
|
2
3
|
/** Serialize the complete read/redeem/write operation, with independent cancellation for waiters. */
|
|
3
4
|
export async function withOAuthSessionTransaction(store, resource, operation, options = {}) {
|
|
@@ -13,6 +14,7 @@ export async function withOAuthSessionTransaction(store, resource, operation, op
|
|
|
13
14
|
const current = new Promise(resolve => { release = resolve; });
|
|
14
15
|
const tail = previous.then(() => current);
|
|
15
16
|
pending.set(resource, tail);
|
|
17
|
+
let running;
|
|
16
18
|
try {
|
|
17
19
|
let timer;
|
|
18
20
|
let rejectWait;
|
|
@@ -30,12 +32,17 @@ export async function withOAuthSessionTransaction(store, resource, operation, op
|
|
|
30
32
|
options.signal?.removeEventListener("abort", abort);
|
|
31
33
|
}
|
|
32
34
|
options.signal?.throwIfAborted();
|
|
33
|
-
|
|
35
|
+
running = (async () => store.withLock === undefined ? operation() : store.withLock(resource, operation, {
|
|
34
36
|
signal: options.signal, timeoutMs: Math.max(0, timeoutMs - (performance.now() - started))
|
|
35
|
-
});
|
|
37
|
+
}))();
|
|
38
|
+
return await waitForOAuthOperation(running, options.signal);
|
|
36
39
|
}
|
|
37
40
|
finally {
|
|
38
|
-
|
|
41
|
+
// Cancellation settles the caller, but unfinished host work still owns the lease.
|
|
42
|
+
if (running === undefined)
|
|
43
|
+
release();
|
|
44
|
+
else
|
|
45
|
+
void running.then(release, release);
|
|
39
46
|
void tail.then(() => { if (pending.get(resource) === tail)
|
|
40
47
|
pending.delete(resource); });
|
|
41
48
|
}
|
|
@@ -4526,7 +4526,7 @@ var storeFactories = {
|
|
|
4526
4526
|
}
|
|
4527
4527
|
};
|
|
4528
4528
|
function createSecretStore(input) {
|
|
4529
|
-
const backend =
|
|
4529
|
+
const backend = resolveSecretStoreBackend(input);
|
|
4530
4530
|
const platform = input.platform ?? process.platform;
|
|
4531
4531
|
if (backend === "keychain" && platform !== MACOS_PLATFORM) {
|
|
4532
4532
|
throw new Error(`Keychain backend is only supported on macOS. Current platform: ${platform}`);
|
|
@@ -4534,7 +4534,7 @@ function createSecretStore(input) {
|
|
|
4534
4534
|
const store = storeFactories[backend](input);
|
|
4535
4535
|
return { backend, store };
|
|
4536
4536
|
}
|
|
4537
|
-
function
|
|
4537
|
+
function resolveSecretStoreBackend(input) {
|
|
4538
4538
|
const envVar = input.backendEnvVar ?? DEFAULT_BACKEND_ENV_VAR;
|
|
4539
4539
|
const configuredBackend = input.backend ?? getOwnEnvValue(input.env, envVar) ?? getOwnEnvValue(process.env, envVar);
|
|
4540
4540
|
const backend = configuredBackend?.trim();
|
|
@@ -4640,6 +4640,7 @@ function createAuthStoreClientStore(options, namespace) {
|
|
|
4640
4640
|
function snapshotPersistenceOptions(options) {
|
|
4641
4641
|
return {
|
|
4642
4642
|
...options,
|
|
4643
|
+
backend: resolveSecretStoreBackend(options),
|
|
4643
4644
|
...options.fileStore === void 0 ? {} : { fileStore: { ...options.fileStore } },
|
|
4644
4645
|
...options.keychainStore === void 0 ? {} : { keychainStore: {
|
|
4645
4646
|
...options.keychainStore,
|
|
@@ -5193,6 +5194,24 @@ function normalizeBearerTokenType(value) {
|
|
|
5193
5194
|
return value.toLowerCase() === "bearer" ? "Bearer" : null;
|
|
5194
5195
|
}
|
|
5195
5196
|
|
|
5197
|
+
// ../mcp-oauth/dist/client/cancellable-operation.js
|
|
5198
|
+
async function waitForOAuthOperation(operation, signal) {
|
|
5199
|
+
if (signal === void 0)
|
|
5200
|
+
return operation;
|
|
5201
|
+
let abort;
|
|
5202
|
+
try {
|
|
5203
|
+
return await new Promise((resolve, reject) => {
|
|
5204
|
+
abort = () => reject(signal.reason);
|
|
5205
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
5206
|
+
operation.then(resolve, reject);
|
|
5207
|
+
if (signal.aborted)
|
|
5208
|
+
abort();
|
|
5209
|
+
});
|
|
5210
|
+
} finally {
|
|
5211
|
+
signal.removeEventListener("abort", abort);
|
|
5212
|
+
}
|
|
5213
|
+
}
|
|
5214
|
+
|
|
5196
5215
|
// ../mcp-oauth/dist/client/session-transaction.js
|
|
5197
5216
|
var queues = /* @__PURE__ */ new WeakMap();
|
|
5198
5217
|
async function withOAuthSessionTransaction(store, resource, operation, options = {}) {
|
|
@@ -5210,6 +5229,7 @@ async function withOAuthSessionTransaction(store, resource, operation, options =
|
|
|
5210
5229
|
});
|
|
5211
5230
|
const tail = previous.then(() => current);
|
|
5212
5231
|
pending.set(resource, tail);
|
|
5232
|
+
let running;
|
|
5213
5233
|
try {
|
|
5214
5234
|
let timer;
|
|
5215
5235
|
let rejectWait;
|
|
@@ -5229,12 +5249,16 @@ async function withOAuthSessionTransaction(store, resource, operation, options =
|
|
|
5229
5249
|
options.signal?.removeEventListener("abort", abort);
|
|
5230
5250
|
}
|
|
5231
5251
|
options.signal?.throwIfAborted();
|
|
5232
|
-
|
|
5252
|
+
running = (async () => store.withLock === void 0 ? operation() : store.withLock(resource, operation, {
|
|
5233
5253
|
signal: options.signal,
|
|
5234
5254
|
timeoutMs: Math.max(0, timeoutMs - (performance.now() - started))
|
|
5235
|
-
});
|
|
5255
|
+
}))();
|
|
5256
|
+
return await waitForOAuthOperation(running, options.signal);
|
|
5236
5257
|
} finally {
|
|
5237
|
-
|
|
5258
|
+
if (running === void 0)
|
|
5259
|
+
release();
|
|
5260
|
+
else
|
|
5261
|
+
void running.then(release, release);
|
|
5238
5262
|
void tail.then(() => {
|
|
5239
5263
|
if (pending.get(resource) === tail)
|
|
5240
5264
|
pending.delete(resource);
|
|
@@ -5311,7 +5335,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5311
5335
|
return { ...initialGrant.tokens };
|
|
5312
5336
|
if (input.discover === void 0)
|
|
5313
5337
|
return;
|
|
5314
|
-
const discovery = await input.discover();
|
|
5338
|
+
const discovery = await waitForOAuthOperation(input.discover(), input.signal);
|
|
5315
5339
|
input.signal?.throwIfAborted();
|
|
5316
5340
|
assertRequestMatchesResource(resource, canonicalizeResourceIndicator(discovery.resource));
|
|
5317
5341
|
session = await ensureAuthorizedSession(resource, discovery, input.fetch, true, false, input.signal);
|
|
@@ -5337,11 +5361,12 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5337
5361
|
},
|
|
5338
5362
|
async handleUnauthorized(input) {
|
|
5339
5363
|
try {
|
|
5364
|
+
input.signal?.throwIfAborted();
|
|
5340
5365
|
assertNoAccessTokenInUrl(input.requestUrl, "Protected resource request URL");
|
|
5341
5366
|
const requestUrl = canonicalizeResourceIndicator(input.requestUrl);
|
|
5342
5367
|
const resource = canonicalizeResourceIndicator(input.discovery.resource);
|
|
5343
5368
|
assertRequestMatchesResource(requestUrl, resource);
|
|
5344
|
-
const cached = await loadSession(resource);
|
|
5369
|
+
const cached = await waitForOAuthOperation(loadSession(resource), input.signal);
|
|
5345
5370
|
const currentTokens = cached?.tokens ?? (!initialGrantConsumed && initialGrant?.resource === resource ? initialGrant.tokens : void 0);
|
|
5346
5371
|
let rejectedCurrentGrant = hasCachedAccessToken(cached) || !initialGrantConsumed && initialGrant?.resource === resource;
|
|
5347
5372
|
let presentedTokens = input.presentedTokens;
|