@agimon-ai/doompi-mcp 0.0.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +35 -0
- package/dist/_virtual/_rolldown/runtime.cjs +1 -0
- package/dist/commands/mcpCommand.cjs +3 -0
- package/dist/commands/mcpCommand.cjs.map +1 -0
- package/dist/commands/mcpCommand.mjs +3 -0
- package/dist/commands/mcpCommand.mjs.map +1 -0
- package/dist/constants.cjs +2 -0
- package/dist/constants.cjs.map +1 -0
- package/dist/constants.mjs +2 -0
- package/dist/constants.mjs.map +1 -0
- package/dist/extension.cjs +2 -0
- package/dist/extension.cjs.map +1 -0
- package/dist/extension.d.cts +18 -0
- package/dist/extension.d.cts.map +1 -0
- package/dist/extension.d.mts +18 -0
- package/dist/extension.d.mts.map +1 -0
- package/dist/extension.mjs +2 -0
- package/dist/extension.mjs.map +1 -0
- package/dist/extensions/doom.cjs +2 -0
- package/dist/extensions/doom.cjs.map +1 -0
- package/dist/extensions/doom.d.cts +6 -0
- package/dist/extensions/doom.d.cts.map +1 -0
- package/dist/extensions/doom.d.mts +7 -0
- package/dist/extensions/doom.d.mts.map +1 -0
- package/dist/extensions/doom.mjs +2 -0
- package/dist/extensions/doom.mjs.map +1 -0
- package/dist/extensions/pi.cjs +2 -0
- package/dist/extensions/pi.cjs.map +1 -0
- package/dist/extensions/pi.d.cts +2 -0
- package/dist/extensions/pi.d.mts +2 -0
- package/dist/extensions/pi.mjs +2 -0
- package/dist/extensions/pi.mjs.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.mjs +1 -0
- package/dist/leader.cjs +2 -0
- package/dist/leader.cjs.map +1 -0
- package/dist/leader.mjs +2 -0
- package/dist/leader.mjs.map +1 -0
- package/dist/services/configSources.cjs +2 -0
- package/dist/services/configSources.cjs.map +1 -0
- package/dist/services/configSources.d.cts +65 -0
- package/dist/services/configSources.d.cts.map +1 -0
- package/dist/services/configSources.d.mts +65 -0
- package/dist/services/configSources.d.mts.map +1 -0
- package/dist/services/configSources.mjs +2 -0
- package/dist/services/configSources.mjs.map +1 -0
- package/dist/services/directTools.cjs +2 -0
- package/dist/services/directTools.cjs.map +1 -0
- package/dist/services/directTools.d.cts +14 -0
- package/dist/services/directTools.d.cts.map +1 -0
- package/dist/services/directTools.d.mts +14 -0
- package/dist/services/directTools.d.mts.map +1 -0
- package/dist/services/directTools.mjs +2 -0
- package/dist/services/directTools.mjs.map +1 -0
- package/dist/services/keyringTokenStore.cjs +2 -0
- package/dist/services/keyringTokenStore.cjs.map +1 -0
- package/dist/services/keyringTokenStore.mjs +2 -0
- package/dist/services/keyringTokenStore.mjs.map +1 -0
- package/dist/services/mcpCatalog.cjs +2 -0
- package/dist/services/mcpCatalog.cjs.map +1 -0
- package/dist/services/mcpCatalog.d.cts +17 -0
- package/dist/services/mcpCatalog.d.cts.map +1 -0
- package/dist/services/mcpCatalog.d.mts +17 -0
- package/dist/services/mcpCatalog.d.mts.map +1 -0
- package/dist/services/mcpCatalog.mjs +2 -0
- package/dist/services/mcpCatalog.mjs.map +1 -0
- package/dist/services/mcpRuntime.cjs +2 -0
- package/dist/services/mcpRuntime.cjs.map +1 -0
- package/dist/services/mcpRuntime.d.cts +26 -0
- package/dist/services/mcpRuntime.d.cts.map +1 -0
- package/dist/services/mcpRuntime.d.mts +26 -0
- package/dist/services/mcpRuntime.d.mts.map +1 -0
- package/dist/services/mcpRuntime.mjs +2 -0
- package/dist/services/mcpRuntime.mjs.map +1 -0
- package/dist/services/mcpSession.cjs +2 -0
- package/dist/services/mcpSession.cjs.map +1 -0
- package/dist/services/mcpSession.mjs +2 -0
- package/dist/services/mcpSession.mjs.map +1 -0
- package/dist/services/sessionConfig.cjs +2 -0
- package/dist/services/sessionConfig.cjs.map +1 -0
- package/dist/services/sessionConfig.d.cts +25 -0
- package/dist/services/sessionConfig.d.cts.map +1 -0
- package/dist/services/sessionConfig.d.mts +25 -0
- package/dist/services/sessionConfig.d.mts.map +1 -0
- package/dist/services/sessionConfig.mjs +2 -0
- package/dist/services/sessionConfig.mjs.map +1 -0
- package/dist/services/sharedRuntime.cjs +2 -0
- package/dist/services/sharedRuntime.cjs.map +1 -0
- package/dist/services/sharedRuntime.mjs +2 -0
- package/dist/services/sharedRuntime.mjs.map +1 -0
- package/dist/tools/mcpTools.cjs +3 -0
- package/dist/tools/mcpTools.cjs.map +1 -0
- package/dist/tools/mcpTools.mjs +3 -0
- package/dist/tools/mcpTools.mjs.map +1 -0
- package/dist/tui/mcpOverlay.cjs +2 -0
- package/dist/tui/mcpOverlay.cjs.map +1 -0
- package/dist/tui/mcpOverlay.mjs +2 -0
- package/dist/tui/mcpOverlay.mjs.map +1 -0
- package/dist/types.d.cts +2 -0
- package/dist/types.d.mts +2 -0
- package/package.json +81 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vuong Ngo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @agimon-ai/doompi-mcp
|
|
2
|
+
|
|
3
|
+
MCP server tools for Doompi.
|
|
4
|
+
|
|
5
|
+
The MCP adapter. Resolves the servers a selection allows and exposes them to the session, so
|
|
6
|
+
a domain with an `mcp` allowlist reaches only the servers and proxy upstreams it names.
|
|
7
|
+
|
|
8
|
+
Part of [Doompi](https://www.npmjs.com/package/@agimon-ai/doompi), a coding agent that loads
|
|
9
|
+
only the skills and tools you name. This package is composed by Doompi and is not usually
|
|
10
|
+
installed on its own.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @agimon-ai/doompi-mcp
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## How it loads
|
|
19
|
+
|
|
20
|
+
Doompi loads this unconditionally. It is core, not a layer, so it does not appear in
|
|
21
|
+
`.doom/modes.yaml`: making it optional would only create broken configurations.
|
|
22
|
+
|
|
23
|
+
Extension entry: `@agimon-ai/doompi-mcp/extensions/pi`
|
|
24
|
+
|
|
25
|
+
## Entry points
|
|
26
|
+
|
|
27
|
+
| Import |
|
|
28
|
+
| --------------------------------------- |
|
|
29
|
+
| `@agimon-ai/doompi-mcp` |
|
|
30
|
+
| `@agimon-ai/doompi-mcp/extensions/pi` |
|
|
31
|
+
| `@agimon-ai/doompi-mcp/extensions/doom` |
|
|
32
|
+
|
|
33
|
+
## License
|
|
34
|
+
|
|
35
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,s)=>(s=n==null?{}:e(i(n)),o(r||!n||!n.__esModule||!a.call(n,`default`)?t(s,`default`,{value:n,enumerable:!0}):s,n));exports.__toESM=s;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
const e=require("../constants.cjs"),t=`info`,n=`warning`,r={status:`status`,auth:`auth`,reload:`reload`},i=Object.values(r).join(`, `);function a(e){let[t=r.status,...n]=e.trim().split(/\s+/).filter(Boolean);return{subcommand:t,argument:n.join(` `)}}function o(e){let{servers:t}=e.getSnapshot();return t.length===0?`No MCP servers are configured for this session.`:[...t.map(e=>{let t=e.error?` (${e.error})`:``;return`${e.name}: ${e.state} · ${e.tools.length} tools${t}`}),...e.getDiagnostics()].join(`
|
|
2
|
+
`)}async function s(e,r,i){if(!r){i.ui.notify(`Name the server to authorize, for example /mcp auth boomlink.`,n);return}try{await e.reauthorize(r),i.ui.notify(`${r} is authorized.`,t)}catch(e){i.ui.notify(`Could not authorize ${r}: ${e instanceof Error?e.message:`unknown error`}`,n)}}function c(c,l,u){c.registerCommand(`mcp`,{description:e.COMMAND_DESCRIPTION,handler:async(e,c)=>{let d=typeof e==`string`?e:``;if(d.trim()===``&&c.hasUI){await u.openOverlay(c);return}let{subcommand:f,argument:p}=a(d);if(f===r.auth){await s(l,p,c);return}if(f===r.reload){c.ui.notify(`Reconnecting MCP servers.`,t),l.start();return}if(f!==r.status){c.ui.notify(`Unknown /mcp subcommand "${f}". Use ${i}.`,n);return}c.ui.notify(o(l),t)}})}exports.formatStatus=o,exports.registerCommand=c;
|
|
3
|
+
//# sourceMappingURL=mcpCommand.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpCommand.cjs","names":["COMMAND_DESCRIPTION"],"sources":["../../src/commands/mcpCommand.ts"],"sourcesContent":["import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { COMMAND_DESCRIPTION, COMMAND_NAME } from '../constants.ts';\nimport type { McpCommandDeps, McpCommandTarget } from '../types.ts';\n\nconst INFO = 'info';\nconst WARNING = 'warning';\n\nconst SUBCOMMAND = { status: 'status', auth: 'auth', reload: 'reload' } as const;\nconst SUBCOMMANDS = Object.values(SUBCOMMAND).join(', ');\n\n/** `/mcp <subcommand> [argument]`, with the bare command showing status. */\nfunction parseArguments(args: string): { subcommand: string; argument: string } {\n const [subcommand = SUBCOMMAND.status, ...rest] = args.trim().split(/\\s+/).filter(Boolean);\n return { subcommand, argument: rest.join(' ') };\n}\n\n/** One line per server: its state, and how many tools it currently offers. */\nexport function formatStatus(session: McpCommandTarget): string {\n const { servers } = session.getSnapshot();\n if (servers.length === 0) return 'No MCP servers are configured for this session.';\n const lines = servers.map((server) => {\n const detail = server.error ? ` (${server.error})` : '';\n return `${server.name}: ${server.state} · ${server.tools.length} tools${detail}`;\n });\n return [...lines, ...session.getDiagnostics()].join('\\n');\n}\n\nasync function runAuth(session: McpCommandTarget, serverName: string, ctx: ExtensionContext): Promise<void> {\n if (!serverName) {\n ctx.ui.notify('Name the server to authorize, for example /mcp auth boomlink.', WARNING);\n return;\n }\n try {\n // Reconnecting is what drives the flow: the OAuth provider surfaces the\n // authorization URL through the handler this extension installed.\n await session.reauthorize(serverName);\n ctx.ui.notify(`${serverName} is authorized.`, INFO);\n } catch (error) {\n ctx.ui.notify(\n `Could not authorize ${serverName}: ${error instanceof Error ? error.message : 'unknown error'}`,\n WARNING,\n );\n }\n}\n\nexport function registerCommand(pi: ExtensionAPI, session: McpCommandTarget, deps: McpCommandDeps): void {\n pi.registerCommand(COMMAND_NAME, {\n description: COMMAND_DESCRIPTION,\n handler: async (args, ctx) => {\n const raw = typeof args === 'string' ? args : '';\n // The bare command opens the browser, where the same information can be acted\n // on. The subcommands stay because an overlay needs a terminal, and a headless\n // or scripted session still has to be able to ask.\n if (raw.trim() === '' && ctx.hasUI) {\n await deps.openOverlay(ctx);\n return;\n }\n const { subcommand, argument } = parseArguments(raw);\n if (subcommand === SUBCOMMAND.auth) {\n await runAuth(session, argument, ctx);\n return;\n }\n if (subcommand === SUBCOMMAND.reload) {\n ctx.ui.notify('Reconnecting MCP servers.', INFO);\n // Not awaited: a server that never answers must not wedge the command.\n void session.start();\n return;\n }\n if (subcommand !== SUBCOMMAND.status) {\n ctx.ui.notify(`Unknown /${COMMAND_NAME} subcommand \"${subcommand}\". Use ${SUBCOMMANDS}.`, WARNING);\n return;\n }\n ctx.ui.notify(formatStatus(session), INFO);\n },\n });\n}\n"],"mappings":"oCAIM,EAAO,OACP,EAAU,UAEV,EAAa,CAAE,OAAQ,SAAU,KAAM,OAAQ,OAAQ,QAAS,EAChE,EAAc,OAAO,OAAO,CAAU,CAAC,CAAC,KAAK,IAAI,EAGvD,SAAS,EAAe,EAAwD,CAC9E,GAAM,CAAC,EAAa,EAAW,OAAQ,GAAG,GAAQ,EAAK,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO,EACzF,MAAO,CAAE,aAAY,SAAU,EAAK,KAAK,GAAG,CAAE,CAChD,CAGA,SAAgB,EAAa,EAAmC,CAC9D,GAAM,CAAE,WAAY,EAAQ,YAAY,EAMxC,OALI,EAAQ,SAAW,EAAU,kDAK1B,CAAC,GAJM,EAAQ,IAAK,GAAW,CACpC,IAAM,EAAS,EAAO,MAAQ,KAAK,EAAO,MAAM,GAAK,GACrD,MAAO,GAAG,EAAO,KAAK,IAAI,EAAO,MAAM,KAAK,EAAO,MAAM,OAAO,QAAQ,GAC1E,CACe,EAAG,GAAG,EAAQ,eAAe,CAAC,CAAC,CAAC,KAAK;CAAI,CAC1D,CAEA,eAAe,EAAQ,EAA2B,EAAoB,EAAsC,CAC1G,GAAI,CAAC,EAAY,CACf,EAAI,GAAG,OAAO,gEAAiE,CAAO,EACtF,MACF,CACA,GAAI,CAGF,MAAM,EAAQ,YAAY,CAAU,EACpC,EAAI,GAAG,OAAO,GAAG,EAAW,iBAAkB,CAAI,CACpD,OAAS,EAAO,CACd,EAAI,GAAG,OACL,uBAAuB,EAAW,IAAI,aAAiB,MAAQ,EAAM,QAAU,kBAC/E,CACF,CACF,CACF,CAEA,SAAgB,EAAgB,EAAkB,EAA2B,EAA4B,CACvG,EAAG,gBAAA,MAA8B,CAC/B,YAAaA,EAAAA,oBACb,QAAS,MAAO,EAAM,IAAQ,CAC5B,IAAM,EAAM,OAAO,GAAS,SAAW,EAAO,GAI9C,GAAI,EAAI,KAAK,IAAM,IAAM,EAAI,MAAO,CAClC,MAAM,EAAK,YAAY,CAAG,EAC1B,MACF,CACA,GAAM,CAAE,aAAY,YAAa,EAAe,CAAG,EACnD,GAAI,IAAe,EAAW,KAAM,CAClC,MAAM,EAAQ,EAAS,EAAU,CAAG,EACpC,MACF,CACA,GAAI,IAAe,EAAW,OAAQ,CACpC,EAAI,GAAG,OAAO,4BAA6B,CAAI,EAE/C,EAAa,MAAM,EACnB,MACF,CACA,GAAI,IAAe,EAAW,OAAQ,CACpC,EAAI,GAAG,OAAO,4BAAwC,EAAW,SAAS,EAAY,GAAI,CAAO,EACjG,MACF,CACA,EAAI,GAAG,OAAO,EAAa,CAAO,EAAG,CAAI,CAC3C,CACF,CAAC,CACH"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{COMMAND_DESCRIPTION as e}from"../constants.mjs";const t=`info`,n=`warning`,r={status:`status`,auth:`auth`,reload:`reload`},i=Object.values(r).join(`, `);function a(e){let[t=r.status,...n]=e.trim().split(/\s+/).filter(Boolean);return{subcommand:t,argument:n.join(` `)}}function o(e){let{servers:t}=e.getSnapshot();return t.length===0?`No MCP servers are configured for this session.`:[...t.map(e=>{let t=e.error?` (${e.error})`:``;return`${e.name}: ${e.state} · ${e.tools.length} tools${t}`}),...e.getDiagnostics()].join(`
|
|
2
|
+
`)}async function s(e,r,i){if(!r){i.ui.notify(`Name the server to authorize, for example /mcp auth boomlink.`,n);return}try{await e.reauthorize(r),i.ui.notify(`${r} is authorized.`,t)}catch(e){i.ui.notify(`Could not authorize ${r}: ${e instanceof Error?e.message:`unknown error`}`,n)}}function c(c,l,u){c.registerCommand(`mcp`,{description:e,handler:async(e,c)=>{let d=typeof e==`string`?e:``;if(d.trim()===``&&c.hasUI){await u.openOverlay(c);return}let{subcommand:f,argument:p}=a(d);if(f===r.auth){await s(l,p,c);return}if(f===r.reload){c.ui.notify(`Reconnecting MCP servers.`,t),l.start();return}if(f!==r.status){c.ui.notify(`Unknown /mcp subcommand "${f}". Use ${i}.`,n);return}c.ui.notify(o(l),t)}})}export{o as formatStatus,c as registerCommand};
|
|
3
|
+
//# sourceMappingURL=mcpCommand.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpCommand.mjs","names":[],"sources":["../../src/commands/mcpCommand.ts"],"sourcesContent":["import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { COMMAND_DESCRIPTION, COMMAND_NAME } from '../constants.ts';\nimport type { McpCommandDeps, McpCommandTarget } from '../types.ts';\n\nconst INFO = 'info';\nconst WARNING = 'warning';\n\nconst SUBCOMMAND = { status: 'status', auth: 'auth', reload: 'reload' } as const;\nconst SUBCOMMANDS = Object.values(SUBCOMMAND).join(', ');\n\n/** `/mcp <subcommand> [argument]`, with the bare command showing status. */\nfunction parseArguments(args: string): { subcommand: string; argument: string } {\n const [subcommand = SUBCOMMAND.status, ...rest] = args.trim().split(/\\s+/).filter(Boolean);\n return { subcommand, argument: rest.join(' ') };\n}\n\n/** One line per server: its state, and how many tools it currently offers. */\nexport function formatStatus(session: McpCommandTarget): string {\n const { servers } = session.getSnapshot();\n if (servers.length === 0) return 'No MCP servers are configured for this session.';\n const lines = servers.map((server) => {\n const detail = server.error ? ` (${server.error})` : '';\n return `${server.name}: ${server.state} · ${server.tools.length} tools${detail}`;\n });\n return [...lines, ...session.getDiagnostics()].join('\\n');\n}\n\nasync function runAuth(session: McpCommandTarget, serverName: string, ctx: ExtensionContext): Promise<void> {\n if (!serverName) {\n ctx.ui.notify('Name the server to authorize, for example /mcp auth boomlink.', WARNING);\n return;\n }\n try {\n // Reconnecting is what drives the flow: the OAuth provider surfaces the\n // authorization URL through the handler this extension installed.\n await session.reauthorize(serverName);\n ctx.ui.notify(`${serverName} is authorized.`, INFO);\n } catch (error) {\n ctx.ui.notify(\n `Could not authorize ${serverName}: ${error instanceof Error ? error.message : 'unknown error'}`,\n WARNING,\n );\n }\n}\n\nexport function registerCommand(pi: ExtensionAPI, session: McpCommandTarget, deps: McpCommandDeps): void {\n pi.registerCommand(COMMAND_NAME, {\n description: COMMAND_DESCRIPTION,\n handler: async (args, ctx) => {\n const raw = typeof args === 'string' ? args : '';\n // The bare command opens the browser, where the same information can be acted\n // on. The subcommands stay because an overlay needs a terminal, and a headless\n // or scripted session still has to be able to ask.\n if (raw.trim() === '' && ctx.hasUI) {\n await deps.openOverlay(ctx);\n return;\n }\n const { subcommand, argument } = parseArguments(raw);\n if (subcommand === SUBCOMMAND.auth) {\n await runAuth(session, argument, ctx);\n return;\n }\n if (subcommand === SUBCOMMAND.reload) {\n ctx.ui.notify('Reconnecting MCP servers.', INFO);\n // Not awaited: a server that never answers must not wedge the command.\n void session.start();\n return;\n }\n if (subcommand !== SUBCOMMAND.status) {\n ctx.ui.notify(`Unknown /${COMMAND_NAME} subcommand \"${subcommand}\". Use ${SUBCOMMANDS}.`, WARNING);\n return;\n }\n ctx.ui.notify(formatStatus(session), INFO);\n },\n });\n}\n"],"mappings":"uDAIA,MAAM,EAAO,OACP,EAAU,UAEV,EAAa,CAAE,OAAQ,SAAU,KAAM,OAAQ,OAAQ,QAAS,EAChE,EAAc,OAAO,OAAO,CAAU,CAAC,CAAC,KAAK,IAAI,EAGvD,SAAS,EAAe,EAAwD,CAC9E,GAAM,CAAC,EAAa,EAAW,OAAQ,GAAG,GAAQ,EAAK,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO,EACzF,MAAO,CAAE,aAAY,SAAU,EAAK,KAAK,GAAG,CAAE,CAChD,CAGA,SAAgB,EAAa,EAAmC,CAC9D,GAAM,CAAE,WAAY,EAAQ,YAAY,EAMxC,OALI,EAAQ,SAAW,EAAU,kDAK1B,CAAC,GAJM,EAAQ,IAAK,GAAW,CACpC,IAAM,EAAS,EAAO,MAAQ,KAAK,EAAO,MAAM,GAAK,GACrD,MAAO,GAAG,EAAO,KAAK,IAAI,EAAO,MAAM,KAAK,EAAO,MAAM,OAAO,QAAQ,GAC1E,CACe,EAAG,GAAG,EAAQ,eAAe,CAAC,CAAC,CAAC,KAAK;CAAI,CAC1D,CAEA,eAAe,EAAQ,EAA2B,EAAoB,EAAsC,CAC1G,GAAI,CAAC,EAAY,CACf,EAAI,GAAG,OAAO,gEAAiE,CAAO,EACtF,MACF,CACA,GAAI,CAGF,MAAM,EAAQ,YAAY,CAAU,EACpC,EAAI,GAAG,OAAO,GAAG,EAAW,iBAAkB,CAAI,CACpD,OAAS,EAAO,CACd,EAAI,GAAG,OACL,uBAAuB,EAAW,IAAI,aAAiB,MAAQ,EAAM,QAAU,kBAC/E,CACF,CACF,CACF,CAEA,SAAgB,EAAgB,EAAkB,EAA2B,EAA4B,CACvG,EAAG,gBAAA,MAA8B,CAC/B,YAAa,EACb,QAAS,MAAO,EAAM,IAAQ,CAC5B,IAAM,EAAM,OAAO,GAAS,SAAW,EAAO,GAI9C,GAAI,EAAI,KAAK,IAAM,IAAM,EAAI,MAAO,CAClC,MAAM,EAAK,YAAY,CAAG,EAC1B,MACF,CACA,GAAM,CAAE,aAAY,YAAa,EAAe,CAAG,EACnD,GAAI,IAAe,EAAW,KAAM,CAClC,MAAM,EAAQ,EAAS,EAAU,CAAG,EACpC,MACF,CACA,GAAI,IAAe,EAAW,OAAQ,CACpC,EAAI,GAAG,OAAO,4BAA6B,CAAI,EAE/C,EAAa,MAAM,EACnB,MACF,CACA,GAAI,IAAe,EAAW,OAAQ,CACpC,EAAI,GAAG,OAAO,4BAAwC,EAAW,SAAS,EAAY,GAAI,CAAO,EACjG,MACF,CACA,EAAI,GAAG,OAAO,EAAa,CAAO,EAAG,CAAI,CAC3C,CACF,CAAC,CACH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e={key:`e`,label:`extension`,order:50};exports.COMMAND_DESCRIPTION=`Browse this session MCP servers: authorize, enable, disable, tools and resources`,exports.LEADER_BINDING_PREFIX=`doom-mcp`,exports.LEADER_DETAIL=`servers and tools`,exports.LEADER_GROUP=e,exports.PACKAGE_SOURCE=`@agimon-ai/doompi-mcp`;
|
|
2
|
+
//# sourceMappingURL=constants.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.cjs","names":[],"sources":["../src/constants.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';\nexport const COMMAND_NAME = 'mcp';\nexport const COMMAND_DESCRIPTION = 'Browse this session MCP servers: authorize, enable, disable, tools and resources';\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,MAYa,EAAe,CAAE,IAAK,IAAK,MAAO,YAAa,MAAO,EAAG"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=`@agimon-ai/doompi-mcp`,t=`doom-mcp`,n=`Browse this session MCP servers: authorize, enable, disable, tools and resources`,r={key:`e`,label:`extension`,order:50},i=`servers and tools`;export{n as COMMAND_DESCRIPTION,t as LEADER_BINDING_PREFIX,i as LEADER_DETAIL,r as LEADER_GROUP,e as PACKAGE_SOURCE};
|
|
2
|
+
//# sourceMappingURL=constants.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.mjs","names":[],"sources":["../src/constants.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';\nexport const COMMAND_NAME = 'mcp';\nexport const COMMAND_DESCRIPTION = 'Browse this session MCP servers: authorize, enable, disable, tools and resources';\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,WAExB,EAAsB,mFAQtB,EAAe,CAAE,IAAK,IAAK,MAAO,YAAa,MAAO,EAAG,EAGzD,EAAgB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./constants.cjs"),t=require("./commands/mcpCommand.cjs"),n=require("./leader.cjs"),r=require("./services/sessionConfig.cjs"),i=require("./services/mcpSession.cjs"),a=require("./tui/mcpOverlay.cjs");let o=require("@agimon-ai/doompi-extension-contracts/mcp-status"),s=require("@agimon-ai/doompi-extension-contracts/protocol");function c(c,l={}){let u,d,f=new i.McpSession({pi:c,onAuthorizationUrl:(e,t)=>{u?.hasUI||u?.ui?.notify(`Authorize ${t} by opening:\n${e.toString()}`,`info`)}}),p=r.readSessionConfig();f.install(l.configurationSource===`doom`?p:{repoRoot:p.repoRoot,stagingDirectory:p.stagingDirectory}),t.registerCommand(c,f,{openOverlay:e=>a.openMcpOverlay(e,f)}),n.registerLeaderContribution(c),c.on(`session_start`,(t,n)=>{u=n,l.configurationSource!==`doom`&&f.install(r.readSessionConfig({},n.cwd));let i=(0,s.createProtocolRuntime)({bus:c.events,source:e.PACKAGE_SOURCE,sessionId:n.sessionManager.getSessionId()});d?.(),d=i.provide(o.McpStatusQueryProtocol,()=>f.getSnapshot()),f.activate();for(let e of f.getDiagnostics())n.ui?.notify(e,`warning`);f.start()}),c.on(`session_shutdown`,()=>{d?.(),d=void 0,u=void 0,f.dispose()})}exports.registerMcpExtension=c;
|
|
2
|
+
//# sourceMappingURL=extension.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.cjs","names":["McpSession","readSessionConfig","openMcpOverlay","createProtocolRuntime","PACKAGE_SOURCE","McpStatusQueryProtocol"],"sources":["../src/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 { registerCommand } from './commands/mcpCommand.ts';\nimport { registerLeaderContribution } from './leader.ts';\nimport { PACKAGE_SOURCE } from './constants.ts';\nimport { McpSession } from './services/mcpSession.ts';\nimport { readSessionConfig } from './services/sessionConfig.ts';\nimport { openMcpOverlay } from './tui/mcpOverlay.ts';\n\nconst SESSION_START = 'session_start';\nconst SESSION_SHUTDOWN = 'session_shutdown';\nconst INFO = 'info';\nconst WARNING = 'warning';\n\nexport interface McpExtensionOptions {\n /** Doom loading retains the environment-provided plugin overlays and allowlists. */\n configurationSource?: 'standard' | 'doom';\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: (url, serverName) => {\n // The overlay carries the URL for anyone who can see one, and it is where the\n // flow was started from. Announcing it here as well would only put it in the\n // transcript underneath a fullscreen panel. A session with no UI has no\n // overlay to read, so that is the one case still worth notifying.\n if (activeContext?.hasUI) return;\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'\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, { openOverlay: (ctx) => openMcpOverlay(ctx, session) });\n registerLeaderContribution(pi);\n\n pi.on(SESSION_START, (_event, ctx) => {\n activeContext = ctx;\n if (options.configurationSource !== 'doom') 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":"qVA4BA,SAAgB,EAAqB,EAAkB,EAA+B,CAAC,EAAS,CAC9F,IAAI,EACA,EAEE,EAAU,IAAIA,EAAAA,WAAW,CAC7B,KACA,oBAAqB,EAAK,IAAe,CAKnC,GAAe,OACnB,GAAe,IAAI,OAAO,aAAa,EAAW,gBAAgB,EAAI,SAAS,IAAK,MAAI,CAC1F,CACF,CAAC,EAEK,EAAuBC,EAAAA,kBAAkB,EAC/C,EAAQ,QACN,EAAQ,sBAAwB,OAC5B,EACA,CACE,SAAU,EAAqB,SAC/B,iBAAkB,EAAqB,gBACzC,CACN,EAIA,EAAA,gBAAgB,EAAI,EAAS,CAAE,YAAc,GAAQC,EAAAA,eAAe,EAAK,CAAO,CAAE,CAAC,EACnF,EAAA,2BAA2B,CAAE,EAE7B,EAAG,GAAG,iBAAgB,EAAQ,IAAQ,CACpC,EAAgB,EACZ,EAAQ,sBAAwB,QAAQ,EAAQ,QAAQD,EAAAA,kBAAkB,CAAC,EAAG,EAAI,GAAG,CAAC,EAC1F,IAAM,GAAA,EAAWE,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,SAAO,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,18 @@
|
|
|
1
|
+
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
//#region src/extension.d.ts
|
|
3
|
+
interface McpExtensionOptions {
|
|
4
|
+
/** Doom loading retains the environment-provided plugin overlays and allowlists. */
|
|
5
|
+
configurationSource?: 'standard' | 'doom';
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* MCP support for a Pi session.
|
|
9
|
+
*
|
|
10
|
+
* Tools are registered here, synchronously, from the catalog the previous run left
|
|
11
|
+
* behind. Connecting starts on `session_start` and runs detached, so the first
|
|
12
|
+
* prompt is never held up by a slow or unreachable server; each server folds into
|
|
13
|
+
* the active tool set as it lands.
|
|
14
|
+
*/
|
|
15
|
+
declare function registerMcpExtension(pi: ExtensionAPI, options?: McpExtensionOptions): void;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { McpExtensionOptions, registerMcpExtension };
|
|
18
|
+
//# sourceMappingURL=extension.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.cts","names":[],"sources":["../src/extension.ts"],"mappings":";;UAeiB;;EAEf;;;;;;;;;;iBAWc,qBAAqB,IAAI,cAAc,UAAS"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
//#region src/extension.d.ts
|
|
3
|
+
interface McpExtensionOptions {
|
|
4
|
+
/** Doom loading retains the environment-provided plugin overlays and allowlists. */
|
|
5
|
+
configurationSource?: 'standard' | 'doom';
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* MCP support for a Pi session.
|
|
9
|
+
*
|
|
10
|
+
* Tools are registered here, synchronously, from the catalog the previous run left
|
|
11
|
+
* behind. Connecting starts on `session_start` and runs detached, so the first
|
|
12
|
+
* prompt is never held up by a slow or unreachable server; each server folds into
|
|
13
|
+
* the active tool set as it lands.
|
|
14
|
+
*/
|
|
15
|
+
declare function registerMcpExtension(pi: ExtensionAPI, options?: McpExtensionOptions): void;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { McpExtensionOptions, registerMcpExtension };
|
|
18
|
+
//# sourceMappingURL=extension.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.mts","names":[],"sources":["../src/extension.ts"],"mappings":";;UAeiB;;EAEf;;;;;;;;;;iBAWc,qBAAqB,IAAI,cAAc,UAAS"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{PACKAGE_SOURCE as e}from"./constants.mjs";import{registerCommand as t}from"./commands/mcpCommand.mjs";import{registerLeaderContribution as n}from"./leader.mjs";import{readSessionConfig as r}from"./services/sessionConfig.mjs";import{McpSession as i}from"./services/mcpSession.mjs";import{openMcpOverlay as a}from"./tui/mcpOverlay.mjs";import{McpStatusQueryProtocol as o}from"@agimon-ai/doompi-extension-contracts/mcp-status";import{createProtocolRuntime as s}from"@agimon-ai/doompi-extension-contracts/protocol";function c(c,l={}){let u,d,f=new i({pi:c,onAuthorizationUrl:(e,t)=>{u?.hasUI||u?.ui?.notify(`Authorize ${t} by opening:\n${e.toString()}`,`info`)}}),p=r();f.install(l.configurationSource===`doom`?p:{repoRoot:p.repoRoot,stagingDirectory:p.stagingDirectory}),t(c,f,{openOverlay:e=>a(e,f)}),n(c),c.on(`session_start`,(t,n)=>{u=n,l.configurationSource!==`doom`&&f.install(r({},n.cwd));let i=s({bus:c.events,source:e,sessionId:n.sessionManager.getSessionId()});d?.(),d=i.provide(o,()=>f.getSnapshot()),f.activate();for(let e of f.getDiagnostics())n.ui?.notify(e,`warning`);f.start()}),c.on(`session_shutdown`,()=>{d?.(),d=void 0,u=void 0,f.dispose()})}export{c as registerMcpExtension};
|
|
2
|
+
//# sourceMappingURL=extension.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.mjs","names":[],"sources":["../src/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 { registerCommand } from './commands/mcpCommand.ts';\nimport { registerLeaderContribution } from './leader.ts';\nimport { PACKAGE_SOURCE } from './constants.ts';\nimport { McpSession } from './services/mcpSession.ts';\nimport { readSessionConfig } from './services/sessionConfig.ts';\nimport { openMcpOverlay } from './tui/mcpOverlay.ts';\n\nconst SESSION_START = 'session_start';\nconst SESSION_SHUTDOWN = 'session_shutdown';\nconst INFO = 'info';\nconst WARNING = 'warning';\n\nexport interface McpExtensionOptions {\n /** Doom loading retains the environment-provided plugin overlays and allowlists. */\n configurationSource?: 'standard' | 'doom';\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: (url, serverName) => {\n // The overlay carries the URL for anyone who can see one, and it is where the\n // flow was started from. Announcing it here as well would only put it in the\n // transcript underneath a fullscreen panel. A session with no UI has no\n // overlay to read, so that is the one case still worth notifying.\n if (activeContext?.hasUI) return;\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'\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, { openOverlay: (ctx) => openMcpOverlay(ctx, session) });\n registerLeaderContribution(pi);\n\n pi.on(SESSION_START, (_event, ctx) => {\n activeContext = ctx;\n if (options.configurationSource !== 'doom') 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":"sgBA4BA,SAAgB,EAAqB,EAAkB,EAA+B,CAAC,EAAS,CAC9F,IAAI,EACA,EAEE,EAAU,IAAI,EAAW,CAC7B,KACA,oBAAqB,EAAK,IAAe,CAKnC,GAAe,OACnB,GAAe,IAAI,OAAO,aAAa,EAAW,gBAAgB,EAAI,SAAS,IAAK,MAAI,CAC1F,CACF,CAAC,EAEK,EAAuB,EAAkB,EAC/C,EAAQ,QACN,EAAQ,sBAAwB,OAC5B,EACA,CACE,SAAU,EAAqB,SAC/B,iBAAkB,EAAqB,gBACzC,CACN,EAIA,EAAgB,EAAI,EAAS,CAAE,YAAc,GAAQ,EAAe,EAAK,CAAO,CAAE,CAAC,EACnF,EAA2B,CAAE,EAE7B,EAAG,GAAG,iBAAgB,EAAQ,IAAQ,CACpC,EAAgB,EACZ,EAAQ,sBAAwB,QAAQ,EAAQ,QAAQ,EAAkB,CAAC,EAAG,EAAI,GAAG,CAAC,EAC1F,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,SAAO,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 @@
|
|
|
1
|
+
{"version":3,"file":"doom.cjs","names":[],"sources":["../../src/extensions/doom.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 default function registerDoomMcpExtension(pi: ExtensionAPI): void {\n registerMcpExtension(pi, { configurationSource: 'doom' });\n}\n"],"mappings":"oCAIA,SAAwB,EAAyB,EAAwB,CACvE,EAAA,qBAAqB,EAAI,CAAE,oBAAqB,MAAO,CAAC,CAC1D"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
//#region src/extensions/doom.d.ts
|
|
3
|
+
/** Explicit Doom adapter retaining environment-provided overlays and allowlists. */
|
|
4
|
+
declare function registerDoomMcpExtension(pi: ExtensionAPI): void;
|
|
5
|
+
export = registerDoomMcpExtension;
|
|
6
|
+
//# sourceMappingURL=doom.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doom.d.cts","names":[],"sources":["../../src/extensions/doom.ts"],"mappings":";;;iBAIwB,yBAAyB,IAAI"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
//#region src/extensions/doom.d.ts
|
|
3
|
+
/** Explicit Doom adapter retaining environment-provided overlays and allowlists. */
|
|
4
|
+
declare function registerDoomMcpExtension(pi: ExtensionAPI): void;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { registerDoomMcpExtension as default };
|
|
7
|
+
//# sourceMappingURL=doom.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doom.d.mts","names":[],"sources":["../../src/extensions/doom.ts"],"mappings":";;;iBAIwB,yBAAyB,IAAI"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doom.mjs","names":[],"sources":["../../src/extensions/doom.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 default function registerDoomMcpExtension(pi: ExtensionAPI): void {\n registerMcpExtension(pi, { configurationSource: 'doom' });\n}\n"],"mappings":"wDAIA,SAAwB,EAAyB,EAAwB,CACvE,EAAqB,EAAI,CAAE,oBAAqB,MAAO,CAAC,CAC1D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi.cjs","names":["registerMcpExtension"],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import { registerMcpExtension } from '../extension.ts';\n\nexport default registerMcpExtension;\n"],"mappings":"AAEA,IAAA,4BAAeA,CAAAA,CAAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi.mjs","names":[],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import { registerMcpExtension } from '../extension.ts';\n\nexport default registerMcpExtension;\n"],"mappings":"wDAEA,IAAA,EAAe"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./services/configSources.cjs"),t=require("./services/directTools.cjs"),n=require("./services/mcpCatalog.cjs"),r=require("./services/mcpRuntime.cjs"),i=require("./services/sessionConfig.cjs"),a=require("./extension.cjs");exports.DIRECT_TOOLS_ENV=t.DIRECT_TOOLS_ENV,exports.NO_DIRECT_TOOLS=t.NO_DIRECT_TOOLS,exports.PROXY_SERVER_NAME=e.PROXY_SERVER_NAME,exports.SESSION_ENV_VAR=i.SESSION_ENV_VAR,exports.buildMcpConfigGroups=e.buildMcpConfigGroups,exports.readCachedCatalog=r.readCachedCatalog,exports.readSessionConfig=i.readSessionConfig,exports.registerMcpExtension=a.registerMcpExtension,exports.sessionConfigEnvironment=i.sessionConfigEnvironment,exports.toPiToolName=n.toPiToolName;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { registerMcpExtension } from "./extension.cjs";
|
|
2
|
+
import { McpAllowlist, McpConfigGroups, PROXY_SERVER_NAME, buildMcpConfigGroups } from "./services/configSources.cjs";
|
|
3
|
+
import { DIRECT_TOOLS_ENV, NO_DIRECT_TOOLS } from "./services/directTools.cjs";
|
|
4
|
+
import { CachedCatalog, readCachedCatalog } from "./services/mcpRuntime.cjs";
|
|
5
|
+
import { toPiToolName } from "./services/mcpCatalog.cjs";
|
|
6
|
+
import { McpSessionConfig, SESSION_ENV_VAR, readSessionConfig, sessionConfigEnvironment } from "./services/sessionConfig.cjs";
|
|
7
|
+
export { type CachedCatalog, DIRECT_TOOLS_ENV, type McpAllowlist, type McpConfigGroups, type McpSessionConfig, NO_DIRECT_TOOLS, PROXY_SERVER_NAME, SESSION_ENV_VAR, buildMcpConfigGroups, readCachedCatalog, readSessionConfig, registerMcpExtension, sessionConfigEnvironment, toPiToolName };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { registerMcpExtension } from "./extension.mjs";
|
|
2
|
+
import { McpAllowlist, McpConfigGroups, PROXY_SERVER_NAME, buildMcpConfigGroups } from "./services/configSources.mjs";
|
|
3
|
+
import { DIRECT_TOOLS_ENV, NO_DIRECT_TOOLS } from "./services/directTools.mjs";
|
|
4
|
+
import { CachedCatalog, readCachedCatalog } from "./services/mcpRuntime.mjs";
|
|
5
|
+
import { toPiToolName } from "./services/mcpCatalog.mjs";
|
|
6
|
+
import { McpSessionConfig, SESSION_ENV_VAR, readSessionConfig, sessionConfigEnvironment } from "./services/sessionConfig.mjs";
|
|
7
|
+
export { type CachedCatalog, DIRECT_TOOLS_ENV, type McpAllowlist, type McpConfigGroups, type McpSessionConfig, NO_DIRECT_TOOLS, PROXY_SERVER_NAME, SESSION_ENV_VAR, buildMcpConfigGroups, readCachedCatalog, readSessionConfig, registerMcpExtension, sessionConfigEnvironment, toPiToolName };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{PROXY_SERVER_NAME as e,buildMcpConfigGroups as t}from"./services/configSources.mjs";import{DIRECT_TOOLS_ENV as n,NO_DIRECT_TOOLS as r}from"./services/directTools.mjs";import{toPiToolName as i}from"./services/mcpCatalog.mjs";import{readCachedCatalog as a}from"./services/mcpRuntime.mjs";import{SESSION_ENV_VAR as o,readSessionConfig as s,sessionConfigEnvironment as c}from"./services/sessionConfig.mjs";import{registerMcpExtension as l}from"./extension.mjs";export{n as DIRECT_TOOLS_ENV,r as NO_DIRECT_TOOLS,e as PROXY_SERVER_NAME,o as SESSION_ENV_VAR,t as buildMcpConfigGroups,a as readCachedCatalog,s as readSessionConfig,l as registerMcpExtension,c as sessionConfigEnvironment,i as toPiToolName};
|
package/dist/leader.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("./constants.cjs");let t=require("@agimon-ai/doompi-extension-contracts/protocol"),n=require("@agimon-ai/doompi-extension-contracts/leader");function r(e){e instanceof t.DoomProtocolError&&e.code===t.DOOM_PROTOCOL_ERROR_CODE.timeout||queueMicrotask(()=>{throw e instanceof Error?e:Error(String(e))})}function i(i){let a=0;i.on(`session_start`,(o,s)=>{let c=++a,l=(0,t.createProtocolRuntime)({bus:i.events,source:e.PACKAGE_SOURCE,sessionId:s.sessionManager.getSessionId()});l.request(n.LeaderRegistrationProtocol,{contribution:{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`}}]},generation:String(c)},{timeoutMs:250}).then(e=>{a!==c&&l.request(n.LeaderUnregistrationProtocol,{handleId:e.handleId},{timeoutMs:250}).catch(r)}).catch(r)}),i.on(`session_shutdown`,()=>{++a})}exports.registerLeaderContribution=i;
|
|
2
|
+
//# sourceMappingURL=leader.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leader.cjs","names":["DoomProtocolError","DOOM_PROTOCOL_ERROR_CODE","createProtocolRuntime","PACKAGE_SOURCE","LeaderRegistrationProtocol","LEADER_BINDING_PREFIX","LEADER_GROUP","LEADER_DETAIL","LeaderUnregistrationProtocol"],"sources":["../src/leader.ts"],"sourcesContent":["import { LeaderRegistrationProtocol, LeaderUnregistrationProtocol } from '@agimon-ai/doompi-extension-contracts/leader';\nimport {\n createProtocolRuntime,\n DOOM_PROTOCOL_ERROR_CODE,\n DoomProtocolError,\n} from '@agimon-ai/doompi-extension-contracts/protocol';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport {\n COMMAND_NAME,\n LEADER_BINDING_PREFIX,\n LEADER_DETAIL,\n LEADER_GROUP,\n LEADER_KEY,\n LEADER_LABEL,\n PACKAGE_SOURCE,\n} from './constants.ts';\n\nconst LEADER_PROVIDER_TIMEOUT_MS = 250;\n\nfunction handleOptionalLeaderFailure(error: unknown): void {\n if (error instanceof DoomProtocolError && error.code === DOOM_PROTOCOL_ERROR_CODE.timeout) return;\n queueMicrotask(() => {\n throw error instanceof Error ? error : new Error(String(error));\n });\n}\n\nexport function registerLeaderContribution(pi: ExtensionAPI): void {\n let generation = 0;\n\n pi.on('session_start', (_event, ctx) => {\n const activeGeneration = ++generation;\n const runtime = createProtocolRuntime({\n bus: pi.events,\n source: PACKAGE_SOURCE,\n sessionId: ctx.sessionManager.getSessionId(),\n });\n void runtime\n .request(\n LeaderRegistrationProtocol,\n {\n contribution: {\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 generation: String(activeGeneration),\n },\n { timeoutMs: LEADER_PROVIDER_TIMEOUT_MS },\n )\n .then((registration) => {\n if (generation === activeGeneration) return;\n void runtime\n .request(\n LeaderUnregistrationProtocol,\n { handleId: registration.handleId },\n { timeoutMs: LEADER_PROVIDER_TIMEOUT_MS },\n )\n .catch(handleOptionalLeaderFailure);\n })\n .catch(handleOptionalLeaderFailure);\n });\n\n pi.on('session_shutdown', () => {\n ++generation;\n });\n}\n"],"mappings":"6JAmBA,SAAS,EAA4B,EAAsB,CACrD,aAAiBA,EAAAA,mBAAqB,EAAM,OAASC,EAAAA,yBAAyB,SAClF,mBAAqB,CACnB,MAAM,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,CAChE,CAAC,CACH,CAEA,SAAgB,EAA2B,EAAwB,CACjE,IAAI,EAAa,EAEjB,EAAG,GAAG,iBAAkB,EAAQ,IAAQ,CACtC,IAAM,EAAmB,EAAE,EACrB,GAAA,EAAUC,EAAAA,sBAAAA,CAAsB,CACpC,IAAK,EAAG,OACR,OAAQC,EAAAA,eACR,UAAW,EAAI,eAAe,aAAa,CAC7C,CAAC,EACD,EACG,QACCC,EAAAA,2BACA,CACE,aAAc,CACZ,OAAQD,EAAAA,eACR,SAAU,CACR,CACE,GAAI,GAAGE,EAAAA,sBAAsB,OAC7B,KAAM,CAACC,EAAAA,aAAc,CAAE,IAAA,IAAiB,MAAA,MAAqB,OAAQC,EAAAA,aAAc,CAAC,EACpF,QAAS,CAAE,KAAA,KAAmB,CAChC,CACF,CACF,EACA,WAAY,OAAO,CAAgB,CACrC,EACA,CAAE,UAAW,GAA2B,CAC1C,CAAC,CACA,KAAM,GAAiB,CAClB,IAAe,GACnB,EACG,QACCC,EAAAA,6BACA,CAAE,SAAU,EAAa,QAAS,EAClC,CAAE,UAAW,GAA2B,CAC1C,CAAC,CACA,MAAM,CAA2B,CACtC,CAAC,CAAC,CACD,MAAM,CAA2B,CACtC,CAAC,EAED,EAAG,GAAG,uBAA0B,CAC9B,EAAE,CACJ,CAAC,CACH"}
|
package/dist/leader.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{LEADER_BINDING_PREFIX as e,LEADER_DETAIL as t,LEADER_GROUP as n,PACKAGE_SOURCE as r}from"./constants.mjs";import{DOOM_PROTOCOL_ERROR_CODE as i,DoomProtocolError as a,createProtocolRuntime as o}from"@agimon-ai/doompi-extension-contracts/protocol";import{LeaderRegistrationProtocol as s,LeaderUnregistrationProtocol as c}from"@agimon-ai/doompi-extension-contracts/leader";function l(e){e instanceof a&&e.code===i.timeout||queueMicrotask(()=>{throw e instanceof Error?e:Error(String(e))})}function u(i){let a=0;i.on(`session_start`,(u,d)=>{let f=++a,p=o({bus:i.events,source:r,sessionId:d.sessionManager.getSessionId()});p.request(s,{contribution:{source:r,bindings:[{id:`${e}.open`,path:[n,{key:`m`,label:`mcp`,detail:t}],command:{name:`mcp`}}]},generation:String(f)},{timeoutMs:250}).then(e=>{a!==f&&p.request(c,{handleId:e.handleId},{timeoutMs:250}).catch(l)}).catch(l)}),i.on(`session_shutdown`,()=>{++a})}export{u as registerLeaderContribution};
|
|
2
|
+
//# sourceMappingURL=leader.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leader.mjs","names":[],"sources":["../src/leader.ts"],"sourcesContent":["import { LeaderRegistrationProtocol, LeaderUnregistrationProtocol } from '@agimon-ai/doompi-extension-contracts/leader';\nimport {\n createProtocolRuntime,\n DOOM_PROTOCOL_ERROR_CODE,\n DoomProtocolError,\n} from '@agimon-ai/doompi-extension-contracts/protocol';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport {\n COMMAND_NAME,\n LEADER_BINDING_PREFIX,\n LEADER_DETAIL,\n LEADER_GROUP,\n LEADER_KEY,\n LEADER_LABEL,\n PACKAGE_SOURCE,\n} from './constants.ts';\n\nconst LEADER_PROVIDER_TIMEOUT_MS = 250;\n\nfunction handleOptionalLeaderFailure(error: unknown): void {\n if (error instanceof DoomProtocolError && error.code === DOOM_PROTOCOL_ERROR_CODE.timeout) return;\n queueMicrotask(() => {\n throw error instanceof Error ? error : new Error(String(error));\n });\n}\n\nexport function registerLeaderContribution(pi: ExtensionAPI): void {\n let generation = 0;\n\n pi.on('session_start', (_event, ctx) => {\n const activeGeneration = ++generation;\n const runtime = createProtocolRuntime({\n bus: pi.events,\n source: PACKAGE_SOURCE,\n sessionId: ctx.sessionManager.getSessionId(),\n });\n void runtime\n .request(\n LeaderRegistrationProtocol,\n {\n contribution: {\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 generation: String(activeGeneration),\n },\n { timeoutMs: LEADER_PROVIDER_TIMEOUT_MS },\n )\n .then((registration) => {\n if (generation === activeGeneration) return;\n void runtime\n .request(\n LeaderUnregistrationProtocol,\n { handleId: registration.handleId },\n { timeoutMs: LEADER_PROVIDER_TIMEOUT_MS },\n )\n .catch(handleOptionalLeaderFailure);\n })\n .catch(handleOptionalLeaderFailure);\n });\n\n pi.on('session_shutdown', () => {\n ++generation;\n });\n}\n"],"mappings":"yXAmBA,SAAS,EAA4B,EAAsB,CACrD,aAAiB,GAAqB,EAAM,OAAS,EAAyB,SAClF,mBAAqB,CACnB,MAAM,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,CAChE,CAAC,CACH,CAEA,SAAgB,EAA2B,EAAwB,CACjE,IAAI,EAAa,EAEjB,EAAG,GAAG,iBAAkB,EAAQ,IAAQ,CACtC,IAAM,EAAmB,EAAE,EACrB,EAAU,EAAsB,CACpC,IAAK,EAAG,OACR,OAAQ,EACR,UAAW,EAAI,eAAe,aAAa,CAC7C,CAAC,EACD,EACG,QACC,EACA,CACE,aAAc,CACZ,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,EACA,WAAY,OAAO,CAAgB,CACrC,EACA,CAAE,UAAW,GAA2B,CAC1C,CAAC,CACA,KAAM,GAAiB,CAClB,IAAe,GACnB,EACG,QACC,EACA,CAAE,SAAU,EAAa,QAAS,EAClC,CAAE,UAAW,GAA2B,CAC1C,CAAC,CACA,MAAM,CAA2B,CACtC,CAAC,CAAC,CACD,MAAM,CAA2B,CACtC,CAAC,EAED,EAAG,GAAG,uBAA0B,CAC9B,EAAE,CACJ,CAAC,CACH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:fs");t=e.__toESM(t,1);let n=require("node:crypto"),r=require("node:path");r=e.__toESM(r,1);const i=`agiflow-proxy`,a=`--config`;function o(e){try{let n=JSON.parse(t.default.readFileSync(e,`utf8`));return n&&typeof n==`object`&&!Array.isArray(n)?n:void 0}catch{return}}function s(e){let n=(o(r.default.join(e,`.mcp.json`))?.mcpServers??{})[i]?.args,s=Array.isArray(n)?n:[],c=s[s.indexOf(a)+1],l=s.includes(a)&&typeof c==`string`?r.default.resolve(e,c):r.default.join(e,`mcp-config.yaml`);return t.default.existsSync(l)?l:void 0}function c(e,t){return e===`agiflow-proxy`?!1:!t||t.length===0||t.includes(e)}function l(e,i,a){let s=o(e);if(!s)return;let l=s.mcpServers??{},u={},d=[];for(let[e,t]of Object.entries(l))c(e,i)?u[e]=t:d.push(e);let f=Object.keys(u);if(d.length===0)return{path:e,kept:f,dropped:d};t.default.mkdirSync(a,{mode:448,recursive:!0});let p=(0,n.createHash)(`sha256`).update(e).digest(`hex`).slice(0,16),m=r.default.join(a,`mcp-${p}.json`);return t.default.writeFileSync(m,`${JSON.stringify({...s,mcpServers:u},null,2)}\n`,{mode:384}),{path:m,kept:f,dropped:d}}function u(e){let n=[r.default.join(e.repoRoot,`.mcp.json`),...(e.pluginConfigPaths??[]).map(t=>r.default.resolve(e.repoRoot,t))].filter(e=>t.default.existsSync(e)),i=[],a=[],o=[];for(let t of n){let n=l(t,e.allowlist?.servers,e.stagingDirectory);n&&(i.push(n.path),a.push(...n.kept),o.push(...n.dropped))}let c=s(e.repoRoot),u=e.allowlist?.proxy?.length?e.allowlist.proxy:void 0;return{shared:{configPaths:c?[c]:[],allowedUpstreams:u},sessionLocal:{configPaths:i,serverNames:[...new Set(a)]},droppedServers:[...new Set(o)]}}exports.PROXY_SERVER_NAME=i,exports.buildMcpConfigGroups=u,exports.resolveSharedConfigPath=s;
|
|
2
|
+
//# sourceMappingURL=configSources.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configSources.cjs","names":["fs","path","createHash"],"sources":["../../src/services/configSources.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n/** Server name in `.mcp.json` whose upstreams are declared in `mcp-config.yaml`. */\nexport const PROXY_SERVER_NAME = 'agiflow-proxy';\n\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst DEFAULT_SHARED_CONFIG = 'mcp-config.yaml';\nconst CONFIG_ARGUMENT = '--config';\n\ntype JsonObject = Record<string, unknown>;\n\n/** Domain policy. An absent or empty list means \"keep everything\". */\nexport interface McpAllowlist {\n /** Servers declared across the `.mcp.json` layers. */\n servers?: string[];\n /** Upstreams declared in `mcp-config.yaml`. */\n proxy?: string[];\n}\n\nexport interface McpConfigGroupsInput {\n repoRoot: string;\n /** Absolute paths to the `.mcp.json` of each plugin selected for this session. */\n pluginConfigPaths?: string[];\n allowlist?: McpAllowlist;\n /** Where filtered copies land. Only written to when the allowlist drops a server. */\n stagingDirectory: string;\n}\n\n/**\n * Worktree-level servers, reached through the shared HTTP runtime.\n *\n * The runtime is shared by every session in the worktree, so its config must stay\n * identical no matter which domains a session selected. The allowlist therefore\n * travels as `allowedUpstreams` and is applied when tools are registered, not by\n * rewriting the config the runtime starts from.\n */\nexport interface SharedConfigGroup {\n configPaths: string[];\n allowedUpstreams?: string[];\n}\n\n/**\n * Servers this session connects in process.\n *\n * Plugin servers live here rather than in the shared runtime: sessions in one\n * worktree routinely select different plugin sets, and folding them into the\n * shared runtime would let one session's plugins leak into another's.\n */\nexport interface SessionConfigGroup {\n configPaths: string[];\n /**\n * Servers these layers declare, after filtering.\n *\n * Reported so a session can list what it is about to connect to before anything\n * has answered, rather than looking empty until the first server lands.\n */\n serverNames: string[];\n}\n\nexport interface McpConfigGroups {\n shared: SharedConfigGroup;\n sessionLocal: SessionConfigGroup;\n /** Servers removed before mcp-proxy saw them, for status reporting. */\n droppedServers: string[];\n}\n\nfunction readJsonObject(filePath: string): JsonObject | undefined {\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(filePath, 'utf8'));\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as JsonObject) : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Resolves the proxy config the shared runtime is started from.\n *\n * `.mcp.json` is the source of truth: its `agiflow-proxy` entry already names the\n * file, so a repo that renamed or relocated it keeps working.\n */\nexport function resolveSharedConfigPath(repoRoot: string): string | undefined {\n const rootConfig = readJsonObject(path.join(repoRoot, '.mcp.json'));\n const servers = (rootConfig?.mcpServers ?? {}) as Record<string, JsonObject | undefined>;\n const rawArgs: unknown = servers[PROXY_SERVER_NAME]?.args;\n const args = Array.isArray(rawArgs) ? (rawArgs as unknown[]) : [];\n const declared = args[args.indexOf(CONFIG_ARGUMENT) + 1];\n const candidate =\n args.includes(CONFIG_ARGUMENT) && typeof declared === 'string'\n ? path.resolve(repoRoot, declared)\n : path.join(repoRoot, DEFAULT_SHARED_CONFIG);\n return fs.existsSync(candidate) ? candidate : undefined;\n}\n\n/** Empty and absent allowlists both mean \"keep everything\". */\nfunction keepsServer(name: string, allowed: string[] | undefined): boolean {\n if (name === PROXY_SERVER_NAME) return false;\n if (!allowed || allowed.length === 0) return true;\n return allowed.includes(name);\n}\n\n/**\n * Rewrites one `.mcp.json` layer with the disallowed servers removed.\n *\n * Entries are dropped from the raw document rather than marked disabled so a\n * filtered stdio server is never spawned at all. Only keys of `mcpServers` are\n * touched, so nothing else in the layer can be lost in translation.\n */\nfunction filterLayer(\n filePath: string,\n allowed: string[] | undefined,\n stagingDirectory: string,\n): { path: string; kept: string[]; dropped: string[] } | undefined {\n const config = readJsonObject(filePath);\n if (!config) return undefined;\n const servers = (config.mcpServers ?? {}) as Record<string, unknown>;\n const kept: Record<string, unknown> = {};\n const dropped: string[] = [];\n for (const [name, definition] of Object.entries(servers)) {\n if (keepsServer(name, allowed)) kept[name] = definition;\n else dropped.push(name);\n }\n const keptNames = Object.keys(kept);\n if (dropped.length === 0) return { path: filePath, kept: keptNames, dropped };\n\n fs.mkdirSync(stagingDirectory, { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n // Hashed so two plugins named `.mcp.json` cannot overwrite each other's copy.\n const digest = createHash('sha256').update(filePath).digest('hex').slice(0, 16);\n const stagedPath = path.join(stagingDirectory, `mcp-${digest}.json`);\n fs.writeFileSync(stagedPath, `${JSON.stringify({ ...config, mcpServers: kept }, null, 2)}\\n`, {\n mode: PRIVATE_FILE_MODE,\n });\n return { path: stagedPath, kept: keptNames, dropped };\n}\n\n/**\n * Splits this session's MCP configuration into the two groups doom-mcp connects\n * differently, applying the domain allowlist where each group allows it.\n *\n * The `agiflow-proxy` entry is always removed: doom-mcp embeds mcp-proxy, so\n * honouring that entry would spawn a second proxy and load the shared upstreams\n * twice. `.mcp.json` keeps the entry for `claude.sh` and `codex.sh`.\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 if (!filtered) continue;\n configPaths.push(filtered.path);\n serverNames.push(...filtered.kept);\n droppedServers.push(...filtered.dropped);\n }\n\n const sharedConfigPath = resolveSharedConfigPath(input.repoRoot);\n const allowedUpstreams = input.allowlist?.proxy?.length ? input.allowlist.proxy : undefined;\n\n return {\n shared: { configPaths: sharedConfigPath ? [sharedConfigPath] : [], allowedUpstreams },\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,65 @@
|
|
|
1
|
+
//#region src/services/configSources.d.ts
|
|
2
|
+
/** Server name in `.mcp.json` whose upstreams are declared in `mcp-config.yaml`. */
|
|
3
|
+
declare const PROXY_SERVER_NAME = "agiflow-proxy";
|
|
4
|
+
/** Domain policy. An absent or empty list means "keep everything". */
|
|
5
|
+
interface McpAllowlist {
|
|
6
|
+
/** Servers declared across the `.mcp.json` layers. */
|
|
7
|
+
servers?: string[];
|
|
8
|
+
/** Upstreams declared in `mcp-config.yaml`. */
|
|
9
|
+
proxy?: string[];
|
|
10
|
+
}
|
|
11
|
+
interface McpConfigGroupsInput {
|
|
12
|
+
repoRoot: string;
|
|
13
|
+
/** Absolute paths to the `.mcp.json` of each plugin selected for this session. */
|
|
14
|
+
pluginConfigPaths?: string[];
|
|
15
|
+
allowlist?: McpAllowlist;
|
|
16
|
+
/** Where filtered copies land. Only written to when the allowlist drops a server. */
|
|
17
|
+
stagingDirectory: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Worktree-level servers, reached through the shared HTTP runtime.
|
|
21
|
+
*
|
|
22
|
+
* The runtime is shared by every session in the worktree, so its config must stay
|
|
23
|
+
* identical no matter which domains a session selected. The allowlist therefore
|
|
24
|
+
* travels as `allowedUpstreams` and is applied when tools are registered, not by
|
|
25
|
+
* rewriting the config the runtime starts from.
|
|
26
|
+
*/
|
|
27
|
+
interface SharedConfigGroup {
|
|
28
|
+
configPaths: string[];
|
|
29
|
+
allowedUpstreams?: string[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Servers this session connects in process.
|
|
33
|
+
*
|
|
34
|
+
* Plugin servers live here rather than in the shared runtime: sessions in one
|
|
35
|
+
* worktree routinely select different plugin sets, and folding them into the
|
|
36
|
+
* shared runtime would let one session's plugins leak into another's.
|
|
37
|
+
*/
|
|
38
|
+
interface SessionConfigGroup {
|
|
39
|
+
configPaths: string[];
|
|
40
|
+
/**
|
|
41
|
+
* Servers these layers declare, after filtering.
|
|
42
|
+
*
|
|
43
|
+
* Reported so a session can list what it is about to connect to before anything
|
|
44
|
+
* has answered, rather than looking empty until the first server lands.
|
|
45
|
+
*/
|
|
46
|
+
serverNames: string[];
|
|
47
|
+
}
|
|
48
|
+
interface McpConfigGroups {
|
|
49
|
+
shared: SharedConfigGroup;
|
|
50
|
+
sessionLocal: SessionConfigGroup;
|
|
51
|
+
/** Servers removed before mcp-proxy saw them, for status reporting. */
|
|
52
|
+
droppedServers: string[];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Splits this session's MCP configuration into the two groups doom-mcp connects
|
|
56
|
+
* differently, applying the domain allowlist where each group allows it.
|
|
57
|
+
*
|
|
58
|
+
* The `agiflow-proxy` entry is always removed: doom-mcp embeds mcp-proxy, so
|
|
59
|
+
* honouring that entry would spawn a second proxy and load the shared upstreams
|
|
60
|
+
* twice. `.mcp.json` keeps the entry for `claude.sh` and `codex.sh`.
|
|
61
|
+
*/
|
|
62
|
+
declare function buildMcpConfigGroups(input: McpConfigGroupsInput): McpConfigGroups;
|
|
63
|
+
//#endregion
|
|
64
|
+
export { McpAllowlist, McpConfigGroups, McpConfigGroupsInput, PROXY_SERVER_NAME, SessionConfigGroup, SharedConfigGroup, buildMcpConfigGroups };
|
|
65
|
+
//# sourceMappingURL=configSources.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configSources.d.cts","names":[],"sources":["../../src/services/configSources.ts"],"mappings":";;cAKa;;UAUI;;EAEf;;EAEA;;UAGe;EACf;;EAEA;EACA,YAAY;;EAEZ;;;;;;;;;;UAWe;EACf;EACA;;;;;;;;;UAUe;EACf;;;;;;;EAOA;;UAGe;EACf,QAAQ;EACR,cAAc;;EAEd;;;;;;;;;;iBAgFc,qBAAqB,OAAO,uBAAuB"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
//#region src/services/configSources.d.ts
|
|
2
|
+
/** Server name in `.mcp.json` whose upstreams are declared in `mcp-config.yaml`. */
|
|
3
|
+
declare const PROXY_SERVER_NAME = "agiflow-proxy";
|
|
4
|
+
/** Domain policy. An absent or empty list means "keep everything". */
|
|
5
|
+
interface McpAllowlist {
|
|
6
|
+
/** Servers declared across the `.mcp.json` layers. */
|
|
7
|
+
servers?: string[];
|
|
8
|
+
/** Upstreams declared in `mcp-config.yaml`. */
|
|
9
|
+
proxy?: string[];
|
|
10
|
+
}
|
|
11
|
+
interface McpConfigGroupsInput {
|
|
12
|
+
repoRoot: string;
|
|
13
|
+
/** Absolute paths to the `.mcp.json` of each plugin selected for this session. */
|
|
14
|
+
pluginConfigPaths?: string[];
|
|
15
|
+
allowlist?: McpAllowlist;
|
|
16
|
+
/** Where filtered copies land. Only written to when the allowlist drops a server. */
|
|
17
|
+
stagingDirectory: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Worktree-level servers, reached through the shared HTTP runtime.
|
|
21
|
+
*
|
|
22
|
+
* The runtime is shared by every session in the worktree, so its config must stay
|
|
23
|
+
* identical no matter which domains a session selected. The allowlist therefore
|
|
24
|
+
* travels as `allowedUpstreams` and is applied when tools are registered, not by
|
|
25
|
+
* rewriting the config the runtime starts from.
|
|
26
|
+
*/
|
|
27
|
+
interface SharedConfigGroup {
|
|
28
|
+
configPaths: string[];
|
|
29
|
+
allowedUpstreams?: string[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Servers this session connects in process.
|
|
33
|
+
*
|
|
34
|
+
* Plugin servers live here rather than in the shared runtime: sessions in one
|
|
35
|
+
* worktree routinely select different plugin sets, and folding them into the
|
|
36
|
+
* shared runtime would let one session's plugins leak into another's.
|
|
37
|
+
*/
|
|
38
|
+
interface SessionConfigGroup {
|
|
39
|
+
configPaths: string[];
|
|
40
|
+
/**
|
|
41
|
+
* Servers these layers declare, after filtering.
|
|
42
|
+
*
|
|
43
|
+
* Reported so a session can list what it is about to connect to before anything
|
|
44
|
+
* has answered, rather than looking empty until the first server lands.
|
|
45
|
+
*/
|
|
46
|
+
serverNames: string[];
|
|
47
|
+
}
|
|
48
|
+
interface McpConfigGroups {
|
|
49
|
+
shared: SharedConfigGroup;
|
|
50
|
+
sessionLocal: SessionConfigGroup;
|
|
51
|
+
/** Servers removed before mcp-proxy saw them, for status reporting. */
|
|
52
|
+
droppedServers: string[];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Splits this session's MCP configuration into the two groups doom-mcp connects
|
|
56
|
+
* differently, applying the domain allowlist where each group allows it.
|
|
57
|
+
*
|
|
58
|
+
* The `agiflow-proxy` entry is always removed: doom-mcp embeds mcp-proxy, so
|
|
59
|
+
* honouring that entry would spawn a second proxy and load the shared upstreams
|
|
60
|
+
* twice. `.mcp.json` keeps the entry for `claude.sh` and `codex.sh`.
|
|
61
|
+
*/
|
|
62
|
+
declare function buildMcpConfigGroups(input: McpConfigGroupsInput): McpConfigGroups;
|
|
63
|
+
//#endregion
|
|
64
|
+
export { McpAllowlist, McpConfigGroups, McpConfigGroupsInput, PROXY_SERVER_NAME, SessionConfigGroup, SharedConfigGroup, buildMcpConfigGroups };
|
|
65
|
+
//# sourceMappingURL=configSources.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configSources.d.mts","names":[],"sources":["../../src/services/configSources.ts"],"mappings":";;cAKa;;UAUI;;EAEf;;EAEA;;UAGe;EACf;;EAEA;EACA,YAAY;;EAEZ;;;;;;;;;;UAWe;EACf;EACA;;;;;;;;;UAUe;EACf;;;;;;;EAOA;;UAGe;EACf,QAAQ;EACR,cAAc;;EAEd;;;;;;;;;;iBAgFc,qBAAqB,OAAO,uBAAuB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import e from"node:fs";import{createHash as t}from"node:crypto";import n from"node:path";const r=`agiflow-proxy`,i=`--config`;function a(t){try{let n=JSON.parse(e.readFileSync(t,`utf8`));return n&&typeof n==`object`&&!Array.isArray(n)?n:void 0}catch{return}}function o(t){let o=(a(n.join(t,`.mcp.json`))?.mcpServers??{})[r]?.args,s=Array.isArray(o)?o:[],c=s[s.indexOf(i)+1],l=s.includes(i)&&typeof c==`string`?n.resolve(t,c):n.join(t,`mcp-config.yaml`);return e.existsSync(l)?l:void 0}function s(e,t){return e===`agiflow-proxy`?!1:!t||t.length===0||t.includes(e)}function c(r,i,o){let c=a(r);if(!c)return;let l=c.mcpServers??{},u={},d=[];for(let[e,t]of Object.entries(l))s(e,i)?u[e]=t:d.push(e);let f=Object.keys(u);if(d.length===0)return{path:r,kept:f,dropped:d};e.mkdirSync(o,{mode:448,recursive:!0});let p=t(`sha256`).update(r).digest(`hex`).slice(0,16),m=n.join(o,`mcp-${p}.json`);return e.writeFileSync(m,`${JSON.stringify({...c,mcpServers:u},null,2)}\n`,{mode:384}),{path:m,kept:f,dropped:d}}function l(t){let r=[n.join(t.repoRoot,`.mcp.json`),...(t.pluginConfigPaths??[]).map(e=>n.resolve(t.repoRoot,e))].filter(t=>e.existsSync(t)),i=[],a=[],s=[];for(let e of r){let n=c(e,t.allowlist?.servers,t.stagingDirectory);n&&(i.push(n.path),a.push(...n.kept),s.push(...n.dropped))}let l=o(t.repoRoot),u=t.allowlist?.proxy?.length?t.allowlist.proxy:void 0;return{shared:{configPaths:l?[l]:[],allowedUpstreams:u},sessionLocal:{configPaths:i,serverNames:[...new Set(a)]},droppedServers:[...new Set(s)]}}export{r as PROXY_SERVER_NAME,l as buildMcpConfigGroups,o as resolveSharedConfigPath};
|
|
2
|
+
//# sourceMappingURL=configSources.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configSources.mjs","names":[],"sources":["../../src/services/configSources.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n/** Server name in `.mcp.json` whose upstreams are declared in `mcp-config.yaml`. */\nexport const PROXY_SERVER_NAME = 'agiflow-proxy';\n\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst DEFAULT_SHARED_CONFIG = 'mcp-config.yaml';\nconst CONFIG_ARGUMENT = '--config';\n\ntype JsonObject = Record<string, unknown>;\n\n/** Domain policy. An absent or empty list means \"keep everything\". */\nexport interface McpAllowlist {\n /** Servers declared across the `.mcp.json` layers. */\n servers?: string[];\n /** Upstreams declared in `mcp-config.yaml`. */\n proxy?: string[];\n}\n\nexport interface McpConfigGroupsInput {\n repoRoot: string;\n /** Absolute paths to the `.mcp.json` of each plugin selected for this session. */\n pluginConfigPaths?: string[];\n allowlist?: McpAllowlist;\n /** Where filtered copies land. Only written to when the allowlist drops a server. */\n stagingDirectory: string;\n}\n\n/**\n * Worktree-level servers, reached through the shared HTTP runtime.\n *\n * The runtime is shared by every session in the worktree, so its config must stay\n * identical no matter which domains a session selected. The allowlist therefore\n * travels as `allowedUpstreams` and is applied when tools are registered, not by\n * rewriting the config the runtime starts from.\n */\nexport interface SharedConfigGroup {\n configPaths: string[];\n allowedUpstreams?: string[];\n}\n\n/**\n * Servers this session connects in process.\n *\n * Plugin servers live here rather than in the shared runtime: sessions in one\n * worktree routinely select different plugin sets, and folding them into the\n * shared runtime would let one session's plugins leak into another's.\n */\nexport interface SessionConfigGroup {\n configPaths: string[];\n /**\n * Servers these layers declare, after filtering.\n *\n * Reported so a session can list what it is about to connect to before anything\n * has answered, rather than looking empty until the first server lands.\n */\n serverNames: string[];\n}\n\nexport interface McpConfigGroups {\n shared: SharedConfigGroup;\n sessionLocal: SessionConfigGroup;\n /** Servers removed before mcp-proxy saw them, for status reporting. */\n droppedServers: string[];\n}\n\nfunction readJsonObject(filePath: string): JsonObject | undefined {\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(filePath, 'utf8'));\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as JsonObject) : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Resolves the proxy config the shared runtime is started from.\n *\n * `.mcp.json` is the source of truth: its `agiflow-proxy` entry already names the\n * file, so a repo that renamed or relocated it keeps working.\n */\nexport function resolveSharedConfigPath(repoRoot: string): string | undefined {\n const rootConfig = readJsonObject(path.join(repoRoot, '.mcp.json'));\n const servers = (rootConfig?.mcpServers ?? {}) as Record<string, JsonObject | undefined>;\n const rawArgs: unknown = servers[PROXY_SERVER_NAME]?.args;\n const args = Array.isArray(rawArgs) ? (rawArgs as unknown[]) : [];\n const declared = args[args.indexOf(CONFIG_ARGUMENT) + 1];\n const candidate =\n args.includes(CONFIG_ARGUMENT) && typeof declared === 'string'\n ? path.resolve(repoRoot, declared)\n : path.join(repoRoot, DEFAULT_SHARED_CONFIG);\n return fs.existsSync(candidate) ? candidate : undefined;\n}\n\n/** Empty and absent allowlists both mean \"keep everything\". */\nfunction keepsServer(name: string, allowed: string[] | undefined): boolean {\n if (name === PROXY_SERVER_NAME) return false;\n if (!allowed || allowed.length === 0) return true;\n return allowed.includes(name);\n}\n\n/**\n * Rewrites one `.mcp.json` layer with the disallowed servers removed.\n *\n * Entries are dropped from the raw document rather than marked disabled so a\n * filtered stdio server is never spawned at all. Only keys of `mcpServers` are\n * touched, so nothing else in the layer can be lost in translation.\n */\nfunction filterLayer(\n filePath: string,\n allowed: string[] | undefined,\n stagingDirectory: string,\n): { path: string; kept: string[]; dropped: string[] } | undefined {\n const config = readJsonObject(filePath);\n if (!config) return undefined;\n const servers = (config.mcpServers ?? {}) as Record<string, unknown>;\n const kept: Record<string, unknown> = {};\n const dropped: string[] = [];\n for (const [name, definition] of Object.entries(servers)) {\n if (keepsServer(name, allowed)) kept[name] = definition;\n else dropped.push(name);\n }\n const keptNames = Object.keys(kept);\n if (dropped.length === 0) return { path: filePath, kept: keptNames, dropped };\n\n fs.mkdirSync(stagingDirectory, { mode: PRIVATE_DIRECTORY_MODE, recursive: true });\n // Hashed so two plugins named `.mcp.json` cannot overwrite each other's copy.\n const digest = createHash('sha256').update(filePath).digest('hex').slice(0, 16);\n const stagedPath = path.join(stagingDirectory, `mcp-${digest}.json`);\n fs.writeFileSync(stagedPath, `${JSON.stringify({ ...config, mcpServers: kept }, null, 2)}\\n`, {\n mode: PRIVATE_FILE_MODE,\n });\n return { path: stagedPath, kept: keptNames, dropped };\n}\n\n/**\n * Splits this session's MCP configuration into the two groups doom-mcp connects\n * differently, applying the domain allowlist where each group allows it.\n *\n * The `agiflow-proxy` entry is always removed: doom-mcp embeds mcp-proxy, so\n * honouring that entry would spawn a second proxy and load the shared upstreams\n * twice. `.mcp.json` keeps the entry for `claude.sh` and `codex.sh`.\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 if (!filtered) continue;\n configPaths.push(filtered.path);\n serverNames.push(...filtered.kept);\n droppedServers.push(...filtered.dropped);\n }\n\n const sharedConfigPath = resolveSharedConfigPath(input.repoRoot);\n const allowedUpstreams = input.allowlist?.proxy?.length ? input.allowlist.proxy : undefined;\n\n return {\n shared: { configPaths: sharedConfigPath ? [sharedConfigPath] : [], allowedUpstreams },\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,2 @@
|
|
|
1
|
+
const e=`MCP_DIRECT_TOOLS`;var t=class{servers=new Set;tools=new Map;constructor(e){for(let t of e){let e=t.trim().replace(/\/+$/,``);if(!e)continue;let n=e.indexOf(`/`);if(n===-1){this.servers.add(e);continue}let r=e.slice(0,n),i=e.slice(n+1);if(!r)continue;if(!i){this.servers.add(r);continue}let a=this.tools.get(r)??new Set;a.add(i),this.tools.set(r,a)}}allows(e,t){return this.servers.has(e)?!0:this.tools.get(e)?.has(t)??!1}};function n(n=process.env){let r=n[e];if(r!==void 0&&r!==``)return r===`__none__`?new t([]):new t(r.split(`,`))}exports.DIRECT_TOOLS_ENV=e,exports.DirectToolFilter=t,exports.NO_DIRECT_TOOLS=`__none__`,exports.readDirectToolFilter=n;
|
|
2
|
+
//# sourceMappingURL=directTools.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directTools.cjs","names":[],"sources":["../../src/services/directTools.ts"],"sourcesContent":["/**\n * Environment contract for a child agent's MCP tool selection.\n *\n * Doom Team launches children with a strict tool allowlist and publishes the\n * selectors it resolved here. The name is inherited from `pi-mcp-adapter` so the\n * launcher side did not have to change when this package replaced it.\n */\nexport const DIRECT_TOOLS_ENV = 'MCP_DIRECT_TOOLS';\n\n/** Explicit \"no MCP tools\", distinct from an unset variable meaning \"no limit\". */\nexport const NO_DIRECT_TOOLS = '__none__';\n\nconst SELECTOR_SEPARATOR = '/';\n\n/**\n * Which downstream tools a child may see.\n *\n * A selector is either a whole server (`pencil`) or one tool of it\n * (`pencil/get_screenshot`).\n */\nexport class DirectToolFilter {\n private readonly servers = new Set<string>();\n private readonly tools = new Map<string, Set<string>>();\n\n constructor(selectors: readonly string[]) {\n for (const raw of selectors) {\n const selector = raw.trim().replace(/\\/+$/, '');\n if (!selector) continue;\n const separator = selector.indexOf(SELECTOR_SEPARATOR);\n if (separator === -1) {\n this.servers.add(selector);\n continue;\n }\n const server = selector.slice(0, separator);\n const tool = selector.slice(separator + 1);\n if (!server) continue;\n if (!tool) {\n this.servers.add(server);\n continue;\n }\n const selected = this.tools.get(server) ?? new Set<string>();\n selected.add(tool);\n this.tools.set(server, selected);\n }\n }\n\n allows(serverName: string, toolName: string): boolean {\n if (this.servers.has(serverName)) return true;\n return this.tools.get(serverName)?.has(toolName) ?? false;\n }\n}\n\n/**\n * Reads the child's tool selection from the environment.\n *\n * An unset variable means this is not a restricted child, so every configured\n * tool is available. The explicit sentinel means the launcher resolved nothing\n * and the child gets no MCP tools at all, which is not the same as unset.\n */\nexport function readDirectToolFilter(environment: NodeJS.ProcessEnv = process.env): DirectToolFilter | undefined {\n const raw = environment[DIRECT_TOOLS_ENV];\n if (raw === undefined || raw === '') return undefined;\n if (raw === NO_DIRECT_TOOLS) return new DirectToolFilter([]);\n return new DirectToolFilter(raw.split(','));\n}\n"],"mappings":"AAOA,MAAa,EAAmB,mBAahC,IAAa,EAAb,KAA8B,CAC5B,QAA2B,IAAI,IAC/B,MAAyB,IAAI,IAE7B,YAAY,EAA8B,CACxC,IAAK,IAAM,KAAO,EAAW,CAC3B,IAAM,EAAW,EAAI,KAAK,CAAC,CAAC,QAAQ,OAAQ,EAAE,EAC9C,GAAI,CAAC,EAAU,SACf,IAAM,EAAY,EAAS,QAAQ,GAAkB,EACrD,GAAI,IAAc,GAAI,CACpB,KAAK,QAAQ,IAAI,CAAQ,EACzB,QACF,CACA,IAAM,EAAS,EAAS,MAAM,EAAG,CAAS,EACpC,EAAO,EAAS,MAAM,EAAY,CAAC,EACzC,GAAI,CAAC,EAAQ,SACb,GAAI,CAAC,EAAM,CACT,KAAK,QAAQ,IAAI,CAAM,EACvB,QACF,CACA,IAAM,EAAW,KAAK,MAAM,IAAI,CAAM,GAAK,IAAI,IAC/C,EAAS,IAAI,CAAI,EACjB,KAAK,MAAM,IAAI,EAAQ,CAAQ,CACjC,CACF,CAEA,OAAO,EAAoB,EAA2B,CAEpD,OADI,KAAK,QAAQ,IAAI,CAAU,EAAU,GAClC,KAAK,MAAM,IAAI,CAAU,CAAC,EAAE,IAAI,CAAQ,GAAK,EACtD,CACF,EASA,SAAgB,EAAqB,EAAiC,QAAQ,IAAmC,CAC/G,IAAM,EAAM,EAAY,GACpB,OAAQ,IAAA,IAAa,IAAQ,GAEjC,OADI,IAAA,WAAgC,IAAI,EAAiB,CAAC,CAAC,EACpD,IAAI,EAAiB,EAAI,MAAM,GAAG,CAAC,CAC5C"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/services/directTools.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Environment contract for a child agent's MCP tool selection.
|
|
4
|
+
*
|
|
5
|
+
* Doom Team launches children with a strict tool allowlist and publishes the
|
|
6
|
+
* selectors it resolved here. The name is inherited from `pi-mcp-adapter` so the
|
|
7
|
+
* launcher side did not have to change when this package replaced it.
|
|
8
|
+
*/
|
|
9
|
+
declare const DIRECT_TOOLS_ENV = "MCP_DIRECT_TOOLS";
|
|
10
|
+
/** Explicit "no MCP tools", distinct from an unset variable meaning "no limit". */
|
|
11
|
+
declare const NO_DIRECT_TOOLS = "__none__";
|
|
12
|
+
//#endregion
|
|
13
|
+
export { DIRECT_TOOLS_ENV, NO_DIRECT_TOOLS };
|
|
14
|
+
//# sourceMappingURL=directTools.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directTools.d.cts","names":[],"sources":["../../src/services/directTools.ts"],"mappings":";;;;;;;;cAOa;;cAGA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/services/directTools.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Environment contract for a child agent's MCP tool selection.
|
|
4
|
+
*
|
|
5
|
+
* Doom Team launches children with a strict tool allowlist and publishes the
|
|
6
|
+
* selectors it resolved here. The name is inherited from `pi-mcp-adapter` so the
|
|
7
|
+
* launcher side did not have to change when this package replaced it.
|
|
8
|
+
*/
|
|
9
|
+
declare const DIRECT_TOOLS_ENV = "MCP_DIRECT_TOOLS";
|
|
10
|
+
/** Explicit "no MCP tools", distinct from an unset variable meaning "no limit". */
|
|
11
|
+
declare const NO_DIRECT_TOOLS = "__none__";
|
|
12
|
+
//#endregion
|
|
13
|
+
export { DIRECT_TOOLS_ENV, NO_DIRECT_TOOLS };
|
|
14
|
+
//# sourceMappingURL=directTools.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directTools.d.mts","names":[],"sources":["../../src/services/directTools.ts"],"mappings":";;;;;;;;cAOa;;cAGA"}
|