@monster-orange/den 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +89 -0
- package/README.md +1 -1
- package/docs/distributions.md +65 -1
- package/package.json +1 -1
- package/service/install.sh +14 -2
- package/src/shared/den-config.ts +69 -0
- package/src/shared/environment-manager/service-runner.ts +214 -2
- package/src/shared/manifest/validate-layers.ts +40 -1
- package/src/shared/stacks/manifest-commands.ts +11 -1
- package/src/shared/stacks/runtime.ts +20 -0
- package/src/ui/cli/flag-parser.ts +2 -1
- package/src/ui/cli/index.ts +184 -34
- package/src/ui/cli/service-command.ts +131 -0
- package/src/ui/cli/stack-command.ts +9 -0
- package/src/ui/mcp/server.ts +82 -1
package/src/ui/mcp/server.ts
CHANGED
|
@@ -115,15 +115,78 @@ function parsePort(argv: string[]): number | undefined {
|
|
|
115
115
|
export const MCP_USAGE = (cli: string): string =>
|
|
116
116
|
[
|
|
117
117
|
`Usage: ${cli} mcp [--port <web port>]`,
|
|
118
|
+
` ${cli} mcp --list`,
|
|
119
|
+
` ${cli} mcp --call <tool> ['<json args>']`,
|
|
118
120
|
"",
|
|
119
121
|
`An MCP server over stdio for agents, a client of the running web server`,
|
|
120
122
|
`(\`${cli} web up\`; default port: config web.port). Read tools always;`,
|
|
121
123
|
"actions only as named in the org config's mcp.actions. See docs/distributions.md.",
|
|
122
124
|
"",
|
|
125
|
+
"--list prints the tool names; --call runs one tool through the same guards,",
|
|
126
|
+
"prints its text result and exits 1 if the tool reports an error.",
|
|
127
|
+
"",
|
|
123
128
|
`Register it with your agents: ${cli} mcp clients, then ${cli} mcp connect <client> (or --all).`,
|
|
124
129
|
].join("\n");
|
|
125
130
|
|
|
126
|
-
/**
|
|
131
|
+
/** A one-shot invocation: `--list`, or `--call <tool> [json]`. */
|
|
132
|
+
export type OneShot =
|
|
133
|
+
| { kind: "list" }
|
|
134
|
+
| { kind: "call"; tool: string; args: Record<string, unknown> };
|
|
135
|
+
|
|
136
|
+
/** The one-shot `argv` asks for, or null for the stdio server. */
|
|
137
|
+
export function parseOneShot(argv: string[]): OneShot | null {
|
|
138
|
+
const i = argv.indexOf("--call");
|
|
139
|
+
if (i !== -1) {
|
|
140
|
+
const tool = argv[i + 1];
|
|
141
|
+
if (!tool || tool.startsWith("-"))
|
|
142
|
+
throw new Error("--call needs a tool name: --call <tool> ['<json>']");
|
|
143
|
+
const raw = argv[i + 2];
|
|
144
|
+
if (raw === undefined || raw.startsWith("--"))
|
|
145
|
+
return { kind: "call", tool, args: {} };
|
|
146
|
+
let args: unknown;
|
|
147
|
+
try {
|
|
148
|
+
args = JSON.parse(raw);
|
|
149
|
+
} catch (e) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`--call ${tool}: arguments are not JSON (${(e as Error).message})`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
if (args === null || typeof args !== "object" || Array.isArray(args))
|
|
155
|
+
throw new Error(`--call ${tool}: arguments must be a JSON object`);
|
|
156
|
+
return { kind: "call", tool, args: args as Record<string, unknown> };
|
|
157
|
+
}
|
|
158
|
+
return argv.includes("--list") ? { kind: "list" } : null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface OneShotOptions {
|
|
162
|
+
cfg: ResolvedMcpConfig;
|
|
163
|
+
cli: string;
|
|
164
|
+
api: DenApi;
|
|
165
|
+
out?: (text: string) => void;
|
|
166
|
+
err?: (text: string) => void;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Run a one-shot through the tool layer the stdio server uses (callTool:
|
|
171
|
+
* mcp.actions, every guard, redaction). Returns the exit code.
|
|
172
|
+
*/
|
|
173
|
+
export async function runOneShot(
|
|
174
|
+
shot: OneShot,
|
|
175
|
+
opts: OneShotOptions,
|
|
176
|
+
): Promise<number> {
|
|
177
|
+
const out = opts.out ?? ((t: string) => process.stdout.write(t));
|
|
178
|
+
if (shot.kind === "list") {
|
|
179
|
+
const names = toolsFor(opts.cfg).map((t) => t.name);
|
|
180
|
+
out(names.length ? `${names.join("\n")}\n` : "");
|
|
181
|
+
return 0;
|
|
182
|
+
}
|
|
183
|
+
const ctx: ToolContext = { api: opts.api, cfg: opts.cfg, cli: opts.cli };
|
|
184
|
+
const r = await callTool(shot.tool, shot.args, ctx);
|
|
185
|
+
out(r.text.endsWith("\n") ? r.text : `${r.text}\n`);
|
|
186
|
+
return r.isError ? 1 : 0;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Run `<cli> mcp` until stdin closes (or one `--call` / `--list`). */
|
|
127
190
|
export async function runMcp(argv: string[]): Promise<void> {
|
|
128
191
|
const cli = product().cli;
|
|
129
192
|
if (argv.includes("-h") || argv.includes("--help")) {
|
|
@@ -140,6 +203,24 @@ export async function runMcp(argv: string[]): Promise<void> {
|
|
|
140
203
|
}
|
|
141
204
|
const cfg = readMcpConfig();
|
|
142
205
|
const baseUrl = `http://localhost:${port}`;
|
|
206
|
+
const shot = parseOneShot(argv);
|
|
207
|
+
if (shot) {
|
|
208
|
+
// Recorded like any MCP action: actor agent, this process, one session.
|
|
209
|
+
const session = randomUUID();
|
|
210
|
+
const api = createDenApi({
|
|
211
|
+
baseUrl,
|
|
212
|
+
cli,
|
|
213
|
+
headers: () =>
|
|
214
|
+
agentAuditHeaders({
|
|
215
|
+
client: `${cli} mcp --call`,
|
|
216
|
+
clientVersion: engineVersion() ?? "unknown",
|
|
217
|
+
mcpPid: process.pid,
|
|
218
|
+
session,
|
|
219
|
+
}),
|
|
220
|
+
});
|
|
221
|
+
process.exitCode = await runOneShot(shot, { cfg, cli, api });
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
143
224
|
const server = createDenMcpServer({ cfg, baseUrl, cli });
|
|
144
225
|
await server.connect(new StdioServerTransport());
|
|
145
226
|
process.stderr.write(
|