From 22adbb47cd97af73b7750eabbc1dd9ad01b2c648 Mon Sep 17 00:00:00 2001 From: galuis116 Date: Tue, 21 Jul 2026 12:25:31 -0300 Subject: [PATCH] feat(mcp): register loopover_get_automation_state as a local stdio tool (#7752) loopover_get_automation_state has been a remote MCP tool since #784 and gained a `maintain automation-state` CLI mirror in #6742, but it was never registered on the local stdio server. #6382 registered five sibling maintain tools; this one landed after that batch, so an agent on the stdio server had to shell out to the CLI to reach it. Mirrors the existing #6152 registrations exactly: - Adds the STDIO_TOOL_DESCRIPTORS entry (category "agent", matching the remote server's category for the same name), so the description flows through the shared stdioToolDescription lookup and `loopover-mcp tools` rather than being hardcoded at the registration. - Registers the tool next to its maintain-family siblings, reusing ownerRepoShape and reaching the same endpoint the CLI subcommand already calls (GET /v1/repos/:owner/:repo/automation-state) through the same apiGet client -- no duplicated HTTP logic and no new paths. Extends test/unit/mcp-cli-maintain-tools.test.ts, the suite covering the five siblings: the tool is added to MAINTAIN_TOOLS so it inherits the registration, `tools --json` descriptor, REST-proxy, and API-failure cases, and the request capture filter now also records automation-state calls. --- packages/loopover-mcp/bin/loopover-mcp.ts | 21 +++++++++++++++++++++ test/unit/mcp-cli-maintain-tools.test.ts | 10 ++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/loopover-mcp/bin/loopover-mcp.ts b/packages/loopover-mcp/bin/loopover-mcp.ts index d164dddd5e..01e27bb10e 100644 --- a/packages/loopover-mcp/bin/loopover-mcp.ts +++ b/packages/loopover-mcp/bin/loopover-mcp.ts @@ -1286,6 +1286,11 @@ const STDIO_TOOL_DESCRIPTORS = [ category: "agent", description: "Set the autonomy level for one action class via a read-merge-write, so the other classes are left untouched. Same as `loopover-mcp maintain set-level `. Maintainer access required.", }, + { + name: "loopover_get_automation_state", + category: "agent", + description: "Return a repo's agent automation state: the per-action autonomy levels, kill-switch / dry-run mode, GitHub write-permission readiness, and how many auto_with_approval actions are awaiting a maintainer decision — the read-side view behind the pause/resume and set-level toggles. Same as `loopover-mcp maintain automation-state`. Maintainer access required.", + }, { name: "loopover_get_gate_precision", category: "maintainer", @@ -2597,6 +2602,22 @@ registerStdioTool( return toolResult(`Gate precision for ${owner}/${repo}.`, payload); }, ); + +// #6742 added this endpoint's REST route and its `maintain automation-state` CLI mirror, but not the stdio tool, +// so it fell outside #6152's batch above (#7752). Read-only: the derived counterpart to the pause/resume and +// set-level writes registered above, reached through the same apiGet client those use. +registerStdioTool( + "loopover_get_automation_state", + { + description: stdioToolDescription("loopover_get_automation_state"), + inputSchema: ownerRepoShape, + }, + async ({ owner, repo }: any) => { + const payload = await apiGet(`${toolRepoBase(owner, repo)}/automation-state`); + return toolResult(`Agent automation state for ${owner}/${repo}.`, payload); + }, +); + // ── Write-tools (#6149): pure LOCAL-execution spec builders. loopover NEVER performs the write -- each tool // returns a spec the caller runs with its OWN gh creds. Brings the local stdio server to parity with the // miner-auto-dev profile's recommendedTools, using the same @loopover/engine builders as the remote server. diff --git a/test/unit/mcp-cli-maintain-tools.test.ts b/test/unit/mcp-cli-maintain-tools.test.ts index 7bb9450726..d714f7dc30 100644 --- a/test/unit/mcp-cli-maintain-tools.test.ts +++ b/test/unit/mcp-cli-maintain-tools.test.ts @@ -22,7 +22,7 @@ async function connect() { const apiUrl = await startFixtureServer({ onApiRequest: (request) => { const url = request.url ?? ""; - if (/pending-actions|settings|gate-precision/.test(url)) capturedRequests.push({ url, method: request.method ?? "GET" }); + if (/pending-actions|settings|gate-precision|automation-state/.test(url)) capturedRequests.push({ url, method: request.method ?? "GET" }); }, }); transport = new StdioClientTransport({ @@ -51,23 +51,25 @@ afterEach(async () => { const REPO = { owner: "owner", repo: "repo" }; -/** Every #6152 tool, with an argument set the fixture serves and a field its real payload carries. */ +/** Every #6152 tool, plus #7752's automation-state read, with an argument set the fixture serves and a field its + * real payload carries. */ const MAINTAIN_TOOLS = [ { name: "loopover_list_pending_actions", args: REPO, contains: "pa-1" }, { name: "loopover_decide_pending_action", args: { ...REPO, id: "pa-1", decision: "accept" }, contains: "accepted" }, { name: "loopover_set_agent_paused", args: { ...REPO, paused: true }, contains: "agentPaused" }, { name: "loopover_set_action_autonomy", args: { ...REPO, action: "merge", level: "auto" }, contains: "autonomy" }, { name: "loopover_get_gate_precision", args: REPO, contains: "falsePositiveRate" }, + { name: "loopover_get_automation_state", args: REPO, contains: "permissionReadiness" }, ] as const; describe("loopover-mcp maintain stdio proxies (#6152)", () => { - it("registers all 5 maintain tools in the stdio server tool list", async () => { + it("registers all 6 maintain tools in the stdio server tool list", async () => { await connect(); const names = (await client!.listTools()).tools.map((tool) => tool.name); for (const tool of MAINTAIN_TOOLS) expect(names).toContain(tool.name); }); - it("lists all 5 maintain tools via `loopover-mcp tools --json` with non-empty descriptions", async () => { + it("lists all 6 maintain tools via `loopover-mcp tools --json` with non-empty descriptions", async () => { await connect(); const payload = JSON.parse(run(["tools", "--json"])) as { tools: Array<{ name: string; description: string; category?: string }> }; for (const tool of MAINTAIN_TOOLS) {