forgejo-mcp: the Agent client half (#23) and the token-scope boundary (#24) #56

Merged
pit merged 4 commits from hermes/17-forgejo-mcp into main 2026-10-07 20:14:13 +00:00
Owner

Summary

Implements the #17 spec's two remaining tickets on one integration branch.

 docs/
 ├── adr/
+│   ├── 0005-agent-token-scope.md      # #24: the capability boundary
+│   └── index.md                       # + the 0005 row
 └── runbooks/
+    ├── index.md                       # 0001's Covers cell names the client half
+    └── 0001-deploy-and-rollback.md    # #23: the Agent client half

#23 adds "The Agent side: the MCP client" to the deploy runbook (§4): the mcp package and its mcp.client.streamable_http import path, the ~/.hermes/config.yaml mcp_servers entry carrying the caller's own token in the Authorization header, the restart with no hot reload, and that the round-trip survives the Edge being down because the Service reaches Forgejo over the Guest's loopback.

#24 adds ADR 0005: per-Agent Forgejo tokens; write:issue for triage (plus read:organization, which the label tools need — recorded honestly); read:repository for PR/code browsing; write:repository refused; the credential-less Service enforces no authorisation, so the token scope is the only boundary.

Evidence

  • #23 round-trip (live, against the PROD endpoint from a LAN host): initialize -> Forgejo MCP Server 3.2.0, tools/list -> 156 tools, list_repo_issues -> isError=False with real pit/infra-forge issues (#49, #24, #23), get_issue_by_index(23) returns the ticket.
  • #17 stories 7–9 (live read-only, PROD endpoint): list_repo_pull_requests returns PR #56; get_pull_request_diff(#48) returns the diff; get_file_content(README.md) and list_repo_contents return real tree content; list_workflow_runs returns run #1 (success).
  • Loopback / no Edge in the path: the unit is --url http://127.0.0.1:3080/; live ss -tn during a held LAN session shows 127.0.0.1 -> 127.0.0.1:3080 and no non-loopback upstream. tofu/npm/forgejo.tfvars declares only the :22 stream and the :3080 proxy host — no :8089 rule — so the Edge is provably not in the path. PRE has no Edge entry at all, so a full round-trip against it is the same fact live. The PROD Edge was UP and was deliberately not taken down; #23's literal "Edge down" criterion is therefore met by substitution (PRE round-trip + static no-rule + loopback socket), not literally.
  • #24 capability seam (live, PRE endpoint on pit/mcp-probe): list_repo_issues succeeds under write:issue; the same call is refused under a read:repository-only token; create_file and merge_pull_request are refused under write:issue.

Review

Two-axis /code-review (Standards + Spec) on the diff. No hard documented-standard violation. Actionable findings fixed in 9fdb0e4: the client subsection no longer restates the credential-less claim above it, and the 0001 "Covers" row now names the Agent MCP-client half. Remaining, stated plainly: the triage grant is write:issue,read:organization rather than write:issue alone (the label tools call GET /orgs/<owner>/labels); and #23's Edge-down criterion is met by substitution because the Edge is healthy.

Merge Danger

Door: two-way — docs only; git revert drops both files and the runbook section.

Blast Radius: docs. No role, template, unit, Stack or secret changed; nothing to rehearse or deploy.

Closes #23
Closes #24

## Summary Implements the #17 spec's two remaining tickets on one integration branch. ```diff docs/ ├── adr/ +│ ├── 0005-agent-token-scope.md # #24: the capability boundary +│ └── index.md # + the 0005 row └── runbooks/ + ├── index.md # 0001's Covers cell names the client half + └── 0001-deploy-and-rollback.md # #23: the Agent client half ``` **#23** adds "The Agent side: the MCP client" to the deploy runbook (§4): the `mcp` package and its `mcp.client.streamable_http` import path, the `~/.hermes/config.yaml` `mcp_servers` entry carrying the caller's own token in the `Authorization` header, the restart with no hot reload, and that the round-trip survives the Edge being down because the Service reaches Forgejo over the Guest's loopback. **#24** adds ADR 0005: per-Agent Forgejo tokens; `write:issue` for triage (plus `read:organization`, which the label tools need — recorded honestly); `read:repository` for PR/code browsing; `write:repository` refused; the credential-less Service enforces no authorisation, so the token scope is the only boundary. ## Evidence - **#23 round-trip** (live, against the PROD endpoint from a LAN host): `initialize -> Forgejo MCP Server 3.2.0`, `tools/list -> 156 tools`, `list_repo_issues -> isError=False` with real `pit/infra-forge` issues (#49, #24, #23), `get_issue_by_index(23)` returns the ticket. - **#17 stories 7–9** (live read-only, PROD endpoint): `list_repo_pull_requests` returns PR #56; `get_pull_request_diff(#48)` returns the diff; `get_file_content(README.md)` and `list_repo_contents` return real tree content; `list_workflow_runs` returns run #1 (success). - **Loopback / no Edge in the path**: the unit is `--url http://127.0.0.1:3080/`; live `ss -tn` during a held LAN session shows `127.0.0.1 -> 127.0.0.1:3080` and no non-loopback upstream. `tofu/npm/forgejo.tfvars` declares only the `:22` stream and the `:3080` proxy host — no `:8089` rule — so the Edge is provably not in the path. PRE has no Edge entry at all, so a full round-trip against it is the same fact live. **The PROD Edge was UP and was deliberately not taken down**; #23's literal "Edge down" criterion is therefore met by substitution (PRE round-trip + static no-rule + loopback socket), not literally. - **#24 capability seam** (live, PRE endpoint on `pit/mcp-probe`): `list_repo_issues` succeeds under `write:issue`; the same call is refused under a `read:repository`-only token; `create_file` and `merge_pull_request` are refused under `write:issue`. ## Review Two-axis `/code-review` (Standards + Spec) on the diff. No hard documented-standard violation. Actionable findings fixed in 9fdb0e4: the client subsection no longer restates the credential-less claim above it, and the 0001 "Covers" row now names the Agent MCP-client half. Remaining, stated plainly: the triage grant is `write:issue,read:organization` rather than `write:issue` alone (the label tools call `GET /orgs/<owner>/labels`); and #23's Edge-down criterion is met by substitution because the Edge is healthy. ## Merge Danger **Door:** two-way — docs only; `git revert` drops both files and the runbook section. **Blast Radius:** docs. No role, template, unit, Stack or secret changed; nothing to rehearse or deploy. Closes #23 Closes #24
Per-Agent Forgejo tokens, write:issue for triage (read:organization added for the label tools), read:repository to browse PRs and code, write:repository refused. The credential-less Service enforces no authorisation, so the token is the only boundary.
Document how a LAN Agent reaches the credential-less endpoint: the mcp
package and its Streamable HTTP import path, the ~/.hermes/config.yaml
mcp_servers entry carrying the caller's own token, the restart with no hot
reload, and that the round-trip survives the Edge being down because the
Service reaches Forgejo over the Guest's loopback.
pit changed title from forgejo-mcp: the Agent client half (#23) and the token-scope boundary (#24) to WIP: forgejo-mcp: the Agent client half (#23) and the token-scope boundary (#24) 2026-10-07 17:01:00 +00:00
pit changed title from WIP: forgejo-mcp: the Agent client half (#23) and the token-scope boundary (#24) to forgejo-mcp: the Agent client half (#23) and the token-scope boundary (#24) 2026-10-07 17:01:03 +00:00
pit changed title from forgejo-mcp: the Agent client half (#23) and the token-scope boundary (#24) to WIP: forgejo-mcp: the Agent client half (#23) and the token-scope boundary (#24) 2026-10-07 17:01:06 +00:00
The 0001 index row now covers the Agent side too, and the client
subsection states the header token as the client's own concern rather
than restating the credential-less Service property already stated
above.
pit changed title from WIP: forgejo-mcp: the Agent client half (#23) and the token-scope boundary (#24) to forgejo-mcp: the Agent client half (#23) and the token-scope boundary (#24) 2026-10-07 17:04:11 +00:00
pit merged commit bd0906cfa4 into main 2026-10-07 20:14:13 +00:00
pit deleted branch hermes/17-forgejo-mcp 2026-10-07 20:14:13 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
olympus/infra-forge!56
No description provided.