feat(mcp): expose git status/sync/log/pull as direct MCP tools (#905)#1363
Merged
Conversation
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>
…e-905 # Conflicts: # docs/memory/feature-flows.md
vybe
approved these changes
Jun 27, 2026
vybe
left a comment
Contributor
There was a problem hiding this comment.
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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_agentround-trip. Newgit.tsmodule with six tools, each proxying an existing/api/agents/{name}/git/*endpoint — no new backend routes.get_git_statusGET …/git/statusget_git_logGET …/git/loglimitclamped 1–100get_git_sync_stateGET …/git/sync-stategit_syncPOST …/git/syncgit_pullPOST …/git/pullreset_to_main_preserve_statePOST …/git/reset-to-main-preserve-stateCloses #905.
Design decisions
{conflict_type, conflict_class, hint}object pointing back tochat_with_agent— the tools never auto-merge. Implemented via a new typedApiErrorinclient.tscarrying.status/.conflictType/.conflictClass;.messageshape unchanged for back-compat.agent_permissions, arch §5), so the{self} ∪ permittedgate is enforced ingit.ts(mirrorsoperator_queue.ts). Mutating ops areOwnedAgentByNameon the backend, so a shared (non-owner) key gets read + pull only.requestId = randomUUID(), stamps it on itsmcp_operationaudit row via the newToolCallContext, and forwards it asX-Request-ID. The backend's request-id middleware adopts the incoming header, so the resultinggit_operationrow carries the same id —GET /api/audit-log?request_id=<id>returns both rows. Also recovers the previously-droppedtarget_id/request_idon the MCP row for all tools (resolveTargetId()→agent_name ?? name).routers/git.py_audit_git()now logs the failure paths (409 conflict / 400 / 500) ofsync/pull/resettoo — previously only the success path was audited. One row per handler exit.Three-surface sync (Invariant #13)
routers/git.py(audit helper + failure-path coverage +Requestinjection on reset),db/audit.py+routers/audit_log.py+models.InternalAuditRequest(request_idfilter/field),routers/internal.py(forwardrequest_id).tools/git.ts, sixclient.tsproxy methods + typedApiError,audit.tsToolCallContext/resolveTargetId,server.tsregistration.Testing
src/mcp-server:npx tsc --noEmitclean; 73/73 node tests pass (newgit.test.ts— requestId mint/stamp/forward, a2a gate, 409 conflict surfacing, log clamp; newaudit.test.ts— read-after-execute requestId).tests/test_audit_log_unit.py57/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,InternalAuditRequestcarriesrequest_id)./cso --diffreport committed underdocs/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.mdindex + newfeature-flows/mcp-git-tools.md+audit-trail.mdPhase 6.Out of scope
A
trinity syncCLI command (this unblocks the MCP surface); arbitrary git config / branch / rebase / merge-conflict resolution (stays inchat_with_agent).🤖 Generated with Claude Code