toolcraft 0.0.316 → 0.0.318
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 +9 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/auth-store-session-store.d.ts +9 -2
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/auth-store-session-store.js +2 -2
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +13 -2
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/resource-bound-store.d.ts +9 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/resource-bound-store.js +118 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/types.d.ts +2 -0
- package/node_modules/tiny-http-mcp-server/package.json +1 -1
- package/node_modules/tiny-mcp-client/README.md +4 -1
- package/node_modules/tiny-mcp-client/dist/index.d.ts +13 -1
- package/node_modules/tiny-mcp-client/dist/index.js +152 -7
- 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.39",
|
|
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.318",
|
|
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.318",
|
|
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.39",
|
|
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.318",
|
|
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.318",
|
|
127
127
|
"license": "MIT"
|
|
128
128
|
},
|
|
129
129
|
{
|
|
@@ -102,6 +102,15 @@ normalized. An imported grant must declare its scope when a scope is configured.
|
|
|
102
102
|
Authorization records the requested set when the endpoint omits scope, and
|
|
103
103
|
refresh retains the previous granted set. Mismatched responses never activate
|
|
104
104
|
credentials; an unusable refresh response retains the pending refresh record.
|
|
105
|
+
`resourceIdentity` selects a native-owned logical server within its optional
|
|
106
|
+
persistence namespace. One encrypted, locked document owns that identity's
|
|
107
|
+
current resource URL, sessions and registrations. Changing the URL retires its
|
|
108
|
+
credentials permanently; returning to the old URL does not restore them.
|
|
109
|
+
Retired identities also withhold stale initial grants; authorize again or select
|
|
110
|
+
a fresh explicit profile to import a new grant. This option requires native persistence; custom
|
|
111
|
+
session stores own their durable resource trust policy. Without it, the native
|
|
112
|
+
client retains its existing resource-URL cache behavior.
|
|
113
|
+
|
|
105
114
|
Select a separate persistence namespace for another scope profile. No scope is
|
|
106
115
|
invented when the client does not configure one.
|
|
107
116
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type CreateSecretStoreInput } from "auth-store";
|
|
2
|
-
import type { OAuthSessionStore, StoredOAuthClient } from "./types.js";
|
|
1
|
+
import { type CreateSecretStoreInput, type SecretStore } from "auth-store";
|
|
2
|
+
import type { OAuthSessionStore, StoredOAuthSession, StoredOAuthClient } from "./types.js";
|
|
3
3
|
export interface OAuthClientStore {
|
|
4
4
|
load(issuer: string): Promise<StoredOAuthClient | null>;
|
|
5
5
|
save(issuer: string, client: StoredOAuthClient): Promise<void>;
|
|
@@ -7,4 +7,11 @@ export interface OAuthClientStore {
|
|
|
7
7
|
}
|
|
8
8
|
export declare function createAuthStoreSessionStore(options?: CreateSecretStoreInput, namespace?: string): OAuthSessionStore;
|
|
9
9
|
export declare function createAuthStoreClientStore(options: CreateSecretStoreInput, namespace?: string): OAuthClientStore;
|
|
10
|
+
export declare function createNamedSecretStore(key: string, options: CreateSecretStoreInput, defaults: {
|
|
11
|
+
salt: string;
|
|
12
|
+
directory: string;
|
|
13
|
+
service: string;
|
|
14
|
+
accountPrefix: string;
|
|
15
|
+
}, namespace?: string): SecretStore;
|
|
10
16
|
export declare function assertPersistenceNamespace(namespace: string | undefined): void;
|
|
17
|
+
export declare function isStoredOAuthSession(value: unknown): value is StoredOAuthSession;
|
|
@@ -79,7 +79,7 @@ export function createAuthStoreClientStore(options, namespace) {
|
|
|
79
79
|
}
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
|
-
function createNamedSecretStore(key, options, defaults, namespace) {
|
|
82
|
+
export function createNamedSecretStore(key, options, defaults, namespace) {
|
|
83
83
|
const hash = crypto.createHash("sha256").update(namespace === undefined ? key : JSON.stringify([namespace, key])).digest("hex");
|
|
84
84
|
const configuredFilePath = options.fileStore?.filePath;
|
|
85
85
|
const parsedFilePath = configuredFilePath === undefined ? null : path.parse(configuredFilePath);
|
|
@@ -132,7 +132,7 @@ function getOwnString(record, key) {
|
|
|
132
132
|
const value = getOwnEntry(record, key);
|
|
133
133
|
return typeof value === "string" ? value : undefined;
|
|
134
134
|
}
|
|
135
|
-
function isStoredOAuthSession(value) {
|
|
135
|
+
export function isStoredOAuthSession(value) {
|
|
136
136
|
if (!isObjectRecord(value)) {
|
|
137
137
|
return false;
|
|
138
138
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createResourceBoundOAuthStores } from "./resource-bound-store.js";
|
|
1
2
|
import { normalizeStoredOAuthClient, parseOAuthClientRegistration, registrationMatchesRedirect } from "./client-registration.js";
|
|
2
3
|
import { normalizeOAuthScope } from "./scope.js";
|
|
3
4
|
import { normalizeOAuthTokenEndpointAuthMethod } from "./token-auth-method.js";
|
|
@@ -26,8 +27,13 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
26
27
|
const configuredClient = normalizeConfiguredClient(options.client);
|
|
27
28
|
const requestedTokenMethod = normalizeOAuthTokenEndpointAuthMethod(options.client.tokenEndpointAuthMethod);
|
|
28
29
|
const configuredTokenMethod = requestedTokenMethod ?? configuredClient?.tokenEndpointAuthMethod;
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
if (options.resourceIdentity !== undefined && options.sessionStore !== undefined)
|
|
31
|
+
throw new Error("OAuth resourceIdentity requires native-owned persistence; custom stores own their resource trust policy");
|
|
32
|
+
const resourceStores = options.resourceIdentity === undefined ? undefined :
|
|
33
|
+
createResourceBoundOAuthStores(options.authStore ?? {}, options.persistenceNamespace, options.resourceIdentity);
|
|
34
|
+
const sessionStore = resourceStores?.sessionStore ?? options.sessionStore ?? createAuthStoreSessionStore(options.authStore, options.persistenceNamespace);
|
|
35
|
+
const clientStore = resourceStores?.clientStore ??
|
|
36
|
+
(options.authStore === undefined ? null : createAuthStoreClientStore(options.authStore, options.persistenceNamespace));
|
|
31
37
|
const now = options.now ?? Date.now;
|
|
32
38
|
const registeredClients = new Map();
|
|
33
39
|
if (options.initialGrant !== undefined) {
|
|
@@ -127,6 +133,11 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
127
133
|
const canonicalResource = canonicalizeResourceIndicator(resource);
|
|
128
134
|
return withOAuthSessionTransaction(sessionStore, canonicalResource, async () => {
|
|
129
135
|
let session = await loadSession(canonicalResource);
|
|
136
|
+
if (resourceStores !== undefined) {
|
|
137
|
+
registeredClients.clear();
|
|
138
|
+
if (!resourceStores.initialGrantAllowed)
|
|
139
|
+
initialGrantConsumed = true;
|
|
140
|
+
}
|
|
130
141
|
if (session !== null)
|
|
131
142
|
assertRegistrationIssuer(session.client, session.authorizationServer);
|
|
132
143
|
if (configuredClient !== null && discovery !== undefined)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CreateSecretStoreInput } from "auth-store";
|
|
2
|
+
import { type OAuthClientStore } from "./auth-store-session-store.js";
|
|
3
|
+
import type { OAuthSessionStore } from "./types.js";
|
|
4
|
+
/** One locked document owns a logical server's URL history, grant and clients. */
|
|
5
|
+
export declare function createResourceBoundOAuthStores(options: CreateSecretStoreInput, namespace: string | undefined, identity: string): {
|
|
6
|
+
sessionStore: OAuthSessionStore;
|
|
7
|
+
clientStore: OAuthClientStore;
|
|
8
|
+
initialGrantAllowed: boolean;
|
|
9
|
+
};
|
package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/resource-bound-store.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { canonicalizeResourceIndicator } from "../resource-indicator.js";
|
|
2
|
+
import { assertPersistenceNamespace, createNamedSecretStore, isStoredOAuthSession } from "./auth-store-session-store.js";
|
|
3
|
+
import { normalizeStoredOAuthClient } from "./client-registration.js";
|
|
4
|
+
/** One locked document owns a logical server's URL history, grant and clients. */
|
|
5
|
+
export function createResourceBoundOAuthStores(options, namespace, identity) {
|
|
6
|
+
assertPersistenceNamespace(identity);
|
|
7
|
+
const store = createNamedSecretStore(identity, options, { salt: "poe-code:mcp-oauth:resources:v1",
|
|
8
|
+
directory: ".poe-code/mcp-oauth/resources", service: "poe-code-mcp-oauth-resources", accountPrefix: "resource" }, namespace);
|
|
9
|
+
const result = { initialGrantAllowed: true, sessionStore: {}, clientStore: {} };
|
|
10
|
+
async function read() {
|
|
11
|
+
const raw = await store.get();
|
|
12
|
+
if (raw === null)
|
|
13
|
+
return null;
|
|
14
|
+
let value;
|
|
15
|
+
try {
|
|
16
|
+
value = JSON.parse(raw);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
throw new Error("Stored OAuth resource identity must be valid JSON; reset explicitly to recover");
|
|
20
|
+
}
|
|
21
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
22
|
+
throw new Error("Invalid stored OAuth resource identity");
|
|
23
|
+
const record = value;
|
|
24
|
+
if (!["version", "resource", "generation", "session", "clients"].every(key => Object.hasOwn(record, key)) || record.version !== 1 || typeof record.resource !== "string" || record.resource !== canonicalizeResourceIndicator(record.resource) ||
|
|
25
|
+
!Number.isSafeInteger(record.generation) || record.generation < 0 ||
|
|
26
|
+
(record.session !== null && (!isStoredOAuthSession(record.session) || record.session.resource !== record.resource)) ||
|
|
27
|
+
typeof record.clients !== "object" || record.clients === null || Array.isArray(record.clients))
|
|
28
|
+
throw new Error("Invalid stored OAuth resource identity; reset explicitly to recover");
|
|
29
|
+
let resourceUrl;
|
|
30
|
+
try {
|
|
31
|
+
resourceUrl = new URL(record.resource);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
throw new Error("Invalid stored OAuth resource identity URL");
|
|
35
|
+
}
|
|
36
|
+
if (!["http:", "https:"].includes(resourceUrl.protocol) || resourceUrl.username || resourceUrl.password || resourceUrl.hash)
|
|
37
|
+
throw new Error("Invalid stored OAuth resource identity URL");
|
|
38
|
+
for (const [issuer, client] of Object.entries(record.clients)) {
|
|
39
|
+
const normalized = normalizeStoredOAuthClient(client);
|
|
40
|
+
let url;
|
|
41
|
+
try {
|
|
42
|
+
url = new URL(issuer);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
throw new Error("Invalid stored OAuth resource client issuer");
|
|
46
|
+
}
|
|
47
|
+
if (normalized === null || !["http:", "https:"].includes(url.protocol) || url.username || url.password || url.hash)
|
|
48
|
+
throw new Error("Invalid stored OAuth resource client");
|
|
49
|
+
Object.defineProperty(record.clients, issuer, { value: normalized, enumerable: true, configurable: true, writable: true });
|
|
50
|
+
}
|
|
51
|
+
return record;
|
|
52
|
+
}
|
|
53
|
+
async function reconcile(resource) {
|
|
54
|
+
resource = canonicalizeResourceIndicator(resource);
|
|
55
|
+
const existing = await read();
|
|
56
|
+
if (existing?.resource === resource) {
|
|
57
|
+
result.initialGrantAllowed = existing.generation === 0;
|
|
58
|
+
return existing;
|
|
59
|
+
}
|
|
60
|
+
const record = { version: 1, resource, generation: existing === null ? 0 : existing.generation + 1, session: null, clients: {} };
|
|
61
|
+
if (!Number.isSafeInteger(record.generation))
|
|
62
|
+
throw new Error("OAuth resource identity generation limit exceeded");
|
|
63
|
+
await store.set(JSON.stringify(record));
|
|
64
|
+
result.initialGrantAllowed = record.generation === 0;
|
|
65
|
+
return record;
|
|
66
|
+
}
|
|
67
|
+
result.sessionStore = {
|
|
68
|
+
async withLock(resource, operation, options) {
|
|
69
|
+
if (store.withLock === undefined)
|
|
70
|
+
throw new Error("OAuth resource identity backend must support transaction locks");
|
|
71
|
+
return store.withLock(async () => {
|
|
72
|
+
options.signal?.throwIfAborted();
|
|
73
|
+
await reconcile(resource);
|
|
74
|
+
options.signal?.throwIfAborted();
|
|
75
|
+
return operation();
|
|
76
|
+
}, options);
|
|
77
|
+
},
|
|
78
|
+
async load(resource) {
|
|
79
|
+
// Unauthorized provenance may peek before acquiring the transaction lock.
|
|
80
|
+
// Only the lock owner may adopt a URL or retire the previous credentials.
|
|
81
|
+
const record = await read();
|
|
82
|
+
return record?.resource === canonicalizeResourceIndicator(resource) ? record.session : null;
|
|
83
|
+
},
|
|
84
|
+
async save(resource, session) {
|
|
85
|
+
const record = await reconcile(resource);
|
|
86
|
+
if (canonicalizeResourceIndicator(session.resource) !== record.resource)
|
|
87
|
+
throw new Error("OAuth session does not match its resource identity");
|
|
88
|
+
record.session = session;
|
|
89
|
+
await store.set(JSON.stringify(record));
|
|
90
|
+
},
|
|
91
|
+
async clear(resource) {
|
|
92
|
+
const record = await reconcile(resource);
|
|
93
|
+
record.session = null;
|
|
94
|
+
// Preserve a tombstone so environment grants cannot revive a cleared family.
|
|
95
|
+
record.generation = Math.max(1, record.generation);
|
|
96
|
+
result.initialGrantAllowed = false;
|
|
97
|
+
await store.set(JSON.stringify(record));
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
result.clientStore = {
|
|
101
|
+
async load(issuer) { const record = await read(); return record !== null && Object.hasOwn(record.clients, issuer) ? record.clients[issuer] : null; },
|
|
102
|
+
async save(issuer, client) {
|
|
103
|
+
const record = await read();
|
|
104
|
+
if (record === null)
|
|
105
|
+
throw new Error("OAuth resource identity must be bound before registering a client");
|
|
106
|
+
Object.defineProperty(record.clients, issuer, { value: client, enumerable: true, configurable: true, writable: true });
|
|
107
|
+
await store.set(JSON.stringify(record));
|
|
108
|
+
},
|
|
109
|
+
async clear(issuer) {
|
|
110
|
+
const record = await read();
|
|
111
|
+
if (record === null || !Object.hasOwn(record.clients, issuer))
|
|
112
|
+
return;
|
|
113
|
+
delete record.clients[issuer];
|
|
114
|
+
await store.set(JSON.stringify(record));
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
@@ -147,6 +147,8 @@ export interface DefaultOAuthClientProviderOptions {
|
|
|
147
147
|
sessionLockTimeoutMs?: number;
|
|
148
148
|
/** Isolate native persisted sessions and registrations for a named profile. */
|
|
149
149
|
persistenceNamespace?: string;
|
|
150
|
+
/** Native-owned durable logical server identity; changing URL retires its credentials. */
|
|
151
|
+
resourceIdentity?: string;
|
|
150
152
|
/** Import an existing grant for one resource. Persisted sessions take precedence. */
|
|
151
153
|
initialGrant?: {
|
|
152
154
|
resource: string;
|
|
@@ -37,7 +37,10 @@ by the server. Announced endpoints must remain on the original origin without
|
|
|
37
37
|
embedded credentials or fragments; endpoint changes close the connection.
|
|
38
38
|
Both transports accept the same headers, OAuth provider and response limits.
|
|
39
39
|
HTTP failures expose `HttpTransportError.status` and `.method`, so callers can
|
|
40
|
-
make transport decisions without parsing error messages.
|
|
40
|
+
make transport decisions without parsing error messages. Legacy HTTP/SSE
|
|
41
|
+
`connect()` also waits for the initialized notification POST to complete before
|
|
42
|
+
reporting ready. Completion failures reject the connection and expose
|
|
43
|
+
`rpcMethod: "notifications/initialized"`; they are not setup transport mismatches.
|
|
41
44
|
|
|
42
45
|
## OAuth HTTP support
|
|
43
46
|
|
|
@@ -250,6 +250,8 @@ interface DefaultOAuthClientProviderOptions {
|
|
|
250
250
|
sessionLockTimeoutMs?: number;
|
|
251
251
|
/** Isolate native persisted sessions and registrations for a named profile. */
|
|
252
252
|
persistenceNamespace?: string;
|
|
253
|
+
/** Native-owned durable logical server identity; changing URL retires its credentials. */
|
|
254
|
+
resourceIdentity?: string;
|
|
253
255
|
/** Import an existing grant for one resource. Persisted sessions take precedence. */
|
|
254
256
|
initialGrant?: {
|
|
255
257
|
resource: string;
|
|
@@ -639,6 +641,11 @@ interface McpTransport {
|
|
|
639
641
|
closed: Promise<McpTransportClosedEvent>;
|
|
640
642
|
dispose(reason?: Error): void;
|
|
641
643
|
filterTools?(tools: Tool[], reset?: boolean): Tool[];
|
|
644
|
+
/** Complete a legacy initialization handshake before the client reports ready. */
|
|
645
|
+
completeInitialization?(options: {
|
|
646
|
+
signal?: AbortSignal;
|
|
647
|
+
timeoutMs: number;
|
|
648
|
+
}): Promise<void>;
|
|
642
649
|
}
|
|
643
650
|
interface InMemoryServerTransport {
|
|
644
651
|
readable: Readable;
|
|
@@ -674,7 +681,8 @@ type HttpTransportFetch = (input: string | URL, init?: RequestInit) => Promise<R
|
|
|
674
681
|
declare class HttpTransportError extends Error {
|
|
675
682
|
readonly status: number;
|
|
676
683
|
readonly method: "GET" | "POST" | "DELETE";
|
|
677
|
-
|
|
684
|
+
readonly rpcMethod?: string | undefined;
|
|
685
|
+
constructor(message: string, status: number, method: "GET" | "POST" | "DELETE", rpcMethod?: string | undefined);
|
|
678
686
|
}
|
|
679
687
|
interface HttpTransportOptions {
|
|
680
688
|
url: string;
|
|
@@ -731,6 +739,10 @@ declare class HttpTransport implements McpTransport {
|
|
|
731
739
|
private readonly toolParameterHeaders;
|
|
732
740
|
private readonly onWarning;
|
|
733
741
|
constructor({ url, mode, headers, fetch: fetchImpl, oauth, oauthDiscoveryCache, onWarning, maxResponseBytes, }: HttpTransportOptions);
|
|
742
|
+
completeInitialization(options: {
|
|
743
|
+
signal?: AbortSignal;
|
|
744
|
+
timeoutMs: number;
|
|
745
|
+
}): Promise<void>;
|
|
734
746
|
filterTools(tools: Tool[], reset?: boolean): Tool[];
|
|
735
747
|
dispose(reason?: Error): void;
|
|
736
748
|
private closeWithSessionTermination;
|
|
@@ -4709,6 +4709,120 @@ function isNonBlankOwnString(record2, key2) {
|
|
|
4709
4709
|
return value !== void 0 && value.trim().length > 0;
|
|
4710
4710
|
}
|
|
4711
4711
|
|
|
4712
|
+
// ../mcp-oauth/dist/client/resource-bound-store.js
|
|
4713
|
+
function createResourceBoundOAuthStores(options, namespace, identity) {
|
|
4714
|
+
assertPersistenceNamespace(identity);
|
|
4715
|
+
const store = createNamedSecretStore(identity, options, {
|
|
4716
|
+
salt: "poe-code:mcp-oauth:resources:v1",
|
|
4717
|
+
directory: ".poe-code/mcp-oauth/resources",
|
|
4718
|
+
service: "poe-code-mcp-oauth-resources",
|
|
4719
|
+
accountPrefix: "resource"
|
|
4720
|
+
}, namespace);
|
|
4721
|
+
const result = { initialGrantAllowed: true, sessionStore: {}, clientStore: {} };
|
|
4722
|
+
async function read() {
|
|
4723
|
+
const raw = await store.get();
|
|
4724
|
+
if (raw === null)
|
|
4725
|
+
return null;
|
|
4726
|
+
let value;
|
|
4727
|
+
try {
|
|
4728
|
+
value = JSON.parse(raw);
|
|
4729
|
+
} catch {
|
|
4730
|
+
throw new Error("Stored OAuth resource identity must be valid JSON; reset explicitly to recover");
|
|
4731
|
+
}
|
|
4732
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
4733
|
+
throw new Error("Invalid stored OAuth resource identity");
|
|
4734
|
+
const record2 = value;
|
|
4735
|
+
if (!["version", "resource", "generation", "session", "clients"].every((key2) => Object.hasOwn(record2, key2)) || record2.version !== 1 || typeof record2.resource !== "string" || record2.resource !== canonicalizeResourceIndicator(record2.resource) || !Number.isSafeInteger(record2.generation) || record2.generation < 0 || record2.session !== null && (!isStoredOAuthSession(record2.session) || record2.session.resource !== record2.resource) || typeof record2.clients !== "object" || record2.clients === null || Array.isArray(record2.clients))
|
|
4736
|
+
throw new Error("Invalid stored OAuth resource identity; reset explicitly to recover");
|
|
4737
|
+
let resourceUrl;
|
|
4738
|
+
try {
|
|
4739
|
+
resourceUrl = new URL(record2.resource);
|
|
4740
|
+
} catch {
|
|
4741
|
+
throw new Error("Invalid stored OAuth resource identity URL");
|
|
4742
|
+
}
|
|
4743
|
+
if (!["http:", "https:"].includes(resourceUrl.protocol) || resourceUrl.username || resourceUrl.password || resourceUrl.hash)
|
|
4744
|
+
throw new Error("Invalid stored OAuth resource identity URL");
|
|
4745
|
+
for (const [issuer, client] of Object.entries(record2.clients)) {
|
|
4746
|
+
const normalized = normalizeStoredOAuthClient(client);
|
|
4747
|
+
let url;
|
|
4748
|
+
try {
|
|
4749
|
+
url = new URL(issuer);
|
|
4750
|
+
} catch {
|
|
4751
|
+
throw new Error("Invalid stored OAuth resource client issuer");
|
|
4752
|
+
}
|
|
4753
|
+
if (normalized === null || !["http:", "https:"].includes(url.protocol) || url.username || url.password || url.hash)
|
|
4754
|
+
throw new Error("Invalid stored OAuth resource client");
|
|
4755
|
+
Object.defineProperty(record2.clients, issuer, { value: normalized, enumerable: true, configurable: true, writable: true });
|
|
4756
|
+
}
|
|
4757
|
+
return record2;
|
|
4758
|
+
}
|
|
4759
|
+
async function reconcile(resource) {
|
|
4760
|
+
resource = canonicalizeResourceIndicator(resource);
|
|
4761
|
+
const existing = await read();
|
|
4762
|
+
if (existing?.resource === resource) {
|
|
4763
|
+
result.initialGrantAllowed = existing.generation === 0;
|
|
4764
|
+
return existing;
|
|
4765
|
+
}
|
|
4766
|
+
const record2 = { version: 1, resource, generation: existing === null ? 0 : existing.generation + 1, session: null, clients: {} };
|
|
4767
|
+
if (!Number.isSafeInteger(record2.generation))
|
|
4768
|
+
throw new Error("OAuth resource identity generation limit exceeded");
|
|
4769
|
+
await store.set(JSON.stringify(record2));
|
|
4770
|
+
result.initialGrantAllowed = record2.generation === 0;
|
|
4771
|
+
return record2;
|
|
4772
|
+
}
|
|
4773
|
+
result.sessionStore = {
|
|
4774
|
+
async withLock(resource, operation, options2) {
|
|
4775
|
+
if (store.withLock === void 0)
|
|
4776
|
+
throw new Error("OAuth resource identity backend must support transaction locks");
|
|
4777
|
+
return store.withLock(async () => {
|
|
4778
|
+
options2.signal?.throwIfAborted();
|
|
4779
|
+
await reconcile(resource);
|
|
4780
|
+
options2.signal?.throwIfAborted();
|
|
4781
|
+
return operation();
|
|
4782
|
+
}, options2);
|
|
4783
|
+
},
|
|
4784
|
+
async load(resource) {
|
|
4785
|
+
const record2 = await read();
|
|
4786
|
+
return record2?.resource === canonicalizeResourceIndicator(resource) ? record2.session : null;
|
|
4787
|
+
},
|
|
4788
|
+
async save(resource, session) {
|
|
4789
|
+
const record2 = await reconcile(resource);
|
|
4790
|
+
if (canonicalizeResourceIndicator(session.resource) !== record2.resource)
|
|
4791
|
+
throw new Error("OAuth session does not match its resource identity");
|
|
4792
|
+
record2.session = session;
|
|
4793
|
+
await store.set(JSON.stringify(record2));
|
|
4794
|
+
},
|
|
4795
|
+
async clear(resource) {
|
|
4796
|
+
const record2 = await reconcile(resource);
|
|
4797
|
+
record2.session = null;
|
|
4798
|
+
record2.generation = Math.max(1, record2.generation);
|
|
4799
|
+
result.initialGrantAllowed = false;
|
|
4800
|
+
await store.set(JSON.stringify(record2));
|
|
4801
|
+
}
|
|
4802
|
+
};
|
|
4803
|
+
result.clientStore = {
|
|
4804
|
+
async load(issuer) {
|
|
4805
|
+
const record2 = await read();
|
|
4806
|
+
return record2 !== null && Object.hasOwn(record2.clients, issuer) ? record2.clients[issuer] : null;
|
|
4807
|
+
},
|
|
4808
|
+
async save(issuer, client) {
|
|
4809
|
+
const record2 = await read();
|
|
4810
|
+
if (record2 === null)
|
|
4811
|
+
throw new Error("OAuth resource identity must be bound before registering a client");
|
|
4812
|
+
Object.defineProperty(record2.clients, issuer, { value: client, enumerable: true, configurable: true, writable: true });
|
|
4813
|
+
await store.set(JSON.stringify(record2));
|
|
4814
|
+
},
|
|
4815
|
+
async clear(issuer) {
|
|
4816
|
+
const record2 = await read();
|
|
4817
|
+
if (record2 === null || !Object.hasOwn(record2.clients, issuer))
|
|
4818
|
+
return;
|
|
4819
|
+
delete record2.clients[issuer];
|
|
4820
|
+
await store.set(JSON.stringify(record2));
|
|
4821
|
+
}
|
|
4822
|
+
};
|
|
4823
|
+
return result;
|
|
4824
|
+
}
|
|
4825
|
+
|
|
4712
4826
|
// ../mcp-oauth/dist/client/default-oauth-client-provider.js
|
|
4713
4827
|
import { isIP } from "node:net";
|
|
4714
4828
|
|
|
@@ -5023,8 +5137,11 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5023
5137
|
const configuredClient = normalizeConfiguredClient(options.client);
|
|
5024
5138
|
const requestedTokenMethod = normalizeOAuthTokenEndpointAuthMethod(options.client.tokenEndpointAuthMethod);
|
|
5025
5139
|
const configuredTokenMethod = requestedTokenMethod ?? configuredClient?.tokenEndpointAuthMethod;
|
|
5026
|
-
|
|
5027
|
-
|
|
5140
|
+
if (options.resourceIdentity !== void 0 && options.sessionStore !== void 0)
|
|
5141
|
+
throw new Error("OAuth resourceIdentity requires native-owned persistence; custom stores own their resource trust policy");
|
|
5142
|
+
const resourceStores = options.resourceIdentity === void 0 ? void 0 : createResourceBoundOAuthStores(options.authStore ?? {}, options.persistenceNamespace, options.resourceIdentity);
|
|
5143
|
+
const sessionStore = resourceStores?.sessionStore ?? options.sessionStore ?? createAuthStoreSessionStore(options.authStore, options.persistenceNamespace);
|
|
5144
|
+
const clientStore = resourceStores?.clientStore ?? (options.authStore === void 0 ? null : createAuthStoreClientStore(options.authStore, options.persistenceNamespace));
|
|
5028
5145
|
const now = options.now ?? Date.now;
|
|
5029
5146
|
const registeredClients = /* @__PURE__ */ new Map();
|
|
5030
5147
|
if (options.initialGrant !== void 0) {
|
|
@@ -5117,6 +5234,11 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5117
5234
|
const canonicalResource = canonicalizeResourceIndicator(resource);
|
|
5118
5235
|
return withOAuthSessionTransaction(sessionStore, canonicalResource, async () => {
|
|
5119
5236
|
let session = await loadSession(canonicalResource);
|
|
5237
|
+
if (resourceStores !== void 0) {
|
|
5238
|
+
registeredClients.clear();
|
|
5239
|
+
if (!resourceStores.initialGrantAllowed)
|
|
5240
|
+
initialGrantConsumed = true;
|
|
5241
|
+
}
|
|
5120
5242
|
if (session !== null)
|
|
5121
5243
|
assertRegistrationIssuer(session.client, session.authorizationServer);
|
|
5122
5244
|
if (configuredClient !== null && discovery !== void 0)
|
|
@@ -6666,7 +6788,8 @@ var McpClient = class {
|
|
|
6666
6788
|
async (params, context) => this.options.onElicitationRequest(params, context)
|
|
6667
6789
|
);
|
|
6668
6790
|
}
|
|
6669
|
-
messageLayer.sendNotification("notifications/initialized");
|
|
6791
|
+
if (transport.completeInitialization === void 0) messageLayer.sendNotification("notifications/initialized");
|
|
6792
|
+
else await transport.completeInitialization({ signal: options.signal, timeoutMs: this.options.requestTimeoutMs ?? 3e4 });
|
|
6670
6793
|
this.currentState = "ready";
|
|
6671
6794
|
return initializeResult;
|
|
6672
6795
|
} catch (error) {
|
|
@@ -7166,14 +7289,16 @@ async function createTestPair(server, createClient) {
|
|
|
7166
7289
|
return { client, cleanup };
|
|
7167
7290
|
}
|
|
7168
7291
|
var HttpTransportError = class extends Error {
|
|
7169
|
-
constructor(message, status, method) {
|
|
7292
|
+
constructor(message, status, method, rpcMethod) {
|
|
7170
7293
|
super(message);
|
|
7171
7294
|
this.status = status;
|
|
7172
7295
|
this.method = method;
|
|
7296
|
+
this.rpcMethod = rpcMethod;
|
|
7173
7297
|
this.name = "HttpTransportError";
|
|
7174
7298
|
}
|
|
7175
7299
|
status;
|
|
7176
7300
|
method;
|
|
7301
|
+
rpcMethod;
|
|
7177
7302
|
};
|
|
7178
7303
|
function defaultStdioSpawn(command, args, options) {
|
|
7179
7304
|
return spawn2(command, args, options);
|
|
@@ -7348,6 +7473,21 @@ var HttpTransport = class {
|
|
|
7348
7473
|
this.dispose(error instanceof Error ? error : new Error(String(error)));
|
|
7349
7474
|
});
|
|
7350
7475
|
}
|
|
7476
|
+
async completeInitialization(options) {
|
|
7477
|
+
const deadline = options.timeoutMs > 0 ? AbortSignal.timeout(Math.ceil(options.timeoutMs)) : void 0;
|
|
7478
|
+
const signals = [options.signal, deadline].filter((signal2) => signal2 !== void 0);
|
|
7479
|
+
const signal = signals.length === 0 ? new AbortController().signal : AbortSignal.any(signals);
|
|
7480
|
+
signal.throwIfAborted();
|
|
7481
|
+
try {
|
|
7482
|
+
await this.sendPost(serializeJsonRpcMessage({ jsonrpc: "2.0", method: "notifications/initialized" }), signal);
|
|
7483
|
+
signal.throwIfAborted();
|
|
7484
|
+
if (this.disposed) throw (await this.closed).reason;
|
|
7485
|
+
} catch (error) {
|
|
7486
|
+
if (error instanceof HttpTransportError)
|
|
7487
|
+
throw new HttpTransportError(error.message, error.status, error.method, "notifications/initialized");
|
|
7488
|
+
throw error;
|
|
7489
|
+
}
|
|
7490
|
+
}
|
|
7351
7491
|
filterTools(tools, reset = true) {
|
|
7352
7492
|
if (reset) this.toolParameterHeaders.clear();
|
|
7353
7493
|
const accepted = [];
|
|
@@ -7459,7 +7599,8 @@ var HttpTransport = class {
|
|
|
7459
7599
|
}
|
|
7460
7600
|
}
|
|
7461
7601
|
}
|
|
7462
|
-
async sendPost(line) {
|
|
7602
|
+
async sendPost(line, signal) {
|
|
7603
|
+
signal?.throwIfAborted();
|
|
7463
7604
|
const parsed = parseJsonRpcMessage(line);
|
|
7464
7605
|
const message = parsed.type === "request" || parsed.type === "notification" ? parsed.message : void 0;
|
|
7465
7606
|
const metadata = isObjectRecord5(message?.params) ? message.params._meta : void 0;
|
|
@@ -7473,7 +7614,10 @@ var HttpTransport = class {
|
|
|
7473
7614
|
this.modernRequests.get(requestId)?.abort();
|
|
7474
7615
|
return;
|
|
7475
7616
|
}
|
|
7476
|
-
const controller = modern && parsed.type === "request" ? new AbortController() : void 0;
|
|
7617
|
+
const controller = modern && parsed.type === "request" || signal !== void 0 ? new AbortController() : void 0;
|
|
7618
|
+
const aborted = () => controller?.abort(signal?.reason);
|
|
7619
|
+
signal?.addEventListener("abort", aborted, { once: true });
|
|
7620
|
+
if (signal?.aborted) aborted();
|
|
7477
7621
|
const id = parsed.type === "request" ? parsed.message.id : void 0;
|
|
7478
7622
|
if (controller !== void 0 && id !== void 0) this.modernRequests.set(id, controller);
|
|
7479
7623
|
try {
|
|
@@ -7482,7 +7626,7 @@ var HttpTransport = class {
|
|
|
7482
7626
|
const response = await this.fetchWithOAuthRetry({
|
|
7483
7627
|
url: postUrl,
|
|
7484
7628
|
method: "POST",
|
|
7485
|
-
createHeaders: (
|
|
7629
|
+
createHeaders: (signal2) => this.createPostHeaders(message, modern, signal2),
|
|
7486
7630
|
body: line,
|
|
7487
7631
|
controller
|
|
7488
7632
|
});
|
|
@@ -7518,6 +7662,7 @@ var HttpTransport = class {
|
|
|
7518
7662
|
} catch (error) {
|
|
7519
7663
|
if (!controller?.signal.aborted) throw error;
|
|
7520
7664
|
} finally {
|
|
7665
|
+
signal?.removeEventListener("abort", aborted);
|
|
7521
7666
|
if (id !== void 0 && this.modernRequests.get(id) === controller)
|
|
7522
7667
|
this.modernRequests.delete(id);
|
|
7523
7668
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.318",
|
|
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.318",
|
|
162
162
|
"toolcraft-design": "*",
|
|
163
163
|
"@poe-code/frontmatter": "*",
|
|
164
164
|
"@poe-code/agent-mcp-config": "*",
|