Skip to content

feat(mcp): register the maintain REST surface as local stdio tools - #6382

Merged
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
luciferlive112116:feat/mcp-stdio-maintain-tools
Jul 16, 2026
Merged

feat(mcp): register the maintain REST surface as local stdio tools#6382
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
luciferlive112116:feat/mcp-stdio-maintain-tools

Conversation

@luciferlive112116

Copy link
Copy Markdown
Contributor

Summary

The maintain CLI subcommands call real REST endpoints that the remote server has exposed as tools since #6087, but the local stdio server registered none of them. An agent on the local server had to shell out to the CLI to list the approval queue, decide a staged action, toggle the kill-switch, set an autonomy level, or read gate precision.

This registers the five missing counterparts:

Tool Endpoint CLI equivalent
loopover_list_pending_actions GET /agent/pending-actions maintain queue
loopover_decide_pending_action POST /agent/pending-actions/:id/:decision maintain approve|reject
loopover_set_agent_paused PUT /settings (agentPaused) maintain pause|resume
loopover_set_action_autonomy GET+PUT /settings (autonomy) maintain set-level
loopover_get_gate_precision GET /gate-precision maintain precision

Each calls the endpoint its CLI subcommand already calls, through the same apiGet/apiPost/apiFetch client — so auth, timeouts and error shaping come from there rather than a second HTTP path, per the issue's "reuse the CLI's existing HTTP-client logic" requirement. Input shapes mirror the remote's, and categories mirror the remote's MCP_TOOL_CATEGORIES entries for the same names, so a caller sees one consistent surface across both servers. The remote server is untouched, as required.

One deliberate divergence from the remote — please read

The remote's listPendingActionsShape takes an optional status, which it can honour because it queries the approval-queue store directly. This server can only reach the queue through GET /v1/repos/:owner/:repo/agent/pending-actions, which takes no query parameters at all and hardcodes status: "pending" (src/api/routes.ts).

Mirroring the remote schema literally would therefore have shipped a filter that does nothing: a caller could ask for "rejected", receive the pending list, and be told it succeeded. That is precisely the failure mode of #6153 (merged in #6378), so I left status out of the schema and made the description name the queue it really returns. An agent picks its arguments from the published schema, so a filter that isn't there is one it won't ask for — and a key sent anyway is dropped by the MCP layer before the handler, so it can never reach the URL either. A test pins both halves.

set_action_autonomy read-merge-writes exactly as maintain set-level does: PUT /settings replaces the whole autonomy map, so sending one class alone would silently clear the others. A test asserts the other classes survive and that the write is a GET-then-PUT.

Validation

  • New suite test/unit/mcp-cli-maintain-tools.test.ts — 15/15 pass: success + an API-failure path for each of the 5 tools, plus the read-merge-write invariant, the no-status-filter contract, and pre-flight rejection of unknown action classes / autonomy levels.
  • mcp-tool-rename-aliases + mcp-cli-tools + mcp-cli-tools-search20/20 pass (the count canary, updated 42 → 47).
  • 7 further mcp-cli/mcp-tool suites (basics, maintain, intake-tools, completion-spec, tool-categories, profiles, doctor) — 80/80 pass.
  • npm run typecheck — 0 errors. npm run build:mcp — passes. npm run docs:drift-check — ok. git diff --check — clean.
  • Rebased on latest main — no base conflict.

Coverage

No patch surface: coverage is collected over src/**, packages/loopover-engine/src/**, and packages/loopover-miner/lib/**. This PR touches only packages/loopover-mcp/** (not collected) and test/** (ignored). The new tools are still exercised end-to-end by the suite above, against the fixture server.

Scope

  • One coherent change; wanted paths (packages/, test/). Remote server untouched; existing CLI subcommands' behaviour unchanged.
  • No secrets/tokens/wallet/trust-score/reward terms; no changelog, site/, CNAME, or lovable changes.
  • The alias-retirement suite intentionally pins the exact tool count — updated 42 → 47 with a note recording why, rather than loosened.

Safety

  • These proxy maintainer-authenticated endpoints; the API enforces authorization exactly as it does for the CLI, and no tool decides access locally. Failure surfaces as a tool error rather than a silent empty success — asserted per tool.
  • No secrets committed.

Closes #6152

The maintain CLI subcommands call real REST endpoints that the remote
server has exposed as tools since JSONbored#6087, but the local stdio server
registered none of them. An agent on the local server had to shell out to
the CLI to list the approval queue, decide a staged action, toggle the
kill-switch, set an autonomy level, or read gate precision.

Register the five missing counterparts: loopover_list_pending_actions,
loopover_decide_pending_action, loopover_set_agent_paused,
loopover_set_action_autonomy, and loopover_get_gate_precision. Each calls
the endpoint its CLI subcommand already calls, through the same
apiGet/apiPost/apiFetch client, so auth, timeouts, and error shaping come
from there rather than a second HTTP path. Shapes mirror the remote's, and
categories mirror the remote's MCP_TOOL_CATEGORIES entries for the same
names, so one caller sees one surface. The remote server is untouched.

set_action_autonomy read-merge-writes like `maintain set-level` does:
PUT /settings replaces the whole autonomy map, so sending one class alone
would silently clear the others. A test pins that the other classes
survive, and that the write is a GET-then-PUT.

One deliberate divergence from the remote: its list_pending_actions takes
an optional `status`, which it can honour because it queries the queue
store directly. This server has only GET /agent/pending-actions, which
takes no query parameters and hardcodes status "pending"
(src/api/routes.ts). Offering the filter here would let a caller ask for
"rejected", receive the pending list, and be told it succeeded -- so the
schema omits it and the description names the queue it really returns.

Tests cover success and an API-failure path for each tool, plus the
read-merge-write invariant and pre-flight rejection of unknown action
classes and autonomy levels.

The alias-retirement suite pins the exact registered-tool count; these five
take it from 42 to 47.

Closes JSONbored#6152
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.60%. Comparing base (d280ce4) to head (aaa4392).
⚠️ Report is 25 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6382      +/-   ##
==========================================
- Coverage   95.60%   95.60%   -0.01%     
==========================================
  Files         599      599              
  Lines       47221    47238      +17     
  Branches    15024    15031       +7     
==========================================
+ Hits        45147    45160      +13     
- Misses       1290     1291       +1     
- Partials      784      787       +3     
Flag Coverage Δ
shard-1 44.06% <ø> (-0.02%) ⬇️
shard-2 36.60% <ø> (-0.01%) ⬇️
shard-3 32.58% <ø> (+0.05%) ⬆️
shard-4 34.74% <ø> (+0.13%) ⬆️
shard-5 31.58% <ø> (-0.02%) ⬇️
shard-6 44.87% <ø> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

@loopover-orb loopover-orb Bot added the gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. label Jul 16, 2026
@loopover-orb

loopover-orb Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Tip

✅ LoopOver review result - approve/merge recommended

Review updated: 2026-07-16 05:50:35 UTC

3 files · 1 AI reviewer · no blockers · readiness 77/100 · CI green · clean

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
This PR registers five stdio MCP tools mirroring the remote server's already-shipped maintain REST endpoints (pending-actions list/decide, pause/resume, autonomy, gate-precision), reusing the existing apiGet/apiPost/apiFetch client rather than adding a new HTTP path. The set_action_autonomy tool correctly does a read-merge-write to avoid clobbering other action classes on PUT /settings, and the deliberate omission of a `status` filter on list_pending_actions is well-justified given the route hardcodes status=pending server-side. Tests cover registration, proxying, autonomy merge-preservation, and the intentionally-absent status filter, and the tool-count assertions in the alias-retirement suite were updated in lockstep (42→47).

Nits — 5 non-blocking
  • packages/loopover-mcp/bin/loopover-mcp.js: the large block comment above the tool shapes duplicates rationale already in the PR description; consider trimming to keep the file size down given it's flagged as long (1603 lines).
  • test/unit/mcp-cli-maintain-tools.test.ts: the 404 error-path test only checks the message matches /404|not_found/ rather than asserting on tool name specifically, so a shared fixture regression across all five tools would still pass individually — minor test-specificity nit.
  • packages/loopover-mcp/bin/loopover-mcp.js: `decision` uses accept|reject (route vocabulary) while the CLI subcommands use approve|reject — this divergence is explained in a comment but could trip up a caller expecting CLI-consistent naming; consider surfacing this explicitly in the tool description too, not just the code comment.
  • Consider extracting the five new tool registrations plus their shapes into a separate module given the file is already flagged as oversized (1603 lines, threshold 400), to ease future review diffs in this file.
  • In gatePrecisionShape, confirm `windowDays` upper-bound isn't needed (e.g., a caller passing an extremely large integer) — currently only `.int().positive()` is enforced, mirroring the route's own lack of a cap, which is fine but worth a one-line note.

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #6152
Related work ⚠️ 3 scoped overlaps Top overlaps are listed below; lower-confidence bulk is hidden.
Change scope ❌ 8/20 High review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 143 registered-repo PR(s), 81 merged, 32 issue(s).
Contributor context ✅ Confirmed Gittensor contributor luciferlive112116; Gittensor profile; 143 PR(s), 32 issue(s).
Improvement ✅ Minor risk: clean · value: minor · LLM: moderate
Linked issue satisfaction

Addressed
The PR registers all five requested tools (loopover_list_pending_actions, loopover_decide_pending_action, loopover_set_agent_paused, loopover_set_action_autonomy, loopover_get_gate_precision) in loopover-mcp.js, each backed by the same REST endpoints via the existing apiGet/apiPost/apiFetch client, and leaves the remote server and CLI subcommands untouched as required.

Review context
Contributor next steps
  • Start here: Review top overlaps.
  • Then work through the remaining 3 steps in the Signals table above.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.

  • @loopover ask &lt;question&gt; answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat &lt;question&gt; answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.

  • Re-run LoopOver review

@loopover-orb loopover-orb Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LoopOver approves — the gate is satisfied and CI is green.

@loopover-orb
loopover-orb Bot merged commit aa83a48 into JSONbored:main Jul 16, 2026
16 checks passed
@github-actions github-actions Bot mentioned this pull request Jul 16, 2026
12 tasks
JSONbored pushed a commit that referenced this pull request Jul 21, 2026
… tool (#7877)

* feat(mcp): register loopover_get_outcome_calibration as a local stdio tool

Mirrors the 5 registerStdioTool siblings PR #6382 added (same
owner/repo/windowDays shape, same toolRepoBase helper), and reuses the
REST call the existing `maintain outcome-calibration` CLI subcommand
already makes -- no duplicated HTTP logic.

Bumps mcp-tool-rename-aliases.test.ts's hardcoded stdio tool-count
invariant from 79 to 80 to match.

Refs #7758.

* fix(ci): skip validate-tests-merge's global coverage threshold under scoped test selection

Scoped test selection (#ci-scoped-test-selection) runs vitest --changed
across all 3 shards for a miner/mcp/discoveryIndex/backend-only PR, so
merging those shards reconstructs only that narrow subset's coverage,
not the whole suite. validate-tests-merge's global 80% threshold check
assumed a merged-shard total always meant whole-suite coverage and
false-failed a fully-tested, scoped-selection PR as a result. Disables
the threshold in that same case, mirroring the per-shard
COVERAGE_NO_THRESHOLDS pattern already used above it -- Codecov's
patch gate still enforces real per-line coverage on the actual diff
regardless.
tryeverything24 added a commit to tryeverything24/loopover that referenced this pull request Jul 21, 2026
Closes JSONbored#7761

loopover_list_notifications was already registered as a remote MCP tool
(src/mcp/server.ts) with a CLI mirror (`notifications`, JSONbored#6745), but had
no local stdio MCP tool registration -- the same gap class PR JSONbored#6382
fixed for the 5 maintain-surface tools. Mirrors that pattern exactly:
a registerStdioTool block using stdioToolDescription for the
centralized description and toolResult for the response shape, placed
alongside the other loginShape-based contributor tools
(loopover_get_decision_pack / loopover_explain_repo_decision /
loopover_monitor_open_prs / loopover_pr_outcome).

The handler reuses getNotifications(login), the exact apiGet call the
existing `notifications` CLI command already makes, so there is one
HTTP call site for this route, not two.

Adds a stdio-proxy test to test/unit/mcp-cli-notifications.test.ts
following the same StdioClientTransport + fixture-server shape as the
sibling loginShape tools' own suites (mcp-cli-pr-outcomes.test.ts,
mcp-cli-monitor-open-prs.test.ts): registration, the one apiGet call,
and tool/CLI mirror parity. Bumps the pinned stdio tool count in
mcp-tool-rename-aliases.test.ts from 79 to 80.
tryeverything24 added a commit to tryeverything24/loopover that referenced this pull request Jul 21, 2026
Closes JSONbored#7753

Mirrors the exact registerStdioTool pattern PR JSONbored#6382 used for the 5
maintain-surface siblings (loopover_list_pending_actions et al): the
handler calls the same bare POST .../agent/pending-actions endpoint
`maintain propose` already calls, through the same apiPost client, and
its description comes from the same stdioToolDescription centralized
lookup. JSONbored#6744 added the route + CLI mirror without a stdio registration,
so it fell outside JSONbored#6152's batch despite being the same family.

The route's response always carries a fully-populated `action`
(id/actionClass/status set unconditionally, per src/api/routes.ts's
POST handler) -- only `created` genuinely varies, so that's the only
branch the handler formats defensively. New dedicated suite
(mcp-cli-propose-action-tool.test.ts) covers registration, the proxy
contract, both the "Staged"/"Already staged" branches, an API-failure
path, and pre-flight schema rejection -- following mcp-cli-maintain-
tools.test.ts's shape. Bumped the pinned stdio tool count 80 -> 81 in
mcp-tool-rename-aliases.test.ts (rebased past JSONbored#7877's own 79 -> 80 bump).
RealDiligent added a commit to RealDiligent/gittensory that referenced this pull request Jul 21, 2026
Mirror the maintain-family registerStdioTool pattern (JSONbored#6382/JSONbored#7877) and reuse
the same REST endpoint the existing `maintain automation-state` CLI already
calls. Bump the stdio tool-count invariant to 83 (correcting the stale pin
left after JSONbored#7887).

Closes JSONbored#7752.

Co-authored-by: Cursor <cursoragent@cursor.com>
tryeverything24 added a commit to tryeverything24/loopover that referenced this pull request Jul 21, 2026
Closes JSONbored#7761

loopover_list_notifications was already registered as a remote MCP tool
(src/mcp/server.ts) with a CLI mirror (`notifications`, JSONbored#6745), but had
no local stdio MCP tool registration -- the same gap class PR JSONbored#6382
fixed for the 5 maintain-surface tools. Mirrors that pattern exactly:
a registerStdioTool block using stdioToolDescription for the
centralized description and toolResult for the response shape, placed
alongside the other loginShape-based contributor tools
(loopover_get_decision_pack / loopover_explain_repo_decision /
loopover_monitor_open_prs / loopover_pr_outcome).

The handler reuses getNotifications(login), the exact apiGet call the
existing `notifications` CLI command already makes, so there is one
HTTP call site for this route, not two.

Adds a stdio-proxy test to test/unit/mcp-cli-notifications.test.ts
following the same StdioClientTransport + fixture-server shape as the
sibling loginShape tools' own suites (mcp-cli-pr-outcomes.test.ts,
mcp-cli-monitor-open-prs.test.ts): registration, the one apiGet call,
and tool/CLI mirror parity. Bumps the pinned stdio tool count in
mcp-tool-rename-aliases.test.ts from 79 to 80.
tryeverything24 added a commit to tryeverything24/loopover that referenced this pull request Jul 21, 2026
Closes JSONbored#7753

Mirrors the exact registerStdioTool pattern PR JSONbored#6382 used for the 5
maintain-surface siblings (loopover_list_pending_actions et al): the
handler calls the same bare POST .../agent/pending-actions endpoint
`maintain propose` already calls, through the same apiPost client, and
its description comes from the same stdioToolDescription centralized
lookup. JSONbored#6744 added the route + CLI mirror without a stdio registration,
so it fell outside JSONbored#6152's batch despite being the same family.

The route's response always carries a fully-populated `action`
(id/actionClass/status set unconditionally, per src/api/routes.ts's
POST handler) -- only `created` genuinely varies, so that's the only
branch the handler formats defensively. New dedicated suite
(mcp-cli-propose-action-tool.test.ts) covers registration, the proxy
contract, both the "Staged"/"Already staged" branches, an API-failure
path, and pre-flight schema rejection -- following mcp-cli-maintain-
tools.test.ts's shape. Bumped the pinned stdio tool count 80 -> 81 in
mcp-tool-rename-aliases.test.ts (rebased past JSONbored#7877's own 79 -> 80 bump).
tryeverything24 added a commit to tryeverything24/loopover that referenced this pull request Jul 21, 2026
Closes JSONbored#7753

Mirrors the exact registerStdioTool pattern PR JSONbored#6382 used for the 5
maintain-surface siblings (loopover_list_pending_actions et al): the
handler calls the same bare POST .../agent/pending-actions endpoint
`maintain propose` already calls, through the same apiPost client, and
its description comes from the same stdioToolDescription centralized
lookup. JSONbored#6744 added the route + CLI mirror without a stdio registration,
so it fell outside JSONbored#6152's batch despite being the same family.

The route's response always carries a fully-populated `action`
(id/actionClass/status set unconditionally, per src/api/routes.ts's
POST handler) -- only `created` genuinely varies, so that's the only
branch the handler formats defensively. New dedicated suite
(mcp-cli-propose-action-tool.test.ts) covers registration, the proxy
contract, both the "Staged"/"Already staged" branches, an API-failure
path, and pre-flight schema rejection -- following mcp-cli-maintain-
tools.test.ts's shape. Bumped the pinned stdio tool count 80 -> 81 in
mcp-tool-rename-aliases.test.ts (rebased past JSONbored#7877's own 79 -> 80 bump).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(mcp): register loopover-mcp maintain queue/approve/reject/pause/resume/set-level/precision as MCP tools (CLI-only today)

1 participant