forgejo-mcp/openspec/changes/add-wiki-support/demo.md
Christoph Görn aad9b361db
docs: 📝 third-pass survival check refinements for add-wiki-support
Third debate team (NEW-critiques-only + agent-ergonomics lens): 3 CONCEDE-PATCH,
1 WITHDRAWN. Prior C1-C8 surface held; new edges caught:

- N1: sliceLines is unexported — add task 2.3a to export repo.SliceLines (or lift
  to shared pkg), get_file_content unchanged, copy-paste forbidden
- N2 WITHDRAWN: feared template dispatch collision disproven — commit/issue/pr/status
  already coexist; mcp-go v0.17.0 matchesTemplate matches by literal-segment regex.
  Added optional dispatch regression test (3.5)
- N3: update_wiki_page has no commit_sha precondition (vs update_file) — acknowledge
  lost-update window, task 5.6a (optional last_commit_sha if upstream supports)
- N4: create-on-existing-title unspecified — conditional guided-error/overwrite-warning
  branch, task 5.6b

Referee finding (verified vs mcp-go server.go:614): literal-slash resource URIs are
rejected by matchesTemplate before ParseWiki, so the guided -32602 cannot fire at the
resource layer — encoding guidance + get_wiki_page fallback moved to template description.

Agent-ergonomics lens (5 gaps): naming convention note, use-returned-page_name,
honest demo reframing (no over-claim of client auto-resolution), %2F dead-end fallback,
total_lines-always-returned guidance.

openspec validate --strict passes.

Refs: #32
2026-05-31 15:59:45 +02:00

130 lines
4.4 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.

# Showboat Demo — Forgejo wiki via forgejo-mcp
End-to-end proof of the whole wiki surface in one sitting: six tools + the
`forgejo://…/wiki/…` resource template, including the agent-discovery payoff (a bare URI
in a prompt resolves to page content with no tool call).
## Prerequisites
- `forgejo-mcp` built from this change and configured against a Forgejo/Codeberg instance.
- A token with **repo write** scope; the target repo has **Wiki enabled** in settings.
- A throwaway repo, referred to below as `<owner>/<repo>`.
> Replace `<owner>/<repo>` throughout. Outputs shown are the *expected shape*; task 6.2
> replaces them with real captured output.
---
## 1. Create a page
```jsonc
// tool: create_wiki_page
{ "owner": "<owner>", "repo": "<repo>", "title": "Home",
"content": "# Welcome\n\nThis wiki is driven by forgejo-mcp.\n" }
```
→ Page created. Content is sent base64-encoded under the hood; the caller only ever sees
plain markdown.
```jsonc
{ "title": "Home", "page_name": "Home", "commit_sha": "a1b2c3…" }
```
## 2. List pages (bounded)
```jsonc
// tool: list_wiki_pages
{ "owner": "<owner>", "repo": "<repo>", "page": 1, "limit": 50 }
```
```jsonc
{ "pages": [ { "title": "Home", "page_name": "Home", "sub_url": "Home" } ],
"page": 1, "has_next": false }
```
## 3. Read via the tool (decoded + total_lines)
```jsonc
// tool: get_wiki_page
{ "owner": "<owner>", "repo": "<repo>", "page_name": "Home" }
```
```jsonc
{ "title": "Home", "commit_sha": "a1b2c3…", "total_lines": 4,
"content": "# Welcome\n\nThis wiki is driven by forgejo-mcp.\n" }
```
> `total_lines` is 4, not 3: the body ends in `\n`, and the shared `sliceLines` split
> (`strings.Split(content, "\n")`) counts the trailing newline as a final empty line.
## 4. Read via the resource URI — the money shot
Hand an agent a prompt containing only the bare URI:
```
Summarize forgejo://repo/<owner>/<repo>/wiki/Home
```
The result is **one `resources/read` call instead of a `get_wiki_page` tool call**,
returning:
- `application/json`: `{ owner, repo, page_name, title, commit_sha, recent_revisions: [...] }`
- `text/markdown` sidecar: the decoded page body.
A client that auto-resolves resource URIs issues that `resources/read` transparently;
clients that do not (the common case today — auto-resolution of a bare URI embedded in
chat text is not yet universal across Claude Code/Desktop, Codex, Cursor) issue it
explicitly. Either way the wiki page becomes first-class, URI-addressable context — the
discoverability win — without over-claiming that every client resolves it with zero
prompting.
## 5. Edit the page
```jsonc
// tool: update_wiki_page
{ "owner": "<owner>", "repo": "<repo>", "page_name": "Home",
"content": "# Welcome\n\nThis wiki is driven by forgejo-mcp.\n\n## Links\n\n- See `list_wiki_pages`.\n" }
```
→ New commit. `title` omitted ⇒ page keeps its name.
## 6. Revision history (bounded)
```jsonc
// tool: get_wiki_revisions
{ "owner": "<owner>", "repo": "<repo>", "page_name": "Home", "page": 1, "limit": 30 }
```
```jsonc
{ "revisions": [ { "sha": "d4e5f6…", "author": "you", "message": "Update wiki page 'Home'" },
{ "sha": "a1b2c3…", "author": "you", "message": "Create wiki page 'Home'" } ],
"page": 1, "has_next": false }
```
## 7. Bounded read of a long page (resumability)
```jsonc
// tool: get_wiki_page
{ "owner": "<owner>", "repo": "<repo>", "page_name": "Home",
"start_line": 1, "end_line": 5 }
```
```jsonc
{ "title": "Home", "total_lines": 8, "start_line": 1, "end_line": 5,
"content": "# Welcome\n\nThis wiki is driven by forgejo-mcp.\n\n## Links" }
```
`total_lines: 8` > `end_line: 5` ⇒ the caller knows to request lines 6–8 next. (The
edited body ends in `\n`, so the split counts a trailing empty 8th line; the same
`sliceLines` routine and count as `get_file_content`.)
## 8. Cleanup
```jsonc
// tool: delete_wiki_page
{ "owner": "<owner>", "repo": "<repo>", "page_name": "Home" }
```
→ a `2xx` (canonically `204 No Content`) reported as success. `list_wiki_pages` now
returns an empty list.
---
## What this proves
- All six tools work against a stock instance with **no `forgejo-sdk` wiki dependency**.
- Content round-trips correctly through base64 (caller never sees encoding).
- Every data-proportional response is **bounded and resumable** (`page`/`limit`/`has_next`,
`start_line`/`end_line`/`total_lines`).
- A wiki page is **discoverable by URI** for agents and humans alike.