@yagni-app/code-staging 1.0.9-staging.1284.1 → 1.0.9-staging.1286.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extension/mcp/cliConfig.d.ts +1 -1
- package/dist/extension/mcp/cliConfig.js +1 -1
- package/dist/extension/mcp/config.d.ts +2 -0
- package/dist/extension/mcp/config.js +2 -0
- package/dist/extension/mcp/tools.js +5 -0
- package/dist/mcpCommand.d.ts +7 -0
- package/dist/mcpCommand.js +40 -1
- package/package.json +2 -2
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
export { McpServerConfig, McpScope, PLUGIN_MCP_ENV, PROJECT_CONFIG_FILENAME, ScopedMcpServerConfig, expandEnvVarsInString, expandServerEnv, loadMcpServers, mcpConfigPath, readPluginMcpServers, readProjectMcpConfig, readUserMcpConfig, resolveProjectRoot, validateServerConfig, writeUserMcpConfig, } from "./config.js";
|
|
8
8
|
export { ProjectApprovalState, decisionFor, readProjectApproval, recordProjectDecision, resetProjectChoices, undecidedProjectServers, } from "./approval.js";
|
|
9
9
|
export { McpAuthFile, StoredOAuthEntry, deleteStoredOAuthEntry, getServerKey, getStoredOAuthEntry, mcpAuthPath, readMcpAuth, updateStoredOAuthEntry, writeMcpAuth, } from "./authStore.js";
|
|
10
|
-
export { revokeTokensOnRemove } from "./auth.js";
|
|
10
|
+
export { authenticate, revokeTokensOnRemove } from "./auth.js";
|
|
11
11
|
export { probeServer, McpHealthResult, McpHealthStatus } from "./manager.js";
|
|
12
12
|
//# sourceMappingURL=cliConfig.d.ts.map
|
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
export { PLUGIN_MCP_ENV, PROJECT_CONFIG_FILENAME, expandEnvVarsInString, expandServerEnv, loadMcpServers, mcpConfigPath, readPluginMcpServers, readProjectMcpConfig, readUserMcpConfig, resolveProjectRoot, validateServerConfig, writeUserMcpConfig, } from "./config.js";
|
|
8
8
|
export { decisionFor, readProjectApproval, recordProjectDecision, resetProjectChoices, undecidedProjectServers, } from "./approval.js";
|
|
9
9
|
export { deleteStoredOAuthEntry, getServerKey, getStoredOAuthEntry, mcpAuthPath, readMcpAuth, updateStoredOAuthEntry, writeMcpAuth, } from "./authStore.js";
|
|
10
|
-
export { revokeTokensOnRemove } from "./auth.js";
|
|
10
|
+
export { authenticate, revokeTokensOnRemove } from "./auth.js";
|
|
11
11
|
export { probeServer } from "./manager.js";
|
|
12
12
|
//# sourceMappingURL=cliConfig.js.map
|
|
@@ -36,6 +36,8 @@ export interface McpHttpServerConfig {
|
|
|
36
36
|
url: string;
|
|
37
37
|
headers?: Record<string, string>;
|
|
38
38
|
oauth?: McpOAuthConfig;
|
|
39
|
+
/** Optional explicit tool names, used by the Worker connection to avoid duplicating existing context tools. */
|
|
40
|
+
tools?: string[];
|
|
39
41
|
}
|
|
40
42
|
export type McpServerConfig = McpStdioServerConfig | McpHttpServerConfig;
|
|
41
43
|
export interface ScopedMcpServerConfig {
|
|
@@ -267,6 +267,8 @@ export function validateServerConfig(value) {
|
|
|
267
267
|
return { ok: true };
|
|
268
268
|
}
|
|
269
269
|
if (type === "http" || type === "sse") {
|
|
270
|
+
if (v["tools"] !== undefined && (!Array.isArray(v["tools"]) || v["tools"].length > 100 || v["tools"].some(name => typeof name !== "string" || name.length === 0 || name.length > 128)))
|
|
271
|
+
return { ok: false, message: "tools must be an array of up to 100 tool names" };
|
|
270
272
|
if (typeof v["url"] !== "string" || v["url"].length === 0) {
|
|
271
273
|
return { ok: false, message: `${type} server requires a non-empty "url"` };
|
|
272
274
|
}
|
|
@@ -26,7 +26,10 @@ export async function registerServerTools(pi, manager, serverName, env = process
|
|
|
26
26
|
return result;
|
|
27
27
|
}
|
|
28
28
|
const toolTimeout = toolTimeoutFromEnv(env);
|
|
29
|
+
const selectedTools = server.config.type === "http" || server.config.type === "sse" ? server.config.tools : undefined;
|
|
29
30
|
for (const tool of toolList.tools ?? []) {
|
|
31
|
+
if (selectedTools && !selectedTools.includes(tool.name))
|
|
32
|
+
continue;
|
|
30
33
|
const fullToolName = buildMcpToolName(serverName, tool.name);
|
|
31
34
|
if (!fullToolName) {
|
|
32
35
|
result.warnings.push(`${serverName}: tool "${tool.name}" produced an empty wire name; skipped`);
|
|
@@ -67,6 +70,8 @@ export function capDescription(description) {
|
|
|
67
70
|
/** Cheap heuristic in the spirit of Claude Code's input-hint check; per-tool annotations arrive via listTools only in newer servers. */
|
|
68
71
|
export function looksMutating(toolName, description) {
|
|
69
72
|
const name = toolName.toLowerCase();
|
|
73
|
+
if (["critique_plan", "review_pr", "engage_worker", "propose_instruction_change", "prepare_review_publication", "resolve_decision"].includes(name))
|
|
74
|
+
return true;
|
|
70
75
|
const writeHints = /^(create|add|update|edit|delete|remove|set|write|send|post|put|patch|deploy|publish|close|merge|assign|move|archive|trash|restore)/;
|
|
71
76
|
if (writeHints.test(name))
|
|
72
77
|
return true;
|
package/dist/mcpCommand.d.ts
CHANGED
|
@@ -38,6 +38,13 @@ export interface McpDeps {
|
|
|
38
38
|
}
|
|
39
39
|
/** The structural slice of the extension's mcp config surface this file needs. */
|
|
40
40
|
export interface McpCliModule {
|
|
41
|
+
authenticate?(serverName: string, config: {
|
|
42
|
+
type: "http";
|
|
43
|
+
url: string;
|
|
44
|
+
tools: string[];
|
|
45
|
+
}): Promise<{
|
|
46
|
+
result: "AUTHORIZED";
|
|
47
|
+
}>;
|
|
41
48
|
loadMcpServers(cwd: string, env: NodeJS.ProcessEnv): McpLoadResult;
|
|
42
49
|
mcpConfigPath(): string;
|
|
43
50
|
PROJECT_CONFIG_FILENAME: string;
|
package/dist/mcpCommand.js
CHANGED
|
@@ -21,7 +21,7 @@ import { fileURLToPath } from "node:url";
|
|
|
21
21
|
import { CLAUDE_PLUGIN_MCP_ENV, claudeCompatArgs } from "./claudeCompat.js";
|
|
22
22
|
import { agentDir } from "./credentials.js";
|
|
23
23
|
import { DISTRIBUTION } from "./distribution.js";
|
|
24
|
-
import { getActiveProfileName } from "./profiles.js";
|
|
24
|
+
import { getActiveProfileName, readActiveProfile } from "./profiles.js";
|
|
25
25
|
export function resolveMcpConfigPath() {
|
|
26
26
|
const bundled = fileURLToPath(new URL("./extension/mcp/cliConfig.js", import.meta.url));
|
|
27
27
|
if (existsSync(bundled))
|
|
@@ -55,6 +55,9 @@ Scopes:
|
|
|
55
55
|
user available in all your projects (~/.yagni-code/mcp.json)
|
|
56
56
|
project shared via .mcp.json at the repo root (approval-gated)
|
|
57
57
|
|
|
58
|
+
Connect YAGNI Workers:
|
|
59
|
+
${DISTRIBUTION.commandName} mcp connect-workers Browser consent for the active environment
|
|
60
|
+
|
|
58
61
|
Examples:
|
|
59
62
|
${DISTRIBUTION.commandName} mcp add --transport http sentry https://mcp.sentry.dev/mcp
|
|
60
63
|
${DISTRIBUTION.commandName} mcp add -e API_KEY=xxx my-server -- npx my-mcp-server
|
|
@@ -272,6 +275,8 @@ export async function mcpCommand(args, deps = {}) {
|
|
|
272
275
|
return 1;
|
|
273
276
|
}
|
|
274
277
|
switch (parsed.subcommand) {
|
|
278
|
+
case "connect-workers":
|
|
279
|
+
return connectWorkers(mod, parsed, { cwd, stdout, stderr }, deps.env ?? process.env);
|
|
275
280
|
case undefined:
|
|
276
281
|
case "help":
|
|
277
282
|
stdout(USAGE);
|
|
@@ -295,6 +300,40 @@ export async function mcpCommand(args, deps = {}) {
|
|
|
295
300
|
return 1;
|
|
296
301
|
}
|
|
297
302
|
}
|
|
303
|
+
const WORKER_TOOL_NAMES = ["get_context", "create_team", "engage_worker", "critique_plan", "review_pr", "list_work", "get_work", "add_feedback", "propose_instruction_change", "prepare_review_publication", "resolve_decision"];
|
|
304
|
+
async function connectWorkers(mod, parsed, io, env) {
|
|
305
|
+
if (parsed.rest.length || parsed.scopeExplicit || parsed.transportExplicit || Object.keys(parsed.headers).length || Object.keys(parsed.env).length || parsed.clientId || parsed.clientSecret || parsed.callbackPort) {
|
|
306
|
+
io.stderr("Usage: yagni mcp connect-workers (uses your active environment and private user configuration)\n");
|
|
307
|
+
return 1;
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
if (!mod.authenticate)
|
|
311
|
+
throw new Error("Update YAGNI Code to connect Workers");
|
|
312
|
+
const profile = await readActiveProfile(env);
|
|
313
|
+
const url = new URL("/mcp", profile.baseUrl);
|
|
314
|
+
if (url.username || url.password || (url.protocol !== "https:" && !(url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname))))
|
|
315
|
+
throw new Error("Worker connections require HTTPS or a local development server");
|
|
316
|
+
const config = { type: "http", url: url.href, tools: WORKER_TOOL_NAMES };
|
|
317
|
+
const { file, errors } = mod.readUserMcpConfig();
|
|
318
|
+
if (errors.length)
|
|
319
|
+
throw new Error("Fix the existing MCP configuration before connecting Workers");
|
|
320
|
+
const previous = file.mcpServers?.["yagni-workers"];
|
|
321
|
+
if (previous && JSON.stringify(previous) !== JSON.stringify(config))
|
|
322
|
+
throw new Error("An existing yagni-workers connection uses different settings. Remove it with yagni mcp remove yagni-workers before reconnecting");
|
|
323
|
+
const shadow = mod.loadMcpServers(io.cwd, env).servers.find(server => server.name === "yagni-workers" && server.scope !== "user");
|
|
324
|
+
if (shadow)
|
|
325
|
+
throw new Error("A project or local yagni-workers entry would override this connection. Remove that entry before connecting");
|
|
326
|
+
io.stdout("Opening YAGNI in your browser. Choose your workspace and permissions; paid Worker requests use that workspace's balance.\n");
|
|
327
|
+
await mod.authenticate("yagni-workers", config);
|
|
328
|
+
writeServerToScope(mod, "yagni-workers", config, "user", io.cwd);
|
|
329
|
+
io.stdout("Workers connected. Start a new YAGNI Code session to use them; /mcp shows connection status.\n");
|
|
330
|
+
return 0;
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
io.stderr("Worker connection did not complete. Check your environment and existing yagni-workers configuration, then retry browser consent.\n");
|
|
334
|
+
return 1;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
298
337
|
function mcpAdd(mod, parsed, io) {
|
|
299
338
|
const { name, rest } = parsed;
|
|
300
339
|
const commandOrUrl = rest[1];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yagni-app/code-staging",
|
|
3
|
-
"version": "1.0.9-staging.
|
|
3
|
+
"version": "1.0.9-staging.1286.1",
|
|
4
4
|
"description": "YAGNI Code: a terminal coding agent that already knows your company. One YAGNI login routes the model and grounds the agent in your team's context.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"author": "YAGNI, Inc. <jack@yagni.app> (https://yagni.app)",
|
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
"turndown": "^7.2.4",
|
|
59
59
|
"typebox": "^1.3.15"
|
|
60
60
|
},
|
|
61
|
-
"yagniSourceSha": "
|
|
61
|
+
"yagniSourceSha": "e0d42d2e96ab52ea3116681d53f9c11924d6859c"
|
|
62
62
|
}
|