@agimon-ai/doompi-mcp 0.0.1-alpha.2 → 0.0.1-alpha.20
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/README.md +21 -14
- package/dist/adapters/node/configSources.cjs +2 -0
- package/dist/adapters/node/configSources.cjs.map +1 -0
- package/dist/adapters/node/configSources.d.cts +16 -0
- package/dist/adapters/node/configSources.d.cts.map +1 -0
- package/dist/adapters/node/configSources.d.mts +16 -0
- package/dist/adapters/node/configSources.d.mts.map +1 -0
- package/dist/adapters/node/configSources.mjs +2 -0
- package/dist/adapters/node/configSources.mjs.map +1 -0
- package/dist/adapters/node/keyringTokenStore.cjs.map +1 -0
- package/dist/adapters/node/keyringTokenStore.mjs.map +1 -0
- package/dist/{services → adapters/node}/mcpRuntime.cjs +1 -1
- package/dist/adapters/node/mcpRuntime.cjs.map +1 -0
- package/dist/{services → adapters/node}/mcpRuntime.d.cts +4 -11
- package/dist/adapters/node/mcpRuntime.d.cts.map +1 -0
- package/dist/{services → adapters/node}/mcpRuntime.d.mts +4 -11
- package/dist/adapters/node/mcpRuntime.d.mts.map +1 -0
- package/dist/{services → adapters/node}/mcpRuntime.mjs +1 -1
- package/dist/adapters/node/mcpRuntime.mjs.map +1 -0
- package/dist/adapters/pi/doomExtension.cjs +2 -0
- package/dist/adapters/pi/doomExtension.cjs.map +1 -0
- package/dist/adapters/pi/doomExtension.d.cts +7 -0
- package/dist/adapters/pi/doomExtension.d.cts.map +1 -0
- package/dist/adapters/pi/doomExtension.d.mts +7 -0
- package/dist/adapters/pi/doomExtension.d.mts.map +1 -0
- package/dist/adapters/pi/doomExtension.mjs +2 -0
- package/dist/adapters/pi/doomExtension.mjs.map +1 -0
- package/dist/adapters/pi/extension.cjs +2 -0
- package/dist/adapters/pi/extension.cjs.map +1 -0
- package/dist/{extension.d.cts → adapters/pi/extension.d.cts} +3 -2
- package/dist/adapters/pi/extension.d.cts.map +1 -0
- package/dist/{extension.d.mts → adapters/pi/extension.d.mts} +3 -2
- package/dist/adapters/pi/extension.d.mts.map +1 -0
- package/dist/adapters/pi/extension.mjs +2 -0
- package/dist/adapters/pi/extension.mjs.map +1 -0
- package/dist/adapters/pi/leader.cjs +2 -0
- package/dist/adapters/pi/leader.cjs.map +1 -0
- package/dist/adapters/pi/leader.mjs +2 -0
- package/dist/adapters/pi/leader.mjs.map +1 -0
- package/dist/adapters/pi/mcpConstants.cjs +2 -0
- package/dist/adapters/pi/mcpConstants.cjs.map +1 -0
- package/dist/adapters/pi/mcpConstants.mjs +2 -0
- package/dist/adapters/pi/mcpConstants.mjs.map +1 -0
- package/dist/adapters/pi/mcpSession.cjs +2 -0
- package/dist/adapters/pi/mcpSession.cjs.map +1 -0
- package/dist/adapters/pi/mcpSession.mjs +2 -0
- package/dist/adapters/pi/mcpSession.mjs.map +1 -0
- package/dist/adapters/pi/mcpTools.cjs +3 -0
- package/dist/adapters/pi/mcpTools.cjs.map +1 -0
- package/dist/adapters/pi/mcpTools.mjs +3 -0
- package/dist/adapters/pi/mcpTools.mjs.map +1 -0
- package/dist/adapters/process/directToolsEnvironment.cjs +2 -0
- package/dist/adapters/process/directToolsEnvironment.cjs.map +1 -0
- package/dist/adapters/process/directToolsEnvironment.mjs +2 -0
- package/dist/adapters/process/directToolsEnvironment.mjs.map +1 -0
- package/dist/adapters/process/sessionConfig.cjs +2 -0
- package/dist/adapters/process/sessionConfig.cjs.map +1 -0
- package/dist/adapters/process/sessionConfig.d.cts +16 -0
- package/dist/adapters/process/sessionConfig.d.cts.map +1 -0
- package/dist/adapters/process/sessionConfig.d.mts +16 -0
- package/dist/adapters/process/sessionConfig.d.mts.map +1 -0
- package/dist/adapters/process/sessionConfig.mjs +2 -0
- package/dist/adapters/process/sessionConfig.mjs.map +1 -0
- package/dist/commands/mcpCommand.cjs +2 -2
- package/dist/commands/mcpCommand.cjs.map +1 -1
- package/dist/commands/mcpCommand.mjs +2 -2
- package/dist/commands/mcpCommand.mjs.map +1 -1
- package/dist/extensions/doom.cjs +1 -2
- package/dist/extensions/doom.d.cts +2 -6
- package/dist/extensions/doom.d.mts +2 -7
- package/dist/extensions/doom.mjs +1 -2
- package/dist/extensions/pi.cjs +1 -2
- package/dist/extensions/pi.d.cts +1 -1
- package/dist/extensions/pi.d.mts +1 -1
- package/dist/extensions/pi.mjs +1 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +8 -5
- package/dist/index.d.mts +8 -5
- package/dist/index.mjs +1 -1
- package/dist/schemas/directTools.cjs +2 -0
- package/dist/schemas/directTools.cjs.map +1 -0
- package/dist/schemas/directTools.d.cts +8 -0
- package/dist/schemas/directTools.d.cts.map +1 -0
- package/dist/schemas/directTools.d.mts +8 -0
- package/dist/schemas/directTools.d.mts.map +1 -0
- package/dist/schemas/directTools.mjs +2 -0
- package/dist/schemas/directTools.mjs.map +1 -0
- package/dist/schemas/sessionConfig.cjs +2 -0
- package/dist/schemas/sessionConfig.cjs.map +1 -0
- package/dist/schemas/sessionConfig.d.cts +6 -0
- package/dist/schemas/sessionConfig.d.cts.map +1 -0
- package/dist/schemas/sessionConfig.d.mts +6 -0
- package/dist/schemas/sessionConfig.d.mts.map +1 -0
- package/dist/schemas/sessionConfig.mjs +2 -0
- package/dist/schemas/sessionConfig.mjs.map +1 -0
- package/dist/services/directTools.cjs +1 -1
- package/dist/services/directTools.cjs.map +1 -1
- package/dist/services/directTools.mjs +1 -1
- package/dist/services/directTools.mjs.map +1 -1
- package/dist/services/mcpCatalog.cjs.map +1 -1
- package/dist/services/mcpCatalog.d.cts +3 -4
- package/dist/services/mcpCatalog.d.cts.map +1 -1
- package/dist/services/mcpCatalog.d.mts +3 -4
- package/dist/services/mcpCatalog.d.mts.map +1 -1
- package/dist/services/mcpCatalog.mjs.map +1 -1
- package/dist/tui/mcpOverlay.cjs +1 -1
- package/dist/tui/mcpOverlay.cjs.map +1 -1
- package/dist/tui/mcpOverlay.mjs +1 -1
- package/dist/tui/mcpOverlay.mjs.map +1 -1
- package/dist/tui/mcpToolRender.cjs +3 -0
- package/dist/tui/mcpToolRender.cjs.map +1 -0
- package/dist/tui/mcpToolRender.mjs +3 -0
- package/dist/tui/mcpToolRender.mjs.map +1 -0
- package/dist/types/mcp.d.cts +1 -0
- package/dist/types/mcp.d.mts +1 -0
- package/dist/types/mcpCatalog.d.cts +1 -0
- package/dist/types/mcpCatalog.d.mts +1 -0
- package/dist/types/mcpConfig.d.cts +38 -0
- package/dist/types/mcpConfig.d.cts.map +1 -0
- package/dist/types/mcpConfig.d.mts +38 -0
- package/dist/types/mcpConfig.d.mts.map +1 -0
- package/dist/types/mcpRuntime.d.cts +13 -0
- package/dist/types/mcpRuntime.d.cts.map +1 -0
- package/dist/types/mcpRuntime.d.mts +13 -0
- package/dist/types/mcpRuntime.d.mts.map +1 -0
- package/package.json +18 -9
- package/dist/constants.cjs +0 -2
- package/dist/constants.cjs.map +0 -1
- package/dist/constants.mjs +0 -2
- package/dist/constants.mjs.map +0 -1
- package/dist/extension.cjs +0 -2
- package/dist/extension.cjs.map +0 -1
- package/dist/extension.d.cts.map +0 -1
- package/dist/extension.d.mts.map +0 -1
- package/dist/extension.mjs +0 -2
- package/dist/extension.mjs.map +0 -1
- package/dist/extensions/doom.cjs.map +0 -1
- package/dist/extensions/doom.d.cts.map +0 -1
- package/dist/extensions/doom.d.mts.map +0 -1
- package/dist/extensions/doom.mjs.map +0 -1
- package/dist/extensions/pi.cjs.map +0 -1
- package/dist/extensions/pi.mjs.map +0 -1
- package/dist/leader.cjs +0 -2
- package/dist/leader.cjs.map +0 -1
- package/dist/leader.mjs +0 -2
- package/dist/leader.mjs.map +0 -1
- package/dist/services/configSources.cjs +0 -2
- package/dist/services/configSources.cjs.map +0 -1
- package/dist/services/configSources.d.cts +0 -65
- package/dist/services/configSources.d.cts.map +0 -1
- package/dist/services/configSources.d.mts +0 -65
- package/dist/services/configSources.d.mts.map +0 -1
- package/dist/services/configSources.mjs +0 -2
- package/dist/services/configSources.mjs.map +0 -1
- package/dist/services/directTools.d.cts +0 -14
- package/dist/services/directTools.d.cts.map +0 -1
- package/dist/services/directTools.d.mts +0 -14
- package/dist/services/directTools.d.mts.map +0 -1
- package/dist/services/keyringTokenStore.cjs.map +0 -1
- package/dist/services/keyringTokenStore.mjs.map +0 -1
- package/dist/services/mcpRuntime.cjs.map +0 -1
- package/dist/services/mcpRuntime.d.cts.map +0 -1
- package/dist/services/mcpRuntime.d.mts.map +0 -1
- package/dist/services/mcpRuntime.mjs.map +0 -1
- package/dist/services/mcpSession.cjs +0 -2
- package/dist/services/mcpSession.cjs.map +0 -1
- package/dist/services/mcpSession.mjs +0 -2
- package/dist/services/mcpSession.mjs.map +0 -1
- package/dist/services/sessionConfig.cjs +0 -2
- package/dist/services/sessionConfig.cjs.map +0 -1
- package/dist/services/sessionConfig.d.cts +0 -25
- package/dist/services/sessionConfig.d.cts.map +0 -1
- package/dist/services/sessionConfig.d.mts +0 -25
- package/dist/services/sessionConfig.d.mts.map +0 -1
- package/dist/services/sessionConfig.mjs +0 -2
- package/dist/services/sessionConfig.mjs.map +0 -1
- package/dist/services/sharedRuntime.cjs +0 -2
- package/dist/services/sharedRuntime.cjs.map +0 -1
- package/dist/services/sharedRuntime.mjs +0 -2
- package/dist/services/sharedRuntime.mjs.map +0 -1
- package/dist/tools/mcpTools.cjs +0 -3
- package/dist/tools/mcpTools.cjs.map +0 -1
- package/dist/tools/mcpTools.mjs +0 -3
- package/dist/tools/mcpTools.mjs.map +0 -1
- package/dist/types.d.cts +0 -2
- package/dist/types.d.mts +0 -2
- /package/dist/{services → adapters/node}/keyringTokenStore.cjs +0 -0
- /package/dist/{services → adapters/node}/keyringTokenStore.mjs +0 -0
package/README.md
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
# @agimon-ai/doompi-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**A session should not start a server it cannot use.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
This is Doompi's MCP gate. It reads repository and plugin MCP config, applies the selected
|
|
6
|
+
domains' allowlist, and removes disallowed servers before any stdio process is spawned. A
|
|
7
|
+
filtered server is absent, not merely hidden from the model.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
Direct servers come from `.mcp.json`. Proxy upstreams come from `mcp-config.yaml`. Doompi
|
|
10
|
+
can filter both, emit the resolved config for Pi, and rebuild it when domains change.
|
|
11
|
+
Outside Doompi, the adapter still works as a plain Pi extension and uses the repository's
|
|
12
|
+
`.mcp.json` without a domain allowlist.
|
|
13
|
+
|
|
14
|
+
This is part of [Doompi](https://www.npmjs.com/package/@agimon-ai/doompi). Most users get
|
|
15
|
+
it with the distribution.
|
|
11
16
|
|
|
12
17
|
## Install
|
|
13
18
|
|
|
@@ -17,18 +22,20 @@ npm install @agimon-ai/doompi-mcp
|
|
|
17
22
|
|
|
18
23
|
## How it loads
|
|
19
24
|
|
|
20
|
-
Doompi loads
|
|
21
|
-
|
|
25
|
+
Doompi loads the adapter as core; server access is what the domain selection makes
|
|
26
|
+
optional. The package does not belong in `.doom/modes.yaml`.
|
|
22
27
|
|
|
23
|
-
|
|
28
|
+
The bare package name follows its Pi manifest and selects the Doom adapter. Use
|
|
29
|
+
`/extensions/pi` for a standalone Pi session or `/extensions/doom` when selecting the Doom
|
|
30
|
+
integration explicitly.
|
|
24
31
|
|
|
25
32
|
## Entry points
|
|
26
33
|
|
|
27
|
-
| Import |
|
|
28
|
-
| --------------------------------------- |
|
|
29
|
-
| `@agimon-ai/doompi-mcp` |
|
|
30
|
-
| `@agimon-ai/doompi-mcp/extensions/pi` |
|
|
31
|
-
| `@agimon-ai/doompi-mcp/extensions/doom` |
|
|
34
|
+
| Import | Purpose |
|
|
35
|
+
| --------------------------------------- | --------------------- |
|
|
36
|
+
| `@agimon-ai/doompi-mcp` | Library API |
|
|
37
|
+
| `@agimon-ai/doompi-mcp/extensions/pi` | Standalone Pi adapter |
|
|
38
|
+
| `@agimon-ai/doompi-mcp/extensions/doom` | Doompi adapter |
|
|
32
39
|
|
|
33
40
|
## License
|
|
34
41
|
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../../_virtual/_rolldown/runtime.cjs");let t=require("node:crypto"),n=require("node:fs");n=e.__toESM(n,1);let r=require("node:path");r=e.__toESM(r,1);let i=require("yaml");const a=`mcp-proxy`,o=new Set([a,`agiflow-proxy`]);function s(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function c(e){let t=r.default.extname(e).toLowerCase();return t===`.yaml`||t===`.yml`}function l(e){try{let t=n.default.readFileSync(e,`utf8`),r=c(e)?(0,i.parse)(t):JSON.parse(t);return s(r)?r:void 0}catch{return}}function u(e){return s(e?.mcpServers)?e.mcpServers:{}}function d(e){return!s(e)||e.disabled!==!0}function f(e){return o.has(e)}function p(e){let t=u(l(r.default.join(e,`.mcp.json`))),i=[...o].map(e=>t[e]).find(e=>s(e))?.args,a=Array.isArray(i)?i:[],c=a.indexOf(`--config`),d=a[c+1],f=c>=0&&typeof d==`string`?r.default.resolve(e,d):r.default.join(e,`mcp-config.yaml`);return n.default.existsSync(f)?f:void 0}function m(e,t){return!t||t.length===0||t.includes(e)}function h(e,n,i){let a=(0,t.createHash)(`sha256`).update(e).update(`\0`).update(n).digest(`hex`).slice(0,16),o=c(e)?`.yaml`:`.json`;return r.default.join(i,`mcp-${a}${o}`)}function g(e,t){return c(e)?(0,i.stringify)(t):`${JSON.stringify(t,null,2)}\n`}function _(e,t,r,i){let a=l(e);if(!a)return;let o=u(a),s={},c=[];for(let[e,n]of Object.entries(o))i.stripProxyWrappers&&f(e)||!m(e,t)?c.push(e):s[e]=n;let p=Object.entries(s).filter(([,e])=>d(e)).map(([e])=>e);if(c.length===0)return{path:e,kept:p,dropped:c};n.default.mkdirSync(r,{mode:448,recursive:!0});let _=g(e,{...a,mcpServers:s}),v=h(e,_,r);return n.default.writeFileSync(v,_,{mode:384}),{path:v,kept:p,dropped:c}}function v(e){let t=[r.default.join(e.repoRoot,`.mcp.json`),...(e.pluginConfigPaths??[]).map(t=>r.default.resolve(e.repoRoot,t))].filter(e=>n.default.existsSync(e)),i=[],a=[],o=[];for(let n of t){let t=_(n,e.allowlist?.servers,e.stagingDirectory,{stripProxyWrappers:!0});t&&(i.push(t.path),a.push(...t.kept),o.push(...t.dropped))}let s=p(e.repoRoot),c=s?_(s,e.allowlist?.proxy,e.stagingDirectory,{stripProxyWrappers:!0}):void 0;return c&&o.push(...c.dropped),{shared:{configPaths:c?[c.path]:[],serverNames:c?c.kept:[]},sessionLocal:{configPaths:i,serverNames:[...new Set(a)]},droppedServers:[...new Set(o)]}}exports.PROXY_SERVER_NAME=a,exports.buildMcpConfigGroups=v,exports.resolveSharedConfigPath=p;
|
|
2
|
+
//# sourceMappingURL=configSources.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configSources.cjs","names":["path","fs","parseYaml","createHash","stringifyYaml"],"sources":["../../../src/adapters/node/configSources.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\nimport type { McpConfigGroups, McpConfigGroupsInput } from '../../types/mcpConfig.ts';\n\n/** Wrapper entry used by non-Doom clients to expose `mcp-config.yaml` as one MCP. */\nexport const PROXY_SERVER_NAME = 'mcp-proxy';\n\nconst PROXY_SERVER_ALIASES = new Set([PROXY_SERVER_NAME, 'agiflow-proxy']);\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst DEFAULT_UPSTREAM_CONFIG = 'mcp-config.yaml';\nconst CONFIG_ARGUMENT = '--config';\n\ntype JsonObject = Record<string, unknown>;\n\ninterface FilteredLayer {\n path: string;\n kept: string[];\n dropped: string[];\n}\n\nfunction isObject(value: unknown): value is JsonObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isYamlPath(filePath: string): boolean {\n const extension = path.extname(filePath).toLowerCase();\n return extension === '.yaml' || extension === '.yml';\n}\n\nfunction readConfigObject(filePath: string): JsonObject | undefined {\n try {\n const content = fs.readFileSync(filePath, 'utf8');\n const parsed: unknown = isYamlPath(filePath) ? parseYaml(content) : JSON.parse(content);\n return isObject(parsed) ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction serverDefinitions(config: JsonObject | undefined): Record<string, unknown> {\n return isObject(config?.mcpServers) ? config.mcpServers : {};\n}\n\nfunction isEnabledDefinition(definition: unknown): boolean {\n return !isObject(definition) || definition.disabled !== true;\n}\n\nfunction isProxyWrapper(name: string): boolean {\n return PROXY_SERVER_ALIASES.has(name);\n}\n\n/**\n * Resolves the upstream config referenced by the repository proxy wrapper.\n *\n * The wrapper remains useful to Claude, Codex, and other external clients. Doom\n * embeds the same client/runtime code, so it reads this path and connects each\n * declared server itself instead of starting the wrapper as a nested MCP.\n */\nexport function resolveSharedConfigPath(repoRoot: string): string | undefined {\n const servers = serverDefinitions(readConfigObject(path.join(repoRoot, '.mcp.json')));\n const proxy = [...PROXY_SERVER_ALIASES]\n .map((name) => servers[name])\n .find((definition): definition is JsonObject => isObject(definition));\n const rawArgs: unknown = proxy?.args;\n const args = Array.isArray(rawArgs) ? (rawArgs as unknown[]) : [];\n const configArgumentIndex = args.indexOf(CONFIG_ARGUMENT);\n const declared = args[configArgumentIndex + 1];\n const candidate =\n configArgumentIndex >= 0 && typeof declared === 'string'\n ? path.resolve(repoRoot, declared)\n : path.join(repoRoot, DEFAULT_UPSTREAM_CONFIG);\n return fs.existsSync(candidate) ? candidate : undefined;\n}\n\n/** Empty and absent allowlists both mean \"keep everything\". */\nfunction isAllowed(name: string, allowed: string[] | undefined): boolean {\n return !allowed || allowed.length === 0 || allowed.includes(name);\n}\n\nfunction stagedPath(filePath: string, contents: string, stagingDirectory: string): string {\n // The filtered bytes are part of the identity because two live sessions can\n // select different domains. Sharing a path would let the later one rewrite the\n // configuration underneath the first one's deferred startup.\n const digest = createHash('sha256').update(filePath).update('\\0').update(contents).digest('hex').slice(0, 16);\n const extension = isYamlPath(filePath) ? '.yaml' : '.json';\n return path.join(stagingDirectory, `mcp-${digest}${extension}`);\n}\n\nfunction serializeConfig(filePath: string, config: JsonObject): string {\n return isYamlPath(filePath) ? stringifyYaml(config) : `${JSON.stringify(config, null, 2)}\\n`;\n}\n\n/**\n * Rewrites one config layer with disallowed servers removed.\n *\n * Proxy wrapper entries are stripped from `.mcp.json` layers because Doom reaches\n * their upstream config directly. Entries are removed rather than marked disabled,\n * so a filtered stdio server is never spawned at all.\n */\nfunction filterLayer(\n filePath: string,\n allowed: string[] | undefined,\n stagingDirectory: string,\n options: { stripProxyWrappers: boolean },\n): FilteredLayer | undefined {\n const config = readConfigObject(filePath);\n if (!config) return undefined;\n const servers = serverDefinitions(config);\n const kept: Record<string, unknown> = {};\n const dropped: string[] = [];\n\n for (const [name, definition] of Object.entries(servers)) {\n const shouldDrop = (options.stripProxyWrappers && isProxyWrapper(name)) || !isAllowed(name, allowed);\n if (shouldDrop) dropped.push(name);\n else kept[name] = definition;\n }\n\n const keptNames = Object.entries(kept)\n .filter(([, definition]) => isEnabledDefinition(definition))\n .map(([name]) => name);\n if (dropped.length === 0) return { path: filePath, kept: keptNames, dropped };\n\n fs.mkdirSync(stagingDirectory, { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n const contents = serializeConfig(filePath, { ...config, mcpServers: kept });\n const target = stagedPath(filePath, contents, stagingDirectory);\n fs.writeFileSync(target, contents, { mode: PRIVATE_FILE_MODE });\n return { path: target, kept: keptNames, dropped };\n}\n\n/**\n * Resolves every config layer for this session and applies its domain allowlist.\n *\n * All returned layers go into one embedded `@agimon-ai/mcp-proxy` client container.\n * The package is used as a library only: its wrapper MCP never becomes a Doom MCP\n * server, so status, enable/disable, resources, and OAuth stay attached to each\n * actual downstream server.\n */\nexport function buildMcpConfigGroups(input: McpConfigGroupsInput): McpConfigGroups {\n const layers = [\n path.join(input.repoRoot, '.mcp.json'),\n ...(input.pluginConfigPaths ?? []).map((candidate) => path.resolve(input.repoRoot, candidate)),\n ].filter((candidate) => fs.existsSync(candidate));\n\n const configPaths: string[] = [];\n const serverNames: string[] = [];\n const droppedServers: string[] = [];\n for (const layer of layers) {\n const filtered = filterLayer(layer, input.allowlist?.servers, input.stagingDirectory, {\n stripProxyWrappers: true,\n });\n if (!filtered) continue;\n configPaths.push(filtered.path);\n serverNames.push(...filtered.kept);\n droppedServers.push(...filtered.dropped);\n }\n\n const upstreamPath = resolveSharedConfigPath(input.repoRoot);\n const upstream = upstreamPath\n ? filterLayer(upstreamPath, input.allowlist?.proxy, input.stagingDirectory, { stripProxyWrappers: true })\n : undefined;\n if (upstream) droppedServers.push(...upstream.dropped);\n\n return {\n shared: {\n configPaths: upstream ? [upstream.path] : [],\n serverNames: upstream ? upstream.kept : [],\n },\n // A later layer overriding an earlier one contributes the same name twice.\n sessionLocal: { configPaths, serverNames: [...new Set(serverNames)] },\n droppedServers: [...new Set(droppedServers)],\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { McpConfigGroups, McpConfigGroupsInput } from "../../types/mcpConfig.cjs";
|
|
2
|
+
//#region src/adapters/node/configSources.d.ts
|
|
3
|
+
/** Wrapper entry used by non-Doom clients to expose `mcp-config.yaml` as one MCP. */
|
|
4
|
+
declare const PROXY_SERVER_NAME = "mcp-proxy";
|
|
5
|
+
/**
|
|
6
|
+
* Resolves every config layer for this session and applies its domain allowlist.
|
|
7
|
+
*
|
|
8
|
+
* All returned layers go into one embedded `@agimon-ai/mcp-proxy` client container.
|
|
9
|
+
* The package is used as a library only: its wrapper MCP never becomes a Doom MCP
|
|
10
|
+
* server, so status, enable/disable, resources, and OAuth stay attached to each
|
|
11
|
+
* actual downstream server.
|
|
12
|
+
*/
|
|
13
|
+
declare function buildMcpConfigGroups(input: McpConfigGroupsInput): McpConfigGroups;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { PROXY_SERVER_NAME, buildMcpConfigGroups };
|
|
16
|
+
//# sourceMappingURL=configSources.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configSources.d.cts","names":[],"sources":["../../../src/adapters/node/configSources.ts"],"mappings":";;;cAOa;;;;;;;;;iBAqIG,qBAAqB,OAAO,uBAAuB"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { McpConfigGroups, McpConfigGroupsInput } from "../../types/mcpConfig.mjs";
|
|
2
|
+
//#region src/adapters/node/configSources.d.ts
|
|
3
|
+
/** Wrapper entry used by non-Doom clients to expose `mcp-config.yaml` as one MCP. */
|
|
4
|
+
declare const PROXY_SERVER_NAME = "mcp-proxy";
|
|
5
|
+
/**
|
|
6
|
+
* Resolves every config layer for this session and applies its domain allowlist.
|
|
7
|
+
*
|
|
8
|
+
* All returned layers go into one embedded `@agimon-ai/mcp-proxy` client container.
|
|
9
|
+
* The package is used as a library only: its wrapper MCP never becomes a Doom MCP
|
|
10
|
+
* server, so status, enable/disable, resources, and OAuth stay attached to each
|
|
11
|
+
* actual downstream server.
|
|
12
|
+
*/
|
|
13
|
+
declare function buildMcpConfigGroups(input: McpConfigGroupsInput): McpConfigGroups;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { PROXY_SERVER_NAME, buildMcpConfigGroups };
|
|
16
|
+
//# sourceMappingURL=configSources.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configSources.d.mts","names":[],"sources":["../../../src/adapters/node/configSources.ts"],"mappings":";;;cAOa;;;;;;;;;iBAqIG,qBAAqB,OAAO,uBAAuB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createHash as e}from"node:crypto";import t from"node:fs";import n from"node:path";import{parse as r,stringify as i}from"yaml";const a=`mcp-proxy`,o=new Set([a,`agiflow-proxy`]);function s(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function c(e){let t=n.extname(e).toLowerCase();return t===`.yaml`||t===`.yml`}function l(e){try{let n=t.readFileSync(e,`utf8`),i=c(e)?r(n):JSON.parse(n);return s(i)?i:void 0}catch{return}}function u(e){return s(e?.mcpServers)?e.mcpServers:{}}function d(e){return!s(e)||e.disabled!==!0}function f(e){return o.has(e)}function p(e){let r=u(l(n.join(e,`.mcp.json`))),i=[...o].map(e=>r[e]).find(e=>s(e))?.args,a=Array.isArray(i)?i:[],c=a.indexOf(`--config`),d=a[c+1],f=c>=0&&typeof d==`string`?n.resolve(e,d):n.join(e,`mcp-config.yaml`);return t.existsSync(f)?f:void 0}function m(e,t){return!t||t.length===0||t.includes(e)}function h(t,r,i){let a=e(`sha256`).update(t).update(`\0`).update(r).digest(`hex`).slice(0,16),o=c(t)?`.yaml`:`.json`;return n.join(i,`mcp-${a}${o}`)}function g(e,t){return c(e)?i(t):`${JSON.stringify(t,null,2)}\n`}function _(e,n,r,i){let a=l(e);if(!a)return;let o=u(a),s={},c=[];for(let[e,t]of Object.entries(o))i.stripProxyWrappers&&f(e)||!m(e,n)?c.push(e):s[e]=t;let p=Object.entries(s).filter(([,e])=>d(e)).map(([e])=>e);if(c.length===0)return{path:e,kept:p,dropped:c};t.mkdirSync(r,{mode:448,recursive:!0});let _=g(e,{...a,mcpServers:s}),v=h(e,_,r);return t.writeFileSync(v,_,{mode:384}),{path:v,kept:p,dropped:c}}function v(e){let r=[n.join(e.repoRoot,`.mcp.json`),...(e.pluginConfigPaths??[]).map(t=>n.resolve(e.repoRoot,t))].filter(e=>t.existsSync(e)),i=[],a=[],o=[];for(let t of r){let n=_(t,e.allowlist?.servers,e.stagingDirectory,{stripProxyWrappers:!0});n&&(i.push(n.path),a.push(...n.kept),o.push(...n.dropped))}let s=p(e.repoRoot),c=s?_(s,e.allowlist?.proxy,e.stagingDirectory,{stripProxyWrappers:!0}):void 0;return c&&o.push(...c.dropped),{shared:{configPaths:c?[c.path]:[],serverNames:c?c.kept:[]},sessionLocal:{configPaths:i,serverNames:[...new Set(a)]},droppedServers:[...new Set(o)]}}export{a as PROXY_SERVER_NAME,v as buildMcpConfigGroups,p as resolveSharedConfigPath};
|
|
2
|
+
//# sourceMappingURL=configSources.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configSources.mjs","names":["parseYaml","stringifyYaml"],"sources":["../../../src/adapters/node/configSources.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\nimport type { McpConfigGroups, McpConfigGroupsInput } from '../../types/mcpConfig.ts';\n\n/** Wrapper entry used by non-Doom clients to expose `mcp-config.yaml` as one MCP. */\nexport const PROXY_SERVER_NAME = 'mcp-proxy';\n\nconst PROXY_SERVER_ALIASES = new Set([PROXY_SERVER_NAME, 'agiflow-proxy']);\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst DEFAULT_UPSTREAM_CONFIG = 'mcp-config.yaml';\nconst CONFIG_ARGUMENT = '--config';\n\ntype JsonObject = Record<string, unknown>;\n\ninterface FilteredLayer {\n path: string;\n kept: string[];\n dropped: string[];\n}\n\nfunction isObject(value: unknown): value is JsonObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isYamlPath(filePath: string): boolean {\n const extension = path.extname(filePath).toLowerCase();\n return extension === '.yaml' || extension === '.yml';\n}\n\nfunction readConfigObject(filePath: string): JsonObject | undefined {\n try {\n const content = fs.readFileSync(filePath, 'utf8');\n const parsed: unknown = isYamlPath(filePath) ? parseYaml(content) : JSON.parse(content);\n return isObject(parsed) ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction serverDefinitions(config: JsonObject | undefined): Record<string, unknown> {\n return isObject(config?.mcpServers) ? config.mcpServers : {};\n}\n\nfunction isEnabledDefinition(definition: unknown): boolean {\n return !isObject(definition) || definition.disabled !== true;\n}\n\nfunction isProxyWrapper(name: string): boolean {\n return PROXY_SERVER_ALIASES.has(name);\n}\n\n/**\n * Resolves the upstream config referenced by the repository proxy wrapper.\n *\n * The wrapper remains useful to Claude, Codex, and other external clients. Doom\n * embeds the same client/runtime code, so it reads this path and connects each\n * declared server itself instead of starting the wrapper as a nested MCP.\n */\nexport function resolveSharedConfigPath(repoRoot: string): string | undefined {\n const servers = serverDefinitions(readConfigObject(path.join(repoRoot, '.mcp.json')));\n const proxy = [...PROXY_SERVER_ALIASES]\n .map((name) => servers[name])\n .find((definition): definition is JsonObject => isObject(definition));\n const rawArgs: unknown = proxy?.args;\n const args = Array.isArray(rawArgs) ? (rawArgs as unknown[]) : [];\n const configArgumentIndex = args.indexOf(CONFIG_ARGUMENT);\n const declared = args[configArgumentIndex + 1];\n const candidate =\n configArgumentIndex >= 0 && typeof declared === 'string'\n ? path.resolve(repoRoot, declared)\n : path.join(repoRoot, DEFAULT_UPSTREAM_CONFIG);\n return fs.existsSync(candidate) ? candidate : undefined;\n}\n\n/** Empty and absent allowlists both mean \"keep everything\". */\nfunction isAllowed(name: string, allowed: string[] | undefined): boolean {\n return !allowed || allowed.length === 0 || allowed.includes(name);\n}\n\nfunction stagedPath(filePath: string, contents: string, stagingDirectory: string): string {\n // The filtered bytes are part of the identity because two live sessions can\n // select different domains. Sharing a path would let the later one rewrite the\n // configuration underneath the first one's deferred startup.\n const digest = createHash('sha256').update(filePath).update('\\0').update(contents).digest('hex').slice(0, 16);\n const extension = isYamlPath(filePath) ? '.yaml' : '.json';\n return path.join(stagingDirectory, `mcp-${digest}${extension}`);\n}\n\nfunction serializeConfig(filePath: string, config: JsonObject): string {\n return isYamlPath(filePath) ? stringifyYaml(config) : `${JSON.stringify(config, null, 2)}\\n`;\n}\n\n/**\n * Rewrites one config layer with disallowed servers removed.\n *\n * Proxy wrapper entries are stripped from `.mcp.json` layers because Doom reaches\n * their upstream config directly. Entries are removed rather than marked disabled,\n * so a filtered stdio server is never spawned at all.\n */\nfunction filterLayer(\n filePath: string,\n allowed: string[] | undefined,\n stagingDirectory: string,\n options: { stripProxyWrappers: boolean },\n): FilteredLayer | undefined {\n const config = readConfigObject(filePath);\n if (!config) return undefined;\n const servers = serverDefinitions(config);\n const kept: Record<string, unknown> = {};\n const dropped: string[] = [];\n\n for (const [name, definition] of Object.entries(servers)) {\n const shouldDrop = (options.stripProxyWrappers && isProxyWrapper(name)) || !isAllowed(name, allowed);\n if (shouldDrop) dropped.push(name);\n else kept[name] = definition;\n }\n\n const keptNames = Object.entries(kept)\n .filter(([, definition]) => isEnabledDefinition(definition))\n .map(([name]) => name);\n if (dropped.length === 0) return { path: filePath, kept: keptNames, dropped };\n\n fs.mkdirSync(stagingDirectory, { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n const contents = serializeConfig(filePath, { ...config, mcpServers: kept });\n const target = stagedPath(filePath, contents, stagingDirectory);\n fs.writeFileSync(target, contents, { mode: PRIVATE_FILE_MODE });\n return { path: target, kept: keptNames, dropped };\n}\n\n/**\n * Resolves every config layer for this session and applies its domain allowlist.\n *\n * All returned layers go into one embedded `@agimon-ai/mcp-proxy` client container.\n * The package is used as a library only: its wrapper MCP never becomes a Doom MCP\n * server, so status, enable/disable, resources, and OAuth stay attached to each\n * actual downstream server.\n */\nexport function buildMcpConfigGroups(input: McpConfigGroupsInput): McpConfigGroups {\n const layers = [\n path.join(input.repoRoot, '.mcp.json'),\n ...(input.pluginConfigPaths ?? []).map((candidate) => path.resolve(input.repoRoot, candidate)),\n ].filter((candidate) => fs.existsSync(candidate));\n\n const configPaths: string[] = [];\n const serverNames: string[] = [];\n const droppedServers: string[] = [];\n for (const layer of layers) {\n const filtered = filterLayer(layer, input.allowlist?.servers, input.stagingDirectory, {\n stripProxyWrappers: true,\n });\n if (!filtered) continue;\n configPaths.push(filtered.path);\n serverNames.push(...filtered.kept);\n droppedServers.push(...filtered.dropped);\n }\n\n const upstreamPath = resolveSharedConfigPath(input.repoRoot);\n const upstream = upstreamPath\n ? filterLayer(upstreamPath, input.allowlist?.proxy, input.stagingDirectory, { stripProxyWrappers: true })\n : undefined;\n if (upstream) droppedServers.push(...upstream.dropped);\n\n return {\n shared: {\n configPaths: upstream ? [upstream.path] : [],\n serverNames: upstream ? upstream.kept : [],\n },\n // A later layer overriding an earlier one contributes the same name twice.\n sessionLocal: { configPaths, serverNames: [...new Set(serverNames)] },\n droppedServers: [...new Set(droppedServers)],\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyringTokenStore.cjs","names":["FileTokenStore"],"sources":["../../../src/adapters/node/keyringTokenStore.ts"],"sourcesContent":["import { type AuthEntry, FileTokenStore, type TokenStore } from '@agimon-ai/mcp-proxy';\n\n/** Keyring service name. One credential per downstream MCP server. */\nexport const KEYRING_SERVICE = 'ai.agimon.doom-mcp.oauth';\n\n/** The slice of `@napi-rs/keyring`'s `AsyncEntry` this store uses. */\nexport interface KeyringEntry {\n getPassword(): Promise<string | undefined>;\n setPassword(password: string): Promise<void>;\n deletePassword(): Promise<unknown>;\n}\n\nexport type KeyringEntryFactory = (service: string, account: string) => KeyringEntry;\n\n/**\n * OAuth credentials in the OS keyring, with an owner-only file store behind it.\n *\n * The keyring is a native module and the platform service it talks to may be absent\n * (a headless Linux box with no secret service, an unsupported architecture). A\n * missing credential is reported as `undefined` rather than raised, so a rejection\n * means the keyring itself is unusable and the store degrades to the file fallback\n * for the rest of the session instead of failing the run.\n */\nexport class KeyringTokenStore implements TokenStore {\n private degraded = false;\n\n constructor(\n private readonly createEntry: KeyringEntryFactory,\n private readonly fallback: TokenStore = new FileTokenStore(),\n ) {}\n\n /** True once the keyring has failed and the file store has taken over. */\n get isDegraded(): boolean {\n return this.degraded;\n }\n\n async read(serverName: string): Promise<AuthEntry | undefined> {\n if (this.degraded) return this.fallback.read(serverName);\n let raw: string | undefined;\n try {\n raw = await this.createEntry(KEYRING_SERVICE, serverName).getPassword();\n } catch {\n this.degraded = true;\n return this.fallback.read(serverName);\n }\n if (!raw) return undefined;\n try {\n return JSON.parse(raw) as AuthEntry;\n } catch {\n // A credential we cannot parse is treated as absent so the next authorization\n // overwrites it, rather than wedging the server on every start.\n return undefined;\n }\n }\n\n async write(serverName: string, entry: AuthEntry): Promise<void> {\n if (!this.degraded) {\n try {\n await this.createEntry(KEYRING_SERVICE, serverName).setPassword(JSON.stringify(entry));\n return;\n } catch {\n this.degraded = true;\n }\n }\n await this.fallback.write(serverName, entry);\n }\n\n /**\n * Removes the credential from both stores.\n *\n * A session that degraded part way through can have left a copy in either place,\n * and \"signed out\" has to mean no credential survives anywhere. A keyring that\n * refuses the delete is reported rather than absorbed: the caller was told the\n * credential is gone, and it is not. The fallback is still cleared first so the\n * failure leaves one surviving copy instead of two.\n */\n async clear(serverName: string): Promise<void> {\n let keyringFailure: unknown;\n if (!this.degraded) {\n try {\n await this.createEntry(KEYRING_SERVICE, serverName).deletePassword();\n } catch (error) {\n keyringFailure = error;\n }\n }\n await this.fallback.clear(serverName);\n if (keyringFailure !== undefined) {\n const detail = keyringFailure instanceof Error ? keyringFailure.message : JSON.stringify(keyringFailure);\n throw new Error(`Keyring credential for \"${serverName}\" could not be removed: ${detail}`, {\n cause: keyringFailure,\n });\n }\n }\n}\n\n/**\n * Builds the token store for this host, falling back when the native module is absent.\n *\n * doom-mcp loads inside Pi, so a platform without a prebuilt keyring binary must\n * still get a working session rather than a failed extension install.\n */\nexport async function createTokenStore(fallback: TokenStore = new FileTokenStore()): Promise<TokenStore> {\n try {\n const { AsyncEntry } = await import('@napi-rs/keyring');\n return new KeyringTokenStore((service, account) => new AsyncEntry(service, account), fallback);\n } catch {\n return fallback;\n }\n}\n"],"mappings":"sCAGA,MAAa,EAAkB,2BAoB/B,IAAa,EAAb,KAAqD,CAIhC,YACA,SAJnB,SAAmB,GAEnB,YACE,EACA,EAAwC,IAAIA,EAAAA,eAC5C,CAFiB,KAAA,YAAA,EACA,KAAA,SAAA,CAChB,CAGH,IAAI,YAAsB,CACxB,OAAO,KAAK,QACd,CAEA,MAAM,KAAK,EAAoD,CAC7D,GAAI,KAAK,SAAU,OAAO,KAAK,SAAS,KAAK,CAAU,EACvD,IAAI,EACJ,GAAI,CACF,EAAM,MAAM,KAAK,YAAY,EAAiB,CAAU,CAAC,CAAC,YAAY,CACxE,MAAQ,CAEN,MADA,MAAK,SAAW,GACT,KAAK,SAAS,KAAK,CAAU,CACtC,CACK,KACL,GAAI,CACF,OAAO,KAAK,MAAM,CAAG,CACvB,MAAQ,CAGN,MACF,CACF,CAEA,MAAM,MAAM,EAAoB,EAAiC,CAC/D,GAAI,CAAC,KAAK,SACR,GAAI,CACF,MAAM,KAAK,YAAY,EAAiB,CAAU,CAAC,CAAC,YAAY,KAAK,UAAU,CAAK,CAAC,EACrF,MACF,MAAQ,CACN,KAAK,SAAW,EAClB,CAEF,MAAM,KAAK,SAAS,MAAM,EAAY,CAAK,CAC7C,CAWA,MAAM,MAAM,EAAmC,CAC7C,IAAI,EACJ,GAAI,CAAC,KAAK,SACR,GAAI,CACF,MAAM,KAAK,YAAY,EAAiB,CAAU,CAAC,CAAC,eAAe,CACrE,OAAS,EAAO,CACd,EAAiB,CACnB,CAGF,GADA,MAAM,KAAK,SAAS,MAAM,CAAU,EAChC,IAAmB,IAAA,GAAW,CAChC,IAAM,EAAS,aAA0B,MAAQ,EAAe,QAAU,KAAK,UAAU,CAAc,EACvG,MAAU,MAAM,2BAA2B,EAAW,0BAA0B,IAAU,CACxF,MAAO,CACT,CAAC,CACH,CACF,CACF,EAQA,eAAsB,EAAiB,EAAuB,IAAIA,EAAAA,eAAuC,CACvG,GAAI,CACF,GAAM,CAAE,cAAe,MAAM,OAAO,oBACpC,OAAO,IAAI,GAAmB,EAAS,IAAY,IAAI,EAAW,EAAS,CAAO,EAAG,CAAQ,CAC/F,MAAQ,CACN,OAAO,CACT,CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyringTokenStore.mjs","names":[],"sources":["../../../src/adapters/node/keyringTokenStore.ts"],"sourcesContent":["import { type AuthEntry, FileTokenStore, type TokenStore } from '@agimon-ai/mcp-proxy';\n\n/** Keyring service name. One credential per downstream MCP server. */\nexport const KEYRING_SERVICE = 'ai.agimon.doom-mcp.oauth';\n\n/** The slice of `@napi-rs/keyring`'s `AsyncEntry` this store uses. */\nexport interface KeyringEntry {\n getPassword(): Promise<string | undefined>;\n setPassword(password: string): Promise<void>;\n deletePassword(): Promise<unknown>;\n}\n\nexport type KeyringEntryFactory = (service: string, account: string) => KeyringEntry;\n\n/**\n * OAuth credentials in the OS keyring, with an owner-only file store behind it.\n *\n * The keyring is a native module and the platform service it talks to may be absent\n * (a headless Linux box with no secret service, an unsupported architecture). A\n * missing credential is reported as `undefined` rather than raised, so a rejection\n * means the keyring itself is unusable and the store degrades to the file fallback\n * for the rest of the session instead of failing the run.\n */\nexport class KeyringTokenStore implements TokenStore {\n private degraded = false;\n\n constructor(\n private readonly createEntry: KeyringEntryFactory,\n private readonly fallback: TokenStore = new FileTokenStore(),\n ) {}\n\n /** True once the keyring has failed and the file store has taken over. */\n get isDegraded(): boolean {\n return this.degraded;\n }\n\n async read(serverName: string): Promise<AuthEntry | undefined> {\n if (this.degraded) return this.fallback.read(serverName);\n let raw: string | undefined;\n try {\n raw = await this.createEntry(KEYRING_SERVICE, serverName).getPassword();\n } catch {\n this.degraded = true;\n return this.fallback.read(serverName);\n }\n if (!raw) return undefined;\n try {\n return JSON.parse(raw) as AuthEntry;\n } catch {\n // A credential we cannot parse is treated as absent so the next authorization\n // overwrites it, rather than wedging the server on every start.\n return undefined;\n }\n }\n\n async write(serverName: string, entry: AuthEntry): Promise<void> {\n if (!this.degraded) {\n try {\n await this.createEntry(KEYRING_SERVICE, serverName).setPassword(JSON.stringify(entry));\n return;\n } catch {\n this.degraded = true;\n }\n }\n await this.fallback.write(serverName, entry);\n }\n\n /**\n * Removes the credential from both stores.\n *\n * A session that degraded part way through can have left a copy in either place,\n * and \"signed out\" has to mean no credential survives anywhere. A keyring that\n * refuses the delete is reported rather than absorbed: the caller was told the\n * credential is gone, and it is not. The fallback is still cleared first so the\n * failure leaves one surviving copy instead of two.\n */\n async clear(serverName: string): Promise<void> {\n let keyringFailure: unknown;\n if (!this.degraded) {\n try {\n await this.createEntry(KEYRING_SERVICE, serverName).deletePassword();\n } catch (error) {\n keyringFailure = error;\n }\n }\n await this.fallback.clear(serverName);\n if (keyringFailure !== undefined) {\n const detail = keyringFailure instanceof Error ? keyringFailure.message : JSON.stringify(keyringFailure);\n throw new Error(`Keyring credential for \"${serverName}\" could not be removed: ${detail}`, {\n cause: keyringFailure,\n });\n }\n }\n}\n\n/**\n * Builds the token store for this host, falling back when the native module is absent.\n *\n * doom-mcp loads inside Pi, so a platform without a prebuilt keyring binary must\n * still get a working session rather than a failed extension install.\n */\nexport async function createTokenStore(fallback: TokenStore = new FileTokenStore()): Promise<TokenStore> {\n try {\n const { AsyncEntry } = await import('@napi-rs/keyring');\n return new KeyringTokenStore((service, account) => new AsyncEntry(service, account), fallback);\n } catch {\n return fallback;\n }\n}\n"],"mappings":"sDAGA,MAAa,EAAkB,2BAoB/B,IAAa,EAAb,KAAqD,CAIhC,YACA,SAJnB,SAAmB,GAEnB,YACE,EACA,EAAwC,IAAI,EAC5C,CAFiB,KAAA,YAAA,EACA,KAAA,SAAA,CAChB,CAGH,IAAI,YAAsB,CACxB,OAAO,KAAK,QACd,CAEA,MAAM,KAAK,EAAoD,CAC7D,GAAI,KAAK,SAAU,OAAO,KAAK,SAAS,KAAK,CAAU,EACvD,IAAI,EACJ,GAAI,CACF,EAAM,MAAM,KAAK,YAAY,EAAiB,CAAU,CAAC,CAAC,YAAY,CACxE,MAAQ,CAEN,MADA,MAAK,SAAW,GACT,KAAK,SAAS,KAAK,CAAU,CACtC,CACK,KACL,GAAI,CACF,OAAO,KAAK,MAAM,CAAG,CACvB,MAAQ,CAGN,MACF,CACF,CAEA,MAAM,MAAM,EAAoB,EAAiC,CAC/D,GAAI,CAAC,KAAK,SACR,GAAI,CACF,MAAM,KAAK,YAAY,EAAiB,CAAU,CAAC,CAAC,YAAY,KAAK,UAAU,CAAK,CAAC,EACrF,MACF,MAAQ,CACN,KAAK,SAAW,EAClB,CAEF,MAAM,KAAK,SAAS,MAAM,EAAY,CAAK,CAC7C,CAWA,MAAM,MAAM,EAAmC,CAC7C,IAAI,EACJ,GAAI,CAAC,KAAK,SACR,GAAI,CACF,MAAM,KAAK,YAAY,EAAiB,CAAU,CAAC,CAAC,eAAe,CACrE,OAAS,EAAO,CACd,EAAiB,CACnB,CAGF,GADA,MAAM,KAAK,SAAS,MAAM,CAAU,EAChC,IAAmB,IAAA,GAAW,CAChC,IAAM,EAAS,aAA0B,MAAQ,EAAe,QAAU,KAAK,UAAU,CAAc,EACvG,MAAU,MAAM,2BAA2B,EAAW,0BAA0B,IAAU,CACxF,MAAO,CACT,CAAC,CACH,CACF,CACF,EAQA,eAAsB,EAAiB,EAAuB,IAAI,EAAuC,CACvG,GAAI,CACF,GAAM,CAAE,cAAe,MAAM,OAAO,oBACpC,OAAO,IAAI,GAAmB,EAAS,IAAY,IAAI,EAAW,EAAS,CAAO,EAAG,CAAQ,CAC/F,MAAQ,CACN,OAAO,CACT,CACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
let e=require("node:
|
|
1
|
+
let e=require("node:crypto"),t=require("node:fs"),n=require("node:path"),r=require("node:os");function i(e){return(0,n.resolve)(e).replace(/^([A-Z]):/,(e,t)=>`${t.toLowerCase()}:`).replace(/[^a-zA-Z0-9._-]+/g,`_`).replace(/^_+|_+$/g,``)}function a(t){if(t.length===0)throw Error(`definitionsCachePath requires at least one config path`);let a=i(t[0]),o=t.length===1?``:`.${(0,e.createHash)(`sha256`).update(t.map(e=>(0,n.resolve)(e)).join(`\0`)).digest(`hex`).slice(0,12)}`;return(0,n.join)((0,r.homedir)(),`.mcp-proxy`,`${a}${o}.definitions-cache.json`)}function o(e,n=a){if(e.length===0)return{servers:[]};try{let r=n(e),i=JSON.parse((0,t.readFileSync)(r,`utf8`));return{servers:Object.values(i.servers??{}).map(e=>({name:e.serverName,tools:e.tools??[]}))}}catch{return{servers:[]}}}var s=class{generation=0;active;unsubscribe;get currentGeneration(){return this.generation}getServices(){return this.active}isCurrent(e){return e!==void 0&&e===this.active}async start(e){let t=++this.generation;if(await this.retire(),e.configPaths.length===0)return;let{createProxyContainer:n}=await import(`@agimon-ai/mcp-proxy`),r=await n({configFilePath:e.configPaths,startupMode:`background`,auth:{tokenStore:e.tokenStore,onAuthorizationUrl:e.onAuthorizationUrl}});if(t!==this.generation){await r.dispose().catch(()=>void 0);return}this.active=r;let i=e.onServerStateChange;return i&&(this.unsubscribe=r.clientManager.onServerStateChange(e=>{t===this.generation&&i(e)})),{generation:t,services:r,connectionsSettled:r.connectionsSettled}}async dispose(){++this.generation,await this.retire()}async retire(){this.unsubscribe?.(),this.unsubscribe=void 0;let e=this.active;this.active=void 0,e&&await e.dispose().catch(()=>void 0)}};exports.McpRuntimeOwner=s,exports.definitionsCachePath=a,exports.readCachedCatalog=o;
|
|
2
2
|
//# sourceMappingURL=mcpRuntime.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpRuntime.cjs","names":["resolve","createHash","join","homedir","readFileSync"],"sources":["../../../src/adapters/node/mcpRuntime.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join, resolve } from 'node:path';\nimport type { McpServerStateChange, McpToolInfo, SharedServices, TokenStore } from '@agimon-ai/mcp-proxy';\nimport type { CachedCatalog } from '../../types/mcpRuntime.ts';\n\nexport interface McpRuntimeOptions {\n /** Ordered config layers. Later layers override earlier ones. */\n configPaths: string[];\n tokenStore?: TokenStore;\n onAuthorizationUrl?: (url: URL, serverName: string) => void;\n onServerStateChange?: (change: McpServerStateChange) => void;\n}\n\nexport interface McpRuntimeHandle {\n generation: number;\n services: SharedServices;\n /** Resolves once the first connection pass settles. Never rejects. */\n connectionsSettled: Promise<void>;\n}\n\ninterface DefinitionsCacheFile {\n servers?: Record<string, { serverName: string; tools?: McpToolInfo[] }>;\n}\n\nfunction sanitizeConfigPathForFilename(configFilePath: string): string {\n const normalized = resolve(configFilePath).replace(/^([A-Z]):/, (_match, drive: string) => `${drive.toLowerCase()}:`);\n return normalized.replace(/[^a-zA-Z0-9._-]+/g, '_').replace(/^_+|_+$/g, '');\n}\n\n/** Lightweight equivalent of mcp-proxy's cache path calculation. */\nexport function definitionsCachePath(configPaths: string[]): string {\n if (configPaths.length === 0) throw new Error('definitionsCachePath requires at least one config path');\n const sanitized = sanitizeConfigPathForFilename(configPaths[0]!);\n const suffix =\n configPaths.length === 1\n ? ''\n : `.${createHash('sha256')\n .update(configPaths.map((configPath) => resolve(configPath)).join('\\0'))\n .digest('hex')\n .slice(0, 12)}`;\n return join(homedir(), '.mcp-proxy', `${sanitized}${suffix}.definitions-cache.json`);\n}\n\n/**\n * Reads the catalog the previous run left behind.\n *\n * Synchronous on purpose: Pi has to be able to register these tools during\n * extension install, before it takes its first turn, and awaiting a file read\n * would let that turn start with an empty tool set. A miss is normal on a first\n * run and resolves to an empty catalog rather than an error.\n *\n * The catalog is not checked against the current configuration: a server that has\n * since been removed simply never reaches `connected` and is dropped from the\n * active tool set when its state lands.\n */\nexport function readCachedCatalog(\n configPaths: string[],\n cachePathFor: (paths: string[]) => string = definitionsCachePath,\n): CachedCatalog {\n if (configPaths.length === 0) return { servers: [] };\n try {\n const cachePath = cachePathFor(configPaths);\n const cache = JSON.parse(readFileSync(cachePath, 'utf8')) as Partial<DefinitionsCacheFile>;\n const servers = Object.values(cache.servers ?? {}).map((server) => ({\n name: server.serverName,\n tools: server.tools ?? [],\n }));\n return { servers };\n } catch {\n return { servers: [] };\n }\n}\n\n/**\n * Owns the one live mcp-proxy container for this extension.\n *\n * Pi reloads extensions in process, so a connect that lands after a session restart\n * or a `/domains` switch would otherwise fold tools into a dead context. Every start\n * takes the next generation; anything an older generation produces is dropped and its\n * container disposed.\n */\nexport class McpRuntimeOwner {\n private generation = 0;\n private active: SharedServices | undefined;\n private unsubscribe: (() => void) | undefined;\n\n get currentGeneration(): number {\n return this.generation;\n }\n\n /** The live container, or undefined before the first start and after dispose. */\n getServices(): SharedServices | undefined {\n return this.active;\n }\n\n /**\n * Whether a container a caller captured earlier is still the live one.\n *\n * An async continuation that started under one container has to check this\n * before writing its result, or a `/domains` switch would fold stale tools into\n * the new session.\n */\n isCurrent(services: SharedServices | undefined): boolean {\n return services !== undefined && services === this.active;\n }\n\n /**\n * Starts a container for the current configuration, replacing any predecessor.\n *\n * Returns undefined when a newer generation started while this one was building,\n * which is the signal for the caller to abandon its half-finished work.\n */\n async start(options: McpRuntimeOptions): Promise<McpRuntimeHandle | undefined> {\n const generation = ++this.generation;\n // Retired before the replacement is built so two containers never hold the same\n // stdio children open at once.\n await this.retire();\n if (options.configPaths.length === 0) return undefined;\n\n // The proxy container pulls in transports, OAuth, schema conversion, and\n // server implementations. Cached tool registration needs none of those, so\n // load the runtime only after session_start has yielded control to Pi.\n const { createProxyContainer } = await import('@agimon-ai/mcp-proxy');\n const services = await createProxyContainer({\n configFilePath: options.configPaths,\n // Never blocking: an unreachable server must not delay Pi's first prompt.\n startupMode: 'background',\n auth: { tokenStore: options.tokenStore, onAuthorizationUrl: options.onAuthorizationUrl },\n });\n\n if (generation !== this.generation) {\n await services.dispose().catch(() => undefined);\n return undefined;\n }\n\n this.active = services;\n const forward = options.onServerStateChange;\n if (forward) {\n this.unsubscribe = services.clientManager.onServerStateChange((change) => {\n if (generation === this.generation) forward(change);\n });\n }\n\n return { generation, services, connectionsSettled: services.connectionsSettled };\n }\n\n /** Tears the container down and invalidates every generation still in flight. */\n async dispose(): Promise<void> {\n ++this.generation;\n await this.retire();\n }\n\n private async retire(): Promise<void> {\n this.unsubscribe?.();\n this.unsubscribe = undefined;\n const services = this.active;\n this.active = undefined;\n // A failed teardown must not leave the owner believing a dead container is live.\n if (services) await services.dispose().catch(() => undefined);\n }\n}\n"],"mappings":"8FA0BA,SAAS,EAA8B,EAAgC,CAErE,OAAA,EADmBA,EAAAA,QAAAA,CAAQ,CAAc,CAAC,CAAC,QAAQ,aAAc,EAAQ,IAAkB,GAAG,EAAM,YAAY,EAAE,EAClG,CAAC,CAAC,QAAQ,oBAAqB,GAAG,CAAC,CAAC,QAAQ,WAAY,EAAE,CAC5E,CAGA,SAAgB,EAAqB,EAA+B,CAClE,GAAI,EAAY,SAAW,EAAG,MAAU,MAAM,wDAAwD,EACtG,IAAM,EAAY,EAA8B,EAAY,EAAG,EACzD,EACJ,EAAY,SAAW,EACnB,GACA,KAAA,EAAIC,EAAAA,WAAAA,CAAW,QAAQ,CAAC,CACrB,OAAO,EAAY,IAAK,IAAA,EAAeD,EAAAA,QAAAA,CAAQ,CAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CACvE,OAAO,KAAK,CAAC,CACb,MAAM,EAAG,EAAE,IACpB,OAAA,EAAOE,EAAAA,KAAAA,EAAAA,EAAKC,EAAAA,QAAAA,CAAQ,EAAG,aAAc,GAAG,IAAY,EAAO,wBAAwB,CACrF,CAcA,SAAgB,EACd,EACA,EAA4C,EAC7B,CACf,GAAI,EAAY,SAAW,EAAG,MAAO,CAAE,QAAS,CAAC,CAAE,EACnD,GAAI,CACF,IAAM,EAAY,EAAa,CAAW,EACpC,EAAQ,KAAK,OAAA,EAAMC,EAAAA,aAAAA,CAAa,EAAW,MAAM,CAAC,EAKxD,MAAO,CAAE,QAJO,OAAO,OAAO,EAAM,SAAW,CAAC,CAAC,CAAC,CAAC,IAAK,IAAY,CAClE,KAAM,EAAO,WACb,MAAO,EAAO,OAAS,CAAC,CAC1B,EACe,CAAE,CACnB,MAAQ,CACN,MAAO,CAAE,QAAS,CAAC,CAAE,CACvB,CACF,CAUA,IAAa,EAAb,KAA6B,CAC3B,WAAqB,EACrB,OACA,YAEA,IAAI,mBAA4B,CAC9B,OAAO,KAAK,UACd,CAGA,aAA0C,CACxC,OAAO,KAAK,MACd,CASA,UAAU,EAA+C,CACvD,OAAO,IAAa,IAAA,IAAa,IAAa,KAAK,MACrD,CAQA,MAAM,MAAM,EAAmE,CAC7E,IAAM,EAAa,EAAE,KAAK,WAI1B,GADA,MAAM,KAAK,OAAO,EACd,EAAQ,YAAY,SAAW,EAAG,OAKtC,GAAM,CAAE,wBAAyB,MAAM,OAAO,wBACxC,EAAW,MAAM,EAAqB,CAC1C,eAAgB,EAAQ,YAExB,YAAa,aACb,KAAM,CAAE,WAAY,EAAQ,WAAY,mBAAoB,EAAQ,kBAAmB,CACzF,CAAC,EAED,GAAI,IAAe,KAAK,WAAY,CAClC,MAAM,EAAS,QAAQ,CAAC,CAAC,UAAY,IAAA,EAAS,EAC9C,MACF,CAEA,KAAK,OAAS,EACd,IAAM,EAAU,EAAQ,oBAOxB,OANI,IACF,KAAK,YAAc,EAAS,cAAc,oBAAqB,GAAW,CACpE,IAAe,KAAK,YAAY,EAAQ,CAAM,CACpD,CAAC,GAGI,CAAE,aAAY,WAAU,mBAAoB,EAAS,kBAAmB,CACjF,CAGA,MAAM,SAAyB,CAC7B,EAAE,KAAK,WACP,MAAM,KAAK,OAAO,CACpB,CAEA,MAAc,QAAwB,CACpC,KAAK,cAAc,EACnB,KAAK,YAAc,IAAA,GACnB,IAAM,EAAW,KAAK,OACtB,KAAK,OAAS,IAAA,GAEV,GAAU,MAAM,EAAS,QAAQ,CAAC,CAAC,UAAY,IAAA,EAAS,CAC9D,CACF"}
|
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
name: string;
|
|
5
|
-
tools: McpToolInfo[];
|
|
6
|
-
}
|
|
7
|
-
/** Server definitions from the previous run, available before any socket opens. */
|
|
8
|
-
interface CachedCatalog {
|
|
9
|
-
servers: CatalogServer[];
|
|
10
|
-
}
|
|
1
|
+
import { CachedCatalog } from "../../types/mcpRuntime.cjs";
|
|
2
|
+
import "@agimon-ai/mcp-proxy";
|
|
3
|
+
//#region src/adapters/node/mcpRuntime.d.ts
|
|
11
4
|
/**
|
|
12
5
|
* Reads the catalog the previous run left behind.
|
|
13
6
|
*
|
|
@@ -22,5 +15,5 @@ interface CachedCatalog {
|
|
|
22
15
|
*/
|
|
23
16
|
declare function readCachedCatalog(configPaths: string[], cachePathFor?: (paths: string[]) => string): CachedCatalog;
|
|
24
17
|
//#endregion
|
|
25
|
-
export {
|
|
18
|
+
export { readCachedCatalog };
|
|
26
19
|
//# sourceMappingURL=mcpRuntime.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpRuntime.d.cts","names":[],"sources":["../../../src/adapters/node/mcpRuntime.ts"],"mappings":";;;;;;;;;;;;;;;iBAyDgB,kBACd,uBACA,gBAAe,6BACd"}
|
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
name: string;
|
|
5
|
-
tools: McpToolInfo[];
|
|
6
|
-
}
|
|
7
|
-
/** Server definitions from the previous run, available before any socket opens. */
|
|
8
|
-
interface CachedCatalog {
|
|
9
|
-
servers: CatalogServer[];
|
|
10
|
-
}
|
|
1
|
+
import { CachedCatalog } from "../../types/mcpRuntime.mjs";
|
|
2
|
+
import "@agimon-ai/mcp-proxy";
|
|
3
|
+
//#region src/adapters/node/mcpRuntime.d.ts
|
|
11
4
|
/**
|
|
12
5
|
* Reads the catalog the previous run left behind.
|
|
13
6
|
*
|
|
@@ -22,5 +15,5 @@ interface CachedCatalog {
|
|
|
22
15
|
*/
|
|
23
16
|
declare function readCachedCatalog(configPaths: string[], cachePathFor?: (paths: string[]) => string): CachedCatalog;
|
|
24
17
|
//#endregion
|
|
25
|
-
export {
|
|
18
|
+
export { readCachedCatalog };
|
|
26
19
|
//# sourceMappingURL=mcpRuntime.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpRuntime.d.mts","names":[],"sources":["../../../src/adapters/node/mcpRuntime.ts"],"mappings":";;;;;;;;;;;;;;;iBAyDgB,kBACd,uBACA,gBAAe,6BACd"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{createHash as e}from"node:crypto";import{readFileSync as t}from"node:fs";import{join as n,resolve as r}from"node:path";import{homedir as i}from"node:os";function a(e){return r(e).replace(/^([A-Z]):/,(e,t)=>`${t.toLowerCase()}:`).replace(/[^a-zA-Z0-9._-]+/g,`_`).replace(/^_+|_+$/g,``)}function o(t){if(t.length===0)throw Error(`definitionsCachePath requires at least one config path`);let o=a(t[0]),s=t.length===1?``:`.${e(`sha256`).update(t.map(e=>r(e)).join(`\0`)).digest(`hex`).slice(0,12)}`;return n(i(),`.mcp-proxy`,`${o}${s}.definitions-cache.json`)}function s(e,n=o){if(e.length===0)return{servers:[]};try{let r=n(e),i=JSON.parse(t(r,`utf8`));return{servers:Object.values(i.servers??{}).map(e=>({name:e.serverName,tools:e.tools??[]}))}}catch{return{servers:[]}}}var c=class{generation=0;active;unsubscribe;get currentGeneration(){return this.generation}getServices(){return this.active}isCurrent(e){return e!==void 0&&e===this.active}async start(e){let t=++this.generation;if(await this.retire(),e.configPaths.length===0)return;let{createProxyContainer:n}=await import(`@agimon-ai/mcp-proxy`),r=await n({configFilePath:e.configPaths,startupMode:`background`,auth:{tokenStore:e.tokenStore,onAuthorizationUrl:e.onAuthorizationUrl}});if(t!==this.generation){await r.dispose().catch(()=>void 0);return}this.active=r;let i=e.onServerStateChange;return i&&(this.unsubscribe=r.clientManager.onServerStateChange(e=>{t===this.generation&&i(e)})),{generation:t,services:r,connectionsSettled:r.connectionsSettled}}async dispose(){++this.generation,await this.retire()}async retire(){this.unsubscribe?.(),this.unsubscribe=void 0;let e=this.active;this.active=void 0,e&&await e.dispose().catch(()=>void 0)}};export{c as McpRuntimeOwner,o as definitionsCachePath,s as readCachedCatalog};
|
|
2
2
|
//# sourceMappingURL=mcpRuntime.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpRuntime.mjs","names":[],"sources":["../../../src/adapters/node/mcpRuntime.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join, resolve } from 'node:path';\nimport type { McpServerStateChange, McpToolInfo, SharedServices, TokenStore } from '@agimon-ai/mcp-proxy';\nimport type { CachedCatalog } from '../../types/mcpRuntime.ts';\n\nexport interface McpRuntimeOptions {\n /** Ordered config layers. Later layers override earlier ones. */\n configPaths: string[];\n tokenStore?: TokenStore;\n onAuthorizationUrl?: (url: URL, serverName: string) => void;\n onServerStateChange?: (change: McpServerStateChange) => void;\n}\n\nexport interface McpRuntimeHandle {\n generation: number;\n services: SharedServices;\n /** Resolves once the first connection pass settles. Never rejects. */\n connectionsSettled: Promise<void>;\n}\n\ninterface DefinitionsCacheFile {\n servers?: Record<string, { serverName: string; tools?: McpToolInfo[] }>;\n}\n\nfunction sanitizeConfigPathForFilename(configFilePath: string): string {\n const normalized = resolve(configFilePath).replace(/^([A-Z]):/, (_match, drive: string) => `${drive.toLowerCase()}:`);\n return normalized.replace(/[^a-zA-Z0-9._-]+/g, '_').replace(/^_+|_+$/g, '');\n}\n\n/** Lightweight equivalent of mcp-proxy's cache path calculation. */\nexport function definitionsCachePath(configPaths: string[]): string {\n if (configPaths.length === 0) throw new Error('definitionsCachePath requires at least one config path');\n const sanitized = sanitizeConfigPathForFilename(configPaths[0]!);\n const suffix =\n configPaths.length === 1\n ? ''\n : `.${createHash('sha256')\n .update(configPaths.map((configPath) => resolve(configPath)).join('\\0'))\n .digest('hex')\n .slice(0, 12)}`;\n return join(homedir(), '.mcp-proxy', `${sanitized}${suffix}.definitions-cache.json`);\n}\n\n/**\n * Reads the catalog the previous run left behind.\n *\n * Synchronous on purpose: Pi has to be able to register these tools during\n * extension install, before it takes its first turn, and awaiting a file read\n * would let that turn start with an empty tool set. A miss is normal on a first\n * run and resolves to an empty catalog rather than an error.\n *\n * The catalog is not checked against the current configuration: a server that has\n * since been removed simply never reaches `connected` and is dropped from the\n * active tool set when its state lands.\n */\nexport function readCachedCatalog(\n configPaths: string[],\n cachePathFor: (paths: string[]) => string = definitionsCachePath,\n): CachedCatalog {\n if (configPaths.length === 0) return { servers: [] };\n try {\n const cachePath = cachePathFor(configPaths);\n const cache = JSON.parse(readFileSync(cachePath, 'utf8')) as Partial<DefinitionsCacheFile>;\n const servers = Object.values(cache.servers ?? {}).map((server) => ({\n name: server.serverName,\n tools: server.tools ?? [],\n }));\n return { servers };\n } catch {\n return { servers: [] };\n }\n}\n\n/**\n * Owns the one live mcp-proxy container for this extension.\n *\n * Pi reloads extensions in process, so a connect that lands after a session restart\n * or a `/domains` switch would otherwise fold tools into a dead context. Every start\n * takes the next generation; anything an older generation produces is dropped and its\n * container disposed.\n */\nexport class McpRuntimeOwner {\n private generation = 0;\n private active: SharedServices | undefined;\n private unsubscribe: (() => void) | undefined;\n\n get currentGeneration(): number {\n return this.generation;\n }\n\n /** The live container, or undefined before the first start and after dispose. */\n getServices(): SharedServices | undefined {\n return this.active;\n }\n\n /**\n * Whether a container a caller captured earlier is still the live one.\n *\n * An async continuation that started under one container has to check this\n * before writing its result, or a `/domains` switch would fold stale tools into\n * the new session.\n */\n isCurrent(services: SharedServices | undefined): boolean {\n return services !== undefined && services === this.active;\n }\n\n /**\n * Starts a container for the current configuration, replacing any predecessor.\n *\n * Returns undefined when a newer generation started while this one was building,\n * which is the signal for the caller to abandon its half-finished work.\n */\n async start(options: McpRuntimeOptions): Promise<McpRuntimeHandle | undefined> {\n const generation = ++this.generation;\n // Retired before the replacement is built so two containers never hold the same\n // stdio children open at once.\n await this.retire();\n if (options.configPaths.length === 0) return undefined;\n\n // The proxy container pulls in transports, OAuth, schema conversion, and\n // server implementations. Cached tool registration needs none of those, so\n // load the runtime only after session_start has yielded control to Pi.\n const { createProxyContainer } = await import('@agimon-ai/mcp-proxy');\n const services = await createProxyContainer({\n configFilePath: options.configPaths,\n // Never blocking: an unreachable server must not delay Pi's first prompt.\n startupMode: 'background',\n auth: { tokenStore: options.tokenStore, onAuthorizationUrl: options.onAuthorizationUrl },\n });\n\n if (generation !== this.generation) {\n await services.dispose().catch(() => undefined);\n return undefined;\n }\n\n this.active = services;\n const forward = options.onServerStateChange;\n if (forward) {\n this.unsubscribe = services.clientManager.onServerStateChange((change) => {\n if (generation === this.generation) forward(change);\n });\n }\n\n return { generation, services, connectionsSettled: services.connectionsSettled };\n }\n\n /** Tears the container down and invalidates every generation still in flight. */\n async dispose(): Promise<void> {\n ++this.generation;\n await this.retire();\n }\n\n private async retire(): Promise<void> {\n this.unsubscribe?.();\n this.unsubscribe = undefined;\n const services = this.active;\n this.active = undefined;\n // A failed teardown must not leave the owner believing a dead container is live.\n if (services) await services.dispose().catch(() => undefined);\n }\n}\n"],"mappings":"gKA0BA,SAAS,EAA8B,EAAgC,CAErE,OADmB,EAAQ,CAAc,CAAC,CAAC,QAAQ,aAAc,EAAQ,IAAkB,GAAG,EAAM,YAAY,EAAE,EAClG,CAAC,CAAC,QAAQ,oBAAqB,GAAG,CAAC,CAAC,QAAQ,WAAY,EAAE,CAC5E,CAGA,SAAgB,EAAqB,EAA+B,CAClE,GAAI,EAAY,SAAW,EAAG,MAAU,MAAM,wDAAwD,EACtG,IAAM,EAAY,EAA8B,EAAY,EAAG,EACzD,EACJ,EAAY,SAAW,EACnB,GACA,IAAI,EAAW,QAAQ,CAAC,CACrB,OAAO,EAAY,IAAK,GAAe,EAAQ,CAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CACvE,OAAO,KAAK,CAAC,CACb,MAAM,EAAG,EAAE,IACpB,OAAO,EAAK,EAAQ,EAAG,aAAc,GAAG,IAAY,EAAO,wBAAwB,CACrF,CAcA,SAAgB,EACd,EACA,EAA4C,EAC7B,CACf,GAAI,EAAY,SAAW,EAAG,MAAO,CAAE,QAAS,CAAC,CAAE,EACnD,GAAI,CACF,IAAM,EAAY,EAAa,CAAW,EACpC,EAAQ,KAAK,MAAM,EAAa,EAAW,MAAM,CAAC,EAKxD,MAAO,CAAE,QAJO,OAAO,OAAO,EAAM,SAAW,CAAC,CAAC,CAAC,CAAC,IAAK,IAAY,CAClE,KAAM,EAAO,WACb,MAAO,EAAO,OAAS,CAAC,CAC1B,EACe,CAAE,CACnB,MAAQ,CACN,MAAO,CAAE,QAAS,CAAC,CAAE,CACvB,CACF,CAUA,IAAa,EAAb,KAA6B,CAC3B,WAAqB,EACrB,OACA,YAEA,IAAI,mBAA4B,CAC9B,OAAO,KAAK,UACd,CAGA,aAA0C,CACxC,OAAO,KAAK,MACd,CASA,UAAU,EAA+C,CACvD,OAAO,IAAa,IAAA,IAAa,IAAa,KAAK,MACrD,CAQA,MAAM,MAAM,EAAmE,CAC7E,IAAM,EAAa,EAAE,KAAK,WAI1B,GADA,MAAM,KAAK,OAAO,EACd,EAAQ,YAAY,SAAW,EAAG,OAKtC,GAAM,CAAE,wBAAyB,MAAM,OAAO,wBACxC,EAAW,MAAM,EAAqB,CAC1C,eAAgB,EAAQ,YAExB,YAAa,aACb,KAAM,CAAE,WAAY,EAAQ,WAAY,mBAAoB,EAAQ,kBAAmB,CACzF,CAAC,EAED,GAAI,IAAe,KAAK,WAAY,CAClC,MAAM,EAAS,QAAQ,CAAC,CAAC,UAAY,IAAA,EAAS,EAC9C,MACF,CAEA,KAAK,OAAS,EACd,IAAM,EAAU,EAAQ,oBAOxB,OANI,IACF,KAAK,YAAc,EAAS,cAAc,oBAAqB,GAAW,CACpE,IAAe,KAAK,YAAY,EAAQ,CAAM,CACpD,CAAC,GAGI,CAAE,aAAY,WAAU,mBAAoB,EAAS,kBAAmB,CACjF,CAGA,MAAM,SAAyB,CAC7B,EAAE,KAAK,WACP,MAAM,KAAK,OAAO,CACpB,CAEA,MAAc,QAAwB,CACpC,KAAK,cAAc,EACnB,KAAK,YAAc,IAAA,GACnB,IAAM,EAAW,KAAK,OACtB,KAAK,OAAS,IAAA,GAEV,GAAU,MAAM,EAAS,QAAQ,CAAC,CAAC,UAAY,IAAA,EAAS,CAC9D,CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doomExtension.cjs","names":[],"sources":["../../../src/adapters/pi/doomExtension.ts"],"sourcesContent":["import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { registerMcpExtension } from './extension.ts';\n\n/** Explicit Doom adapter retaining environment-provided overlays and allowlists. */\nexport function registerDoomMcpExtension(pi: ExtensionAPI): void {\n registerMcpExtension(pi, { configurationSource: 'doom' });\n}\n"],"mappings":"mCAIA,SAAgB,EAAyB,EAAwB,CAC/D,EAAA,qBAAqB,EAAI,CAAE,oBAAqB,MAAO,CAAC,CAC1D"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
//#region src/adapters/pi/doomExtension.d.ts
|
|
3
|
+
/** Explicit Doom adapter retaining environment-provided overlays and allowlists. */
|
|
4
|
+
declare function registerDoomMcpExtension(pi: ExtensionAPI): void;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { registerDoomMcpExtension };
|
|
7
|
+
//# sourceMappingURL=doomExtension.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doomExtension.d.cts","names":[],"sources":["../../../src/adapters/pi/doomExtension.ts"],"mappings":";;;iBAIgB,yBAAyB,IAAI"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
//#region src/adapters/pi/doomExtension.d.ts
|
|
3
|
+
/** Explicit Doom adapter retaining environment-provided overlays and allowlists. */
|
|
4
|
+
declare function registerDoomMcpExtension(pi: ExtensionAPI): void;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { registerDoomMcpExtension };
|
|
7
|
+
//# sourceMappingURL=doomExtension.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doomExtension.d.mts","names":[],"sources":["../../../src/adapters/pi/doomExtension.ts"],"mappings":";;;iBAIgB,yBAAyB,IAAI"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doomExtension.mjs","names":[],"sources":["../../../src/adapters/pi/doomExtension.ts"],"sourcesContent":["import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { registerMcpExtension } from './extension.ts';\n\n/** Explicit Doom adapter retaining environment-provided overlays and allowlists. */\nexport function registerDoomMcpExtension(pi: ExtensionAPI): void {\n registerMcpExtension(pi, { configurationSource: 'doom' });\n}\n"],"mappings":"uDAIA,SAAgB,EAAyB,EAAwB,CAC/D,EAAqB,EAAI,CAAE,oBAAqB,MAAO,CAAC,CAC1D"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../commands/mcpCommand.cjs"),n=require("../process/sessionConfig.cjs"),r=require("./mcpConstants.cjs"),i=require("./leader.cjs"),a=require("./mcpSession.cjs");let o=require("@agimon-ai/doompi-extension-contracts/mcp-status"),s=require("@agimon-ai/doompi-extension-contracts/protocol"),c=require("open");c=e.__toESM(c,1);const l=`warning`,u=`doom`;function d(e,d={}){let f,p,m=new a.McpSession({pi:e,onAuthorizationUrl:async(e,t,{openBrowser:n})=>{if(f?.mode===`tui`){if(!n)return;try{await(0,c.default)(e.toString())}catch(e){throw f.ui.notify(`Could not open the authorization page for ${t}: ${e instanceof Error?e.message:String(e)}. Press a in the MCP overlay to retry.`,l),e}return}f?.ui?.notify(`Authorize ${t} by opening:\n${e.toString()}`,`info`)}}),h=n.readSessionConfig();m.install(d.configurationSource===u?h:{repoRoot:h.repoRoot,stagingDirectory:h.stagingDirectory}),t.registerCommand(e,m,{openOverlay:async e=>{let{openMcpOverlay:t}=await Promise.resolve().then(()=>require("../../tui/mcpOverlay.cjs"));return t(e,m)}}),i.registerLeaderContribution(e),e.on(`session_start`,(t,i)=>{f=i,d.configurationSource!==u&&m.install(n.readSessionConfig({},i.cwd));let a=(0,s.createProtocolRuntime)({bus:e.events,source:r.PACKAGE_SOURCE,sessionId:i.sessionManager.getSessionId()});p?.(),p=a.provide(o.McpStatusQueryProtocol,()=>m.getSnapshot()),m.activate();for(let e of m.getDiagnostics())i.ui?.notify(e,l);m.start()}),e.on(`session_shutdown`,()=>{p?.(),p=void 0,f=void 0,m.dispose()})}exports.registerMcpExtension=d;
|
|
2
|
+
//# sourceMappingURL=extension.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.cjs","names":["McpSession","readSessionConfig","createProtocolRuntime","PACKAGE_SOURCE","McpStatusQueryProtocol"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { McpStatusQueryProtocol } from '@agimon-ai/doompi-extension-contracts/mcp-status';\nimport { createProtocolRuntime } from '@agimon-ai/doompi-extension-contracts/protocol';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport open from 'open';\nimport { registerCommand } from '../../commands/mcpCommand.ts';\nimport { readSessionConfig } from '../process/sessionConfig.ts';\nimport { registerLeaderContribution } from './leader.ts';\nimport { PACKAGE_SOURCE } from './mcpConstants.ts';\nimport { McpSession } from './mcpSession.ts';\n\nconst SESSION_START = 'session_start';\nconst SESSION_SHUTDOWN = 'session_shutdown';\nconst INFO = 'info';\nconst WARNING = 'warning';\nconst DOOM_CONFIGURATION_SOURCE = 'doom';\n\nexport interface McpExtensionOptions {\n /** Doom loading retains the environment-provided plugin overlays and allowlists. */\n configurationSource?: 'standard' | typeof DOOM_CONFIGURATION_SOURCE;\n}\n\n/**\n * MCP support for a Pi session.\n *\n * Tools are registered here, synchronously, from the catalog the previous run left\n * behind. Connecting starts on `session_start` and runs detached, so the first\n * prompt is never held up by a slow or unreachable server; each server folds into\n * the active tool set as it lands.\n */\nexport function registerMcpExtension(pi: ExtensionAPI, options: McpExtensionOptions = {}): void {\n let activeContext: ExtensionContext | undefined;\n let disposeStatusProvider: (() => void) | undefined;\n\n const session = new McpSession({\n pi,\n onAuthorizationUrl: async (url, serverName, { openBrowser }) => {\n // Explicit auth from a terminal owns a desktop browser. Background startup\n // still only records the URL, so opening Pi never creates surprise tabs.\n if (activeContext?.mode === 'tui') {\n if (!openBrowser) return;\n try {\n await open(url.toString());\n } catch (error) {\n activeContext.ui.notify(\n `Could not open the authorization page for ${serverName}: ${error instanceof Error ? error.message : String(error)}. Press a in the MCP overlay to retry.`,\n WARNING,\n );\n throw error;\n }\n return;\n }\n // Headless and RPC sessions cannot launch a local desktop browser, so their\n // usable fallback remains the complete URL in the host notification stream.\n activeContext?.ui?.notify(`Authorize ${serverName} by opening:\\n${url.toString()}`, INFO);\n },\n });\n\n const initialConfiguration = readSessionConfig();\n session.install(\n options.configurationSource === DOOM_CONFIGURATION_SOURCE\n ? initialConfiguration\n : {\n repoRoot: initialConfiguration.repoRoot,\n stagingDirectory: initialConfiguration.stagingDirectory,\n },\n );\n // The opener is injected rather than imported by the command: `src/commands/**`\n // may not reach into `src/tui/**`, and handing it in is also what lets the\n // command be tested without a terminal.\n registerCommand(pi, session, {\n openOverlay: async (ctx) => {\n const { openMcpOverlay } = await import('../../tui/mcpOverlay.ts');\n return openMcpOverlay(ctx, session);\n },\n });\n registerLeaderContribution(pi);\n\n pi.on(SESSION_START, (_event, ctx) => {\n activeContext = ctx;\n if (options.configurationSource !== DOOM_CONFIGURATION_SOURCE) session.install(readSessionConfig({}, ctx.cwd));\n const protocol = createProtocolRuntime({\n bus: pi.events,\n source: PACKAGE_SOURCE,\n sessionId: ctx.sessionManager.getSessionId(),\n });\n disposeStatusProvider?.();\n disposeStatusProvider = protocol.provide(McpStatusQueryProtocol, () => session.getSnapshot());\n\n // First point Pi accepts an active-list call: the tools registered while the\n // extension loaded are held back until a server actually answers.\n session.activate();\n for (const diagnostic of session.getDiagnostics()) ctx.ui?.notify(diagnostic, WARNING);\n // Detached on purpose: awaiting here would put every downstream connection on\n // the path between launching Pi and its first prompt.\n void session.start();\n });\n\n pi.on(SESSION_SHUTDOWN, () => {\n disposeStatusProvider?.();\n disposeStatusProvider = undefined;\n activeContext = undefined;\n void session.dispose();\n });\n}\n"],"mappings":"sYAUA,MAGM,EAAU,UACV,EAA4B,OAelC,SAAgB,EAAqB,EAAkB,EAA+B,CAAC,EAAS,CAC9F,IAAI,EACA,EAEE,EAAU,IAAIA,EAAAA,WAAW,CAC7B,KACA,mBAAoB,MAAO,EAAK,EAAY,CAAE,iBAAkB,CAG9D,GAAI,GAAe,OAAS,MAAO,CACjC,GAAI,CAAC,EAAa,OAClB,GAAI,CACF,MAAA,EAAM,EAAA,QAAA,CAAK,EAAI,SAAS,CAAC,CAC3B,OAAS,EAAO,CAKd,MAJA,EAAc,GAAG,OACf,6CAA6C,EAAW,IAAI,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EAAE,wCACnH,CACF,EACM,CACR,CACA,MACF,CAGA,GAAe,IAAI,OAAO,aAAa,EAAW,gBAAgB,EAAI,SAAS,IAAK,MAAI,CAC1F,CACF,CAAC,EAEK,EAAuBC,EAAAA,kBAAkB,EAC/C,EAAQ,QACN,EAAQ,sBAAwB,EAC5B,EACA,CACE,SAAU,EAAqB,SAC/B,iBAAkB,EAAqB,gBACzC,CACN,EAIA,EAAA,gBAAgB,EAAI,EAAS,CAC3B,YAAa,KAAO,IAAQ,CAC1B,GAAM,CAAE,kBAAmB,MAAA,QAAA,QAAA,CAAA,CAAA,SAAA,QAAM,0BAAA,CAAA,EACjC,OAAO,EAAe,EAAK,CAAO,CACpC,CACF,CAAC,EACD,EAAA,2BAA2B,CAAE,EAE7B,EAAG,GAAG,iBAAgB,EAAQ,IAAQ,CACpC,EAAgB,EACZ,EAAQ,sBAAwB,GAA2B,EAAQ,QAAQA,EAAAA,kBAAkB,CAAC,EAAG,EAAI,GAAG,CAAC,EAC7G,IAAM,GAAA,EAAWC,EAAAA,sBAAAA,CAAsB,CACrC,IAAK,EAAG,OACR,OAAQC,EAAAA,eACR,UAAW,EAAI,eAAe,aAAa,CAC7C,CAAC,EACD,IAAwB,EACxB,EAAwB,EAAS,QAAQC,EAAAA,2BAA8B,EAAQ,YAAY,CAAC,EAI5F,EAAQ,SAAS,EACjB,IAAK,IAAM,KAAc,EAAQ,eAAe,EAAG,EAAI,IAAI,OAAO,EAAY,CAAO,EAGrF,EAAa,MAAM,CACrB,CAAC,EAED,EAAG,GAAG,uBAAwB,CAC5B,IAAwB,EACxB,EAAwB,IAAA,GACxB,EAAgB,IAAA,GAChB,EAAa,QAAQ,CACvB,CAAC,CACH"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
//#region src/extension.d.ts
|
|
2
|
+
//#region src/adapters/pi/extension.d.ts
|
|
3
|
+
declare const DOOM_CONFIGURATION_SOURCE = "doom";
|
|
3
4
|
interface McpExtensionOptions {
|
|
4
5
|
/** Doom loading retains the environment-provided plugin overlays and allowlists. */
|
|
5
|
-
configurationSource?: 'standard' |
|
|
6
|
+
configurationSource?: 'standard' | typeof DOOM_CONFIGURATION_SOURCE;
|
|
6
7
|
}
|
|
7
8
|
/**
|
|
8
9
|
* MCP support for a Pi session.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;cAcM;UAEW;;EAEf,0CAA0C;;;;;;;;;;iBAW5B,qBAAqB,IAAI,cAAc,UAAS"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
//#region src/extension.d.ts
|
|
2
|
+
//#region src/adapters/pi/extension.d.ts
|
|
3
|
+
declare const DOOM_CONFIGURATION_SOURCE = "doom";
|
|
3
4
|
interface McpExtensionOptions {
|
|
4
5
|
/** Doom loading retains the environment-provided plugin overlays and allowlists. */
|
|
5
|
-
configurationSource?: 'standard' |
|
|
6
|
+
configurationSource?: 'standard' | typeof DOOM_CONFIGURATION_SOURCE;
|
|
6
7
|
}
|
|
7
8
|
/**
|
|
8
9
|
* MCP support for a Pi session.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;cAcM;UAEW;;EAEf,0CAA0C;;;;;;;;;;iBAW5B,qBAAqB,IAAI,cAAc,UAAS"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{registerCommand as e}from"../../commands/mcpCommand.mjs";import{readSessionConfig as t}from"../process/sessionConfig.mjs";import{PACKAGE_SOURCE as n}from"./mcpConstants.mjs";import{registerLeaderContribution as r}from"./leader.mjs";import{McpSession as i}from"./mcpSession.mjs";import{McpStatusQueryProtocol as a}from"@agimon-ai/doompi-extension-contracts/mcp-status";import{createProtocolRuntime as o}from"@agimon-ai/doompi-extension-contracts/protocol";import s from"open";const c=`warning`,l=`doom`;function u(u,d={}){let f,p,m=new i({pi:u,onAuthorizationUrl:async(e,t,{openBrowser:n})=>{if(f?.mode===`tui`){if(!n)return;try{await s(e.toString())}catch(e){throw f.ui.notify(`Could not open the authorization page for ${t}: ${e instanceof Error?e.message:String(e)}. Press a in the MCP overlay to retry.`,c),e}return}f?.ui?.notify(`Authorize ${t} by opening:\n${e.toString()}`,`info`)}}),h=t();m.install(d.configurationSource===l?h:{repoRoot:h.repoRoot,stagingDirectory:h.stagingDirectory}),e(u,m,{openOverlay:async e=>{let{openMcpOverlay:t}=await import(`../../tui/mcpOverlay.mjs`);return t(e,m)}}),r(u),u.on(`session_start`,(e,r)=>{f=r,d.configurationSource!==l&&m.install(t({},r.cwd));let i=o({bus:u.events,source:n,sessionId:r.sessionManager.getSessionId()});p?.(),p=i.provide(a,()=>m.getSnapshot()),m.activate();for(let e of m.getDiagnostics())r.ui?.notify(e,c);m.start()}),u.on(`session_shutdown`,()=>{p?.(),p=void 0,f=void 0,m.dispose()})}export{u as registerMcpExtension};
|
|
2
|
+
//# sourceMappingURL=extension.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.mjs","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { McpStatusQueryProtocol } from '@agimon-ai/doompi-extension-contracts/mcp-status';\nimport { createProtocolRuntime } from '@agimon-ai/doompi-extension-contracts/protocol';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport open from 'open';\nimport { registerCommand } from '../../commands/mcpCommand.ts';\nimport { readSessionConfig } from '../process/sessionConfig.ts';\nimport { registerLeaderContribution } from './leader.ts';\nimport { PACKAGE_SOURCE } from './mcpConstants.ts';\nimport { McpSession } from './mcpSession.ts';\n\nconst SESSION_START = 'session_start';\nconst SESSION_SHUTDOWN = 'session_shutdown';\nconst INFO = 'info';\nconst WARNING = 'warning';\nconst DOOM_CONFIGURATION_SOURCE = 'doom';\n\nexport interface McpExtensionOptions {\n /** Doom loading retains the environment-provided plugin overlays and allowlists. */\n configurationSource?: 'standard' | typeof DOOM_CONFIGURATION_SOURCE;\n}\n\n/**\n * MCP support for a Pi session.\n *\n * Tools are registered here, synchronously, from the catalog the previous run left\n * behind. Connecting starts on `session_start` and runs detached, so the first\n * prompt is never held up by a slow or unreachable server; each server folds into\n * the active tool set as it lands.\n */\nexport function registerMcpExtension(pi: ExtensionAPI, options: McpExtensionOptions = {}): void {\n let activeContext: ExtensionContext | undefined;\n let disposeStatusProvider: (() => void) | undefined;\n\n const session = new McpSession({\n pi,\n onAuthorizationUrl: async (url, serverName, { openBrowser }) => {\n // Explicit auth from a terminal owns a desktop browser. Background startup\n // still only records the URL, so opening Pi never creates surprise tabs.\n if (activeContext?.mode === 'tui') {\n if (!openBrowser) return;\n try {\n await open(url.toString());\n } catch (error) {\n activeContext.ui.notify(\n `Could not open the authorization page for ${serverName}: ${error instanceof Error ? error.message : String(error)}. Press a in the MCP overlay to retry.`,\n WARNING,\n );\n throw error;\n }\n return;\n }\n // Headless and RPC sessions cannot launch a local desktop browser, so their\n // usable fallback remains the complete URL in the host notification stream.\n activeContext?.ui?.notify(`Authorize ${serverName} by opening:\\n${url.toString()}`, INFO);\n },\n });\n\n const initialConfiguration = readSessionConfig();\n session.install(\n options.configurationSource === DOOM_CONFIGURATION_SOURCE\n ? initialConfiguration\n : {\n repoRoot: initialConfiguration.repoRoot,\n stagingDirectory: initialConfiguration.stagingDirectory,\n },\n );\n // The opener is injected rather than imported by the command: `src/commands/**`\n // may not reach into `src/tui/**`, and handing it in is also what lets the\n // command be tested without a terminal.\n registerCommand(pi, session, {\n openOverlay: async (ctx) => {\n const { openMcpOverlay } = await import('../../tui/mcpOverlay.ts');\n return openMcpOverlay(ctx, session);\n },\n });\n registerLeaderContribution(pi);\n\n pi.on(SESSION_START, (_event, ctx) => {\n activeContext = ctx;\n if (options.configurationSource !== DOOM_CONFIGURATION_SOURCE) session.install(readSessionConfig({}, ctx.cwd));\n const protocol = createProtocolRuntime({\n bus: pi.events,\n source: PACKAGE_SOURCE,\n sessionId: ctx.sessionManager.getSessionId(),\n });\n disposeStatusProvider?.();\n disposeStatusProvider = protocol.provide(McpStatusQueryProtocol, () => session.getSnapshot());\n\n // First point Pi accepts an active-list call: the tools registered while the\n // extension loaded are held back until a server actually answers.\n session.activate();\n for (const diagnostic of session.getDiagnostics()) ctx.ui?.notify(diagnostic, WARNING);\n // Detached on purpose: awaiting here would put every downstream connection on\n // the path between launching Pi and its first prompt.\n void session.start();\n });\n\n pi.on(SESSION_SHUTDOWN, () => {\n disposeStatusProvider?.();\n disposeStatusProvider = undefined;\n activeContext = undefined;\n void session.dispose();\n });\n}\n"],"mappings":"keAUA,MAGM,EAAU,UACV,EAA4B,OAelC,SAAgB,EAAqB,EAAkB,EAA+B,CAAC,EAAS,CAC9F,IAAI,EACA,EAEE,EAAU,IAAI,EAAW,CAC7B,KACA,mBAAoB,MAAO,EAAK,EAAY,CAAE,iBAAkB,CAG9D,GAAI,GAAe,OAAS,MAAO,CACjC,GAAI,CAAC,EAAa,OAClB,GAAI,CACF,MAAM,EAAK,EAAI,SAAS,CAAC,CAC3B,OAAS,EAAO,CAKd,MAJA,EAAc,GAAG,OACf,6CAA6C,EAAW,IAAI,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EAAE,wCACnH,CACF,EACM,CACR,CACA,MACF,CAGA,GAAe,IAAI,OAAO,aAAa,EAAW,gBAAgB,EAAI,SAAS,IAAK,MAAI,CAC1F,CACF,CAAC,EAEK,EAAuB,EAAkB,EAC/C,EAAQ,QACN,EAAQ,sBAAwB,EAC5B,EACA,CACE,SAAU,EAAqB,SAC/B,iBAAkB,EAAqB,gBACzC,CACN,EAIA,EAAgB,EAAI,EAAS,CAC3B,YAAa,KAAO,IAAQ,CAC1B,GAAM,CAAE,kBAAmB,MAAM,OAAO,4BACxC,OAAO,EAAe,EAAK,CAAO,CACpC,CACF,CAAC,EACD,EAA2B,CAAE,EAE7B,EAAG,GAAG,iBAAgB,EAAQ,IAAQ,CACpC,EAAgB,EACZ,EAAQ,sBAAwB,GAA2B,EAAQ,QAAQ,EAAkB,CAAC,EAAG,EAAI,GAAG,CAAC,EAC7G,IAAM,EAAW,EAAsB,CACrC,IAAK,EAAG,OACR,OAAQ,EACR,UAAW,EAAI,eAAe,aAAa,CAC7C,CAAC,EACD,IAAwB,EACxB,EAAwB,EAAS,QAAQ,MAA8B,EAAQ,YAAY,CAAC,EAI5F,EAAQ,SAAS,EACjB,IAAK,IAAM,KAAc,EAAQ,eAAe,EAAG,EAAI,IAAI,OAAO,EAAY,CAAO,EAGrF,EAAa,MAAM,CACrB,CAAC,EAED,EAAG,GAAG,uBAAwB,CAC5B,IAAwB,EACxB,EAAwB,IAAA,GACxB,EAAgB,IAAA,GAChB,EAAa,QAAQ,CACvB,CAAC,CACH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./mcpConstants.cjs");let t=require("@agimon-ai/doompi-extension-contracts/leader");function n(n){(0,t.registerDoomLeaderContribution)(n,{source:e.PACKAGE_SOURCE,bindings:[{id:`${e.LEADER_BINDING_PREFIX}.open`,path:[e.LEADER_GROUP,{key:`m`,label:`mcp`,detail:e.LEADER_DETAIL}],command:{name:`mcp`}}]})}exports.registerLeaderContribution=n;
|
|
2
|
+
//# sourceMappingURL=leader.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leader.cjs","names":["PACKAGE_SOURCE","LEADER_BINDING_PREFIX","LEADER_GROUP","LEADER_DETAIL"],"sources":["../../../src/adapters/pi/leader.ts"],"sourcesContent":["import { registerDoomLeaderContribution } from '@agimon-ai/doompi-extension-contracts/leader';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { COMMAND_NAME } from '../../schemas/mcpCommands.ts';\nimport {\n LEADER_BINDING_PREFIX,\n LEADER_DETAIL,\n LEADER_GROUP,\n LEADER_KEY,\n LEADER_LABEL,\n PACKAGE_SOURCE,\n} from './mcpConstants.ts';\n\nexport function registerLeaderContribution(pi: ExtensionAPI): void {\n registerDoomLeaderContribution(pi, {\n source: PACKAGE_SOURCE,\n bindings: [\n {\n id: `${LEADER_BINDING_PREFIX}.open`,\n path: [LEADER_GROUP, { key: LEADER_KEY, label: LEADER_LABEL, detail: LEADER_DETAIL }],\n command: { name: COMMAND_NAME },\n },\n ],\n });\n}\n"],"mappings":"oGAYA,SAAgB,EAA2B,EAAwB,EACjE,EAAA,EAAA,+BAAA,CAA+B,EAAI,CACjC,OAAQA,EAAAA,eACR,SAAU,CACR,CACE,GAAI,GAAGC,EAAAA,sBAAsB,OAC7B,KAAM,CAACC,EAAAA,aAAc,CAAE,IAAA,IAAiB,MAAA,MAAqB,OAAQC,EAAAA,aAAc,CAAC,EACpF,QAAS,CAAE,KAAA,KAAmB,CAChC,CACF,CACF,CAAC,CACH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{LEADER_BINDING_PREFIX as e,LEADER_DETAIL as t,LEADER_GROUP as n,PACKAGE_SOURCE as r}from"./mcpConstants.mjs";import{registerDoomLeaderContribution as i}from"@agimon-ai/doompi-extension-contracts/leader";function a(a){i(a,{source:r,bindings:[{id:`${e}.open`,path:[n,{key:`m`,label:`mcp`,detail:t}],command:{name:`mcp`}}]})}export{a as registerLeaderContribution};
|
|
2
|
+
//# sourceMappingURL=leader.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leader.mjs","names":[],"sources":["../../../src/adapters/pi/leader.ts"],"sourcesContent":["import { registerDoomLeaderContribution } from '@agimon-ai/doompi-extension-contracts/leader';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { COMMAND_NAME } from '../../schemas/mcpCommands.ts';\nimport {\n LEADER_BINDING_PREFIX,\n LEADER_DETAIL,\n LEADER_GROUP,\n LEADER_KEY,\n LEADER_LABEL,\n PACKAGE_SOURCE,\n} from './mcpConstants.ts';\n\nexport function registerLeaderContribution(pi: ExtensionAPI): void {\n registerDoomLeaderContribution(pi, {\n source: PACKAGE_SOURCE,\n bindings: [\n {\n id: `${LEADER_BINDING_PREFIX}.open`,\n path: [LEADER_GROUP, { key: LEADER_KEY, label: LEADER_LABEL, detail: LEADER_DETAIL }],\n command: { name: COMMAND_NAME },\n },\n ],\n });\n}\n"],"mappings":"kNAYA,SAAgB,EAA2B,EAAwB,CACjE,EAA+B,EAAI,CACjC,OAAQ,EACR,SAAU,CACR,CACE,GAAI,GAAG,EAAsB,OAC7B,KAAM,CAAC,EAAc,CAAE,IAAA,IAAiB,MAAA,MAAqB,OAAQ,CAAc,CAAC,EACpF,QAAS,CAAE,KAAA,KAAmB,CAChC,CACF,CACF,CAAC,CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpConstants.cjs","names":[],"sources":["../../../src/adapters/pi/mcpConstants.ts"],"sourcesContent":["export const PACKAGE_SOURCE = '@agimon-ai/doompi-mcp';\n/** Leader binding ids are restricted to `[a-z0-9][a-z0-9._:-]*`, so the scope and slash are dropped. */\nexport const LEADER_BINDING_PREFIX = 'doom-mcp';\n/**\n * Sits under the existing `extension` group, beside `tools` and `config`.\n *\n * A top-level key would have to be unique across every extension, and MCP belongs\n * next to the tool browser a user reaches for anyway. `t` in particular is taken:\n * it is Doom Task's top-level group.\n */\nexport const LEADER_GROUP = { key: 'e', label: 'extension', order: 50 } as const;\nexport const LEADER_KEY = 'm';\nexport const LEADER_LABEL = 'mcp';\nexport const LEADER_DETAIL = 'servers and tools';\n"],"mappings":"AAAA,MAUa,EAAe,CAAE,IAAK,IAAK,MAAO,YAAa,MAAO,EAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpConstants.mjs","names":[],"sources":["../../../src/adapters/pi/mcpConstants.ts"],"sourcesContent":["export const PACKAGE_SOURCE = '@agimon-ai/doompi-mcp';\n/** Leader binding ids are restricted to `[a-z0-9][a-z0-9._:-]*`, so the scope and slash are dropped. */\nexport const LEADER_BINDING_PREFIX = 'doom-mcp';\n/**\n * Sits under the existing `extension` group, beside `tools` and `config`.\n *\n * A top-level key would have to be unique across every extension, and MCP belongs\n * next to the tool browser a user reaches for anyway. `t` in particular is taken:\n * it is Doom Task's top-level group.\n */\nexport const LEADER_GROUP = { key: 'e', label: 'extension', order: 50 } as const;\nexport const LEADER_KEY = 'm';\nexport const LEADER_LABEL = 'mcp';\nexport const LEADER_DETAIL = 'servers and tools';\n"],"mappings":"AAAA,MAAa,EAAiB,wBAEjB,EAAwB,WAQxB,EAAe,CAAE,IAAK,IAAK,MAAO,YAAa,MAAO,EAAG,EAGzD,EAAgB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../node/configSources.cjs"),t=require("../node/mcpRuntime.cjs"),n=require("../process/sessionConfig.cjs"),r=require("../../services/mcpCatalog.cjs"),i=require("../process/directToolsEnvironment.cjs"),a=require("./mcpTools.cjs"),o=`The MCP runtime has not started yet.`;var s=class{pi;runtime;catalog;groups={shared:{configPaths:[],serverNames:[]},sessionLocal:{configPaths:[],serverNames:[]},droppedServers:[]};options;resourceCache=new Map;changeListeners=new Set;authorizationUrls=new Map;browserAuthorizationRequests=new Set;lifecycleGeneration=0;clientManager=()=>this.runtime?.getServices()?.clientManager;constructor(e){this.pi=e.pi,this.options=e,this.catalog=this.createCatalog()}activate(){a.applyActiveTools(this.pi,this.catalog)}createCatalog(){return new r.McpCatalog(i.readDirectToolFilter(this.options.environment))}install(r=n.readSessionConfig(this.options.environment)){this.groups=e.buildMcpConfigGroups(r),this.catalog=this.createCatalog();let i=[...new Set([...this.groups.sessionLocal.serverNames,...this.groups.shared.serverNames])];return this.catalog.seed(t.readCachedCatalog(this.configPaths()),i),a.registerNewTools(this.pi,this.clientManager,this.catalog.allTools()),this.catalog.toSnapshot()}async start(){let e=++this.lifecycleGeneration,[{createTokenStore:t},{McpRuntimeOwner:n}]=await Promise.all([Promise.resolve().then(()=>require("../node/keyringTokenStore.cjs")),Promise.resolve().then(()=>require("../node/mcpRuntime.cjs"))]);if(e!==this.lifecycleGeneration)return;this.runtime??=new n;let r=this.runtime;this.resourceCache.clear();let i=this.configPaths();if(this.configuredServerNames().length===0)return;let a=this.options.tokenStore??await t();await r.start({configPaths:i,tokenStore:a,onAuthorizationUrl:(e,t)=>this.onAuthorizationUrl(e,t),onServerStateChange:e=>this.onServerStateChange(e)}),e!==this.lifecycleGeneration&&await r.dispose()}onAuthorizationUrl(e,t){this.authorizationUrls.set(t,e.toString()),this.emitChange();let n=this.browserAuthorizationRequests.has(t);Promise.resolve().then(()=>this.options.onAuthorizationUrl?.(e,t,{openBrowser:n})).catch(e=>{let n=e instanceof Error?e.message:String(e);this.catalog.addDiagnostic(`Could not open authorization page for "${t}": ${n}`),this.emitChange()})}configPaths(){return[...this.groups.sessionLocal.configPaths,...this.groups.shared.configPaths]}configuredServerNames(){return[...new Set([...this.groups.sessionLocal.serverNames,...this.groups.shared.serverNames])]}async reauthorize(e){let t=this.clientManager();if(!t)throw Error(o);this.resourceCache.delete(e),this.browserAuthorizationRequests.add(e);try{await t.disconnectServer(e),await t.ensureConnected(e)}finally{this.browserAuthorizationRequests.delete(e)}}async openAuthorizationPage(e){let t=this.authorizationUrls.get(e);if(!t)throw Error(`No authorization page is waiting for this MCP server.`);let n=this.options.onAuthorizationUrl;if(!n)throw Error(`This host cannot open authorization pages.`);await n(new URL(t),e,{openBrowser:!0})}getSnapshot(){return this.catalog.toSnapshot()}getServers(){return this.catalog.toView().map(e=>{let t=this.authorizationUrls.get(e.name);return t?{...e,authorizationUrl:t}:e})}setEnabled(e,t){this.catalog.setDisabled(e,!t),a.applyActiveTools(this.pi,this.catalog),this.emitChange()}async listResources(e,t={}){if(!t.refresh){let t=this.resourceCache.get(e);if(t)return t}let n=this.runtime,r=n?.getServices();if(!n||!r)throw Error(o);let i=await(await r.clientManager.ensureConnected(e)).listResources();return n.isCurrent(r)?(this.resourceCache.set(e,i),this.catalog.setResourceCount(e,i.length),this.emitChange(),i):i}onChange(e){return this.changeListeners.add(e),()=>{this.changeListeners.delete(e)}}emitChange(){for(let e of this.changeListeners)try{e()}catch(e){this.catalog.addDiagnostic(`An MCP change listener failed: ${e instanceof Error?e.message:String(e)}`)}}getDiagnostics(){return this.catalog.getDiagnostics()}async dispose(){++this.lifecycleGeneration,this.browserAuthorizationRequests.clear(),await this.runtime?.dispose()}onServerStateChange(e){e.state!==`connecting`&&this.authorizationUrls.delete(e.serverName);let t=this.runtime,n=t?.getServices();(e.state===`connected`&&n?n.clientManager.ensureConnected(e.serverName).then(e=>e.listTools()):Promise.resolve([])).catch(()=>[]).then(r=>{if(!t?.isCurrent(n))return;let i=this.catalog.applyStateChange(e,r);a.registerNewTools(this.pi,this.clientManager,i),a.applyActiveTools(this.pi,this.catalog),this.emitChange()})}};exports.McpSession=s;
|
|
2
|
+
//# sourceMappingURL=mcpSession.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpSession.cjs","names":["McpCatalog","readDirectToolFilter","readSessionConfig","buildMcpConfigGroups","readCachedCatalog"],"sources":["../../../src/adapters/pi/mcpSession.ts"],"sourcesContent":["import type { McpStatusSnapshot } from '@agimon-ai/doompi-extension-contracts/mcp-status';\nimport type { McpClientManagerService, McpServerStateChange, TokenStore } from '@agimon-ai/mcp-proxy';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { McpCatalog } from '../../services/mcpCatalog.ts';\nimport type { McpResourceView, McpServerView } from '../../types/mcp.ts';\nimport type { McpConfigGroups, McpSessionConfig } from '../../types/mcpConfig.ts';\nimport { buildMcpConfigGroups } from '../node/configSources.ts';\nimport { type McpRuntimeOwner, readCachedCatalog } from '../node/mcpRuntime.ts';\nimport { readDirectToolFilter } from '../process/directToolsEnvironment.ts';\nimport { readSessionConfig } from '../process/sessionConfig.ts';\nimport { applyActiveTools, registerNewTools } from './mcpTools.ts';\n\n/** Raised by anything needing a live container before one has been built. */\nconst RUNTIME_NOT_STARTED = 'The MCP runtime has not started yet.';\nconst AUTHORIZATION_URL_NOT_READY = 'No authorization page is waiting for this MCP server.';\n\nexport interface McpSessionOptions {\n pi: ExtensionAPI;\n environment?: NodeJS.ProcessEnv;\n /** Surfaces an OAuth URL, opening it when an explicit authorization requested that. */\n onAuthorizationUrl?: (url: URL, serverName: string, options: { openBrowser: boolean }) => void | Promise<void>;\n /** Overrides the OS keyring store, for tests. */\n tokenStore?: TokenStore;\n}\n\n/**\n * The MCP picture for one Pi session.\n *\n * Install is synchronous and cheap: the previous run's catalog is read off disk and\n * its tools registered, so Pi's first turn already has them. Connecting happens on\n * a detached promise afterwards, and each server folds into the active tool set as\n * it lands. Nothing on this path waits for a socket.\n */\nexport class McpSession {\n private readonly pi: ExtensionAPI;\n /** Created after session_start, never while Pi is importing extensions. */\n private runtime: McpRuntimeOwner | undefined;\n private catalog: McpCatalog;\n private groups: McpConfigGroups = {\n shared: { configPaths: [], serverNames: [] },\n sessionLocal: { configPaths: [], serverNames: [] },\n droppedServers: [],\n };\n private readonly options: McpSessionOptions;\n /** Resources per server, listed on demand. Errors are never cached, so a retry re-dials. */\n private readonly resourceCache = new Map<string, readonly McpResourceView[]>();\n private readonly changeListeners = new Set<() => void>();\n /** Authorization URLs waiting to be opened, by server. */\n private readonly authorizationUrls = new Map<string, string>();\n /** Servers whose explicit auth action should launch the browser when its URL arrives. */\n private readonly browserAuthorizationRequests = new Set<string>();\n /** Invalidates deferred startup work when a session reloads or shuts down. */\n private lifecycleGeneration = 0;\n\n /** Resolved per tool call, so a registration outlives the container it was made under. */\n private readonly clientManager = (): McpClientManagerService | undefined =>\n this.runtime?.getServices()?.clientManager;\n\n constructor(options: McpSessionOptions) {\n this.pi = options.pi;\n this.options = options;\n this.catalog = this.createCatalog();\n }\n\n /**\n * Applies the tool set to Pi. Runs on session start, never during install.\n *\n * Pi's active-list calls throw until the runtime is bound, so the tools\n * registered at install stay at Pi's default visibility until this runs, which\n * is the first point the session can express that nothing has connected yet.\n */\n activate(): void {\n applyActiveTools(this.pi, this.catalog);\n }\n\n private createCatalog(): McpCatalog {\n return new McpCatalog(readDirectToolFilter(this.options.environment));\n }\n\n /**\n * Registers the cached catalog with Pi. Runs at extension install.\n *\n * Returns the servers it seeded, so a caller can report what the session started\n * with before anything connected.\n */\n install(configuration: McpSessionConfig = readSessionConfig(this.options.environment)): McpStatusSnapshot {\n this.groups = buildMcpConfigGroups(configuration);\n this.catalog = this.createCatalog();\n // Doom embeds the client runtime, so upstreams referenced by a proxy wrapper\n // remain individual servers in the catalog rather than collapsing into it.\n const configuredServers = [\n ...new Set([...this.groups.sessionLocal.serverNames, ...this.groups.shared.serverNames]),\n ];\n this.catalog.seed(readCachedCatalog(this.configPaths()), configuredServers);\n // Registration only. Pi allows registerTool while extensions load but rejects\n // the active-list calls until the runtime is bound, so activation waits for\n // `activate()` on session start.\n registerNewTools(this.pi, this.clientManager, this.catalog.allTools());\n return this.catalog.toSnapshot();\n }\n\n /**\n * Starts connecting. Never awaited on Pi's critical path.\n *\n * Resolves once the container exists, not once servers are reachable. A start\n * that a newer generation superseded resolves to nothing and registers nothing.\n */\n async start(): Promise<void> {\n const generation = ++this.lifecycleGeneration;\n const [{ createTokenStore }, { McpRuntimeOwner }] = await Promise.all([\n import('../node/keyringTokenStore.ts'),\n import('../node/mcpRuntime.ts'),\n ]);\n if (generation !== this.lifecycleGeneration) return;\n this.runtime ??= new McpRuntimeOwner();\n const runtime = this.runtime;\n // The container is about to be replaced, so every resource list read from the\n // old one is now hearsay.\n this.resourceCache.clear();\n const configPaths = this.configPaths();\n // A repository whose layers declare nothing still yields a config path, and\n // building a container for it would spend a keyring probe on a session that\n // has nothing to connect to.\n if (this.configuredServerNames().length === 0) return;\n const tokenStore = this.options.tokenStore ?? (await createTokenStore());\n await runtime.start({\n configPaths,\n tokenStore,\n onAuthorizationUrl: (url, serverName) => this.onAuthorizationUrl(url, serverName),\n onServerStateChange: (change) => this.onServerStateChange(change),\n });\n // Shutdown can land while the proxy package or token store is loading. Make\n // sure that late container cannot survive and mutate a closed Pi session.\n if (generation !== this.lifecycleGeneration) await runtime.dispose();\n }\n\n /**\n * Records the URL a flow is waiting on, then passes it to the host.\n *\n * Kept here as well as announced, because the announcement lands in the\n * transcript and the panel a user starts the flow from covers it. Host work is\n * detached so a failed desktop browser launch never cancels a still-usable OAuth\n * callback; the overlay retains a clickable fallback and a retry action.\n */\n private onAuthorizationUrl(url: URL, serverName: string): void {\n this.authorizationUrls.set(serverName, url.toString());\n this.emitChange();\n const openBrowser = this.browserAuthorizationRequests.has(serverName);\n void Promise.resolve()\n .then(() => this.options.onAuthorizationUrl?.(url, serverName, { openBrowser }))\n .catch((error: unknown) => {\n const detail = error instanceof Error ? error.message : String(error);\n this.catalog.addDiagnostic(`Could not open authorization page for \"${serverName}\": ${detail}`);\n this.emitChange();\n });\n }\n\n /** Ordered layers for the one in-process client container. */\n private configPaths(): string[] {\n return [...this.groups.sessionLocal.configPaths, ...this.groups.shared.configPaths];\n }\n\n private configuredServerNames(): string[] {\n return [...new Set([...this.groups.sessionLocal.serverNames, ...this.groups.shared.serverNames])];\n }\n\n /**\n * Reconnects one server, running its OAuth flow when it demands one.\n *\n * The existing connection is dropped first: a server holding a stale token would\n * otherwise keep answering from cache and never reach the authorization step.\n */\n async reauthorize(serverName: string): Promise<void> {\n const clientManager = this.clientManager();\n if (!clientManager) throw new Error(RUNTIME_NOT_STARTED);\n // What this server offers is exactly what the reconnect is about to settle.\n this.resourceCache.delete(serverName);\n this.browserAuthorizationRequests.add(serverName);\n try {\n await clientManager.disconnectServer(serverName);\n await clientManager.ensureConnected(serverName);\n } finally {\n this.browserAuthorizationRequests.delete(serverName);\n }\n }\n\n /** Reopens the URL already reserved for a flow without restarting that flow. */\n async openAuthorizationPage(serverName: string): Promise<void> {\n const authorizationUrl = this.authorizationUrls.get(serverName);\n if (!authorizationUrl) throw new Error(AUTHORIZATION_URL_NOT_READY);\n const onAuthorizationUrl = this.options.onAuthorizationUrl;\n if (!onAuthorizationUrl) throw new Error('This host cannot open authorization pages.');\n await onAuthorizationUrl(new URL(authorizationUrl), serverName, { openBrowser: true });\n }\n\n getSnapshot(): McpStatusSnapshot {\n return this.catalog.toSnapshot();\n }\n\n /** The fuller picture the overlay renders, with tool names and descriptions. */\n getServers(): readonly McpServerView[] {\n return this.catalog.toView().map((server) => {\n const authorizationUrl = this.authorizationUrls.get(server.name);\n return authorizationUrl ? { ...server, authorizationUrl } : server;\n });\n }\n\n /**\n * Hides or restores a server's tools for the rest of this session.\n *\n * Deliberately does not disconnect: this is a session visibility control, so a\n * later enable is instant and does not discard an authenticated live connection.\n * Because each catalog row is now an actual downstream server, the control acts\n * on only that server's tools rather than on an aggregate proxy group.\n *\n * Nothing is re-registered on the way back: Pi 0.84 cannot unregister a tool, so\n * every tool is still registered and only its place in the active list moved.\n */\n setEnabled(serverName: string, enabled: boolean): void {\n this.catalog.setDisabled(serverName, !enabled);\n applyActiveTools(this.pi, this.catalog);\n this.emitChange();\n }\n\n /**\n * Lists one server's resources, from cache unless asked for a fresh call.\n *\n * The cache lives here rather than in the overlay so closing and reopening the\n * panel does not re-dial. A rejection is never cached, so a server that has no\n * `resources/list` capability, or one that was briefly unreachable, is retried by\n * the next request rather than remembered as empty.\n */\n async listResources(serverName: string, options: { refresh?: boolean } = {}): Promise<readonly McpResourceView[]> {\n if (!options.refresh) {\n const cached = this.resourceCache.get(serverName);\n if (cached) return cached;\n }\n const runtime = this.runtime;\n const services = runtime?.getServices();\n if (!runtime || !services) throw new Error(RUNTIME_NOT_STARTED);\n const connection = await services.clientManager.ensureConnected(serverName);\n const resources = await connection.listResources();\n // A reload that landed while this was in flight retired the container it was\n // read from, so the answer is returned to the caller but never written down.\n if (!runtime.isCurrent(services)) return resources;\n this.resourceCache.set(serverName, resources);\n this.catalog.setResourceCount(serverName, resources.length);\n this.emitChange();\n return resources;\n }\n\n /** Fires whenever the server picture changes. Returns its own disposer. */\n onChange(listener: () => void): () => void {\n this.changeListeners.add(listener);\n return () => {\n this.changeListeners.delete(listener);\n };\n }\n\n /**\n * Announces a change to whoever is watching.\n *\n * Listener failures are contained rather than propagated: this runs inside the\n * continuation that folds a server into Pi's tool set, and a throw escaping here\n * would abandon that fold, leaving the session silently short of tools because\n * something failed to repaint.\n */\n private emitChange(): void {\n for (const listener of this.changeListeners) {\n try {\n listener();\n } catch (error) {\n // Recorded rather than rethrown: a watcher that cannot render is a defect\n // worth seeing, but not one worth abandoning the tool set over.\n this.catalog.addDiagnostic(\n `An MCP change listener failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n }\n\n getDiagnostics(): readonly string[] {\n return this.catalog.getDiagnostics();\n }\n\n async dispose(): Promise<void> {\n ++this.lifecycleGeneration;\n this.browserAuthorizationRequests.clear();\n await this.runtime?.dispose();\n }\n\n /**\n * Folds one server transition into Pi's tool set.\n *\n * The tool list is read from the live connection rather than the state event,\n * which carries no tools; a server that is not readable contributes none and its\n * cached tools simply stay inactive.\n *\n * `ensureConnected` rather than `getClient`: the manager announces `connected`\n * before it registers the client, so a listener that looks the client up\n * synchronously finds nothing and the server lands with an empty tool list.\n * This handler runs detached from the connect, so awaiting it cannot deadlock.\n */\n private onServerStateChange(change: McpServerStateChange): void {\n // The flow this URL belonged to is over, one way or the other: a server that\n // reached a terminal state is not still waiting on a redirect.\n if (change.state !== 'connecting') this.authorizationUrls.delete(change.serverName);\n const runtime = this.runtime;\n const services = runtime?.getServices();\n const tools =\n change.state === 'connected' && services\n ? services.clientManager.ensureConnected(change.serverName).then((connection) => connection.listTools())\n : Promise.resolve([]);\n void tools\n .catch(() => [])\n .then((tools) => {\n if (!runtime?.isCurrent(services)) return;\n const added = this.catalog.applyStateChange(change, tools);\n registerNewTools(this.pi, this.clientManager, added);\n applyActiveTools(this.pi, this.catalog);\n this.emitChange();\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{buildMcpConfigGroups as e}from"../node/configSources.mjs";import{readCachedCatalog as t}from"../node/mcpRuntime.mjs";import{readSessionConfig as n}from"../process/sessionConfig.mjs";import{McpCatalog as r}from"../../services/mcpCatalog.mjs";import{readDirectToolFilter as i}from"../process/directToolsEnvironment.mjs";import{applyActiveTools as a,registerNewTools as o}from"./mcpTools.mjs";const s=`The MCP runtime has not started yet.`;var c=class{pi;runtime;catalog;groups={shared:{configPaths:[],serverNames:[]},sessionLocal:{configPaths:[],serverNames:[]},droppedServers:[]};options;resourceCache=new Map;changeListeners=new Set;authorizationUrls=new Map;browserAuthorizationRequests=new Set;lifecycleGeneration=0;clientManager=()=>this.runtime?.getServices()?.clientManager;constructor(e){this.pi=e.pi,this.options=e,this.catalog=this.createCatalog()}activate(){a(this.pi,this.catalog)}createCatalog(){return new r(i(this.options.environment))}install(r=n(this.options.environment)){this.groups=e(r),this.catalog=this.createCatalog();let i=[...new Set([...this.groups.sessionLocal.serverNames,...this.groups.shared.serverNames])];return this.catalog.seed(t(this.configPaths()),i),o(this.pi,this.clientManager,this.catalog.allTools()),this.catalog.toSnapshot()}async start(){let e=++this.lifecycleGeneration,[{createTokenStore:t},{McpRuntimeOwner:n}]=await Promise.all([import(`../node/keyringTokenStore.mjs`),import(`../node/mcpRuntime.mjs`)]);if(e!==this.lifecycleGeneration)return;this.runtime??=new n;let r=this.runtime;this.resourceCache.clear();let i=this.configPaths();if(this.configuredServerNames().length===0)return;let a=this.options.tokenStore??await t();await r.start({configPaths:i,tokenStore:a,onAuthorizationUrl:(e,t)=>this.onAuthorizationUrl(e,t),onServerStateChange:e=>this.onServerStateChange(e)}),e!==this.lifecycleGeneration&&await r.dispose()}onAuthorizationUrl(e,t){this.authorizationUrls.set(t,e.toString()),this.emitChange();let n=this.browserAuthorizationRequests.has(t);Promise.resolve().then(()=>this.options.onAuthorizationUrl?.(e,t,{openBrowser:n})).catch(e=>{let n=e instanceof Error?e.message:String(e);this.catalog.addDiagnostic(`Could not open authorization page for "${t}": ${n}`),this.emitChange()})}configPaths(){return[...this.groups.sessionLocal.configPaths,...this.groups.shared.configPaths]}configuredServerNames(){return[...new Set([...this.groups.sessionLocal.serverNames,...this.groups.shared.serverNames])]}async reauthorize(e){let t=this.clientManager();if(!t)throw Error(s);this.resourceCache.delete(e),this.browserAuthorizationRequests.add(e);try{await t.disconnectServer(e),await t.ensureConnected(e)}finally{this.browserAuthorizationRequests.delete(e)}}async openAuthorizationPage(e){let t=this.authorizationUrls.get(e);if(!t)throw Error(`No authorization page is waiting for this MCP server.`);let n=this.options.onAuthorizationUrl;if(!n)throw Error(`This host cannot open authorization pages.`);await n(new URL(t),e,{openBrowser:!0})}getSnapshot(){return this.catalog.toSnapshot()}getServers(){return this.catalog.toView().map(e=>{let t=this.authorizationUrls.get(e.name);return t?{...e,authorizationUrl:t}:e})}setEnabled(e,t){this.catalog.setDisabled(e,!t),a(this.pi,this.catalog),this.emitChange()}async listResources(e,t={}){if(!t.refresh){let t=this.resourceCache.get(e);if(t)return t}let n=this.runtime,r=n?.getServices();if(!n||!r)throw Error(s);let i=await(await r.clientManager.ensureConnected(e)).listResources();return n.isCurrent(r)?(this.resourceCache.set(e,i),this.catalog.setResourceCount(e,i.length),this.emitChange(),i):i}onChange(e){return this.changeListeners.add(e),()=>{this.changeListeners.delete(e)}}emitChange(){for(let e of this.changeListeners)try{e()}catch(e){this.catalog.addDiagnostic(`An MCP change listener failed: ${e instanceof Error?e.message:String(e)}`)}}getDiagnostics(){return this.catalog.getDiagnostics()}async dispose(){++this.lifecycleGeneration,this.browserAuthorizationRequests.clear(),await this.runtime?.dispose()}onServerStateChange(e){e.state!==`connecting`&&this.authorizationUrls.delete(e.serverName);let t=this.runtime,n=t?.getServices();(e.state===`connected`&&n?n.clientManager.ensureConnected(e.serverName).then(e=>e.listTools()):Promise.resolve([])).catch(()=>[]).then(r=>{if(!t?.isCurrent(n))return;let i=this.catalog.applyStateChange(e,r);o(this.pi,this.clientManager,i),a(this.pi,this.catalog),this.emitChange()})}};export{c as McpSession};
|
|
2
|
+
//# sourceMappingURL=mcpSession.mjs.map
|