forgejo-mcp/openspec/changes/add-wiki-support/proposal.md
Christoph Görn de6229d973
docs: 📝 apply convergence-round refinements to add-wiki-support
Second debate round (convergence) hardened the C-series fixes:
- C2: limit+1 over-fetch is unsound at the server page-size ceiling — send
  limit+1 explicit, compute has_next from effective rows, pin limit<=ceiling-1
  (task 5.3 extended)
- C5: encoded-slash sub-page round-trip is server-dependent (proxies/AllowEncodedSlashes
  may reject %2F) — documented fallback to get_wiki_page tool (new task 5.7);
  ParseWiki MUST read EscapedPath, never splitPath(u.Path); test asserts RawPath populated
- C7/C3: parenthetical pinning that resource degrade-to-empty (page-GET-first)
  does not collide with the revisions tool's 404-is-missing-page
- 2xx-vs-204 delete drift: lifted demo.md + proposal.md to '2xx (204 canonical)'
  to match spec/tasks
- C6 doc-policy task 4.6: extend output-bounding.md to resource content blocks

openspec validate --strict passes.

Refs: #32
2026-05-31 15:49:42 +02:00

96 lines
6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## Why
Codeberg issue [#32](https://codeberg.org/goern/forgejo-mcp/issues/32) asks for wiki
support. The repository already ships `operation/wiki/wiki.go`, but it is dead code:
it sits behind a `//go:build wiki` tag and calls `client.ListWikiPages`,
`client.CreateWikiPage`, `client.EditWikiPage`, `CreateWikiPageOption`, and
`EditWikiPageOption` — **none of which exist in `forgejo-sdk/forgejo/v3`** (verified:
the SDK has no `wiki*.go` and defines no wiki types or methods). The build tag exists
solely to stop the broken file from breaking `make build`. That is why the issue
carries `Status/Blocked`.
The prior plan (`docs/plans/wiki-support.md`) proposed unblocking by contributing wiki
methods upstream to `forgejo-sdk` first — a 1–2 week external dependency on someone
else's review cycle. We can unblock today instead: `pkg/forgejo/rawhttp.go` already
provides `DoJSON` / `DoJSONList` helpers (built for issue attachments when the SDK fell
short) that hit the Forgejo REST API directly with the same auth, user-agent, and
error-mapping the SDK path uses. Wiki is the identical situation, so we use the same
escape hatch. No upstream dependency, no new external module.
The Forgejo/Gitea wiki API is stable and documented. Implementing against it directly
gives us all six wiki operations now, plus a `forgejo://` resource template so humans
and agents can reference a wiki page by URI without an explicit tool call.
## What Changes
- **Replace the broken SDK-based wiki package with a direct-API implementation.** Remove
the `//go:build wiki` tag from `operation/wiki/wiki.go`; reimplement all handlers on
top of `forgejo.DoJSON` / `forgejo.DoJSONList` (raw REST), dropping the
`forgejo-sdk` wiki imports entirely. Register the package from
`operation/operation.go` (it is currently never wired in).
- **Ship six wiki tools** (the create/update tools already exist in name; this change
makes them compile and adds three read tools + delete):
| Tool | Method + endpoint | Notes |
|------|-------------------|-------|
| `list_wiki_pages` | `GET /repos/{o}/{r}/wiki/pages` | Bounded: `page` + `limit`, `has_next` |
| `get_wiki_page` | `GET /repos/{o}/{r}/wiki/page/{pageName}` | base64-decoded content; `start_line`/`end_line` bound + `total_lines` |
| `get_wiki_revisions` | `GET /repos/{o}/{r}/wiki/revisions/{pageName}` | Bounded: `page` + `limit` |
| `create_wiki_page` | `POST /repos/{o}/{r}/wiki/new` | base64-encodes content |
| `update_wiki_page` | `PATCH /repos/{o}/{r}/wiki/page/{pageName}` | base64-encodes content |
| `delete_wiki_page` | `DELETE /repos/{o}/{r}/wiki/page/{pageName}` | 2xx → success (204 canonical) |
- **Add a wiki-page resource template** `forgejo://repo/{owner}/{repo}/wiki/{pageName}`
returning page metadata + a `text/markdown` content sidecar (decoded) + a bounded
`recent_revisions` array (cap 30, sentinel names `get_wiki_revisions`). This lets a
page name dropped in chat resolve to its content with no tool call, consistent with
the existing issue/pr/commit resources.
- **Honor the output-bounding contract** (`docs/design/output-bounding.md`) on every new
tool: `list_wiki_pages` and `get_wiki_revisions` use `page`/`limit` paging;
`get_wiki_page` content is data-proportional so it reuses the `start_line`/`end_line`
line-range vocabulary from `get_file_content` and returns `total_lines` for
resumability.
- **Make the feature discoverable to humans and agents** (explicit issue requirement):
a new **Wiki** section in the README tool table, a wiki row in the README Resources
table, a Wiki note in `AGENTS.md`, rich per-tool/per-parameter descriptions, and an
update to `docs/plans/wiki-support.md` recording that the SDK-contribution path is
superseded by direct API calls. The change also removes the `Status/Blocked` label
from issue #32 on archive.
- **Ship a showboat demo** (explicit issue requirement): a copy-pasteable end-to-end
script (`demo.md`) that creates a page, lists pages, reads it through both the tool
and the `forgejo://…/wiki/…` resource URI, edits it, and shows the revision history —
proving the full surface and the agent-discovery story in one run.
## Capabilities
### New Capabilities
- `wiki-tools`: Six MCP tools for Forgejo wiki page CRUD + revision history, implemented
via direct REST calls, each output-bounded per `docs/design/output-bounding.md`.
- `mcp-resource-wiki`: `forgejo://repo/{owner}/{repo}/wiki/{pageName}` resource template
— single wiki page content + metadata + bounded recent revisions.
### Modified Capabilities
<!-- None. Purely additive. The previously-untagged wiki package never compiled or
registered, so no shipped behavior changes; existing tools and resources are untouched. -->
## Impact
- **Affected code**: `operation/wiki/wiki.go` (rewrite, drop build tag), new
`operation/wiki/resources.go` (resource template), `operation/resource/parse.go`
(`ParseWiki` + `WikiParams`), `operation/operation.go` (wire `RegisterWikiTool` and
`RegisterWikiResource`), `pkg/forgejo` (a small `Wiki*` helper layer or inline
`DoJSON` calls), `operation/params/` (wiki param descriptions already exist; add
`page`/`limit`/line-range reuse).
- **No new external dependencies.** Uses the existing raw-HTTP helper and `mcp-go`.
- **No breaking changes.** Additive surface; clients without resource-template support
use the new tools.
- **Documentation**: README (tools + resources), `AGENTS.md`, `docs/plans/wiki-support.md`,
CHANGELOG, and the `demo.md` showboat script.
- **Load-bearing risk**: the base64 (`content_base64`) field name and the page-name URL
encoding (spaces → dashes) are asserted from the documented Gitea/Forgejo API and
must be confirmed against a live instance — captured as an explicit verification task,
not assumed.
- **Out of scope**: a wiki *index* resource (`forgejo://repo/{o}/{r}/wiki` listing all
pages — enumeration stays the job of `list_wiki_pages`, matching the established
"resources fetch single entities, tools enumerate" rule); wiki attachments; rendered
HTML (we return raw markdown).