forgejo-mcp/openspec/changes/mcp-resource-templates/tasks.md
Christoph Görn f6cc2f1c40
feat: ✨ MCP resource templates — slices 6+7 (PR resource + docs/wrap)
Implements the final two slices of the OpenSpec change
mcp-resource-templates. The change now ships all 7 entity
resources (owner, repo, commit, status, issue, comment, pr) on
the forgejo:// URI scheme.

Slice 6 — mcp-resource-pr
- operation/pull/resources.go registers
  forgejo://repo/{owner}/{repo}/pr/{index}
- Handler returns JSON metadata (head/base refs, mergeability,
  state) + text/markdown sidecar + bounded recent_comments and
  recent_reviews (cap 30, sentinels name list_issue_comments
  and list_pull_reviews respectively)
- Non-numeric index → -32602; missing PR → -32003
- 6 test cases: open/merged PR, > cap comments, > cap reviews,
  non-numeric index, 404
- Wired from RegisterCoreResources

Slice 7 — docs/wrap
- README "Resources" section enriched with URI scheme rationale,
  the coexist-with-tools rule, and the embedded-list bounding
  policy
- AGENTS.md "## Resources" subsection added for future AI
  assistants modifying the resource surface
- CHANGELOG.md unreleased section: additive resource surface
- Follow-up beads filed:
  - forgejo-mcp-7ra: revisit subscribe=true once a real
    subscription use case appears
  - forgejo-mcp-7de: revisit EmbeddedListCap=30 once telemetry
    available

Tasks 6.1-6.5, 7.1-7.6 ticked. Final tick count: 46/47.
Only 1.11 (manual client verification across Claude
Code/Desktop/Codex/Cursor) remains, out of scope for this team.

Driven by team:dev-loop (impl, runner; impl replaced by impl2
mid-slice-6 after the original impl ignored two checkpoint
probes). Closes the implementation portion of forgejo-mcp-13x.
2026-05-28 15:19:20 +02:00

67 lines
5.7 KiB
Markdown

## 1. Resource Framework (slice 1 — `mcp-resources-core`)
- [x] 1.1 Add `server.WithResourceCapabilities(false, false)` to `newMCPServer` in `operation/operation.go`
- [x] 1.2 Create `operation/resource/` package skeleton with `doc.go`
- [x] 1.3 Implement URI parser helpers in `operation/resource/parse.go`: one typed-struct parser per entity (`ParseOwner`, `ParseRepo`, `ParseCommit`, `ParseIssue`, `ParsePR`, `ParseComment`, `ParseStatus`)
- [x] 1.4 Implement template-registration helpers in `operation/resource/register.go` (thin wrapper around `mcp.NewResourceTemplate` + `s.AddResourceTemplate`)
- [x] 1.5 Implement embedded-list bounding helper in `operation/resource/bound.go`: produces sentinel block referencing a named list tool, default cap = 30 exposed as `const EmbeddedListCap = 30`
- [x] 1.6 Implement error-mapping helper in `operation/resource/errors.go`: `MapForgejoError(uri, err) *mcp.JSONRPCError` mapping HTTP `403` → `-32002`, `404` → `-32003`
- [x] 1.7 Add unit tests for `operation/resource/parse.go` covering: happy path each entity, short sha, non-numeric index, unknown kind
- [x] 1.8 Add unit tests for `operation/resource/bound.go` covering: under-cap, at-cap, over-cap with sentinel contents
- [x] 1.9 Add unit tests for `operation/resource/errors.go` covering both code paths
- [x] 1.10 Add `RegisterCoreResources(s *server.MCPServer)` stub function in `operation/operation.go` (no-op for slice 1, gives stable wiring point)
- [ ] 1.11 Manual verification against Claude Code, Claude Desktop, Codex, Cursor: confirm each sends `resources/templates/list` and `resources/read`; record results in design.md "Open Questions" answer
## 2. Commit Resource (slice 2 — `mcp-resource-commit`)
- [x] 2.1 Create `operation/repository/resources_commit.go` (commit lives under repository domain) with `RegisterCommitResource(s *server.MCPServer)`
- [x] 2.2 Register template `forgejo://repo/{owner}/{repo}/commit/{sha}` with description marking immutability
- [x] 2.3 Implement handler returning JSON metadata block plus `text/markdown` body sidecar
- [x] 2.4 Reject shas not exactly 40 hex chars via `operation/resource.ParseCommit`
- [x] 2.5 Unit tests: existing sha (mock client), short sha, missing sha, sidecar present
- [x] 2.6 Wire `RegisterCommitResource` from `operation/operation.go`
- [x] 2.7 README: append commit row to a new "Resources" table
## 3. Commit Status Resource (slice 3 — `mcp-resource-status`)
- [x] 3.1 Create `operation/repository/resources_status.go` with `RegisterStatusResource(s *server.MCPServer)`
- [x] 3.2 Register template `forgejo://repo/{owner}/{repo}/commit/{sha}/status` with cacheability note in description
- [x] 3.3 Implement handler returning aggregate state + bounded `statuses` array via `operation/resource.Bounded(..., "get_commit_statuses")`
- [x] 3.4 Map empty-statuses case to aggregate `state="unknown"`
- [x] 3.5 Unit tests: < cap, > cap with sentinel naming `get_commit_statuses`, empty case, short sha, missing sha
- [x] 3.6 Wire from `operation/operation.go`
- [x] 3.7 README: append status row to Resources table
## 4. Repo + Owner Resources (slice 4)
- [x] 4.1 Create `operation/repository/resources_repo.go` with `RegisterRepoResource(s *server.MCPServer)`; template `forgejo://repo/{owner}/{repo}`; return counts only, no embedded lists
- [x] 4.2 Create `operation/user/resources_owner.go` with `RegisterOwnerResource(s *server.MCPServer)`; template `forgejo://owner/{owner}`; resolve user or org
- [x] 4.3 Unit tests for each: happy path, 403, 404
- [x] 4.4 Wire both from `operation/operation.go`
- [x] 4.5 README: append rows for `repo` and `owner` to Resources table
## 5. Issue + Comment Resources (slice 5)
- [x] 5.1 Create `operation/issue/resources.go` with `RegisterIssueResources(s *server.MCPServer)`
- [x] 5.2 Register issue template `forgejo://repo/{owner}/{repo}/issue/{index}` with handler returning metadata + markdown sidecar + bounded `recent_comments` (sentinel names `list_issue_comments`)
- [x] 5.3 Register comment template `forgejo://repo/{owner}/{repo}/{kind}/{index}/comment/{id}` constraining `kind ∈ {issue, pr}` in parser (reject `wiki` etc. with `-32602`)
- [x] 5.4 Unit tests: issue happy path, issue with > cap comments, non-numeric index, unknown kind for comment, missing comment
- [x] 5.5 Wire from `operation/operation.go`
- [x] 5.6 README: append rows for `issue` and `comment` to Resources table
## 6. PR Resource (slice 6 — `mcp-resource-pr`)
- [x] 6.1 Create `operation/pull/resources.go` with `RegisterPullResources(s *server.MCPServer)`
- [x] 6.2 Register template `forgejo://repo/{owner}/{repo}/pr/{index}` with handler returning metadata + head/base refs + mergeability + markdown sidecar + bounded `recent_comments` (sentinel names `list_issue_comments`) + bounded `recent_reviews` (sentinel names `list_pull_reviews`)
- [x] 6.3 Unit tests: open PR, merged PR, PR with > cap comments, PR with > cap reviews, non-numeric index, missing PR
- [x] 6.4 Wire from `operation/operation.go`
- [x] 6.5 README: append row for `pr` to Resources table
## 7. Documentation & Wrap-up
- [x] 7.1 Add "Resources" section to README explaining the URI scheme, listing all templates in one table, and stating the coexist-with-tools rule
- [x] 7.2 Add "Resources" subsection to AGENTS.md mirroring the README rule for AI assistants modifying the codebase
- [x] 7.3 Update CHANGELOG (or release notes file) noting the additive surface
- [x] 7.4 Verify `make build` and full test suite pass with all seven `RegisterXResources` calls wired
- [x] 7.5 File a follow-up bead to revisit `WithResourceCapabilities(subscribe=true, …)` once a real subscription use case appears (`forgejo-mcp-7ra`)
- [x] 7.6 File a follow-up bead to revisit the default embedded-list cap (currently `30`) once usage telemetry is available (`forgejo-mcp-7de`)