apex-code 0.0.6 → 0.1.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 +33 -0
- package/README.md +8 -9
- package/dist/cli/args.d.ts +0 -12
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +2 -32
- package/dist/cli/args.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -130
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +0 -8
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/delegation/agents.d.ts.map +1 -1
- package/dist/core/delegation/agents.js +2 -1
- package/dist/core/delegation/agents.js.map +1 -1
- package/dist/core/delegation/runtime.d.ts +6 -14
- package/dist/core/delegation/runtime.d.ts.map +1 -1
- package/dist/core/delegation/runtime.js +1 -2
- package/dist/core/delegation/runtime.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +3 -4
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/source-runtime.d.ts +4 -5
- package/dist/core/extensions/source-runtime.d.ts.map +1 -1
- package/dist/core/extensions/source-runtime.js +4 -5
- package/dist/core/extensions/source-runtime.js.map +1 -1
- package/dist/core/formatter-lifecycle.d.ts +2 -1
- package/dist/core/formatter-lifecycle.d.ts.map +1 -1
- package/dist/core/formatter-lifecycle.js +2 -1
- package/dist/core/formatter-lifecycle.js.map +1 -1
- package/dist/core/hooks/types.d.ts +2 -3
- package/dist/core/hooks/types.d.ts.map +1 -1
- package/dist/core/hooks/types.js +2 -3
- package/dist/core/hooks/types.js.map +1 -1
- package/dist/core/http-idle-timeout.d.ts +3 -5
- package/dist/core/http-idle-timeout.d.ts.map +1 -1
- package/dist/core/http-idle-timeout.js +3 -5
- package/dist/core/http-idle-timeout.js.map +1 -1
- package/dist/core/mcp/config.d.ts +18 -1
- package/dist/core/mcp/config.d.ts.map +1 -1
- package/dist/core/mcp/config.js +24 -7
- package/dist/core/mcp/config.js.map +1 -1
- package/dist/core/mcp/connector.d.ts +2 -3
- package/dist/core/mcp/connector.d.ts.map +1 -1
- package/dist/core/mcp/connector.js.map +1 -1
- package/dist/core/mcp/oauth/authorize.d.ts +2 -0
- package/dist/core/mcp/oauth/authorize.d.ts.map +1 -1
- package/dist/core/mcp/oauth/authorize.js +5 -2
- package/dist/core/mcp/oauth/authorize.js.map +1 -1
- package/dist/core/mcp/oauth/mcp-token.d.ts +4 -6
- package/dist/core/mcp/oauth/mcp-token.d.ts.map +1 -1
- package/dist/core/mcp/oauth/mcp-token.js +5 -7
- package/dist/core/mcp/oauth/mcp-token.js.map +1 -1
- package/dist/core/mcp/runtime.d.ts +1 -0
- package/dist/core/mcp/runtime.d.ts.map +1 -1
- package/dist/core/mcp/runtime.js +2 -1
- package/dist/core/mcp/runtime.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +0 -15
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/permissions/store.d.ts +6 -11
- package/dist/core/permissions/store.d.ts.map +1 -1
- package/dist/core/permissions/store.js +23 -51
- package/dist/core/permissions/store.js.map +1 -1
- package/dist/core/project-resources.d.ts +62 -0
- package/dist/core/project-resources.d.ts.map +1 -0
- package/dist/core/project-resources.js +42 -0
- package/dist/core/project-resources.js.map +1 -0
- package/dist/core/sdk.d.ts +1 -9
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +10 -35
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-share.d.ts +6 -8
- package/dist/core/session-share.d.ts.map +1 -1
- package/dist/core/session-share.js +9 -12
- package/dist/core/session-share.js.map +1 -1
- package/dist/core/settings-manager.d.ts +0 -21
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +4 -12
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +0 -47
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/web-fetch.d.ts.map +1 -1
- package/dist/core/tools/web-fetch.js +4 -8
- package/dist/core/tools/web-fetch.js.map +1 -1
- package/dist/core/tools/web-search-exa.d.ts +3 -6
- package/dist/core/tools/web-search-exa.d.ts.map +1 -1
- package/dist/core/tools/web-search-exa.js +5 -8
- package/dist/core/tools/web-search-exa.js.map +1 -1
- package/dist/core/trust-manager.d.ts +6 -5
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +42 -19
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/web-search-provider.d.ts +0 -10
- package/dist/core/web-search-provider.d.ts.map +1 -1
- package/dist/core/web-search-provider.js +2 -16
- package/dist/core/web-search-provider.js.map +1 -1
- package/dist/main.d.ts +0 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +2 -3
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +1 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +0 -29
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +1 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +0 -25
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +3 -52
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts +0 -2
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +0 -2
- package/dist/utils/version-check.js.map +1 -1
- package/docs/containerization.md +8 -4
- package/docs/sdk.md +17 -24
- package/docs/security.md +7 -5
- package/docs/settings.md +2 -7
- package/docs/skills.md +4 -7
- package/npm-shrinkwrap.json +5 -5
- package/package.json +2 -2
- package/dist/core/sandbox/bwrap-arguments.d.ts +0 -41
- package/dist/core/sandbox/bwrap-arguments.d.ts.map +0 -1
- package/dist/core/sandbox/bwrap-arguments.js +0 -136
- package/dist/core/sandbox/bwrap-arguments.js.map +0 -1
- package/dist/core/sandbox/child-entry.d.ts +0 -2
- package/dist/core/sandbox/child-entry.d.ts.map +0 -1
- package/dist/core/sandbox/child-entry.js +0 -24
- package/dist/core/sandbox/child-entry.js.map +0 -1
- package/dist/core/sandbox/cli-launch.d.ts +0 -162
- package/dist/core/sandbox/cli-launch.d.ts.map +0 -1
- package/dist/core/sandbox/cli-launch.js +0 -366
- package/dist/core/sandbox/cli-launch.js.map +0 -1
- package/dist/core/sandbox/cli-supervisor.d.ts +0 -31
- package/dist/core/sandbox/cli-supervisor.d.ts.map +0 -1
- package/dist/core/sandbox/cli-supervisor.js +0 -147
- package/dist/core/sandbox/cli-supervisor.js.map +0 -1
- package/dist/core/sandbox/default-hosts.d.ts +0 -28
- package/dist/core/sandbox/default-hosts.d.ts.map +0 -1
- package/dist/core/sandbox/default-hosts.js +0 -69
- package/dist/core/sandbox/default-hosts.js.map +0 -1
- package/dist/core/sandbox/full-access.d.ts +0 -28
- package/dist/core/sandbox/full-access.d.ts.map +0 -1
- package/dist/core/sandbox/full-access.js +0 -63
- package/dist/core/sandbox/full-access.js.map +0 -1
- package/dist/core/sandbox/git-identity.d.ts +0 -57
- package/dist/core/sandbox/git-identity.d.ts.map +0 -1
- package/dist/core/sandbox/git-identity.js +0 -78
- package/dist/core/sandbox/git-identity.js.map +0 -1
- package/dist/core/sandbox/host-approval.d.ts +0 -42
- package/dist/core/sandbox/host-approval.d.ts.map +0 -1
- package/dist/core/sandbox/host-approval.js +0 -99
- package/dist/core/sandbox/host-approval.js.map +0 -1
- package/dist/core/sandbox/linux-backend.d.ts +0 -48
- package/dist/core/sandbox/linux-backend.d.ts.map +0 -1
- package/dist/core/sandbox/linux-backend.js +0 -328
- package/dist/core/sandbox/linux-backend.js.map +0 -1
- package/dist/core/sandbox/macos-backend.d.ts +0 -30
- package/dist/core/sandbox/macos-backend.d.ts.map +0 -1
- package/dist/core/sandbox/macos-backend.js +0 -385
- package/dist/core/sandbox/macos-backend.js.map +0 -1
- package/dist/core/sandbox/network-proxy.d.ts +0 -38
- package/dist/core/sandbox/network-proxy.d.ts.map +0 -1
- package/dist/core/sandbox/network-proxy.js +0 -153
- package/dist/core/sandbox/network-proxy.js.map +0 -1
- package/dist/core/sandbox/network-refusal.d.ts +0 -24
- package/dist/core/sandbox/network-refusal.d.ts.map +0 -1
- package/dist/core/sandbox/network-refusal.js +0 -72
- package/dist/core/sandbox/network-refusal.js.map +0 -1
- package/dist/core/sandbox/policy.d.ts +0 -40
- package/dist/core/sandbox/policy.d.ts.map +0 -1
- package/dist/core/sandbox/policy.js +0 -55
- package/dist/core/sandbox/policy.js.map +0 -1
- package/dist/core/sandbox/profiles.d.ts +0 -25
- package/dist/core/sandbox/profiles.d.ts.map +0 -1
- package/dist/core/sandbox/profiles.js +0 -23
- package/dist/core/sandbox/profiles.js.map +0 -1
- package/dist/core/sandbox/rpc/command-client.d.ts +0 -28
- package/dist/core/sandbox/rpc/command-client.d.ts.map +0 -1
- package/dist/core/sandbox/rpc/command-client.js +0 -86
- package/dist/core/sandbox/rpc/command-client.js.map +0 -1
- package/dist/core/sandbox/rpc/command-proxy.d.ts +0 -48
- package/dist/core/sandbox/rpc/command-proxy.d.ts.map +0 -1
- package/dist/core/sandbox/rpc/command-proxy.js +0 -120
- package/dist/core/sandbox/rpc/command-proxy.js.map +0 -1
- package/dist/core/sandbox/rpc/credential-client.d.ts +0 -20
- package/dist/core/sandbox/rpc/credential-client.d.ts.map +0 -1
- package/dist/core/sandbox/rpc/credential-client.js +0 -170
- package/dist/core/sandbox/rpc/credential-client.js.map +0 -1
- package/dist/core/sandbox/rpc/credential-proxy.d.ts +0 -44
- package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +0 -1
- package/dist/core/sandbox/rpc/credential-proxy.js +0 -287
- package/dist/core/sandbox/rpc/credential-proxy.js.map +0 -1
- package/dist/core/sandbox/rpc/framing.d.ts +0 -35
- package/dist/core/sandbox/rpc/framing.d.ts.map +0 -1
- package/dist/core/sandbox/rpc/framing.js +0 -114
- package/dist/core/sandbox/rpc/framing.js.map +0 -1
- package/dist/core/sandbox/rpc/git-credential-helper.d.ts +0 -47
- package/dist/core/sandbox/rpc/git-credential-helper.d.ts.map +0 -1
- package/dist/core/sandbox/rpc/git-credential-helper.js +0 -222
- package/dist/core/sandbox/rpc/git-credential-helper.js.map +0 -1
- package/dist/core/sandbox/rpc/git-credential-proxy.d.ts +0 -66
- package/dist/core/sandbox/rpc/git-credential-proxy.d.ts.map +0 -1
- package/dist/core/sandbox/rpc/git-credential-proxy.js +0 -209
- package/dist/core/sandbox/rpc/git-credential-proxy.js.map +0 -1
- package/dist/core/sandbox/supervisor-temp.d.ts +0 -18
- package/dist/core/sandbox/supervisor-temp.d.ts.map +0 -1
- package/dist/core/sandbox/supervisor-temp.js +0 -21
- package/dist/core/sandbox/supervisor-temp.js.map +0 -1
- package/dist/core/sandbox/supervisor.d.ts +0 -62
- package/dist/core/sandbox/supervisor.d.ts.map +0 -1
- package/dist/core/sandbox/supervisor.js +0 -32
- package/dist/core/sandbox/supervisor.js.map +0 -1
- package/dist/core/sandbox/terminal-handoff.d.ts +0 -68
- package/dist/core/sandbox/terminal-handoff.d.ts.map +0 -1
- package/dist/core/sandbox/terminal-handoff.js +0 -221
- package/dist/core/sandbox/terminal-handoff.js.map +0 -1
- package/dist/core/sandbox/terminal-size.d.ts +0 -53
- package/dist/core/sandbox/terminal-size.d.ts.map +0 -1
- package/dist/core/sandbox/terminal-size.js +0 -126
- package/dist/core/sandbox/terminal-size.js.map +0 -1
- package/dist/core/sandbox/violations.d.ts +0 -23
- package/dist/core/sandbox/violations.d.ts.map +0 -1
- package/dist/core/sandbox/violations.js +0 -30
- package/dist/core/sandbox/violations.js.map +0 -1
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import { AuthStorage } from "../../auth-storage.js";
|
|
9
|
-
import { loadMcpConfig, PROJECT_CONFIG_FILENAME } from "../config.js";
|
|
9
|
+
import { globalMcpConfigPath, loadMcpConfig, PROJECT_CONFIG_FILENAME } from "../config.js";
|
|
10
10
|
import { runMcpOAuthFlow } from "./flow.js";
|
|
11
11
|
export async function authorizeConfiguredServer(options) {
|
|
12
|
-
const { servers } = loadMcpConfig({
|
|
12
|
+
const { servers } = loadMcpConfig({
|
|
13
|
+
projectPath: join(options.cwd ?? process.cwd(), PROJECT_CONFIG_FILENAME),
|
|
14
|
+
globalPath: globalMcpConfigPath(options.agentDir),
|
|
15
|
+
});
|
|
13
16
|
const server = servers.get(options.serverName);
|
|
14
17
|
if (!server) {
|
|
15
18
|
const names = [...servers.keys()];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../../../src/core/mcp/oauth/authorize.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../../../src/core/mcp/oauth/authorize.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAC3F,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAgB5C,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,OAA+B,EAAiB;IAC/F,MAAM,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC;QACjC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,uBAAuB,CAAC;QACxE,UAAU,EAAE,mBAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC;KACjD,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QAClC,MAAM,IAAI,KAAK,CACd,eAAe,OAAO,CAAC,UAAU,4CAA4C,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,8BAA8B,CACrI,CAAC;IACH,CAAC;IACD,IAAI,MAAM,CAAC,SAAS,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,eAAe,OAAO,CAAC,UAAU,0DAA0D,CAAC,CAAC;IAC9G,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACd,eAAe,OAAO,CAAC,UAAU,kEAAkE,CACnG,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,eAAe,CAAC,CAAC;QACtD,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,GAAG;QAC/B,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,WAAW,CAAC,MAAM,EAAE;QACxD,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,CAAC,GAAG,EAAE,CAAC,EAAC,CAAC,CAAC,EAAE;KAC5E,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CACV,eAAe,OAAO,CAAC,UAAU,4BAA4B,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,GAAG,CACxG,CAAC;AAAA,CACF","sourcesContent":["/**\n * Shared entry point for `apex-code mcp auth <server>` and the TUI `/mcp auth`\n * command. Resolves the named server from the merged MCP config, checks it is an\n * OAuth-configured HTTP server, and runs the flow. UI-agnostic: callers supply the\n * line printer and the browser opener.\n */\n\nimport { join } from \"node:path\";\nimport type { CredentialStore } from \"@earendil-works/pi-ai\";\nimport { AuthStorage } from \"../../auth-storage.ts\";\nimport { globalMcpConfigPath, loadMcpConfig, PROJECT_CONFIG_FILENAME } from \"../config.ts\";\nimport { runMcpOAuthFlow } from \"./flow.ts\";\n\nexport interface AuthorizeServerOptions {\n\tserverName: string;\n\t/** Project root for `.mcp.json` discovery. Default: process.cwd(). */\n\tcwd?: string;\n\t/** Agent directory the user-scope `mcp.json` is resolved against. Default: the host's. */\n\tagentDir?: string;\n\t/** Credential sink. Default: the host store (`AuthStorage.create()`). */\n\tcredentials?: CredentialStore;\n\topenBrowser?: (url: string) => void;\n\t/** Injectable flow, for tests. */\n\tflow?: typeof runMcpOAuthFlow;\n\tlog: (line: string) => void;\n}\n\nexport async function authorizeConfiguredServer(options: AuthorizeServerOptions): Promise<void> {\n\tconst { servers } = loadMcpConfig({\n\t\tprojectPath: join(options.cwd ?? process.cwd(), PROJECT_CONFIG_FILENAME),\n\t\tglobalPath: globalMcpConfigPath(options.agentDir),\n\t});\n\tconst server = servers.get(options.serverName);\n\tif (!server) {\n\t\tconst names = [...servers.keys()];\n\t\tthrow new Error(\n\t\t\t`MCP server \"${options.serverName}\" is not configured. Configured servers: ${names.join(\", \") || \"none\"}. Add it to .mcp.json first.`,\n\t\t);\n\t}\n\tif (server.transport.kind !== \"http\") {\n\t\tthrow new Error(`MCP server \"${options.serverName}\" is a stdio server; OAuth applies to HTTP servers only.`);\n\t}\n\tif (server.auth !== \"oauth\") {\n\t\tthrow new Error(\n\t\t\t`MCP server \"${options.serverName}\" is not configured for OAuth. Add \"auth\": \"oauth\" to its entry.`,\n\t\t);\n\t}\n\tconst result = await (options.flow ?? runMcpOAuthFlow)({\n\t\tserverName: options.serverName,\n\t\tserverUrl: server.transport.url,\n\t\tcredentials: options.credentials ?? AuthStorage.create(),\n\t\tdeps: { print: options.log, openBrowser: options.openBrowser ?? (() => {}) },\n\t});\n\toptions.log(\n\t\t`Authorized \"${options.serverName}\". Token stored; expires ${new Date(result.expiresAt).toISOString()}.`,\n\t);\n}\n"]}
|
|
@@ -2,12 +2,10 @@
|
|
|
2
2
|
* The MCP credential seam: how an MCP server's OAuth token is keyed, read, and
|
|
3
3
|
* refreshed through the session's existing `CredentialStore`.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* store whose `modify` throws, which is the fail-closed path: refresh never
|
|
10
|
-
* silently degrades to "pretend it worked".
|
|
5
|
+
* That store is `AuthStorage` (direct, lock-serialized writes) unless an embedder
|
|
6
|
+
* supplies its own. A session with neither has a read-only store whose `modify`
|
|
7
|
+
* throws, which is the fail-closed path: refresh never silently degrades to
|
|
8
|
+
* "pretend it worked".
|
|
11
9
|
*/
|
|
12
10
|
import type { CredentialStore } from "@earendil-works/pi-ai";
|
|
13
11
|
import type { McpServerConfig } from "../types.ts";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-token.d.ts","sourceRoot":"","sources":["../../../../src/core/mcp/oauth/mcp-token.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"mcp-token.d.ts","sourceRoot":"","sources":["../../../../src/core/mcp/oauth/mcp-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAc,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAA+B,KAAK,SAAS,EAAqB,MAAM,eAAe,CAAC;AAW/F,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,0FAA0F;AAC1F,UAAU,cAAc;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;CACvB;AAED,qBAAa,qBAAsB,SAAQ,KAAK;CAAG;AAEnD,qBAAa,oBAAqB,SAAQ,KAAK;IAC9C,6FAA6F;IAC7F,KAAK,EAAE,SAAS,GAAG,aAAa,CAAC;IAEjC,YAAY,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,aAAa,EAG5D;CACD;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,qBAAqB,CAItE;AAqBD,wBAAsB,eAAe,CACpC,WAAW,EAAE,eAAe,EAC5B,UAAU,EAAE,MAAM,GAChB,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAGxC;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE;IACrD,MAAM,EAAE,eAAe,CAAC;IACxB,WAAW,EAAE,eAAe,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC;CAClB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8ElB","sourcesContent":["/**\n * The MCP credential seam: how an MCP server's OAuth token is keyed, read, and\n * refreshed through the session's existing `CredentialStore`.\n *\n * That store is `AuthStorage` (direct, lock-serialized writes) unless an embedder\n * supplies its own. A session with neither has a read-only store whose `modify`\n * throws, which is the fail-closed path: refresh never silently degrades to\n * \"pretend it worked\".\n */\n\nimport type { Credential, CredentialStore } from \"@earendil-works/pi-ai\";\nimport type { McpServerConfig } from \"../types.ts\";\nimport { discoverAuthorizationServer, type FetchLike, oauthErrorMessage } from \"./discover.ts\";\nimport {\n\trefreshAccessToken,\n\tTokenEndpointError,\n\tTokenEndpointUnreachableError,\n\ttype TokenResponse,\n} from \"./token-client.ts\";\n\n/** Refresh this long before expiry so an in-flight call never rides a dying token. */\nconst REFRESH_SKEW_MS = 60_000;\n\nexport function mcpCredentialKey(serverName: string): string {\n\treturn `mcp/${serverName}`;\n}\n\n/** Extra fields carried on the `OAuthCredential` index signature, written by the flow. */\ninterface McpTokenExtras {\n\ttokenEndpoint?: string;\n\tresource?: string;\n\tclientId?: string;\n}\n\nexport interface StoredServerToken {\n\taccessToken: string;\n\t/** Empty when the server issued no refresh token; refresh is impossible then. */\n\trefreshToken: string;\n\texpiresAt: number;\n\textras: McpTokenExtras;\n}\n\nexport class McpOAuthRequiredError extends Error {}\n\nexport class McpOAuthRefreshError extends Error {\n\t/** \"refused\": the server said no; \"unavailable\": the path to the store or network failed. */\n\tstage: \"refused\" | \"unavailable\";\n\n\tconstructor(message: string, stage: \"refused\" | \"unavailable\") {\n\t\tsuper(message);\n\t\tthis.stage = stage;\n\t}\n}\n\nexport function authRequired(serverName: string): McpOAuthRequiredError {\n\treturn new McpOAuthRequiredError(\n\t\t`MCP server \"${serverName}\" requires OAuth and has no stored authorization. Run \\`apex-code mcp auth ${serverName}\\` to authorize it, then retry.`,\n\t);\n}\n\nfunction extrasOf(credential: Credential): McpTokenExtras {\n\tif (credential.type !== \"oauth\") return {};\n\tconst extras: McpTokenExtras = {};\n\tif (typeof credential.tokenEndpoint === \"string\") extras.tokenEndpoint = credential.tokenEndpoint;\n\tif (typeof credential.resource === \"string\") extras.resource = credential.resource;\n\tif (typeof credential.clientId === \"string\") extras.clientId = credential.clientId;\n\treturn extras;\n}\n\nfunction toStoredToken(credential: Credential): StoredServerToken | undefined {\n\tif (credential.type !== \"oauth\") return undefined;\n\treturn {\n\t\taccessToken: credential.access,\n\t\trefreshToken: credential.refresh,\n\t\texpiresAt: credential.expires,\n\t\textras: extrasOf(credential),\n\t};\n}\n\nexport async function readServerToken(\n\tcredentials: CredentialStore,\n\tserverName: string,\n): Promise<StoredServerToken | undefined> {\n\tconst credential: Credential | undefined = await credentials.read(mcpCredentialKey(serverName));\n\treturn credential ? toStoredToken(credential) : undefined;\n}\n\n/**\n * Returns a current access token for the server, refreshing first when the stored one\n * is at the edge of expiry. Throws `McpOAuthRequiredError` when nothing is stored and\n * `McpOAuthRefreshError` when a refresh cannot be completed; both name the auth\n * command in their message.\n */\nexport async function ensureFreshServerToken(options: {\n\tserver: McpServerConfig;\n\tcredentials: CredentialStore;\n\tnow?: () => number;\n\tfetch?: FetchLike;\n}): Promise<string> {\n\tconst { server, credentials } = options;\n\tconst now = options.now ?? Date.now;\n\tconst serverName = server.name;\n\tconst stored = await readServerToken(credentials, serverName);\n\tif (!stored) throw authRequired(serverName);\n\tif (stored.expiresAt - REFRESH_SKEW_MS > now()) return stored.accessToken;\n\tif (!stored.refreshToken) {\n\t\tthrow new McpOAuthRefreshError(\n\t\t\t`MCP server \"${serverName}\" authorization has expired and no refresh token was issued. Run \\`apex-code mcp auth ${serverName}\\` to authorize it again.`,\n\t\t\t\"refused\",\n\t\t);\n\t}\n\tif (server.transport.kind !== \"http\") {\n\t\tthrow new McpOAuthRefreshError(\n\t\t\t`MCP server \"${serverName}\" is not an HTTP server; run \\`apex-code mcp auth ${serverName}\\`.`,\n\t\t\t\"refused\",\n\t\t);\n\t}\n\tconst resource = stored.extras.resource ?? server.transport.url;\n\n\tlet tokenEndpoint = stored.extras.tokenEndpoint;\n\tif (!tokenEndpoint) {\n\t\tconst metadata = await discoverAuthorizationServer({ serverUrl: server.transport.url, fetch: options.fetch });\n\t\ttokenEndpoint = metadata.tokenEndpoint;\n\t}\n\n\tlet refreshed: TokenResponse;\n\ttry {\n\t\trefreshed = await refreshAccessToken({\n\t\t\ttokenEndpoint: new URL(tokenEndpoint),\n\t\t\trefreshToken: stored.refreshToken,\n\t\t\tclientId: stored.extras.clientId ?? `apex-code-mcp-${serverName}`,\n\t\t\tresource,\n\t\t\tfetch: options.fetch,\n\t\t});\n\t} catch (error) {\n\t\tif (error instanceof TokenEndpointError && error.code === \"invalid_grant\") {\n\t\t\tthrow new McpOAuthRefreshError(\n\t\t\t\t`MCP server \"${serverName}\" rejected its refresh token. Run \\`apex-code mcp auth ${serverName}\\` to authorize it again.`,\n\t\t\t\t\"refused\",\n\t\t\t);\n\t\t}\n\t\tif (error instanceof TokenEndpointUnreachableError) {\n\t\t\tthrow new McpOAuthRefreshError(`MCP server \"${serverName}\" refresh failed: ${error.message}`, \"unavailable\");\n\t\t}\n\t\tif (error instanceof TokenEndpointError) {\n\t\t\tthrow new McpOAuthRefreshError(`MCP server \"${serverName}\" refresh failed: ${error.message}`, \"refused\");\n\t\t}\n\t\tthrow new McpOAuthRefreshError(\n\t\t\t`MCP server \"${serverName}\" refresh failed: ${oauthErrorMessage(error)}`,\n\t\t\t\"unavailable\",\n\t\t);\n\t}\n\n\tconst expiresAt = now() + refreshed.expiresInSeconds * 1000 - REFRESH_SKEW_MS;\n\tconst credential: Credential = {\n\t\ttype: \"oauth\",\n\t\taccess: refreshed.accessToken,\n\t\t// No rotation: keep the refresh token the server still honors.\n\t\trefresh: refreshed.refreshToken ?? stored.refreshToken,\n\t\texpires: expiresAt,\n\t\ttokenEndpoint,\n\t\tresource,\n\t\tclientId: stored.extras.clientId,\n\t};\n\ttry {\n\t\tawait credentials.modify(mcpCredentialKey(serverName), async () => credential);\n\t} catch (error) {\n\t\t// The fresh access token works for this connection, but the store could not\n\t\t// record the rotation, which a read-only store does by throwing.\n\t\t// Fail this call closed rather than run ahead of what the store knows.\n\t\tthrow new McpOAuthRefreshError(\n\t\t\t`MCP server \"${serverName}\" was refreshed but the session cannot store the new token: ${oauthErrorMessage(error)}`,\n\t\t\t\"unavailable\",\n\t\t);\n\t}\n\treturn refreshed.accessToken;\n}\n"]}
|
|
@@ -2,12 +2,10 @@
|
|
|
2
2
|
* The MCP credential seam: how an MCP server's OAuth token is keyed, read, and
|
|
3
3
|
* refreshed through the session's existing `CredentialStore`.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* store whose `modify` throws, which is the fail-closed path: refresh never
|
|
10
|
-
* silently degrades to "pretend it worked".
|
|
5
|
+
* That store is `AuthStorage` (direct, lock-serialized writes) unless an embedder
|
|
6
|
+
* supplies its own. A session with neither has a read-only store whose `modify`
|
|
7
|
+
* throws, which is the fail-closed path: refresh never silently degrades to
|
|
8
|
+
* "pretend it worked".
|
|
11
9
|
*/
|
|
12
10
|
import { discoverAuthorizationServer, oauthErrorMessage } from "./discover.js";
|
|
13
11
|
import { refreshAccessToken, TokenEndpointError, TokenEndpointUnreachableError, } from "./token-client.js";
|
|
@@ -120,7 +118,7 @@ export async function ensureFreshServerToken(options) {
|
|
|
120
118
|
}
|
|
121
119
|
catch (error) {
|
|
122
120
|
// The fresh access token works for this connection, but the store could not
|
|
123
|
-
// record the rotation
|
|
121
|
+
// record the rotation, which a read-only store does by throwing.
|
|
124
122
|
// Fail this call closed rather than run ahead of what the store knows.
|
|
125
123
|
throw new McpOAuthRefreshError(`MCP server "${serverName}" was refreshed but the session cannot store the new token: ${oauthErrorMessage(error)}`, "unavailable");
|
|
126
124
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-token.js","sourceRoot":"","sources":["../../../../src/core/mcp/oauth/mcp-token.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The MCP credential seam: how an MCP server's OAuth token is keyed, read, and\n * refreshed through the session's existing `CredentialStore`.\n *\n * Which store instance that is depends on where the session runs. On the host it is\n * `AuthStorage` (direct, lock-serialized writes); in a sandboxed child it is\n * `SandboxAuthStorage` (reads from the read-only projection, writes through the\n * supervisor-mediated channel, ADR 0015). A session with neither has a read-only\n * store whose `modify` throws, which is the fail-closed path: refresh never\n * silently degrades to \"pretend it worked\".\n */\n\nimport type { Credential, CredentialStore } from \"@earendil-works/pi-ai\";\nimport type { McpServerConfig } from \"../types.ts\";\nimport { discoverAuthorizationServer, type FetchLike, oauthErrorMessage } from \"./discover.ts\";\nimport {\n\trefreshAccessToken,\n\tTokenEndpointError,\n\tTokenEndpointUnreachableError,\n\ttype TokenResponse,\n} from \"./token-client.ts\";\n\n/** Refresh this long before expiry so an in-flight call never rides a dying token. */\nconst REFRESH_SKEW_MS = 60_000;\n\nexport function mcpCredentialKey(serverName: string): string {\n\treturn `mcp/${serverName}`;\n}\n\n/** Extra fields carried on the `OAuthCredential` index signature, written by the flow. */\ninterface McpTokenExtras {\n\ttokenEndpoint?: string;\n\tresource?: string;\n\tclientId?: string;\n}\n\nexport interface StoredServerToken {\n\taccessToken: string;\n\t/** Empty when the server issued no refresh token; refresh is impossible then. */\n\trefreshToken: string;\n\texpiresAt: number;\n\textras: McpTokenExtras;\n}\n\nexport class McpOAuthRequiredError extends Error {}\n\nexport class McpOAuthRefreshError extends Error {\n\t/** \"refused\": the server said no; \"unavailable\": the path to the store or network failed. */\n\tstage: \"refused\" | \"unavailable\";\n\n\tconstructor(message: string, stage: \"refused\" | \"unavailable\") {\n\t\tsuper(message);\n\t\tthis.stage = stage;\n\t}\n}\n\nexport function authRequired(serverName: string): McpOAuthRequiredError {\n\treturn new McpOAuthRequiredError(\n\t\t`MCP server \"${serverName}\" requires OAuth and has no stored authorization. Run \\`apex-code mcp auth ${serverName}\\` to authorize it, then retry.`,\n\t);\n}\n\nfunction extrasOf(credential: Credential): McpTokenExtras {\n\tif (credential.type !== \"oauth\") return {};\n\tconst extras: McpTokenExtras = {};\n\tif (typeof credential.tokenEndpoint === \"string\") extras.tokenEndpoint = credential.tokenEndpoint;\n\tif (typeof credential.resource === \"string\") extras.resource = credential.resource;\n\tif (typeof credential.clientId === \"string\") extras.clientId = credential.clientId;\n\treturn extras;\n}\n\nfunction toStoredToken(credential: Credential): StoredServerToken | undefined {\n\tif (credential.type !== \"oauth\") return undefined;\n\treturn {\n\t\taccessToken: credential.access,\n\t\trefreshToken: credential.refresh,\n\t\texpiresAt: credential.expires,\n\t\textras: extrasOf(credential),\n\t};\n}\n\nexport async function readServerToken(\n\tcredentials: CredentialStore,\n\tserverName: string,\n): Promise<StoredServerToken | undefined> {\n\tconst credential: Credential | undefined = await credentials.read(mcpCredentialKey(serverName));\n\treturn credential ? toStoredToken(credential) : undefined;\n}\n\n/**\n * Returns a current access token for the server, refreshing first when the stored one\n * is at the edge of expiry. Throws `McpOAuthRequiredError` when nothing is stored and\n * `McpOAuthRefreshError` when a refresh cannot be completed; both name the auth\n * command in their message.\n */\nexport async function ensureFreshServerToken(options: {\n\tserver: McpServerConfig;\n\tcredentials: CredentialStore;\n\tnow?: () => number;\n\tfetch?: FetchLike;\n}): Promise<string> {\n\tconst { server, credentials } = options;\n\tconst now = options.now ?? Date.now;\n\tconst serverName = server.name;\n\tconst stored = await readServerToken(credentials, serverName);\n\tif (!stored) throw authRequired(serverName);\n\tif (stored.expiresAt - REFRESH_SKEW_MS > now()) return stored.accessToken;\n\tif (!stored.refreshToken) {\n\t\tthrow new McpOAuthRefreshError(\n\t\t\t`MCP server \"${serverName}\" authorization has expired and no refresh token was issued. Run \\`apex-code mcp auth ${serverName}\\` to authorize it again.`,\n\t\t\t\"refused\",\n\t\t);\n\t}\n\tif (server.transport.kind !== \"http\") {\n\t\tthrow new McpOAuthRefreshError(\n\t\t\t`MCP server \"${serverName}\" is not an HTTP server; run \\`apex-code mcp auth ${serverName}\\`.`,\n\t\t\t\"refused\",\n\t\t);\n\t}\n\tconst resource = stored.extras.resource ?? server.transport.url;\n\n\tlet tokenEndpoint = stored.extras.tokenEndpoint;\n\tif (!tokenEndpoint) {\n\t\tconst metadata = await discoverAuthorizationServer({ serverUrl: server.transport.url, fetch: options.fetch });\n\t\ttokenEndpoint = metadata.tokenEndpoint;\n\t}\n\n\tlet refreshed: TokenResponse;\n\ttry {\n\t\trefreshed = await refreshAccessToken({\n\t\t\ttokenEndpoint: new URL(tokenEndpoint),\n\t\t\trefreshToken: stored.refreshToken,\n\t\t\tclientId: stored.extras.clientId ?? `apex-code-mcp-${serverName}`,\n\t\t\tresource,\n\t\t\tfetch: options.fetch,\n\t\t});\n\t} catch (error) {\n\t\tif (error instanceof TokenEndpointError && error.code === \"invalid_grant\") {\n\t\t\tthrow new McpOAuthRefreshError(\n\t\t\t\t`MCP server \"${serverName}\" rejected its refresh token. Run \\`apex-code mcp auth ${serverName}\\` to authorize it again.`,\n\t\t\t\t\"refused\",\n\t\t\t);\n\t\t}\n\t\tif (error instanceof TokenEndpointUnreachableError) {\n\t\t\tthrow new McpOAuthRefreshError(`MCP server \"${serverName}\" refresh failed: ${error.message}`, \"unavailable\");\n\t\t}\n\t\tif (error instanceof TokenEndpointError) {\n\t\t\tthrow new McpOAuthRefreshError(`MCP server \"${serverName}\" refresh failed: ${error.message}`, \"refused\");\n\t\t}\n\t\tthrow new McpOAuthRefreshError(\n\t\t\t`MCP server \"${serverName}\" refresh failed: ${oauthErrorMessage(error)}`,\n\t\t\t\"unavailable\",\n\t\t);\n\t}\n\n\tconst expiresAt = now() + refreshed.expiresInSeconds * 1000 - REFRESH_SKEW_MS;\n\tconst credential: Credential = {\n\t\ttype: \"oauth\",\n\t\taccess: refreshed.accessToken,\n\t\t// No rotation: keep the refresh token the server still honors.\n\t\trefresh: refreshed.refreshToken ?? stored.refreshToken,\n\t\texpires: expiresAt,\n\t\ttokenEndpoint,\n\t\tresource,\n\t\tclientId: stored.extras.clientId,\n\t};\n\ttry {\n\t\tawait credentials.modify(mcpCredentialKey(serverName), async () => credential);\n\t} catch (error) {\n\t\t// The fresh access token works for this connection, but the store could not\n\t\t// record the rotation — for a sandboxed child, that is the channel refusing.\n\t\t// Fail this call closed rather than run ahead of what the store knows.\n\t\tthrow new McpOAuthRefreshError(\n\t\t\t`MCP server \"${serverName}\" was refreshed but the session cannot store the new token: ${oauthErrorMessage(error)}`,\n\t\t\t\"unavailable\",\n\t\t);\n\t}\n\treturn refreshed.accessToken;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mcp-token.js","sourceRoot":"","sources":["../../../../src/core/mcp/oauth/mcp-token.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The MCP credential seam: how an MCP server's OAuth token is keyed, read, and\n * refreshed through the session's existing `CredentialStore`.\n *\n * That store is `AuthStorage` (direct, lock-serialized writes) unless an embedder\n * supplies its own. A session with neither has a read-only store whose `modify`\n * throws, which is the fail-closed path: refresh never silently degrades to\n * \"pretend it worked\".\n */\n\nimport type { Credential, CredentialStore } from \"@earendil-works/pi-ai\";\nimport type { McpServerConfig } from \"../types.ts\";\nimport { discoverAuthorizationServer, type FetchLike, oauthErrorMessage } from \"./discover.ts\";\nimport {\n\trefreshAccessToken,\n\tTokenEndpointError,\n\tTokenEndpointUnreachableError,\n\ttype TokenResponse,\n} from \"./token-client.ts\";\n\n/** Refresh this long before expiry so an in-flight call never rides a dying token. */\nconst REFRESH_SKEW_MS = 60_000;\n\nexport function mcpCredentialKey(serverName: string): string {\n\treturn `mcp/${serverName}`;\n}\n\n/** Extra fields carried on the `OAuthCredential` index signature, written by the flow. */\ninterface McpTokenExtras {\n\ttokenEndpoint?: string;\n\tresource?: string;\n\tclientId?: string;\n}\n\nexport interface StoredServerToken {\n\taccessToken: string;\n\t/** Empty when the server issued no refresh token; refresh is impossible then. */\n\trefreshToken: string;\n\texpiresAt: number;\n\textras: McpTokenExtras;\n}\n\nexport class McpOAuthRequiredError extends Error {}\n\nexport class McpOAuthRefreshError extends Error {\n\t/** \"refused\": the server said no; \"unavailable\": the path to the store or network failed. */\n\tstage: \"refused\" | \"unavailable\";\n\n\tconstructor(message: string, stage: \"refused\" | \"unavailable\") {\n\t\tsuper(message);\n\t\tthis.stage = stage;\n\t}\n}\n\nexport function authRequired(serverName: string): McpOAuthRequiredError {\n\treturn new McpOAuthRequiredError(\n\t\t`MCP server \"${serverName}\" requires OAuth and has no stored authorization. Run \\`apex-code mcp auth ${serverName}\\` to authorize it, then retry.`,\n\t);\n}\n\nfunction extrasOf(credential: Credential): McpTokenExtras {\n\tif (credential.type !== \"oauth\") return {};\n\tconst extras: McpTokenExtras = {};\n\tif (typeof credential.tokenEndpoint === \"string\") extras.tokenEndpoint = credential.tokenEndpoint;\n\tif (typeof credential.resource === \"string\") extras.resource = credential.resource;\n\tif (typeof credential.clientId === \"string\") extras.clientId = credential.clientId;\n\treturn extras;\n}\n\nfunction toStoredToken(credential: Credential): StoredServerToken | undefined {\n\tif (credential.type !== \"oauth\") return undefined;\n\treturn {\n\t\taccessToken: credential.access,\n\t\trefreshToken: credential.refresh,\n\t\texpiresAt: credential.expires,\n\t\textras: extrasOf(credential),\n\t};\n}\n\nexport async function readServerToken(\n\tcredentials: CredentialStore,\n\tserverName: string,\n): Promise<StoredServerToken | undefined> {\n\tconst credential: Credential | undefined = await credentials.read(mcpCredentialKey(serverName));\n\treturn credential ? toStoredToken(credential) : undefined;\n}\n\n/**\n * Returns a current access token for the server, refreshing first when the stored one\n * is at the edge of expiry. Throws `McpOAuthRequiredError` when nothing is stored and\n * `McpOAuthRefreshError` when a refresh cannot be completed; both name the auth\n * command in their message.\n */\nexport async function ensureFreshServerToken(options: {\n\tserver: McpServerConfig;\n\tcredentials: CredentialStore;\n\tnow?: () => number;\n\tfetch?: FetchLike;\n}): Promise<string> {\n\tconst { server, credentials } = options;\n\tconst now = options.now ?? Date.now;\n\tconst serverName = server.name;\n\tconst stored = await readServerToken(credentials, serverName);\n\tif (!stored) throw authRequired(serverName);\n\tif (stored.expiresAt - REFRESH_SKEW_MS > now()) return stored.accessToken;\n\tif (!stored.refreshToken) {\n\t\tthrow new McpOAuthRefreshError(\n\t\t\t`MCP server \"${serverName}\" authorization has expired and no refresh token was issued. Run \\`apex-code mcp auth ${serverName}\\` to authorize it again.`,\n\t\t\t\"refused\",\n\t\t);\n\t}\n\tif (server.transport.kind !== \"http\") {\n\t\tthrow new McpOAuthRefreshError(\n\t\t\t`MCP server \"${serverName}\" is not an HTTP server; run \\`apex-code mcp auth ${serverName}\\`.`,\n\t\t\t\"refused\",\n\t\t);\n\t}\n\tconst resource = stored.extras.resource ?? server.transport.url;\n\n\tlet tokenEndpoint = stored.extras.tokenEndpoint;\n\tif (!tokenEndpoint) {\n\t\tconst metadata = await discoverAuthorizationServer({ serverUrl: server.transport.url, fetch: options.fetch });\n\t\ttokenEndpoint = metadata.tokenEndpoint;\n\t}\n\n\tlet refreshed: TokenResponse;\n\ttry {\n\t\trefreshed = await refreshAccessToken({\n\t\t\ttokenEndpoint: new URL(tokenEndpoint),\n\t\t\trefreshToken: stored.refreshToken,\n\t\t\tclientId: stored.extras.clientId ?? `apex-code-mcp-${serverName}`,\n\t\t\tresource,\n\t\t\tfetch: options.fetch,\n\t\t});\n\t} catch (error) {\n\t\tif (error instanceof TokenEndpointError && error.code === \"invalid_grant\") {\n\t\t\tthrow new McpOAuthRefreshError(\n\t\t\t\t`MCP server \"${serverName}\" rejected its refresh token. Run \\`apex-code mcp auth ${serverName}\\` to authorize it again.`,\n\t\t\t\t\"refused\",\n\t\t\t);\n\t\t}\n\t\tif (error instanceof TokenEndpointUnreachableError) {\n\t\t\tthrow new McpOAuthRefreshError(`MCP server \"${serverName}\" refresh failed: ${error.message}`, \"unavailable\");\n\t\t}\n\t\tif (error instanceof TokenEndpointError) {\n\t\t\tthrow new McpOAuthRefreshError(`MCP server \"${serverName}\" refresh failed: ${error.message}`, \"refused\");\n\t\t}\n\t\tthrow new McpOAuthRefreshError(\n\t\t\t`MCP server \"${serverName}\" refresh failed: ${oauthErrorMessage(error)}`,\n\t\t\t\"unavailable\",\n\t\t);\n\t}\n\n\tconst expiresAt = now() + refreshed.expiresInSeconds * 1000 - REFRESH_SKEW_MS;\n\tconst credential: Credential = {\n\t\ttype: \"oauth\",\n\t\taccess: refreshed.accessToken,\n\t\t// No rotation: keep the refresh token the server still honors.\n\t\trefresh: refreshed.refreshToken ?? stored.refreshToken,\n\t\texpires: expiresAt,\n\t\ttokenEndpoint,\n\t\tresource,\n\t\tclientId: stored.extras.clientId,\n\t};\n\ttry {\n\t\tawait credentials.modify(mcpCredentialKey(serverName), async () => credential);\n\t} catch (error) {\n\t\t// The fresh access token works for this connection, but the store could not\n\t\t// record the rotation, which a read-only store does by throwing.\n\t\t// Fail this call closed rather than run ahead of what the store knows.\n\t\tthrow new McpOAuthRefreshError(\n\t\t\t`MCP server \"${serverName}\" was refreshed but the session cannot store the new token: ${oauthErrorMessage(error)}`,\n\t\t\t\"unavailable\",\n\t\t);\n\t}\n\treturn refreshed.accessToken;\n}\n"]}
|
|
@@ -16,5 +16,6 @@ export interface McpRuntime extends McpToolOptions {
|
|
|
16
16
|
}
|
|
17
17
|
export declare function createMcpRuntime(cwd: string, connector?: McpConnector, options?: {
|
|
18
18
|
projectTrusted?: boolean;
|
|
19
|
+
agentDir?: string;
|
|
19
20
|
}): McpRuntime | undefined;
|
|
20
21
|
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEpE,MAAM,WAAW,UAAW,SAAQ,cAAc;IACjD,WAAW,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC5C,oFAAoF;IACpF,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,wBAAgB,gBAAgB,CAC/B,GAAG,EAAE,MAAM,EACX,SAAS,GAAE,YAA+B,EAC1C,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEpE,MAAM,WAAW,UAAW,SAAQ,cAAc;IACjD,WAAW,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC5C,oFAAoF;IACpF,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,wBAAgB,gBAAgB,CAC/B,GAAG,EAAE,MAAM,EACX,SAAS,GAAE,YAA+B,EAC1C,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAC3D,UAAU,GAAG,SAAS,CAyBxB","sourcesContent":["/**\n * Assembles the MCP subsystem for one session, or returns undefined when nothing is\n * configured.\n *\n * The undefined case is the point. A session with no `.mcp.json` builds no cache, no\n * manager, and no tool, so its registry and static prompt prefix are identical to a\n * build from before this subsystem existed.\n */\n\nimport { join } from \"node:path\";\nimport { globalMcpConfigPath, loadMcpConfig, PROJECT_CONFIG_FILENAME } from \"./config.ts\";\nimport { connectMcpServer } from \"./connector.ts\";\nimport type { McpToolOptions } from \"./mcp-tool.ts\";\nimport { McpMetadataCache } from \"./metadata-cache.ts\";\nimport { McpServerManager } from \"./server-manager.ts\";\nimport type { McpConfigDiagnostic, McpConnector } from \"./types.ts\";\n\nexport interface McpRuntime extends McpToolOptions {\n\tdiagnostics: readonly McpConfigDiagnostic[];\n\t/** Connect `eager` servers and cache their tools, so they are discoverable cold. */\n\twarm(): Promise<void>;\n\tclose(): Promise<void>;\n}\n\nexport function createMcpRuntime(\n\tcwd: string,\n\tconnector: McpConnector = connectMcpServer,\n\toptions: { projectTrusted?: boolean; agentDir?: string } = {},\n): McpRuntime | undefined {\n\tconst { servers, diagnostics } = loadMcpConfig({\n\t\tprojectPath: options.projectTrusted === false ? undefined : join(cwd, PROJECT_CONFIG_FILENAME),\n\t\tglobalPath: globalMcpConfigPath(options.agentDir),\n\t});\n\tif (servers.size === 0) return undefined;\n\n\tconst cache = new McpMetadataCache();\n\t// A server removed from config stops appearing in search immediately, rather than\n\t// lingering until something happens to overwrite its cache entry.\n\tcache.prune([...servers.values()]);\n\n\tconst manager = new McpServerManager({ servers, connector });\n\n\treturn {\n\t\tservers,\n\t\tcache,\n\t\tmanager,\n\t\tdiagnostics,\n\t\twarm: async () => {\n\t\t\tawait manager.warmEagerServers((server, tools) => cache.set(server, tools));\n\t\t\tcache.save();\n\t\t},\n\t\tclose: () => manager.closeAll(),\n\t};\n}\n"]}
|
package/dist/core/mcp/runtime.js
CHANGED
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
* build from before this subsystem existed.
|
|
8
8
|
*/
|
|
9
9
|
import { join } from "node:path";
|
|
10
|
-
import { loadMcpConfig, PROJECT_CONFIG_FILENAME } from "./config.js";
|
|
10
|
+
import { globalMcpConfigPath, loadMcpConfig, PROJECT_CONFIG_FILENAME } from "./config.js";
|
|
11
11
|
import { connectMcpServer } from "./connector.js";
|
|
12
12
|
import { McpMetadataCache } from "./metadata-cache.js";
|
|
13
13
|
import { McpServerManager } from "./server-manager.js";
|
|
14
14
|
export function createMcpRuntime(cwd, connector = connectMcpServer, options = {}) {
|
|
15
15
|
const { servers, diagnostics } = loadMcpConfig({
|
|
16
16
|
projectPath: options.projectTrusted === false ? undefined : join(cwd, PROJECT_CONFIG_FILENAME),
|
|
17
|
+
globalPath: globalMcpConfigPath(options.agentDir),
|
|
17
18
|
});
|
|
18
19
|
if (servers.size === 0)
|
|
19
20
|
return undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/core/mcp/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/core/mcp/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC1F,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAUvD,MAAM,UAAU,gBAAgB,CAC/B,GAAW,EACX,SAAS,GAAiB,gBAAgB,EAC1C,OAAO,GAAoD,EAAE,EACpC;IACzB,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,aAAa,CAAC;QAC9C,WAAW,EAAE,OAAO,CAAC,cAAc,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,uBAAuB,CAAC;QAC9F,UAAU,EAAE,mBAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC;KACjD,CAAC,CAAC;IACH,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAEzC,MAAM,KAAK,GAAG,IAAI,gBAAgB,EAAE,CAAC;IACrC,kFAAkF;IAClF,kEAAkE;IAClE,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAEnC,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;IAE7D,OAAO;QACN,OAAO;QACP,KAAK;QACL,OAAO;QACP,WAAW;QACX,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YACjB,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;YAC5E,KAAK,CAAC,IAAI,EAAE,CAAC;QAAA,CACb;QACD,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE;KAC/B,CAAC;AAAA,CACF","sourcesContent":["/**\n * Assembles the MCP subsystem for one session, or returns undefined when nothing is\n * configured.\n *\n * The undefined case is the point. A session with no `.mcp.json` builds no cache, no\n * manager, and no tool, so its registry and static prompt prefix are identical to a\n * build from before this subsystem existed.\n */\n\nimport { join } from \"node:path\";\nimport { globalMcpConfigPath, loadMcpConfig, PROJECT_CONFIG_FILENAME } from \"./config.ts\";\nimport { connectMcpServer } from \"./connector.ts\";\nimport type { McpToolOptions } from \"./mcp-tool.ts\";\nimport { McpMetadataCache } from \"./metadata-cache.ts\";\nimport { McpServerManager } from \"./server-manager.ts\";\nimport type { McpConfigDiagnostic, McpConnector } from \"./types.ts\";\n\nexport interface McpRuntime extends McpToolOptions {\n\tdiagnostics: readonly McpConfigDiagnostic[];\n\t/** Connect `eager` servers and cache their tools, so they are discoverable cold. */\n\twarm(): Promise<void>;\n\tclose(): Promise<void>;\n}\n\nexport function createMcpRuntime(\n\tcwd: string,\n\tconnector: McpConnector = connectMcpServer,\n\toptions: { projectTrusted?: boolean; agentDir?: string } = {},\n): McpRuntime | undefined {\n\tconst { servers, diagnostics } = loadMcpConfig({\n\t\tprojectPath: options.projectTrusted === false ? undefined : join(cwd, PROJECT_CONFIG_FILENAME),\n\t\tglobalPath: globalMcpConfigPath(options.agentDir),\n\t});\n\tif (servers.size === 0) return undefined;\n\n\tconst cache = new McpMetadataCache();\n\t// A server removed from config stops appearing in search immediately, rather than\n\t// lingering until something happens to overwrite its cache entry.\n\tcache.prune([...servers.values()]);\n\n\tconst manager = new McpServerManager({ servers, connector });\n\n\treturn {\n\t\tservers,\n\t\tcache,\n\t\tmanager,\n\t\tdiagnostics,\n\t\twarm: async () => {\n\t\t\tawait manager.warmEagerServers((server, tools) => cache.set(server, tools));\n\t\t\tcache.save();\n\t\t},\n\t\tclose: () => manager.closeAll(),\n\t};\n}\n"]}
|