@qduc/term2 0.24.1 → 0.25.0
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/agent.d.ts +3 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +2 -1
- package/dist/agent.js.map +1 -1
- package/dist/app.d.ts +10 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +33 -6
- package/dist/app.js.map +1 -1
- package/dist/cli.js +43 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/mcp-login-command.d.ts +24 -0
- package/dist/commands/mcp-login-command.d.ts.map +1 -0
- package/dist/commands/mcp-login-command.js +65 -0
- package/dist/commands/mcp-login-command.js.map +1 -0
- package/dist/commands/mcp-status-command.d.ts +19 -0
- package/dist/commands/mcp-status-command.d.ts.map +1 -0
- package/dist/commands/mcp-status-command.js +19 -0
- package/dist/commands/mcp-status-command.js.map +1 -0
- package/dist/components/input/ApplicationInputSurface.d.ts +4 -0
- package/dist/components/input/ApplicationInputSurface.d.ts.map +1 -1
- package/dist/components/input/ApplicationInputSurface.js +2 -0
- package/dist/components/input/ApplicationInputSurface.js.map +1 -1
- package/dist/components/input/McpMenuSession.d.ts +9 -0
- package/dist/components/input/McpMenuSession.d.ts.map +1 -0
- package/dist/components/input/McpMenuSession.js +370 -0
- package/dist/components/input/McpMenuSession.js.map +1 -0
- package/dist/components/input/mcp-intent-host.d.ts +10 -0
- package/dist/components/input/mcp-intent-host.d.ts.map +1 -0
- package/dist/components/input/mcp-intent-host.js +31 -0
- package/dist/components/input/mcp-intent-host.js.map +1 -0
- package/dist/components/input/menu-controller.d.ts.map +1 -1
- package/dist/components/input/menu-controller.js +8 -5
- package/dist/components/input/menu-controller.js.map +1 -1
- package/dist/components/input/menu-registry.d.ts.map +1 -1
- package/dist/components/input/menu-registry.js +2 -0
- package/dist/components/input/menu-registry.js.map +1 -1
- package/dist/components/input/menu-types.d.ts +22 -1
- package/dist/components/input/menu-types.d.ts.map +1 -1
- package/dist/components/layout/BottomArea.d.ts +6 -1
- package/dist/components/layout/BottomArea.d.ts.map +1 -1
- package/dist/components/layout/BottomArea.js +9 -6
- package/dist/components/layout/BottomArea.js.map +1 -1
- package/dist/components/menu/McpSelectionMenu.d.ts +15 -0
- package/dist/components/menu/McpSelectionMenu.d.ts.map +1 -0
- package/dist/components/menu/McpSelectionMenu.js +19 -0
- package/dist/components/menu/McpSelectionMenu.js.map +1 -0
- package/dist/components/message/command-message-helpers.d.ts +2 -2
- package/dist/components/message/command-message-helpers.d.ts.map +1 -1
- package/dist/components/prompt/ApprovalPrompt.d.ts.map +1 -1
- package/dist/components/prompt/ApprovalPrompt.js +3 -0
- package/dist/components/prompt/ApprovalPrompt.js.map +1 -1
- package/dist/contracts/conversation.d.ts +2 -0
- package/dist/contracts/conversation.d.ts.map +1 -1
- package/dist/contracts/conversation.js.map +1 -1
- package/dist/hooks/conversation-ui-reducer.d.ts +1 -1
- package/dist/hooks/conversation-ui-reducer.d.ts.map +1 -1
- package/dist/hooks/settings-completion-config.d.ts.map +1 -1
- package/dist/hooks/settings-completion-config.js +1 -0
- package/dist/hooks/settings-completion-config.js.map +1 -1
- package/dist/hooks/use-app-commands.d.ts +9 -1
- package/dist/hooks/use-app-commands.d.ts.map +1 -1
- package/dist/hooks/use-app-commands.js +14 -1
- package/dist/hooks/use-app-commands.js.map +1 -1
- package/dist/hooks/use-conversation.d.ts +1 -1
- package/dist/hooks/use-mcp-notices.d.ts +10 -0
- package/dist/hooks/use-mcp-notices.d.ts.map +1 -0
- package/dist/hooks/use-mcp-notices.js +47 -0
- package/dist/hooks/use-mcp-notices.js.map +1 -0
- package/dist/hooks/use-settings-value-completion.d.ts +3 -2
- package/dist/hooks/use-settings-value-completion.d.ts.map +1 -1
- package/dist/hooks/use-settings-value-completion.js +21 -3
- package/dist/hooks/use-settings-value-completion.js.map +1 -1
- package/dist/hooks/use-shell-mode.d.ts +1 -0
- package/dist/hooks/use-shell-mode.d.ts.map +1 -1
- package/dist/hooks/use-shell-mode.js +1 -0
- package/dist/hooks/use-shell-mode.js.map +1 -1
- package/dist/lib/agent-client.d.ts +1 -0
- package/dist/lib/agent-client.d.ts.map +1 -1
- package/dist/lib/agent-client.js +2 -0
- package/dist/lib/agent-client.js.map +1 -1
- package/dist/lib/agent-configuration.d.ts +4 -0
- package/dist/lib/agent-configuration.d.ts.map +1 -1
- package/dist/lib/agent-configuration.js +8 -0
- package/dist/lib/agent-configuration.js.map +1 -1
- package/dist/lib/agent-factory.d.ts +2 -0
- package/dist/lib/agent-factory.d.ts.map +1 -1
- package/dist/lib/agent-factory.js +1 -0
- package/dist/lib/agent-factory.js.map +1 -1
- package/dist/lib/oauth-loopback.d.ts +79 -0
- package/dist/lib/oauth-loopback.d.ts.map +1 -0
- package/dist/lib/oauth-loopback.js +221 -0
- package/dist/lib/oauth-loopback.js.map +1 -0
- package/dist/non-interactive.d.ts +2 -0
- package/dist/non-interactive.d.ts.map +1 -1
- package/dist/non-interactive.js +19 -0
- package/dist/non-interactive.js.map +1 -1
- package/dist/providers/oauth-pkce.d.ts +5 -1
- package/dist/providers/oauth-pkce.d.ts.map +1 -1
- package/dist/providers/oauth-pkce.js +28 -208
- package/dist/providers/oauth-pkce.js.map +1 -1
- package/dist/providers/openrouter-decisions.d.ts +15 -0
- package/dist/providers/openrouter-decisions.d.ts.map +1 -0
- package/dist/providers/openrouter-decisions.js +25 -0
- package/dist/providers/openrouter-decisions.js.map +1 -0
- package/dist/services/approval/decision-shadow.d.ts +15 -0
- package/dist/services/approval/decision-shadow.d.ts.map +1 -0
- package/dist/services/approval/decision-shadow.js +77 -0
- package/dist/services/approval/decision-shadow.js.map +1 -0
- package/dist/services/approval/non-interactive-approval-policy.d.ts +5 -0
- package/dist/services/approval/non-interactive-approval-policy.d.ts.map +1 -1
- package/dist/services/approval/non-interactive-approval-policy.js +14 -0
- package/dist/services/approval/non-interactive-approval-policy.js.map +1 -1
- package/dist/services/approval/shell-auto-approval-evaluator.d.ts +5 -1
- package/dist/services/approval/shell-auto-approval-evaluator.d.ts.map +1 -1
- package/dist/services/approval/shell-auto-approval-evaluator.js +80 -4
- package/dist/services/approval/shell-auto-approval-evaluator.js.map +1 -1
- package/dist/services/conversation/conversation-orchestrator.d.ts.map +1 -1
- package/dist/services/conversation/conversation-orchestrator.js +16 -2
- package/dist/services/conversation/conversation-orchestrator.js.map +1 -1
- package/dist/services/conversation/conversation-orchestrator.types.d.ts +1 -1
- package/dist/services/conversation/conversation-orchestrator.types.d.ts.map +1 -1
- package/dist/services/mcp/mcp-config-controller.d.ts +32 -0
- package/dist/services/mcp/mcp-config-controller.d.ts.map +1 -0
- package/dist/services/mcp/mcp-config-controller.js +89 -0
- package/dist/services/mcp/mcp-config-controller.js.map +1 -0
- package/dist/services/mcp/mcp-config.d.ts +61 -0
- package/dist/services/mcp/mcp-config.d.ts.map +1 -0
- package/dist/services/mcp/mcp-config.js +232 -0
- package/dist/services/mcp/mcp-config.js.map +1 -0
- package/dist/services/mcp/mcp-connection-manager.d.ts +126 -0
- package/dist/services/mcp/mcp-connection-manager.d.ts.map +1 -0
- package/dist/services/mcp/mcp-connection-manager.js +488 -0
- package/dist/services/mcp/mcp-connection-manager.js.map +1 -0
- package/dist/services/mcp/mcp-oauth-login.d.ts +79 -0
- package/dist/services/mcp/mcp-oauth-login.d.ts.map +1 -0
- package/dist/services/mcp/mcp-oauth-login.js +126 -0
- package/dist/services/mcp/mcp-oauth-login.js.map +1 -0
- package/dist/services/mcp/mcp-oauth-provider.d.ts +77 -0
- package/dist/services/mcp/mcp-oauth-provider.d.ts.map +1 -0
- package/dist/services/mcp/mcp-oauth-provider.js +121 -0
- package/dist/services/mcp/mcp-oauth-provider.js.map +1 -0
- package/dist/services/mcp/mcp-oauth-store.d.ts +106 -0
- package/dist/services/mcp/mcp-oauth-store.d.ts.map +1 -0
- package/dist/services/mcp/mcp-oauth-store.js +278 -0
- package/dist/services/mcp/mcp-oauth-store.js.map +1 -0
- package/dist/services/mcp/mcp-status.d.ts +41 -0
- package/dist/services/mcp/mcp-status.d.ts.map +1 -0
- package/dist/services/mcp/mcp-status.js +69 -0
- package/dist/services/mcp/mcp-status.js.map +1 -0
- package/dist/services/mcp/mcp-stdio-launcher.d.ts +26 -0
- package/dist/services/mcp/mcp-stdio-launcher.d.ts.map +1 -0
- package/dist/services/mcp/mcp-stdio-launcher.js +8 -0
- package/dist/services/mcp/mcp-stdio-launcher.js.map +1 -0
- package/dist/services/mcp/mcp-tool-source.d.ts +77 -0
- package/dist/services/mcp/mcp-tool-source.d.ts.map +1 -0
- package/dist/services/mcp/mcp-tool-source.js +10 -0
- package/dist/services/mcp/mcp-tool-source.js.map +1 -0
- package/dist/services/mcp/test-fixtures/fake-authorization-server.d.ts +25 -0
- package/dist/services/mcp/test-fixtures/fake-authorization-server.d.ts.map +1 -0
- package/dist/services/mcp/test-fixtures/fake-authorization-server.js +147 -0
- package/dist/services/mcp/test-fixtures/fake-authorization-server.js.map +1 -0
- package/dist/services/mcp/test-fixtures/protected-mcp-server.d.ts +29 -0
- package/dist/services/mcp/test-fixtures/protected-mcp-server.d.ts.map +1 -0
- package/dist/services/mcp/test-fixtures/protected-mcp-server.js +87 -0
- package/dist/services/mcp/test-fixtures/protected-mcp-server.js.map +1 -0
- package/dist/services/mcp/turn-stable-mcp-tool-source.d.ts +19 -0
- package/dist/services/mcp/turn-stable-mcp-tool-source.d.ts.map +1 -0
- package/dist/services/mcp/turn-stable-mcp-tool-source.js +37 -0
- package/dist/services/mcp/turn-stable-mcp-tool-source.js.map +1 -0
- package/dist/services/models/decision-model-listing.d.ts +26 -0
- package/dist/services/models/decision-model-listing.d.ts.map +1 -0
- package/dist/services/models/decision-model-listing.js +61 -0
- package/dist/services/models/decision-model-listing.js.map +1 -0
- package/dist/services/profiles/registry.d.ts +1 -1
- package/dist/services/profiles/registry.d.ts.map +1 -1
- package/dist/services/profiles/registry.js +2 -1
- package/dist/services/profiles/registry.js.map +1 -1
- package/dist/services/settings/settings-schema.d.ts +4 -0
- package/dist/services/settings/settings-schema.d.ts.map +1 -1
- package/dist/services/settings/settings-schema.js +8 -0
- package/dist/services/settings/settings-schema.js.map +1 -1
- package/dist/services/settings/settings-sources.d.ts +2 -0
- package/dist/services/settings/settings-sources.d.ts.map +1 -1
- package/dist/services/settings/settings-sources.js +2 -0
- package/dist/services/settings/settings-sources.js.map +1 -1
- package/dist/services/settings/test-helpers/settings-consumer-inventory.d.ts +1 -1
- package/dist/services/settings/test-helpers/settings-consumer-inventory.d.ts.map +1 -1
- package/dist/services/settings/test-helpers/settings-consumer-inventory.js +1 -0
- package/dist/services/settings/test-helpers/settings-consumer-inventory.js.map +1 -1
- package/dist/services/shell/shell-interaction-session.d.ts +2 -0
- package/dist/services/shell/shell-interaction-session.d.ts.map +1 -1
- package/dist/services/shell/shell-interaction-session.js +11 -2
- package/dist/services/shell/shell-interaction-session.js.map +1 -1
- package/dist/services/subagents/subagent-notification-store.js.map +1 -1
- package/dist/tools/system/run-code/mcp-script-surface.d.ts +28 -0
- package/dist/tools/system/run-code/mcp-script-surface.d.ts.map +1 -0
- package/dist/tools/system/run-code/mcp-script-surface.js +165 -0
- package/dist/tools/system/run-code/mcp-script-surface.js.map +1 -0
- package/dist/tools/system/run-code/run-code-runtime.d.ts.map +1 -1
- package/dist/tools/system/run-code/run-code-runtime.js +13 -1
- package/dist/tools/system/run-code/run-code-runtime.js.map +1 -1
- package/dist/tools/system/run-code/run-code.d.ts +3 -0
- package/dist/tools/system/run-code/run-code.d.ts.map +1 -1
- package/dist/tools/system/run-code/run-code.js +32 -4
- package/dist/tools/system/run-code/run-code.js.map +1 -1
- package/dist/utils/conversation/conversation-event-handler.js.map +1 -1
- package/dist/utils/conversation/conversation-utils.d.ts +1 -5
- package/dist/utils/conversation/conversation-utils.d.ts.map +1 -1
- package/dist/utils/conversation/conversation-utils.js +4 -1
- package/dist/utils/conversation/conversation-utils.js.map +1 -1
- package/dist/utils/value-suggestions.d.ts +14 -0
- package/dist/utils/value-suggestions.d.ts.map +1 -1
- package/dist/utils/value-suggestions.js +31 -0
- package/dist/utils/value-suggestions.js.map +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The interactive half of MCP OAuth: bind a loopback redirect, drive the SDK's
|
|
3
|
+
* `auth()` through both legs, and persist the result via {@linkcode McpOAuthProvider}.
|
|
4
|
+
*
|
|
5
|
+
* This module owns the *mechanics* of a login, never the *policy* of when one
|
|
6
|
+
* may start. Plan decision D1 (2026-09-18) puts the trigger behind an explicit
|
|
7
|
+
* `/mcp-login <server>`: nothing here runs on its own, so a server returning 401
|
|
8
|
+
* mid-session can never pop a browser window. The connection manager only ever
|
|
9
|
+
* refreshes silently (see {@linkcode McpInteractiveLoginRequiredError}).
|
|
10
|
+
*
|
|
11
|
+
* Redirect ports follow D3: an ephemeral port first (RFC 8252), falling back to
|
|
12
|
+
* the server's configured `redirectPorts` for authorization servers that match
|
|
13
|
+
* the redirect against a registered allow-list.
|
|
14
|
+
*/
|
|
15
|
+
import { auth as sdkAuth } from '@modelcontextprotocol/client';
|
|
16
|
+
import { awaitLoopbackCallback, bindLoopbackRedirect, closeLoopbackServer, openInBrowser, } from '../../lib/oauth-loopback.js';
|
|
17
|
+
import { McpOAuthProvider } from './mcp-oauth-provider.js';
|
|
18
|
+
const CALLBACK_PATH = '/mcp-oauth-callback';
|
|
19
|
+
/**
|
|
20
|
+
* Thrown by a refresh-only provider when the SDK concludes the user must visit
|
|
21
|
+
* an authorization server. The connection manager turns this into the
|
|
22
|
+
* `needs-auth` state rather than a browser window.
|
|
23
|
+
*/
|
|
24
|
+
export class McpInteractiveLoginRequiredError extends Error {
|
|
25
|
+
serverName;
|
|
26
|
+
constructor(serverName) {
|
|
27
|
+
super(`MCP server "${serverName}" requires an interactive OAuth login`);
|
|
28
|
+
this.serverName = serverName;
|
|
29
|
+
this.name = 'McpInteractiveLoginRequiredError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** The exact text a user acts on when a server is waiting for a login. */
|
|
33
|
+
export const mcpNeedsAuthMessage = (serverName) => `run /mcp-login ${serverName} to authenticate`;
|
|
34
|
+
/**
|
|
35
|
+
* Runs a full OAuth login for one MCP server and stores the tokens.
|
|
36
|
+
*
|
|
37
|
+
* Resolves without opening a browser when the stored refresh token still works,
|
|
38
|
+
* so re-running `/mcp-login` on a healthy server is cheap and safe.
|
|
39
|
+
*/
|
|
40
|
+
export async function runMcpOAuthLogin(options) {
|
|
41
|
+
const auth = options.auth ?? sdkAuth;
|
|
42
|
+
const open = options.openBrowser ?? openInBrowser;
|
|
43
|
+
// Port 0 first (RFC 8252); the configured list only exists so a server that
|
|
44
|
+
// pins its redirect URI still works, not to find a free port.
|
|
45
|
+
const ports = [0, ...(options.redirectPorts ?? [])];
|
|
46
|
+
const { server, port } = await bindLoopbackRedirect({
|
|
47
|
+
ports,
|
|
48
|
+
label: `MCP server "${options.serverName}"`,
|
|
49
|
+
portConflictHint: 'another login may be in progress',
|
|
50
|
+
});
|
|
51
|
+
const redirectUri = `http://127.0.0.1:${port}${CALLBACK_PATH}`;
|
|
52
|
+
const flow = {
|
|
53
|
+
label: `MCP server "${options.serverName}"`,
|
|
54
|
+
callbackPath: CALLBACK_PATH,
|
|
55
|
+
redirectUri,
|
|
56
|
+
exampleCallbackUrl: `${redirectUri}?code=...&state=...`,
|
|
57
|
+
};
|
|
58
|
+
let authorizationUrl;
|
|
59
|
+
const provider = new McpOAuthProvider({
|
|
60
|
+
store: options.store,
|
|
61
|
+
serverUrl: options.serverUrl,
|
|
62
|
+
redirectUrl: redirectUri,
|
|
63
|
+
...(options.clientMetadataUrl !== undefined ? { clientMetadataUrl: options.clientMetadataUrl } : {}),
|
|
64
|
+
...(options.clientId !== undefined ? { clientId: options.clientId } : {}),
|
|
65
|
+
onAuthorizationRedirect: (url) => {
|
|
66
|
+
authorizationUrl = url;
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
try {
|
|
70
|
+
const first = await auth(provider, { serverUrl: options.serverUrl });
|
|
71
|
+
if (first === 'AUTHORIZED') {
|
|
72
|
+
// A stored refresh token still works; no browser round trip needed.
|
|
73
|
+
return { interactive: false, ...issuerOf(provider) };
|
|
74
|
+
}
|
|
75
|
+
if (authorizationUrl === undefined) {
|
|
76
|
+
throw new Error(`OAuth login for "${options.serverName}" produced no authorization URL`);
|
|
77
|
+
}
|
|
78
|
+
options.onAuthorizationUrl?.(authorizationUrl.toString());
|
|
79
|
+
open(authorizationUrl.toString());
|
|
80
|
+
// `state` is bound by the provider when the SDK built the URL; the listener
|
|
81
|
+
// rejects any callback that does not carry it back.
|
|
82
|
+
const expectedState = provider.authorizationState;
|
|
83
|
+
if (expectedState === undefined) {
|
|
84
|
+
throw new Error(`OAuth login for "${options.serverName}" started without a state parameter`);
|
|
85
|
+
}
|
|
86
|
+
const code = await awaitLoopbackCallback(server, flow, expectedState, {
|
|
87
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
88
|
+
});
|
|
89
|
+
const second = await auth(provider, { serverUrl: options.serverUrl, authorizationCode: code });
|
|
90
|
+
if (second !== 'AUTHORIZED') {
|
|
91
|
+
throw new Error(`OAuth login for "${options.serverName}" ended in state ${second} instead of AUTHORIZED`);
|
|
92
|
+
}
|
|
93
|
+
return { interactive: true, ...issuerOf(provider) };
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
// The browser holds a keep-alive socket on the callback port; without this
|
|
97
|
+
// the next login on a configured (non-ephemeral) port would fail to bind.
|
|
98
|
+
closeLoopbackServer(server);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const issuerOf = (provider) => {
|
|
102
|
+
const issuer = provider.tokens()?.issuer;
|
|
103
|
+
return issuer !== undefined ? { issuer } : {};
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* A provider that persists and refreshes but refuses to send the user anywhere.
|
|
107
|
+
*
|
|
108
|
+
* Handed to HTTP transports so an expired access token is renewed silently
|
|
109
|
+
* mid-session, while a genuinely unauthenticated server surfaces as `needs-auth`
|
|
110
|
+
* instead of hijacking the terminal with a browser launch.
|
|
111
|
+
*/
|
|
112
|
+
export function createRefreshOnlyProvider(options) {
|
|
113
|
+
return new McpOAuthProvider({
|
|
114
|
+
store: options.store,
|
|
115
|
+
serverUrl: options.serverUrl,
|
|
116
|
+
// No listener is bound on this path. The value is only read when the SDK
|
|
117
|
+
// builds an authorization URL, which the throw below always preempts.
|
|
118
|
+
redirectUrl: `http://127.0.0.1:0${CALLBACK_PATH}`,
|
|
119
|
+
...(options.clientMetadataUrl !== undefined ? { clientMetadataUrl: options.clientMetadataUrl } : {}),
|
|
120
|
+
...(options.clientId !== undefined ? { clientId: options.clientId } : {}),
|
|
121
|
+
onAuthorizationRedirect: () => {
|
|
122
|
+
throw new McpInteractiveLoginRequiredError(options.serverName);
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=mcp-oauth-login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-oauth-login.js","sourceRoot":"","sources":["../../../source/services/mcp/mcp-oauth-login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { type OAuthClientInformationContext, type OAuthClientMetadata, type OAuthClientProvider, type OAuthDiscoveryState, type StoredOAuthClientInformation, type StoredOAuthTokens } from '@modelcontextprotocol/client';
|
|
2
|
+
import type { McpOAuthCredentialScope, McpOAuthStore } from './mcp-oauth-store.js';
|
|
3
|
+
/** The client name authorization servers (and the user) see in consent screens. */
|
|
4
|
+
export declare const MCP_OAUTH_CLIENT_NAME = "term2-mcp";
|
|
5
|
+
export type McpOAuthProviderOptions = {
|
|
6
|
+
/** Credential storage, keyed by server URL and issuer. */
|
|
7
|
+
store: McpOAuthStore;
|
|
8
|
+
/** The MCP server URL from the config; the credential storage key. */
|
|
9
|
+
serverUrl: string;
|
|
10
|
+
/**
|
|
11
|
+
* The loopback redirect URI. A thunk when the port is only known after the
|
|
12
|
+
* listener is bound — the SDK reads `redirectUrl` before the redirect is
|
|
13
|
+
* built, so the bound port must be in place by then.
|
|
14
|
+
*/
|
|
15
|
+
redirectUrl: string | (() => string);
|
|
16
|
+
/**
|
|
17
|
+
* CIMD: an HTTPS URL the authorization server fetches client metadata from,
|
|
18
|
+
* replacing dynamic client registration.
|
|
19
|
+
*/
|
|
20
|
+
clientMetadataUrl?: string;
|
|
21
|
+
/**
|
|
22
|
+
* A pre-registered client id from config. When set, the SDK never attempts
|
|
23
|
+
* dynamic registration, which is the only way to reach an authorization
|
|
24
|
+
* server that offers neither DCR nor CIMD.
|
|
25
|
+
*/
|
|
26
|
+
clientId?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Receives the authorization URL. Opening a browser, prompting the user, or
|
|
29
|
+
* refusing (non-interactive, project server) is the caller's policy; this
|
|
30
|
+
* adapter only reports what the SDK asked for.
|
|
31
|
+
*/
|
|
32
|
+
onAuthorizationRedirect: (authorizationUrl: URL) => void | Promise<void>;
|
|
33
|
+
/** PKCE state factory, injectable for deterministic tests. */
|
|
34
|
+
createState?: () => string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* The `OAuthClientProvider` the MCP SDK's `auth()` drives, backed by
|
|
38
|
+
* `McpOAuthStore`.
|
|
39
|
+
*
|
|
40
|
+
* Everything policy-shaped is injected: this class decides nothing about when a
|
|
41
|
+
* login may start, which authorization server is acceptable, or how the user is
|
|
42
|
+
* sent there. It persists what the SDK hands it and returns what the SDK asks
|
|
43
|
+
* for, keyed by the server URL — never by the config entry's name, so renaming a
|
|
44
|
+
* server in `.mcp.json` cannot reach another server's token.
|
|
45
|
+
*/
|
|
46
|
+
export declare class McpOAuthProvider implements OAuthClientProvider {
|
|
47
|
+
/** CIMD URL, when the server publishes one; `undefined` falls back to DCR. */
|
|
48
|
+
readonly clientMetadataUrl?: string;
|
|
49
|
+
private readonly options;
|
|
50
|
+
/** The state most recently handed to the SDK, for callback validation. */
|
|
51
|
+
private pendingState?;
|
|
52
|
+
constructor(options: McpOAuthProviderOptions);
|
|
53
|
+
get redirectUrl(): string;
|
|
54
|
+
get clientMetadata(): OAuthClientMetadata;
|
|
55
|
+
/**
|
|
56
|
+
* The `state` the SDK puts in the authorization URL, recorded so the loopback
|
|
57
|
+
* listener can reject a callback that did not come from this request.
|
|
58
|
+
*/
|
|
59
|
+
state(): string;
|
|
60
|
+
get authorizationState(): string | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* With no issuer there is no way to tell an unregistered client from another
|
|
63
|
+
* authorization server's, and the SDK's contract for that call is "undefined
|
|
64
|
+
* means not registered" — never another server's credential.
|
|
65
|
+
*/
|
|
66
|
+
clientInformation(ctx?: OAuthClientInformationContext): StoredOAuthClientInformation | undefined;
|
|
67
|
+
saveClientInformation(clientInformation: StoredOAuthClientInformation, ctx?: OAuthClientInformationContext): void;
|
|
68
|
+
tokens(ctx?: OAuthClientInformationContext): StoredOAuthTokens | undefined;
|
|
69
|
+
saveTokens(tokens: StoredOAuthTokens, ctx?: OAuthClientInformationContext): void;
|
|
70
|
+
redirectToAuthorization(authorizationUrl: URL): void | Promise<void>;
|
|
71
|
+
saveCodeVerifier(codeVerifier: string): void;
|
|
72
|
+
codeVerifier(): string;
|
|
73
|
+
saveDiscoveryState(state: OAuthDiscoveryState): void;
|
|
74
|
+
discoveryState(): OAuthDiscoveryState | undefined;
|
|
75
|
+
invalidateCredentials(scope: McpOAuthCredentialScope): void;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=mcp-oauth-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-oauth-provider.d.ts","sourceRoot":"","sources":["../../../source/services/mcp/mcp-oauth-provider.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,6BAA6B,EAClC,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,4BAA4B,EACjC,KAAK,iBAAiB,EACvB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAEnF,mFAAmF;AACnF,eAAO,MAAM,qBAAqB,cAAc,CAAC;AAEjD,MAAM,MAAM,uBAAuB,GAAG;IACpC,0DAA0D;IAC1D,KAAK,EAAE,aAAa,CAAC;IACrB,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,WAAW,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC;IACrC;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,uBAAuB,EAAE,CAAC,gBAAgB,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,MAAM,CAAC;CAC5B,CAAC;AAoBF;;;;;;;;;GASG;AACH,qBAAa,gBAAiB,YAAW,mBAAmB;IAC1D,8EAA8E;IAC9E,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAEpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;IAClD,0EAA0E;IAC1E,OAAO,CAAC,YAAY,CAAC,CAAS;IAE9B,YAAY,OAAO,EAAE,uBAAuB,EAI3C;IAED,IAAI,WAAW,IAAI,MAAM,CAGxB;IAED,IAAI,cAAc,IAAI,mBAAmB,CAUxC;IAED;;;OAGG;IACH,KAAK,IAAI,MAAM,CAGd;IAED,IAAI,kBAAkB,IAAI,MAAM,GAAG,SAAS,CAE3C;IAED;;;;OAIG;IACH,iBAAiB,CAAC,GAAG,CAAC,EAAE,6BAA6B,GAAG,4BAA4B,GAAG,SAAS,CAW/F;IAED,qBAAqB,CAAC,iBAAiB,EAAE,4BAA4B,EAAE,GAAG,CAAC,EAAE,6BAA6B,GAAG,IAAI,CAMhH;IAED,MAAM,CAAC,GAAG,CAAC,EAAE,6BAA6B,GAAG,iBAAiB,GAAG,SAAS,CAEzE;IAED,UAAU,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,CAAC,EAAE,6BAA6B,GAAG,IAAI,CAG/E;IAED,uBAAuB,CAAC,gBAAgB,EAAE,GAAG,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAEnE;IAED,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAE3C;IAED,YAAY,IAAI,MAAM,CAQrB;IAED,kBAAkB,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,CAEnD;IAED,cAAc,IAAI,mBAAmB,GAAG,SAAS,CAEhD;IAED,qBAAqB,CAAC,KAAK,EAAE,uBAAuB,GAAG,IAAI,CAE1D;CACF"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import { validateClientMetadataUrl, } from '@modelcontextprotocol/client';
|
|
3
|
+
/** The client name authorization servers (and the user) see in consent screens. */
|
|
4
|
+
export const MCP_OAUTH_CLIENT_NAME = 'term2-mcp';
|
|
5
|
+
function defaultState() {
|
|
6
|
+
return crypto.randomBytes(16).toString('base64url');
|
|
7
|
+
}
|
|
8
|
+
function requireIssuer(ctx, stampedIssuer, method) {
|
|
9
|
+
const issuer = ctx?.issuer ?? stampedIssuer;
|
|
10
|
+
if (!issuer) {
|
|
11
|
+
// Guessing would risk filing one authorization server's credential under
|
|
12
|
+
// another's. The SDK always passes a context when it saves.
|
|
13
|
+
throw new Error(`McpOAuthProvider.${method} requires the authorization server issuer`);
|
|
14
|
+
}
|
|
15
|
+
return issuer;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The `OAuthClientProvider` the MCP SDK's `auth()` drives, backed by
|
|
19
|
+
* `McpOAuthStore`.
|
|
20
|
+
*
|
|
21
|
+
* Everything policy-shaped is injected: this class decides nothing about when a
|
|
22
|
+
* login may start, which authorization server is acceptable, or how the user is
|
|
23
|
+
* sent there. It persists what the SDK hands it and returns what the SDK asks
|
|
24
|
+
* for, keyed by the server URL — never by the config entry's name, so renaming a
|
|
25
|
+
* server in `.mcp.json` cannot reach another server's token.
|
|
26
|
+
*/
|
|
27
|
+
export class McpOAuthProvider {
|
|
28
|
+
/** CIMD URL, when the server publishes one; `undefined` falls back to DCR. */
|
|
29
|
+
clientMetadataUrl;
|
|
30
|
+
options;
|
|
31
|
+
/** The state most recently handed to the SDK, for callback validation. */
|
|
32
|
+
pendingState;
|
|
33
|
+
constructor(options) {
|
|
34
|
+
validateClientMetadataUrl(options.clientMetadataUrl);
|
|
35
|
+
this.options = options;
|
|
36
|
+
this.clientMetadataUrl = options.clientMetadataUrl;
|
|
37
|
+
}
|
|
38
|
+
get redirectUrl() {
|
|
39
|
+
const { redirectUrl } = this.options;
|
|
40
|
+
return typeof redirectUrl === 'function' ? redirectUrl() : redirectUrl;
|
|
41
|
+
}
|
|
42
|
+
get clientMetadata() {
|
|
43
|
+
return {
|
|
44
|
+
client_name: MCP_OAUTH_CLIENT_NAME,
|
|
45
|
+
redirect_uris: [this.redirectUrl],
|
|
46
|
+
// `refresh_token` is declared because term2 does refresh: the SDK only
|
|
47
|
+
// appends the `offline_access` scope when the client metadata says so.
|
|
48
|
+
grant_types: ['authorization_code', 'refresh_token'],
|
|
49
|
+
response_types: ['code'],
|
|
50
|
+
token_endpoint_auth_method: 'none',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The `state` the SDK puts in the authorization URL, recorded so the loopback
|
|
55
|
+
* listener can reject a callback that did not come from this request.
|
|
56
|
+
*/
|
|
57
|
+
state() {
|
|
58
|
+
this.pendingState = (this.options.createState ?? defaultState)();
|
|
59
|
+
return this.pendingState;
|
|
60
|
+
}
|
|
61
|
+
get authorizationState() {
|
|
62
|
+
return this.pendingState;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* With no issuer there is no way to tell an unregistered client from another
|
|
66
|
+
* authorization server's, and the SDK's contract for that call is "undefined
|
|
67
|
+
* means not registered" — never another server's credential.
|
|
68
|
+
*/
|
|
69
|
+
clientInformation(ctx) {
|
|
70
|
+
const stored = ctx?.issuer
|
|
71
|
+
? this.options.store.getClientInformation(this.options.serverUrl, ctx.issuer)
|
|
72
|
+
: undefined;
|
|
73
|
+
if (stored)
|
|
74
|
+
return stored;
|
|
75
|
+
// A configured client id is not bound to an issuer the way a dynamically
|
|
76
|
+
// registered one is: the user registered this app themselves, for this
|
|
77
|
+
// server, so it is returned even when the SDK asks without a context.
|
|
78
|
+
// Returning it here is what stops `auth()` from attempting registration.
|
|
79
|
+
if (this.options.clientId)
|
|
80
|
+
return { client_id: this.options.clientId };
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
saveClientInformation(clientInformation, ctx) {
|
|
84
|
+
// A configured client id is owned by config, not by the store; persisting a
|
|
85
|
+
// copy would leave a stale credential behind if the user later changes it.
|
|
86
|
+
if (this.options.clientId)
|
|
87
|
+
return;
|
|
88
|
+
const issuer = requireIssuer(ctx, clientInformation.issuer, 'saveClientInformation');
|
|
89
|
+
this.options.store.saveClientInformation(this.options.serverUrl, issuer, clientInformation);
|
|
90
|
+
}
|
|
91
|
+
tokens(ctx) {
|
|
92
|
+
return this.options.store.getTokens(this.options.serverUrl, ctx?.issuer);
|
|
93
|
+
}
|
|
94
|
+
saveTokens(tokens, ctx) {
|
|
95
|
+
const issuer = requireIssuer(ctx, tokens.issuer, 'saveTokens');
|
|
96
|
+
this.options.store.saveTokens(this.options.serverUrl, issuer, tokens);
|
|
97
|
+
}
|
|
98
|
+
redirectToAuthorization(authorizationUrl) {
|
|
99
|
+
return this.options.onAuthorizationRedirect(authorizationUrl);
|
|
100
|
+
}
|
|
101
|
+
saveCodeVerifier(codeVerifier) {
|
|
102
|
+
this.options.store.saveCodeVerifier(this.options.serverUrl, codeVerifier);
|
|
103
|
+
}
|
|
104
|
+
codeVerifier() {
|
|
105
|
+
const verifier = this.options.store.getCodeVerifier(this.options.serverUrl);
|
|
106
|
+
if (!verifier) {
|
|
107
|
+
throw new Error(`No PKCE code verifier is stored for ${this.options.serverUrl}; the authorization flow must save one before the token exchange`);
|
|
108
|
+
}
|
|
109
|
+
return verifier;
|
|
110
|
+
}
|
|
111
|
+
saveDiscoveryState(state) {
|
|
112
|
+
this.options.store.saveDiscoveryState(this.options.serverUrl, state);
|
|
113
|
+
}
|
|
114
|
+
discoveryState() {
|
|
115
|
+
return this.options.store.getDiscoveryState(this.options.serverUrl);
|
|
116
|
+
}
|
|
117
|
+
invalidateCredentials(scope) {
|
|
118
|
+
this.options.store.invalidateCredentials(this.options.serverUrl, scope);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=mcp-oauth-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-oauth-provider.js","sourceRoot":"","sources":["../../../source/services/mcp/mcp-oauth-provider.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EACL,yBAAyB,GAO1B,MAAM,8BAA8B,CAAC;AAGtC,mFAAmF;AACnF,MAAM,CAAC,MAAM,qBAAqB,GAAG,WAAW,CAAC;AAkCjD,SAAS,YAAY;IACnB,OAAO,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,aAAa,CACpB,GAA8C,EAC9C,aAAiC,EACjC,MAAc;IAEd,MAAM,MAAM,GAAG,GAAG,EAAE,MAAM,IAAI,aAAa,CAAC;IAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,yEAAyE;QACzE,4DAA4D;QAC5D,MAAM,IAAI,KAAK,CAAC,oBAAoB,MAAM,2CAA2C,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,OAAO,gBAAgB;IAC3B,8EAA8E;IACrE,iBAAiB,CAAU;IAEnB,OAAO,CAA0B;IAClD,0EAA0E;IAClE,YAAY,CAAU;IAE9B,YAAY,OAAgC;QAC1C,yBAAyB,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACrD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IACrD,CAAC;IAED,IAAI,WAAW;QACb,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QACrC,OAAO,OAAO,WAAW,KAAK,UAAU,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC;IACzE,CAAC;IAED,IAAI,cAAc;QAChB,OAAO;YACL,WAAW,EAAE,qBAAqB;YAClC,aAAa,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC;YACjC,uEAAuE;YACvE,uEAAuE;YACvE,WAAW,EAAE,CAAC,oBAAoB,EAAE,eAAe,CAAC;YACpD,cAAc,EAAE,CAAC,MAAM,CAAC;YACxB,0BAA0B,EAAE,MAAM;SACnC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK;QACH,IAAI,CAAC,YAAY,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,IAAI,YAAY,CAAC,EAAE,CAAC;QACjE,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,IAAI,kBAAkB;QACpB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED;;;;OAIG;IACH,iBAAiB,CAAC,GAAmC;QACnD,MAAM,MAAM,GAAG,GAAG,EAAE,MAAM;YACxB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC;YAC7E,CAAC,CAAC,SAAS,CAAC;QACd,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAC1B,yEAAyE;QACzE,uEAAuE;QACvE,sEAAsE;QACtE,yEAAyE;QACzE,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ;YAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACvE,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,qBAAqB,CAAC,iBAA+C,EAAE,GAAmC;QACxG,4EAA4E;QAC5E,2EAA2E;QAC3E,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ;YAAE,OAAO;QAClC,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,EAAE,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;QACrF,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC9F,CAAC;IAED,MAAM,CAAC,GAAmC;QACxC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IAC3E,CAAC;IAED,UAAU,CAAC,MAAyB,EAAE,GAAmC;QACvE,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAC/D,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACxE,CAAC;IAED,uBAAuB,CAAC,gBAAqB;QAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,uBAAuB,CAAC,gBAAgB,CAAC,CAAC;IAChE,CAAC;IAED,gBAAgB,CAAC,YAAoB;QACnC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IAC5E,CAAC;IAED,YAAY;QACV,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC5E,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CACb,uCAAuC,IAAI,CAAC,OAAO,CAAC,SAAS,kEAAkE,CAChI,CAAC;QACJ,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,kBAAkB,CAAC,KAA0B;QAC3C,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACvE,CAAC;IAED,cAAc;QACZ,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACtE,CAAC;IAED,qBAAqB,CAAC,KAA8B;QAClD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC1E,CAAC;CACF"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import type { OAuthDiscoveryState, StoredOAuthClientInformation, StoredOAuthTokens } from '@modelcontextprotocol/client';
|
|
3
|
+
/**
|
|
4
|
+
* Credential storage for MCP servers that authenticate with OAuth.
|
|
5
|
+
*
|
|
6
|
+
* One file holds every server's credentials, keyed by the server URL from the
|
|
7
|
+
* config rather than by the config entry's name: renaming `"linear"` to
|
|
8
|
+
* `"linear-remote"` in `.mcp.json` must not lose the login, and pointing a
|
|
9
|
+
* different entry at another URL must never reach a token issued for this one.
|
|
10
|
+
*
|
|
11
|
+
* Within a server, client information and tokens are sub-keyed by the
|
|
12
|
+
* authorization server's `issuer` (stamped by the SDK on every save), because a
|
|
13
|
+
* client id is only meaningful to the authorization server that issued it. The
|
|
14
|
+
* PKCE code verifier and the discovery state have no issuer — the SDK passes no
|
|
15
|
+
* context for them — so they belong to the server entry itself.
|
|
16
|
+
*
|
|
17
|
+
* This is the persistence half of `OAuthClientProvider`; the policy half (when
|
|
18
|
+
* to log in, whether to open a browser) lives in `mcp-oauth-provider.ts` and its
|
|
19
|
+
* caller.
|
|
20
|
+
*/
|
|
21
|
+
export declare const MCP_OAUTH_STORE_VERSION = 1;
|
|
22
|
+
export type McpOAuthCredentialScope = 'all' | 'client' | 'tokens' | 'verifier' | 'discovery';
|
|
23
|
+
export type McpOAuthIssuerEntry = {
|
|
24
|
+
client?: StoredOAuthClientInformation;
|
|
25
|
+
tokens?: StoredOAuthTokens;
|
|
26
|
+
};
|
|
27
|
+
export type McpOAuthServerEntry = {
|
|
28
|
+
byIssuer: Record<string, McpOAuthIssuerEntry>;
|
|
29
|
+
/**
|
|
30
|
+
* The issuer whose tokens were saved most recently. The transport reads
|
|
31
|
+
* `tokens()` with no context on every request, and a server has one usable
|
|
32
|
+
* token set at a time, so that read must be unambiguous.
|
|
33
|
+
*/
|
|
34
|
+
lastIssuer?: string;
|
|
35
|
+
/** Survives the redirect round-trip; the token exchange needs it back. */
|
|
36
|
+
codeVerifier?: string;
|
|
37
|
+
/** Cached RFC 9728/RFC 8414 discovery, so re-auth skips the HTTP probes. */
|
|
38
|
+
discovery?: OAuthDiscoveryState;
|
|
39
|
+
};
|
|
40
|
+
export type McpOAuthStoreFile = {
|
|
41
|
+
version: number;
|
|
42
|
+
servers: Record<string, McpOAuthServerEntry>;
|
|
43
|
+
};
|
|
44
|
+
export type McpOAuthStoreOptions = {
|
|
45
|
+
/** Absolute path to the credential file. Defaults to `defaultMcpOAuthStorePath()`. */
|
|
46
|
+
filePath?: string;
|
|
47
|
+
/** Injectable filesystem seam for failure/cleanup tests. */
|
|
48
|
+
filesystem?: typeof fs;
|
|
49
|
+
};
|
|
50
|
+
/** term2's own MCP credential file, beside `grok-auth.json` and `codex-auth.json`. */
|
|
51
|
+
export declare function defaultMcpOAuthStorePath(): string;
|
|
52
|
+
/**
|
|
53
|
+
* The storage key for a server URL: lowercase scheme and host, no default port,
|
|
54
|
+
* no trailing slash, no query or fragment.
|
|
55
|
+
*
|
|
56
|
+
* The path keeps its case — URLs are only case-insensitive before the path — and
|
|
57
|
+
* a URL that cannot be parsed is keyed by its trimmed text, which keeps the
|
|
58
|
+
* function total while still giving two spellings of one bad string the same key.
|
|
59
|
+
*/
|
|
60
|
+
export declare function normalizeMcpServerUrl(raw: string): string;
|
|
61
|
+
export declare class McpOAuthStore {
|
|
62
|
+
private readonly filesystem;
|
|
63
|
+
private readonly filePath;
|
|
64
|
+
constructor(options?: McpOAuthStoreOptions);
|
|
65
|
+
get path(): string;
|
|
66
|
+
/**
|
|
67
|
+
* Reads the store. A missing, unreadable, or corrupt file is an empty store:
|
|
68
|
+
* a credential file is not worth crashing a session over, and the next write
|
|
69
|
+
* replaces the damaged body.
|
|
70
|
+
*/
|
|
71
|
+
read(): McpOAuthStoreFile;
|
|
72
|
+
getClientInformation(serverUrl: string, issuer: string): StoredOAuthClientInformation | undefined;
|
|
73
|
+
saveClientInformation(serverUrl: string, issuer: string, client: StoredOAuthClientInformation): void;
|
|
74
|
+
/**
|
|
75
|
+
* Reads stored tokens. With no issuer, returns the most recently saved set —
|
|
76
|
+
* the transport's per-request read, which must not come back empty just
|
|
77
|
+
* because it has no context.
|
|
78
|
+
*/
|
|
79
|
+
getTokens(serverUrl: string, issuer?: string): StoredOAuthTokens | undefined;
|
|
80
|
+
saveTokens(serverUrl: string, issuer: string, tokens: StoredOAuthTokens): void;
|
|
81
|
+
getCodeVerifier(serverUrl: string): string | undefined;
|
|
82
|
+
saveCodeVerifier(serverUrl: string, codeVerifier: string): void;
|
|
83
|
+
getDiscoveryState(serverUrl: string): OAuthDiscoveryState | undefined;
|
|
84
|
+
saveDiscoveryState(serverUrl: string, discovery: OAuthDiscoveryState): void;
|
|
85
|
+
/**
|
|
86
|
+
* Forgets one class of credential for a server. Scopes are the SDK's: the
|
|
87
|
+
* transport calls `'client'` and `'tokens'` when the server rejects them,
|
|
88
|
+
* and hosts call `'discovery'` after repeated 401s so a changed
|
|
89
|
+
* `authorization_servers` list is picked up.
|
|
90
|
+
*
|
|
91
|
+
* `'client'` and `'tokens'` clear every issuer's entry, because the call
|
|
92
|
+
* carries no issuer and the rejected credential may be any of them.
|
|
93
|
+
*/
|
|
94
|
+
invalidateCredentials(serverUrl: string, scope: McpOAuthCredentialScope): void;
|
|
95
|
+
private serverEntry;
|
|
96
|
+
/** Applies one mutation and writes the whole store back atomically. */
|
|
97
|
+
private update;
|
|
98
|
+
/**
|
|
99
|
+
* Writes through a temporary file in the same directory, fsynced before the
|
|
100
|
+
* rename, so a reader — including another term2 instance — sees either the old
|
|
101
|
+
* file or the complete new one, never a half-written credential file.
|
|
102
|
+
*/
|
|
103
|
+
private write;
|
|
104
|
+
private sweepTemporaryFiles;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=mcp-oauth-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-oauth-store.d.ts","sourceRoot":"","sources":["../../../source/services/mcp/mcp-oauth-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAIzB,OAAO,KAAK,EACV,mBAAmB,EACnB,4BAA4B,EAC5B,iBAAiB,EAClB,MAAM,8BAA8B,CAAC;AAEtC;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,MAAM,MAAM,uBAAuB,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC;AAE7F,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,CAAC,EAAE,4BAA4B,CAAC;IACtC,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC9C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,mBAAmB,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC;CACxB,CAAC;AAEF,sFAAsF;AACtF,wBAAgB,wBAAwB,IAAI,MAAM,CAGjD;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAuBzD;AAcD,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC,YAAY,OAAO,GAAE,oBAAyB,EAI7C;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;OAIG;IACH,IAAI,IAAI,iBAAiB,CA4BxB;IAED,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,4BAA4B,GAAG,SAAS,CAEhG;IAED,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,4BAA4B,GAAG,IAAI,CAKnG;IAED;;;;OAIG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAM3E;IAED,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAM7E;IAED,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAErD;IAED,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAK9D;IAED,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAEpE;IAED,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,mBAAmB,GAAG,IAAI,CAK1E;IAED;;;;;;;;OAQG;IACH,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,uBAAuB,GAAG,IAAI,CAqB7E;IAED,OAAO,CAAC,WAAW;IAInB,uEAAuE;IACvE,OAAO,CAAC,MAAM;IASd;;;;OAIG;IACH,OAAO,CAAC,KAAK;IA+Bb,OAAO,CAAC,mBAAmB;CAkB5B"}
|