feat(mcp): dir= on madt_api_call — the escape hatch can't escape the launch scope #87
Labels
No labels
breaking
bug
documentation
enhancement
epic
good first issue
help wanted
refactoring
resolution/duplicate
resolution/invalid
resolution/wontfix
security
severity/critical
severity/high
severity/low
severity/medium
status/abandoned
status/blocked
status/needs-decision
status/needs-info
status/needs-verification
testing
upstream
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
sixfold-space/madtea#87
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
madt_api_callis the escape hatch, but it cannot escape the MCP server's launch scope. Its input struct is method/endpoint/body/raw/jq/template/paginate only (internal/mcp/tools_api.go:20) and it resolves credentials viaconfig.GetCredentials(ctx)against the server's launch environment (env vars → the launch checkout's git config → global). There is nodir=, unlike the git-plane tools (madt_commit,madt_finish,madt_worktrees, …).Concrete case (2026-07-15): I was in an unauthed scratch checkout of madtea and needed one read against Codeberg -
GET /user/gpg_keysas the identity configured in a correctly-authed local clone of madtea.madt_api_call→not configured - run 'madtea auth login' first. Dead end on the MCP surface.cd <authed-clone> && madtea api /user/gpg_keys) - exactly what the server instructions tell agents NOT to do, and what the CLI-steering hook (ADR 0009) blocks in stricter setups.The same dead end then forced the whole follow-up (issue search, view, comment) through the CLI too — one missing param turned an MCP session into a shell session.
Why
owner_repo=can never substitute hereThe passthrough hits endpoints with no repo scope at all (
/user/gpg_keys,/user/keys,/orgs/...,/admin/...).dir=onmadt_api_callis purely a credential/forge-resolution selector — "call forge X as the identity configured in clone Y" — not a repo scoper. That also makes it a clean data point for the #62 matrix: a remote-API tool whose correct scope isdir=, notRepoScope.Ask
Add
dir=tomadt_api_call: resolve base URL + token from that clone's credential scope (the ADR 0025config.GetConfigForDirmachinery the git plane already uses), falling back to today's launch-scope resolution when omitted.Notes:
dir=here reads config only. Document that distinction on the param.madtea api) already gets this for free by being run from the target dir; MCP parity is the whole point.Acceptance
madt_api_call dir=/path/to/clone endpoint=/user/gpg_keyssucceeds from a session whose launch checkout is unauthed or bound to a different forge, using the clone's forge + token.dir=behaves exactly as today.Refs #59 (typed API tools dir= targeting — this is the passthrough sibling; see also the 2026-07-15 comment there), #62 (DirScope matrix: classify api_call as dir=-scoped).
Won't-do — inverted by maintainer ruling the same day (2026-07-15): an agent must be PREVENTED from crossing outside the launch pwd and using auth tokens it finds in other checkouts, so dir=-as-credential-selector on the passthrough is an anti-goal, not a feature. The underlying need (unauthed session can't reach the forge) is now tracked by the sanctioned remedies instead: #88 (loud connect-time warning) and #89 (in-MCP OAuth login for the launch scope). The boundary this ruling defines is enforced via #90.