@jini-ai/mcp 0.3.4 → 0.4.1
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/CHANGELOG.md +156 -0
- package/README.md +97 -22
- package/dist/agent-install/install.d.ts +20 -4
- package/dist/agent-install/install.js +11 -11
- package/dist/bin/serve.d.ts +25 -13
- package/dist/bin/serve.js +42 -21
- package/dist/client/client.d.ts +28 -8
- package/dist/client/client.js +25 -21
- package/dist/core/config.d.ts +57 -18
- package/dist/core/config.js +76 -48
- package/dist/core/index.d.ts +1 -2
- package/dist/core/index.js +1 -2
- package/dist/core/install-info.d.ts +4 -1
- package/dist/core/install-info.js +5 -5
- package/dist/core/tokens.d.ts +39 -13
- package/dist/core/tokens.js +54 -24
- package/dist/federation/adapter.http.d.ts +67 -0
- package/dist/federation/adapter.http.js +414 -0
- package/dist/federation/approvals/call-confirmation.d.ts +23 -0
- package/dist/federation/approvals/call-confirmation.js +124 -0
- package/dist/federation/approvals/index.d.ts +4 -0
- package/dist/federation/approvals/index.js +4 -0
- package/dist/federation/approvals/ports.d.ts +107 -0
- package/dist/federation/approvals/ports.js +2 -0
- package/dist/federation/approvals/revocation.d.ts +61 -0
- package/dist/federation/approvals/revocation.js +52 -0
- package/dist/federation/bootstrap.d.ts +100 -0
- package/dist/federation/bootstrap.js +182 -0
- package/dist/federation/config.d.ts +68 -0
- package/dist/federation/config.js +40 -0
- package/dist/federation/index.d.ts +14 -0
- package/dist/federation/index.js +14 -0
- package/dist/federation/mcp-protocol.d.ts +115 -0
- package/dist/federation/mcp-protocol.js +151 -0
- package/dist/federation/messages.d.ts +54 -0
- package/dist/federation/messages.js +35 -0
- package/dist/federation/ports.d.ts +287 -0
- package/dist/federation/ports.js +5 -0
- package/dist/federation/presets.d.ts +66 -0
- package/dist/federation/presets.js +30 -0
- package/dist/federation/refusal-notice.d.ts +198 -0
- package/dist/federation/refusal-notice.js +193 -0
- package/dist/federation/registrations.d.ts +171 -0
- package/dist/federation/registrations.js +181 -0
- package/dist/federation/reload.d.ts +112 -0
- package/dist/federation/reload.js +110 -0
- package/dist/federation/stdio/adapter.stdio.d.ts +157 -0
- package/dist/federation/stdio/adapter.stdio.js +526 -0
- package/dist/federation/stdio/default-connect.d.ts +29 -0
- package/dist/federation/stdio/default-connect.js +92 -0
- package/dist/federation/stdio/index.d.ts +4 -0
- package/dist/federation/stdio/index.js +4 -0
- package/dist/federation/stdio/stdio-launch-resolver.d.ts +139 -0
- package/dist/federation/stdio/stdio-launch-resolver.js +227 -0
- package/dist/federation/testing/adapter.memory.d.ts +177 -0
- package/dist/federation/testing/adapter.memory.js +189 -0
- package/dist/federation/testing/index.d.ts +3 -0
- package/dist/federation/testing/index.js +3 -0
- package/dist/federation/testing/tool-approvals.memory.d.ts +27 -0
- package/dist/federation/testing/tool-approvals.memory.js +38 -0
- package/dist/federation/tool-approvals.d.ts +97 -0
- package/dist/federation/tool-approvals.js +42 -0
- package/dist/federation/trust.d.ts +471 -0
- package/dist/federation/trust.js +707 -0
- package/dist/index.d.ts +175 -2
- package/dist/index.js +172 -2
- package/dist/server/daemon-client.d.ts +16 -4
- package/dist/server/daemon-client.js +41 -19
- package/dist/server/ports.d.ts +9 -0
- package/dist/server/ports.js +2 -0
- package/dist/server/resource-protocol.d.ts +14 -4
- package/dist/server/resource-protocol.js +6 -6
- package/dist/server/resources/active-resource.js +3 -3
- package/dist/server/sdk-transport.d.ts +11 -0
- package/dist/server/sdk-transport.js +31 -0
- package/dist/server/tool-protocol.d.ts +31 -9
- package/dist/server/tool-protocol.js +20 -18
- package/dist/server/tool-server.d.ts +60 -43
- package/dist/server/tool-server.js +51 -27
- package/dist/server/tools/component-catalog-tools.js +7 -7
- package/dist/server/tools/delegated-tool.d.ts +7 -4
- package/dist/server/tools/delegated-tool.js +18 -16
- package/dist/server/tools/run-tools.js +15 -15
- package/dist/server/tools/tool-catalog-tools.js +7 -7
- package/dist/tools/ask-choice/index.d.ts +123 -0
- package/dist/tools/ask-choice/index.js +98 -0
- package/dist/tools/ask-choice/pending.d.ts +11 -0
- package/dist/tools/ask-choice/pending.js +34 -0
- package/dist/tools/ask-choice/question.d.ts +16 -0
- package/dist/tools/ask-choice/question.js +60 -0
- package/dist/tools/ask-choice/schema.d.ts +91 -0
- package/dist/tools/ask-choice/schema.js +27 -0
- package/dist/tools/ask-choice/types.d.ts +106 -0
- package/dist/tools/ask-choice/types.js +2 -0
- package/package.json +43 -5
- package/dist/core/oauth.d.ts +0 -274
- package/dist/core/oauth.js +0 -667
- package/dist/core/secure-write.d.ts +0 -17
- package/dist/core/secure-write.js +0 -72
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/** Model input schema. Callback correlation params are carried by the rendered form only. */
|
|
2
|
+
export declare const ASK_CHOICE_INPUT_SCHEMA: {
|
|
3
|
+
readonly type: "object";
|
|
4
|
+
readonly additionalProperties: false;
|
|
5
|
+
readonly required: readonly ["title"];
|
|
6
|
+
readonly anyOf: readonly [{
|
|
7
|
+
readonly required: readonly ["singleSelect"];
|
|
8
|
+
}, {
|
|
9
|
+
readonly required: readonly ["multiSelect"];
|
|
10
|
+
}];
|
|
11
|
+
readonly properties: {
|
|
12
|
+
readonly title: {
|
|
13
|
+
readonly type: "string";
|
|
14
|
+
};
|
|
15
|
+
readonly description: {
|
|
16
|
+
readonly type: "string";
|
|
17
|
+
};
|
|
18
|
+
readonly submitLabel: {
|
|
19
|
+
readonly type: "string";
|
|
20
|
+
};
|
|
21
|
+
readonly singleSelect: {
|
|
22
|
+
readonly type: "object";
|
|
23
|
+
readonly additionalProperties: false;
|
|
24
|
+
readonly required: readonly ["label", "options"];
|
|
25
|
+
readonly properties: {
|
|
26
|
+
readonly label: {
|
|
27
|
+
readonly type: "string";
|
|
28
|
+
};
|
|
29
|
+
readonly options: {
|
|
30
|
+
readonly type: "array";
|
|
31
|
+
readonly minItems: 1;
|
|
32
|
+
readonly items: {
|
|
33
|
+
readonly type: "object";
|
|
34
|
+
readonly additionalProperties: false;
|
|
35
|
+
readonly required: readonly ["value", "label"];
|
|
36
|
+
readonly properties: {
|
|
37
|
+
readonly value: {
|
|
38
|
+
readonly type: "string";
|
|
39
|
+
};
|
|
40
|
+
readonly label: {
|
|
41
|
+
readonly type: "string";
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
readonly multiSelect: {
|
|
49
|
+
readonly properties: {
|
|
50
|
+
readonly hint: {
|
|
51
|
+
readonly type: "string";
|
|
52
|
+
};
|
|
53
|
+
readonly label: {
|
|
54
|
+
readonly type: "string";
|
|
55
|
+
};
|
|
56
|
+
readonly options: {
|
|
57
|
+
readonly type: "array";
|
|
58
|
+
readonly minItems: 1;
|
|
59
|
+
readonly items: {
|
|
60
|
+
readonly type: "object";
|
|
61
|
+
readonly additionalProperties: false;
|
|
62
|
+
readonly required: readonly ["value", "label"];
|
|
63
|
+
readonly properties: {
|
|
64
|
+
readonly value: {
|
|
65
|
+
readonly type: "string";
|
|
66
|
+
};
|
|
67
|
+
readonly label: {
|
|
68
|
+
readonly type: "string";
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
readonly type: "object";
|
|
75
|
+
readonly additionalProperties: false;
|
|
76
|
+
readonly required: readonly ["label", "options"];
|
|
77
|
+
};
|
|
78
|
+
readonly choice: {
|
|
79
|
+
readonly type: "string";
|
|
80
|
+
readonly description: "Set by the rendered form; never by the model.";
|
|
81
|
+
};
|
|
82
|
+
readonly selections: {
|
|
83
|
+
readonly type: "array";
|
|
84
|
+
readonly items: {
|
|
85
|
+
readonly type: "string";
|
|
86
|
+
};
|
|
87
|
+
readonly description: "Set by the rendered form; never by the model.";
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const optionSchema = {
|
|
2
|
+
type: 'object', additionalProperties: false, required: ['value', 'label'],
|
|
3
|
+
properties: { value: { type: 'string' }, label: { type: 'string' } },
|
|
4
|
+
};
|
|
5
|
+
const selectSchema = {
|
|
6
|
+
type: 'object', additionalProperties: false, required: ['label', 'options'],
|
|
7
|
+
properties: {
|
|
8
|
+
label: { type: 'string' },
|
|
9
|
+
options: { type: 'array', minItems: 1, items: optionSchema },
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
/** Model input schema. Callback correlation params are carried by the rendered form only. */
|
|
13
|
+
export const ASK_CHOICE_INPUT_SCHEMA = {
|
|
14
|
+
type: 'object', additionalProperties: false, required: ['title'],
|
|
15
|
+
anyOf: [{ required: ['singleSelect'] }, { required: ['multiSelect'] }],
|
|
16
|
+
properties: {
|
|
17
|
+
title: { type: 'string' }, description: { type: 'string' }, submitLabel: { type: 'string' },
|
|
18
|
+
singleSelect: selectSchema,
|
|
19
|
+
multiSelect: {
|
|
20
|
+
...selectSchema,
|
|
21
|
+
properties: { ...selectSchema.properties, hint: { type: 'string' } },
|
|
22
|
+
},
|
|
23
|
+
choice: { type: 'string', description: 'Set by the rendered form; never by the model.' },
|
|
24
|
+
selections: { type: 'array', items: { type: 'string' }, description: 'Set by the rendered form; never by the model.' },
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/** Product-neutral question content; rendering and copy belong to the host. */
|
|
2
|
+
export interface AskChoiceSelect {
|
|
3
|
+
readonly label: string;
|
|
4
|
+
readonly hint?: string;
|
|
5
|
+
readonly options: ReadonlyArray<{
|
|
6
|
+
value: string;
|
|
7
|
+
label: string;
|
|
8
|
+
}>;
|
|
9
|
+
}
|
|
10
|
+
export interface AskChoiceQuestion {
|
|
11
|
+
readonly title: string;
|
|
12
|
+
readonly description?: string;
|
|
13
|
+
readonly submitLabel?: string;
|
|
14
|
+
readonly singleSelect?: AskChoiceSelect;
|
|
15
|
+
readonly multiSelect?: AskChoiceSelect;
|
|
16
|
+
}
|
|
17
|
+
export interface AskChoiceMessages {
|
|
18
|
+
readonly pending: string;
|
|
19
|
+
readonly forgedAnswer: string;
|
|
20
|
+
readonly submitted: string;
|
|
21
|
+
readonly typed: string;
|
|
22
|
+
readonly noAnswer: string;
|
|
23
|
+
readonly cancelled: string;
|
|
24
|
+
readonly expired: string;
|
|
25
|
+
readonly abandoned: string;
|
|
26
|
+
}
|
|
27
|
+
export type AskChoiceMessage = {
|
|
28
|
+
status: 'received';
|
|
29
|
+
params: Record<string, unknown>;
|
|
30
|
+
} | {
|
|
31
|
+
status: 'expired' | 'abandoned';
|
|
32
|
+
};
|
|
33
|
+
export interface AskChoiceEmission {
|
|
34
|
+
channel: 'mcp-ui';
|
|
35
|
+
payload: {
|
|
36
|
+
resource: unknown;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export type AskChoiceEmitter = (args: {
|
|
40
|
+
emission: AskChoiceEmission;
|
|
41
|
+
}) => Promise<void>;
|
|
42
|
+
/** The host owns correlation, principal binding, buffering, and expiry of live exchanges. */
|
|
43
|
+
export interface AskChoiceExchange {
|
|
44
|
+
readonly id: string;
|
|
45
|
+
send(args: {
|
|
46
|
+
emission: AskChoiceEmission;
|
|
47
|
+
}): Promise<void>;
|
|
48
|
+
receive(args: Record<string, never>): Promise<AskChoiceMessage>;
|
|
49
|
+
close(args: Record<string, never>): void;
|
|
50
|
+
}
|
|
51
|
+
export interface AskChoiceExchangeStore {
|
|
52
|
+
open(args: {
|
|
53
|
+
toolId: string;
|
|
54
|
+
principalId: string;
|
|
55
|
+
emit: AskChoiceEmitter;
|
|
56
|
+
}): AskChoiceExchange;
|
|
57
|
+
}
|
|
58
|
+
/** Redeem consumes before checking binding, time, and offered options; rejection is fail-closed. */
|
|
59
|
+
export interface AskChoicePendingStore {
|
|
60
|
+
mint(args: {
|
|
61
|
+
principalId: string;
|
|
62
|
+
question: AskChoiceQuestion;
|
|
63
|
+
}): string;
|
|
64
|
+
redeem(args: {
|
|
65
|
+
ticket: string | undefined;
|
|
66
|
+
principalId: string;
|
|
67
|
+
params: Record<string, unknown>;
|
|
68
|
+
}): boolean;
|
|
69
|
+
}
|
|
70
|
+
export interface AskChoiceCall {
|
|
71
|
+
principalId: string;
|
|
72
|
+
input: unknown;
|
|
73
|
+
signal: AbortSignal;
|
|
74
|
+
emitSurface?: AskChoiceEmitter;
|
|
75
|
+
}
|
|
76
|
+
/** Authorization is invoked before either displaying a question or redeeming its answer. */
|
|
77
|
+
export interface AskChoicePolicy {
|
|
78
|
+
authorize(args: {
|
|
79
|
+
ctx: AskChoiceCall;
|
|
80
|
+
toolId: string;
|
|
81
|
+
}): void | Promise<void>;
|
|
82
|
+
inputError(args: {
|
|
83
|
+
message: string;
|
|
84
|
+
}): Error;
|
|
85
|
+
shapeError(args: {
|
|
86
|
+
message: string;
|
|
87
|
+
toolId: string;
|
|
88
|
+
inputSchema: Readonly<Record<string, unknown>>;
|
|
89
|
+
}): Error;
|
|
90
|
+
}
|
|
91
|
+
export interface AskChoiceForm {
|
|
92
|
+
toolName: string;
|
|
93
|
+
principalId: string;
|
|
94
|
+
question: AskChoiceQuestion;
|
|
95
|
+
baseParams: Record<string, unknown>;
|
|
96
|
+
cancelParams?: Record<string, unknown>;
|
|
97
|
+
}
|
|
98
|
+
/** The host supplies resource identity, labels, field presentation, app metadata, and result encoding. */
|
|
99
|
+
export interface AskChoicePresentation {
|
|
100
|
+
render(args: Omit<AskChoiceForm, 'cancelParams'>, options?: Pick<AskChoiceForm, 'cancelParams'>): unknown;
|
|
101
|
+
buildResult(args: {
|
|
102
|
+
modelText: string;
|
|
103
|
+
ui: unknown;
|
|
104
|
+
}): Record<string, unknown>;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=types.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Model Context Protocol adapters: server config schema and IO, an MCP client, and agent-side MCP server installation.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -11,7 +11,16 @@
|
|
|
11
11
|
"jini": {
|
|
12
12
|
"domain": "integration",
|
|
13
13
|
"kind": "protocol-adapter",
|
|
14
|
-
"runtime": "node"
|
|
14
|
+
"runtime": "node",
|
|
15
|
+
"entries": {
|
|
16
|
+
".": "node",
|
|
17
|
+
"./bin": "node",
|
|
18
|
+
"./tools/ask-choice": "universal",
|
|
19
|
+
"./federation": "node",
|
|
20
|
+
"./federation/approvals": "node",
|
|
21
|
+
"./federation/stdio": "node",
|
|
22
|
+
"./federation/testing": "universal"
|
|
23
|
+
}
|
|
15
24
|
},
|
|
16
25
|
"type": "module",
|
|
17
26
|
"sideEffects": false,
|
|
@@ -23,7 +32,34 @@
|
|
|
23
32
|
"import": "./dist/index.js",
|
|
24
33
|
"default": "./dist/index.js"
|
|
25
34
|
},
|
|
35
|
+
"./tools/ask-choice": {
|
|
36
|
+
"types": "./dist/tools/ask-choice/index.d.ts",
|
|
37
|
+
"import": "./dist/tools/ask-choice/index.js",
|
|
38
|
+
"default": "./dist/tools/ask-choice/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./federation": {
|
|
41
|
+
"types": "./dist/federation/index.d.ts",
|
|
42
|
+
"import": "./dist/federation/index.js",
|
|
43
|
+
"default": "./dist/federation/index.js"
|
|
44
|
+
},
|
|
45
|
+
"./federation/approvals": {
|
|
46
|
+
"types": "./dist/federation/approvals/index.d.ts",
|
|
47
|
+
"import": "./dist/federation/approvals/index.js",
|
|
48
|
+
"default": "./dist/federation/approvals/index.js"
|
|
49
|
+
},
|
|
50
|
+
"./federation/stdio": {
|
|
51
|
+
"types": "./dist/federation/stdio/index.d.ts",
|
|
52
|
+
"import": "./dist/federation/stdio/index.js",
|
|
53
|
+
"default": "./dist/federation/stdio/index.js"
|
|
54
|
+
},
|
|
55
|
+
"./federation/testing": {
|
|
56
|
+
"types": "./dist/federation/testing/index.d.ts",
|
|
57
|
+
"import": "./dist/federation/testing/index.js",
|
|
58
|
+
"default": "./dist/federation/testing/index.js"
|
|
59
|
+
},
|
|
26
60
|
"./bin": {
|
|
61
|
+
"types": "./dist/bin/serve.d.ts",
|
|
62
|
+
"import": "./dist/bin/serve.js",
|
|
27
63
|
"default": "./dist/bin/serve.js"
|
|
28
64
|
}
|
|
29
65
|
},
|
|
@@ -32,6 +68,9 @@
|
|
|
32
68
|
},
|
|
33
69
|
"files": [
|
|
34
70
|
"dist",
|
|
71
|
+
"README.md",
|
|
72
|
+
"CHANGELOG.md",
|
|
73
|
+
"LICENSE",
|
|
35
74
|
"!dist/**/*.test.js",
|
|
36
75
|
"!dist/**/*.test.d.ts",
|
|
37
76
|
"!dist/**/*.test.js.map",
|
|
@@ -46,9 +85,8 @@
|
|
|
46
85
|
"dependencies": {
|
|
47
86
|
"@cfworker/json-schema": "^4.1.1",
|
|
48
87
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
49
|
-
"
|
|
50
|
-
"@jini-ai/
|
|
51
|
-
"@jini-ai/cli": "0.3.1"
|
|
88
|
+
"@jini-ai/platform": "^0.5.1",
|
|
89
|
+
"@jini-ai/core": "^0.4.1"
|
|
52
90
|
},
|
|
53
91
|
"scripts": {
|
|
54
92
|
"build": "tsc -p tsconfig.json && chmod +x dist/bin/serve.js",
|
package/dist/core/oauth.d.ts
DELETED
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module @jini-ai/mcp/core/oauth
|
|
3
|
-
* Daemon-side OAuth 2.1 / PKCE client for remote (HTTP / SSE) MCP servers:
|
|
4
|
-
* auth-server discovery, dynamic client registration, the authorize/token/refresh
|
|
5
|
-
* exchanges, and the in-memory `PendingAuthCache`. Part of the MCP `core` kernel;
|
|
6
|
-
* depends on no sibling subdirectory.
|
|
7
|
-
*/
|
|
8
|
-
import { createValidatingLookup } from '@jini-ai/platform';
|
|
9
|
-
/** RFC 9728 `oauth-protected-resource` document fields we use. */
|
|
10
|
-
export interface ProtectedResourceMetadata {
|
|
11
|
-
resource?: string;
|
|
12
|
-
authorization_servers?: string[];
|
|
13
|
-
scopes_supported?: string[];
|
|
14
|
-
}
|
|
15
|
-
/** RFC 8414 / OIDC discovery document fields we use. */
|
|
16
|
-
export interface AuthorizationServerMetadata {
|
|
17
|
-
issuer: string;
|
|
18
|
-
authorization_endpoint: string;
|
|
19
|
-
token_endpoint: string;
|
|
20
|
-
registration_endpoint?: string;
|
|
21
|
-
scopes_supported?: string[];
|
|
22
|
-
response_types_supported?: string[];
|
|
23
|
-
grant_types_supported?: string[];
|
|
24
|
-
code_challenge_methods_supported?: string[];
|
|
25
|
-
token_endpoint_auth_methods_supported?: string[];
|
|
26
|
-
}
|
|
27
|
-
/** Cached client registration for a given auth server + redirect URI. */
|
|
28
|
-
export interface RegisteredClient {
|
|
29
|
-
authServerIssuer: string;
|
|
30
|
-
redirectUri: string;
|
|
31
|
-
clientId: string;
|
|
32
|
-
clientSecret?: string;
|
|
33
|
-
registeredAt: number;
|
|
34
|
-
}
|
|
35
|
-
/** RFC 6749 §5.1 token endpoint response (subset). */
|
|
36
|
-
export interface OAuthTokenResponse {
|
|
37
|
-
access_token: string;
|
|
38
|
-
token_type?: string;
|
|
39
|
-
expires_in?: number;
|
|
40
|
-
refresh_token?: string;
|
|
41
|
-
scope?: string;
|
|
42
|
-
}
|
|
43
|
-
/** In-flight authorization request. Stashed in memory while the user
|
|
44
|
-
* approves in their browser. */
|
|
45
|
-
export interface PendingAuthState {
|
|
46
|
-
serverId: string;
|
|
47
|
-
authServerIssuer: string;
|
|
48
|
-
tokenEndpoint: string;
|
|
49
|
-
clientId: string;
|
|
50
|
-
clientSecret?: string;
|
|
51
|
-
redirectUri: string;
|
|
52
|
-
codeVerifier: string;
|
|
53
|
-
scope?: string;
|
|
54
|
-
resourceUrl?: string;
|
|
55
|
-
createdAt: number;
|
|
56
|
-
}
|
|
57
|
-
type DnsLookupCb = Parameters<typeof createValidatingLookup>[0];
|
|
58
|
-
/**
|
|
59
|
-
* Read a response body up to `maxBytes`, aborting `controller` (when given)
|
|
60
|
-
* and throwing the moment the cap is exceeded rather than buffering an
|
|
61
|
-
* unbounded body first. Falls back to a single `res.text()` call (tolerant
|
|
62
|
-
* of a throw, matching the old `safeText`'s forgiving error-path behavior)
|
|
63
|
-
* for response-like test doubles that don't expose a real `ReadableStream`
|
|
64
|
-
* body.
|
|
65
|
-
*
|
|
66
|
-
* `controller` is real, intentional API surface — a caller that already has
|
|
67
|
-
* an in-flight fetch's `AbortController` can pass it so an oversized
|
|
68
|
-
* response stops being pulled from upstream the moment the cap is exceeded,
|
|
69
|
-
* not merely rejected after the fact — but no real call site in this file
|
|
70
|
-
* currently has one in scope to pass (each of the four call sites reads a
|
|
71
|
-
* `Response` it received after its own `safeOAuthFetch` call already
|
|
72
|
-
* returned, by which point that call's own `AbortController` is out of
|
|
73
|
-
* scope). Exported (not just internal) so this real, functioning behavior
|
|
74
|
-
* is directly unit-testable without inventing an unused call site just to
|
|
75
|
-
* reach it — matching this repo's established "extract into a directly-
|
|
76
|
-
* testable pure function" convention.
|
|
77
|
-
*/
|
|
78
|
-
export declare function readCappedText(res: Response, maxBytes: number, controller?: AbortController): Promise<string>;
|
|
79
|
-
/**
|
|
80
|
-
* Generate a cryptographically random PKCE code verifier (RFC 7636 §4.1).
|
|
81
|
-
* Produces a 64-byte base64url-encoded string, within the 43–128 character range.
|
|
82
|
-
* @returns A fresh code verifier string for use in a single authorization request.
|
|
83
|
-
*/
|
|
84
|
-
export declare function generateCodeVerifier(): string;
|
|
85
|
-
/**
|
|
86
|
-
* Derive the S256 PKCE code challenge from a code verifier (RFC 7636 §4.2).
|
|
87
|
-
* Computes `BASE64URL(SHA256(ASCII(verifier)))`.
|
|
88
|
-
* @param verifier The code verifier string produced by `generateCodeVerifier`.
|
|
89
|
-
* @returns The base64url-encoded SHA-256 hash to pass as `code_challenge`.
|
|
90
|
-
*/
|
|
91
|
-
export declare function deriveCodeChallenge(verifier: string): string;
|
|
92
|
-
/**
|
|
93
|
-
* Generate a cryptographically random OAuth `state` parameter.
|
|
94
|
-
* Used as the CSRF token for the authorization request; must be unique per flow.
|
|
95
|
-
* @returns A base64url-encoded 32-byte random string.
|
|
96
|
-
*/
|
|
97
|
-
export declare function generateState(): string;
|
|
98
|
-
/**
|
|
99
|
-
* Try to fetch the protected-resource metadata for a given MCP URL.
|
|
100
|
-
*
|
|
101
|
-
* Per RFC 9728, the well-known is at the resource origin's
|
|
102
|
-
* `/.well-known/oauth-protected-resource[<path>]`. We try both the
|
|
103
|
-
* path-suffixed form and the bare `/.well-known/...` so servers that
|
|
104
|
-
* only publish at the root still work.
|
|
105
|
-
*
|
|
106
|
-
* @param lookupImpl Injectable `dns.lookup` for the connection-time SSRF
|
|
107
|
-
* guard (tests only — see `safeOAuthFetch`).
|
|
108
|
-
*/
|
|
109
|
-
export declare function discoverProtectedResource(resourceUrl: string, fetchImpl?: typeof fetch, lookupImpl?: DnsLookupCb): Promise<ProtectedResourceMetadata | null>;
|
|
110
|
-
/**
|
|
111
|
-
* Fetch the authorization-server metadata for an issuer URL. Tries both
|
|
112
|
-
* the OAuth (RFC 8414) and OIDC layouts (`/.well-known/oauth-authorization-server`
|
|
113
|
-
* and `/.well-known/openid-configuration`); some providers only publish one.
|
|
114
|
-
*
|
|
115
|
-
* Per RFC 8414 §3.3, a discovery document's `issuer` — when present — MUST
|
|
116
|
-
* match the issuer used to construct the request; a mismatched document is
|
|
117
|
-
* rejected rather than trusted (SEC-RB-001 / CR-005). Every endpoint the
|
|
118
|
-
* document names (`authorization_endpoint`, `token_endpoint`,
|
|
119
|
-
* `registration_endpoint`) is additionally required to be an absolute
|
|
120
|
-
* `https:` URL sharing the queried issuer's own origin, so a compromised or
|
|
121
|
-
* hostile document can't redirect DCR or token exchange at an unrelated
|
|
122
|
-
* origin.
|
|
123
|
-
*
|
|
124
|
-
* @param lookupImpl Injectable `dns.lookup` for the connection-time SSRF
|
|
125
|
-
* guard (tests only — see `safeOAuthFetch`).
|
|
126
|
-
*/
|
|
127
|
-
export declare function discoverAuthServer(issuer: string, fetchImpl?: typeof fetch, lookupImpl?: DnsLookupCb): Promise<AuthorizationServerMetadata | null>;
|
|
128
|
-
/**
|
|
129
|
-
* POST to the auth server's `registration_endpoint` per RFC 7591. Returns
|
|
130
|
-
* a freshly issued client_id (and optional client_secret). Caller is
|
|
131
|
-
* responsible for caching the result.
|
|
132
|
-
* @param lookupImpl Injectable `dns.lookup` for the connection-time SSRF
|
|
133
|
-
* guard (tests only — see `safeOAuthFetch`).
|
|
134
|
-
*/
|
|
135
|
-
export declare function registerClient(registrationEndpoint: string, redirectUri: string, fetchImpl?: typeof fetch, lookupImpl?: DnsLookupCb): Promise<{
|
|
136
|
-
clientId: string;
|
|
137
|
-
clientSecret?: string;
|
|
138
|
-
}>;
|
|
139
|
-
/**
|
|
140
|
-
* Cached version of `registerClient`. Looks up `(authServerIssuer, redirectUri)`
|
|
141
|
-
* in the cache file and re-uses the existing client; falls back to a fresh
|
|
142
|
-
* DCR call when nothing is cached.
|
|
143
|
-
* @param lookupImpl Injectable `dns.lookup` for the connection-time SSRF
|
|
144
|
-
* guard (tests only — see `safeOAuthFetch`).
|
|
145
|
-
*/
|
|
146
|
-
export declare function getOrRegisterClient(dataDir: string, authServer: AuthorizationServerMetadata, redirectUri: string, fetchImpl?: typeof fetch, lookupImpl?: DnsLookupCb): Promise<RegisteredClient>;
|
|
147
|
-
/** Inputs required to assemble the authorization redirect URL. */
|
|
148
|
-
export interface AuthorizeUrlInput {
|
|
149
|
-
authServer: AuthorizationServerMetadata;
|
|
150
|
-
clientId: string;
|
|
151
|
-
redirectUri: string;
|
|
152
|
-
state: string;
|
|
153
|
-
codeChallenge: string;
|
|
154
|
-
scope?: string;
|
|
155
|
-
/** RFC 8707 resource indicator — narrows the issued token to the target MCP server. */
|
|
156
|
-
resource?: string;
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Assemble the authorization endpoint URL the user's browser must be directed to.
|
|
160
|
-
* Sets PKCE parameters (`code_challenge`, `code_challenge_method=S256`), the `state`
|
|
161
|
-
* CSRF token, and optionally the RFC 8707 `resource` indicator.
|
|
162
|
-
* @param input All parameters needed to build the URL.
|
|
163
|
-
* @returns The fully-qualified authorization URL as a string.
|
|
164
|
-
*/
|
|
165
|
-
export declare function buildAuthorizeUrl(input: AuthorizeUrlInput): string;
|
|
166
|
-
/** Inputs for the authorization-code → token exchange (RFC 6749 §4.1.3). */
|
|
167
|
-
export interface ExchangeCodeInput {
|
|
168
|
-
tokenEndpoint: string;
|
|
169
|
-
clientId: string;
|
|
170
|
-
clientSecret?: string;
|
|
171
|
-
redirectUri: string;
|
|
172
|
-
code: string;
|
|
173
|
-
codeVerifier: string;
|
|
174
|
-
/** RFC 8707 resource indicator to include in the token request. */
|
|
175
|
-
resource?: string;
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Exchange an authorization code for access and refresh tokens (RFC 6749 §4.1.3).
|
|
179
|
-
* Includes the PKCE `code_verifier` and, when supplied, the RFC 8707 `resource`
|
|
180
|
-
* indicator. Throws when the token endpoint returns a non-2xx status.
|
|
181
|
-
* @param input The code-exchange parameters.
|
|
182
|
-
* @param fetchImpl Injectable fetch, defaults to the global `fetch`.
|
|
183
|
-
* @param lookupImpl Injectable `dns.lookup` for the connection-time SSRF
|
|
184
|
-
* guard (tests only — see `safeOAuthFetch`).
|
|
185
|
-
* @returns The token endpoint response containing at least an `access_token`.
|
|
186
|
-
*/
|
|
187
|
-
export declare function exchangeCodeForToken(input: ExchangeCodeInput, fetchImpl?: typeof fetch, lookupImpl?: DnsLookupCb): Promise<OAuthTokenResponse>;
|
|
188
|
-
/** Inputs for the refresh-token → new-access-token exchange (RFC 6749 §6). */
|
|
189
|
-
export interface RefreshTokenInput {
|
|
190
|
-
tokenEndpoint: string;
|
|
191
|
-
clientId: string;
|
|
192
|
-
clientSecret?: string;
|
|
193
|
-
refreshToken: string;
|
|
194
|
-
scope?: string;
|
|
195
|
-
/** RFC 8707 resource indicator, included when the original token was resource-scoped. */
|
|
196
|
-
resource?: string;
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Exchange a refresh token for a new access token (RFC 6749 §6).
|
|
200
|
-
* Preserves scope and resource binding from the original authorization.
|
|
201
|
-
* Throws when the token endpoint returns a non-2xx status.
|
|
202
|
-
* @param input The refresh parameters.
|
|
203
|
-
* @param fetchImpl Injectable fetch, defaults to the global `fetch`.
|
|
204
|
-
* @param lookupImpl Injectable `dns.lookup` for the connection-time SSRF
|
|
205
|
-
* guard (tests only — see `safeOAuthFetch`).
|
|
206
|
-
* @returns A fresh `OAuthTokenResponse`; the server may issue a new refresh token.
|
|
207
|
-
*/
|
|
208
|
-
export declare function refreshAccessToken(input: RefreshTokenInput, fetchImpl?: typeof fetch, lookupImpl?: DnsLookupCb): Promise<OAuthTokenResponse>;
|
|
209
|
-
/**
|
|
210
|
-
* The OAuth dance is split across two HTTP requests on our side:
|
|
211
|
-
* 1. the "start" request — we mint state + verifier
|
|
212
|
-
* 2. the "callback" request — browser returns code + state
|
|
213
|
-
* State has to survive between (1) and (2) on the daemon. We keep it in a
|
|
214
|
-
* Map with a TTL sweeper; persistence isn't needed because the user has
|
|
215
|
-
* to complete auth in the same daemon process anyway (state is single-use).
|
|
216
|
-
*/
|
|
217
|
-
export declare class PendingAuthCache {
|
|
218
|
-
private readonly ttlMs;
|
|
219
|
-
private store;
|
|
220
|
-
private timer;
|
|
221
|
-
constructor(ttlMs?: number);
|
|
222
|
-
/**
|
|
223
|
-
* Store a pending auth state keyed by the OAuth `state` parameter.
|
|
224
|
-
* Starts the TTL sweeper if it is not already running.
|
|
225
|
-
* @param state The random `state` string from the authorization request.
|
|
226
|
-
* @param value The associated pending auth metadata to stash.
|
|
227
|
-
*/
|
|
228
|
-
put(state: string, value: PendingAuthState): void;
|
|
229
|
-
/** One-shot consume — any successful callback removes the state so a
|
|
230
|
-
* replay can't reuse it. */
|
|
231
|
-
consume(state: string): PendingAuthState | null;
|
|
232
|
-
/** Return the number of pending auth states currently held in the cache. */
|
|
233
|
-
size(): number;
|
|
234
|
-
/** Stop the background sweeper. Used by tests; production lets the
|
|
235
|
-
* timer ride on the process lifetime. */
|
|
236
|
-
stop(): void;
|
|
237
|
-
private startSweeper;
|
|
238
|
-
private sweep;
|
|
239
|
-
}
|
|
240
|
-
/** Inputs for the full pre-redirect OAuth dance. */
|
|
241
|
-
export interface BeginAuthInput {
|
|
242
|
-
/** The `McpServerConfig.id` of the server being authorized. */
|
|
243
|
-
serverId: string;
|
|
244
|
-
/** The MCP server endpoint URL (used for protected-resource discovery). */
|
|
245
|
-
serverUrl: string;
|
|
246
|
-
/** The OAuth redirect URI the daemon has registered. */
|
|
247
|
-
redirectUri: string;
|
|
248
|
-
/** The resolved data directory (for caching the client registration). */
|
|
249
|
-
dataDir: string;
|
|
250
|
-
scope?: string;
|
|
251
|
-
/** Injectable fetch implementation; defaults to the global `fetch`. */
|
|
252
|
-
fetchImpl?: typeof fetch;
|
|
253
|
-
/** Injectable `dns.lookup` for the connection-time SSRF guard (tests only). */
|
|
254
|
-
lookupImpl?: DnsLookupCb;
|
|
255
|
-
}
|
|
256
|
-
/** Result of `beginAuth`: everything needed to redirect the browser and later complete the flow. */
|
|
257
|
-
export interface BeginAuthResult {
|
|
258
|
-
/** The fully-qualified authorization URL to redirect the user's browser to. */
|
|
259
|
-
authorizeUrl: string;
|
|
260
|
-
/** The random `state` string; must be passed to `PendingAuthCache.put` before redirecting. */
|
|
261
|
-
state: string;
|
|
262
|
-
/** The pending-auth metadata to store in `PendingAuthCache` keyed by `state`. */
|
|
263
|
-
pending: PendingAuthState;
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Run the entire pre-redirect half of the OAuth dance:
|
|
267
|
-
* discovery → DCR (cached) → PKCE → authorize URL.
|
|
268
|
-
*
|
|
269
|
-
* Returns everything the caller needs to (a) push the user's browser at the
|
|
270
|
-
* correct authorize URL, and (b) finish the flow when the callback hits.
|
|
271
|
-
*/
|
|
272
|
-
export declare function beginAuth(input: BeginAuthInput): Promise<BeginAuthResult>;
|
|
273
|
-
export {};
|
|
274
|
-
//# sourceMappingURL=oauth.d.ts.map
|