Skip to content

feat(mcp): expose git status/sync/log/pull as direct MCP tools (#905)#1363

Merged
vybe merged 2 commits into
devfrom
AndriiPasternak31/issue-905
Jun 27, 2026
Merged

feat(mcp): expose git status/sync/log/pull as direct MCP tools (#905)#1363
vybe merged 2 commits into
devfrom
AndriiPasternak31/issue-905

Conversation

@AndriiPasternak31

Copy link
Copy Markdown
Contributor

Summary

Exposes the agent git surface as direct, deterministic (non-LLM) MCP tools so an orchestrator (CLI, local trinity sync, scripts) can run git without a chat_with_agent round-trip. New git.ts module with six tools, each proxying an existing /api/agents/{name}/git/* endpoint — no new backend routes.

Tool Endpoint Notes
get_git_status GET …/git/status read-only
get_git_log GET …/git/log read-only, limit clamped 1–100
get_git_sync_state GET …/git/sync-state read-only (#389)
git_sync POST …/git/sync owner-only
git_pull POST …/git/pull owner + shared
reset_to_main_preserve_state POST …/git/reset-to-main-preserve-state ⚠️ destructive, owner-only (#384)

Closes #905.

Design decisions

  • Conflicts stay LLM-mediated. A 409 is surfaced as a structured {conflict_type, conflict_class, hint} object pointing back to chat_with_agent — the tools never auto-merge. Implemented via a new typed ApiError in client.ts carrying .status/.conflictType/.conflictClass; .message shape unchanged for back-compat.
  • Agent-to-agent gate lives in the MCP layer. The backend resolves an agent-scoped key to its owner and applies ownership checks only (it does not apply agent_permissions, arch §5), so the {self} ∪ permitted gate is enforced in git.ts (mirrors operator_queue.ts). Mutating ops are OwnedAgentByName on the backend, so a shared (non-owner) key gets read + pull only.
  • Cross-surface audit correlation (feat(mcp): expose git status/sync/log/pull as direct MCP tools (bypass LLM) #905). Each tool mints requestId = randomUUID(), stamps it on its mcp_operation audit row via the new ToolCallContext, and forwards it as X-Request-ID. The backend's request-id middleware adopts the incoming header, so the resulting git_operation row carries the same id — GET /api/audit-log?request_id=<id> returns both rows. Also recovers the previously-dropped target_id/request_id on the MCP row for all tools (resolveTargetId()agent_name ?? name).
  • Symmetric backend auditing. routers/git.py _audit_git() now logs the failure paths (409 conflict / 400 / 500) of sync/pull/reset too — previously only the success path was audited. One row per handler exit.

Three-surface sync (Invariant #13)

  • Backend: routers/git.py (audit helper + failure-path coverage + Request injection on reset), db/audit.py + routers/audit_log.py + models.InternalAuditRequest (request_id filter/field), routers/internal.py (forward request_id).
  • MCP server: new tools/git.ts, six client.ts proxy methods + typed ApiError, audit.ts ToolCallContext/resolveTargetId, server.ts registration.
  • Agent server: unchanged — endpoints already mirrored.

Testing

  • src/mcp-server: npx tsc --noEmit clean; 73/73 node tests pass (new git.test.ts — requestId mint/stamp/forward, a2a gate, 409 conflict surfacing, log clamp; new audit.test.ts — read-after-execute requestId).
  • Backend: tests/test_audit_log_unit.py 57/57 pass (4 new feat(mcp): expose git status/sync/log/pull as direct MCP tools (bypass LLM) #905 cases: request_id correlation returns both rows, filter isolation, failure-path audited, InternalAuditRequest carries request_id).
  • Security: /cso --diff report committed under docs/security-reports/cso-2026-06-27-905-diff.{md,json}.

Docs

architecture.md (MCP tools table + audit coverage note), TRINITY_COMPATIBLE_AGENT_GUIDE.md (direct-git surface section), feature-flows.md index + new feature-flows/mcp-git-tools.md + audit-trail.md Phase 6.

Out of scope

A trinity sync CLI command (this unblocks the MCP surface); arbitrary git config / branch / rebase / merge-conflict resolution (stays in chat_with_agent).

🤖 Generated with Claude Code

Add a `git.ts` MCP tool module exposing the agent git surface as direct,
deterministic (non-LLM) tools, so an orchestrator can run git without a
`chat_with_agent` round-trip: get_git_status, git_sync, get_git_log,
git_pull, get_git_sync_state, reset_to_main_preserve_state. Each proxies an
existing `/api/agents/{name}/git/*` endpoint via new TrinityClient methods.

Conflicts stay LLM-mediated: a 409 is surfaced as a structured
{conflict_type, conflict_class, hint} via a new typed `ApiError` in
client.ts (carries .status/.conflictType/.conflictClass; .message unchanged
for back-compat), pointing the caller back to chat_with_agent. The
{self} ∪ permitted agent-to-agent gate lives in git.ts (backend resolves an
agent-scoped key to its owner and skips agent_permissions, arch §5);
mutating ops (git_sync/reset) are OwnedAgentByName so a shared key gets
read+pull only.

Audit correlation: each tool mints requestId = randomUUID(), stamps it on
its mcp_operation row via the new ToolCallContext, AND forwards it as
X-Request-ID; the backend adopts the header onto the git_operation row, so
GET /api/audit-log?request_id=<id> returns both rows. Also recovers the
previously-dropped target_id/request_id on the MCP row for ALL tools
(resolveTargetId() -> agent_name ?? name). Backend `_audit_git()` now audits
the failure paths (409/400/500) of sync/pull/reset symmetrically;
db/audit.py + routers/audit_log.py + InternalAuditRequest gain the
request_id filter/field.

Tests: new git.test.ts + audit.test.ts (MCP), 4 new cases in
test_audit_log_unit.py. Docs: architecture.md MCP table + audit note,
TRINITY_COMPATIBLE_AGENT_GUIDE.md, feature-flows index + new
mcp-git-tools.md + audit-trail.md Phase 6.

Closes #905

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@AndriiPasternak31
AndriiPasternak31 marked this pull request as ready for review June 27, 2026 18:48
@AndriiPasternak31
AndriiPasternak31 requested a review from dolho June 27, 2026 18:48
@AndriiPasternak31 AndriiPasternak31 self-assigned this Jun 27, 2026
@AndriiPasternak31
AndriiPasternak31 requested a review from vybe June 27, 2026 18:48
…e-905

# Conflicts:
#	docs/memory/feature-flows.md

@vybe vybe 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.

Approved via /validate-pr: clean MCP git-tools feature. 6 new tools proxy existing /api/agents/{name}/git/* endpoints (no new routes); auth boundaries correct (read=AuthorizedAgent, mutating sync/reset=OwnedAgent); a2a {self}∪permitted gate mirrors operator_queue.ts; no git-arg injection (zod-enum strategy, clamped limit, encodeURIComponent name); request_id audit correlation joins mcp_operation↔git_operation rows; failure paths audited symmetrically. Three-surface sync (Inv #13) intact, docs+tests substantive. Resolved a feature-flows.md index conflict against #1364 (kept both rows).

@vybe
vybe merged commit f4bcb4f into dev Jun 27, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants