- **Date:** 2026-07-29 - **Tree:** `1b0b743` (main, post #130 rebase + #133 §6 fixes) - **Mode:** with-skill (fresh general-purpose subagent on `claude-fable-5`; read `SKILL.md` + all eight `references/` files, explicitly forbidden from `tests/` and `decisions/`; 11 tool-uses, all reads) - **Score:** 8/8
Scanned 9/3/2026
Install to Claude Code
npx -y skills add briandconnelly/codex-in-claude --skill runs --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Runs?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/briandconnelly-runs-9da8dbe2)More formats (shields.io, HTML) on the badges page.
# Scenario 5 (Resources) — with-skill run (2026-07-28 rebase re-run)
- **Date:** 2026-07-29
- **Tree:** `1b0b743` (main, post #130 rebase + #133 §6 fixes)
- **Mode:** with-skill (fresh general-purpose subagent on `claude-fable-5`; read `SKILL.md` + all eight `references/` files, explicitly forbidden from `tests/` and `decisions/`; 11 tool-uses, all reads)
- **Score:** 8/8
## Exact prompt given
Same wiki prompt as the baseline (see `2026-07-29-scenario5-baseline.md`), preceded by an instruction to read `agent-friendly-mcp/SKILL.md` and every file under `references/` as authoritative guidance, and **not** to read anything under `tests/` or `decisions/`.
## Per-assertion scoring
| # | Assertion | Result | Evidence |
| --- | --- | --- | --- |
| A1 | URIs stable, hierarchical, domain nouns, not internal numeric ids | **PASS** | `wiki://spaces/{space}/pages/{slug}` built from "domain nouns the wiki already guarantees — space keys and page slugs, never internal page ids" (`[4.uri-stable]`, `[4.uri-nouns]` cited). |
| A2 | Index entries carry native triage metadata, no inlined body, ≤3-sentence summary | **PASS** | Entry uses native `uri`/`name`/`title`/`description`/`mimeType`/`size`/`annotations.lastModified`; "`description` is capped at three sentences"; indexes "never bodies". |
| A3 | Chunkable large bodies with per-chunk callable URIs via `resources/templates/list` (or labeled tool fallback) | **PASS** | `…/chunks/{chunk_id}` URIs with a published chunk template ("because native `resources/read` takes only a URI"); tool fallback `wiki_get_page` accepts `chunk_id`. |
| A4 | Chunk ids stable across reads of the same page version; version change observable | **PASS** | "Chunk ids derive from heading anchors, so they are **stable across reads of the same page version**; when the page changes, ids may change but the change is observable via `lastModified`/`version`"; a stale chunk read returns `chunk_stale` with a re-read repair. |
| A5 | Custom metadata under namespaced `_meta`, never top-level `Resource` fields | **PASS** | Index entries are native-fields-only; "anything custom rides under a namespaced `_meta` key"; the chunk catalog is deliberately kept out of `_meta`-only delivery because clients may not surface `_meta`. |
| A6 | Subscriptions: `resources.subscribe` advertised, `resourceSubscriptions` filter of `subscriptions/listen`, `notifications/resources/updated` tagged with `subscriptionId`, distinguished from `list_changed` | **PASS** | Exactly the 2026-07-28 mechanism: `subscriptions/listen` with `resourceSubscriptions: [uri]` ("the replacement for the removed `resources/subscribe`"), notifications tagged `_meta.io.modelcontextprotocol/subscriptionId`, catalog changes routed via the `resourcesListChanged` filter → `list_changed`; `ttlMs` + `lastModified` stated as the freshness bound for non-subscribers. |
| A7 | Templates + completion for `{space}`/`{slug}` where `completions` negotiated | **PASS** | Templates published; `completion/complete` for `{space}` (enum-like) and `{slug}` (prefix, scoped by entered `{space}`); no-completions fallback declared. |
| A8 | Tool fallback self-sufficient from `tools/list` alone | **PASS** | `wiki_list_spaces`/`wiki_search_pages`/`wiki_get_page` — "callable without any value learnable only from resources"; `wiki_list_spaces` explicitly provides the tool-reachable enumeration of the constrained `space` value. |
**Passed:** all (8/8).
Notable beyond the assertions: honest `ttlMs`/`cacheScope` on every read (`[8.cacheable-results]`), rename-as-remove-plus-add with a `page_moved` repair window, and the `[6.rename]` spellings on the JSON-RPC carrier — all 2026-07-28 rebase content, all absent from the baseline.
## Full final output
Verbatim final message returned by the with-skill subagent (11 tool-uses, all file reads):
All nine files read (SKILL.md plus the eight references). Design below follows the contract-checklist rules; rule ids cited inline.
---
# Resource surface design: `docs-wiki` MCP server
**Revision target** (design-workflow Step 0, `[1.spec-revision]`): MCP **2026-07-28**, no extensions required. Depends on server capabilities `resources.subscribe`, `resources.listChanged`, and `completions`; every dependency has a declared weaker-client fallback (below). Cache hints (`ttlMs`/`cacheScope`) and `subscriptions/listen` are used natively.
## 1. URI scheme
Stable, hierarchical, built from domain nouns the wiki already guarantees — space keys and page slugs, never internal page ids (`[4.uri-stable]`, `[4.uri-nouns]`):
```
wiki://server/capabilities capability summary (read this first)
wiki://spaces index of spaces
wiki://spaces/{space}/pages{?query,updated_after,cursor} filtered, paginated page index for one space
wiki://spaces/{space}/pages/{slug} one page (summary-gated body, see §3)
wiki://spaces/{space}/pages/{slug}/chunks/{chunk_id} one chunk of a large page
```
`resources/list` stays small and deterministic (`[9.deterministic-order]`): the capability summary, `wiki://spaces`, and one index resource per space — a dozen entries, not thousands of pages (`[2.index-not-bodies]`). Page enumeration happens by *reading* a space's index resource; its `cursor`/`query`/`updated_after` variables are published in the resource template so filtering and continuation are discoverable, in-band, and don't pretend to be native list-method parameters (`[8.native-pagination]` governs only the four native list methods, which here return the small catalog with native `nextCursor` semantics — omission means done).
Every `resources/list` and `resources/read` result carries the required `ttlMs` and `cacheScope` (`[8.cacheable-results]`). Because wiki content changes during the day and page visibility follows per-user ACLs: page bodies and indexes get `ttlMs: 300000` (5 min), `cacheScope: "private"`; the space list gets `ttlMs: 3600000`. These are honest volatility statements, and they are the staleness bound for clients that never open a listen stream (see §5).
## 2. Index entry vs full body
An index read (`wiki://spaces/eng/pages?query=deploy`) returns triage metadata only — never bodies (`[4.index-vs-body]`). Each entry uses **native `Resource` fields only**; anything custom rides under a namespaced `_meta` key (`[4.triage-metadata]`, SKILL.md native-vs-convention rule):
```json
{
"uri": "wiki://spaces/eng/pages/deploy-runbook",
"name": "deploy-runbook",
"title": "Production deploy runbook",
"description": "Step-by-step production deploy procedure for the api and worker fleets. Covers rollback and the freeze calendar.",
"mimeType": "text/markdown",
"size": 148230,
"annotations": {"lastModified": "2026-07-29T14:02:11Z"}
}
```
`description` is capped at three sentences (`[4.short-summaries]`); `size` lets the agent estimate token cost before fetching; `annotations.lastModified` is the version signal it compares against a cached copy. The index body itself carries in-band continuation (`next_cursor` as a URI-buildable value for the template's `{cursor}` variable) and explicit truncation with a repair hint naming `query`/`updated_after` when a cap is hit (`[8.truncation]`).
## 3. Large-page delivery
Summary-first, with stable chunking (`[4.summary-first]`, `[4.chunkable]`):
- **Small pages** (≤ ~16 KB): `resources/read` of the page URI returns the full markdown body. One read, no ceremony.
- **Large pages**: the page URI returns a *summary document* — an abstract, the heading outline, and a chunk map — so the agent opts in to bulk content instead of receiving 100 KB by default:
```json
{
"resource_uri": "wiki://spaces/eng/pages/deploy-runbook",
"version": "2026-07-29T14:02:11Z",
"summary": "Production deploy procedure: preflight checks, staged rollout, verification, rollback.",
"outline": [
{"chunk_id": "preflight", "title": "Preflight checks", "size": 9210},
{"chunk_id": "rollout", "title": "Staged rollout", "size": 41022},
{"chunk_id": "rollback", "title": "Rollback", "size": 18734}
],
"next_chunk_id": "preflight",
"next_chunk_uri": "wiki://spaces/eng/pages/deploy-runbook/chunks/preflight"
}
```
Chunk ids derive from heading anchors, so they are **stable across reads of the same page version**; when the page changes, ids may change but the change is observable via `lastModified`/`version` (`[4.chunk-stability]`). Each chunk body echoes `resource_uri`, `chunk_id`, `version`, and an in-band `next_chunk_uri` — the agent can walk chunk N+1 without the outline, and can cite `resource_uri` + `chunk_id` back to any tool. Every chunk has its own URI published as a resource template, because native `resources/read` takes only a URI (`[4.chunk-fetch-path]`); a chunk catalog under `_meta` would be auxiliary only, since clients may never surface `_meta` to the model.
## 4. Discovering parameterized lookup (space + slug)
Two native primitives, together:
**Resource templates** (`[4.templates]`) — `resources/templates/list` publishes the shapes, so an agent that knows a space and slug constructs the URI directly instead of browsing:
```json
{
"uriTemplate": "wiki://spaces/{space}/pages/{slug}",
"name": "wiki_page",
"title": "Wiki page",
"description": "One wiki page by space key and page slug. Large pages return a summary plus chunk map; fetch chunks via the wiki_page_chunk template.",
"mimeType": "text/markdown"
}
```
(plus templates for the space index and `.../chunks/{chunk_id}`.)
**Completion** (`[4.template-completion]`, `[2.completion]`) — the server advertises `completions` via `server/discover` and implements `completion/complete` for `{space}` (small, enum-like) and `{slug}` (prefix match, scoped by the already-entered `{space}`). This is exactly the "large, dynamic, hard-to-guess value set" completion exists for: the agent builds a valid URI proactively instead of learning the slug space through failed reads. For clients that never negotiate completions, the fallback is the structured not-found error whose `repair` routes to the search tool (§6).
## 5. Change notification
Content changes during the day, so mutable-resource subscriptions are advertised (`[4.subscriptions]`):
- The server declares `resources.subscribe`. A client watching a page opens `subscriptions/listen` with `resourceSubscriptions: ["wiki://spaces/eng/pages/deploy-runbook"]` (the 2026-07-28 replacement for the removed `resources/subscribe`), and the server emits `notifications/resources/updated` on that stream, tagged with `_meta.io.modelcontextprotocol/subscriptionId`. The notification says *re-read*; the fresh read carries the new `lastModified`, and the new chunk map if chunk ids moved.
- Catalog membership changes (page created, deleted, or re-slugged; space added) are a different fact: `resourcesListChanged` filter → `notifications/resources/list_changed` (`[9.list-vs-update]` — emit both only when both are true).
- Subscriptions are opt-in streams a client may never open, so they are **not** the freshness contract: the honest `ttlMs: 300000` on every `resources/read` plus `annotations.lastModified` bound staleness for everyone else.
- A page rename is remove-plus-add on the catalog: the old slug URI keeps resolving for a documented window, returning a `page_moved` error whose `repair.arguments` carries the new URI (`[9.rename]` discipline applied to resources).
## 6. Failure contract on the resource surface
Resource failures are JSON-RPC errors (`-32602` for not-found under 2026-07-28) carrying the unified §6 envelope in `error.data` — `machine_code`/`human_message` (the mandatory rename), `details`, `temporary`, `retry_after_ms`, one `repair` object naming a real callable surface (`[4.jsonrpc-errors]`, `[6.rename]`, `[6.repair-callable]`):
```json
{
"machine_code": "page_not_found",
"human_message": "No page 'deploy-runbok' in space 'eng'.",
"details": {"field": "uri", "value": "wiki://spaces/eng/pages/deploy-runbok", "reason": "Slug does not match any page in this space."},
"temporary": false,
"retry_after_ms": null,
"repair": {
"next_step": "search_then_read",
"tool": "wiki_search_pages",
"arguments": {"query": "deploy runbok", "space": "eng"},
"alternative": "Read the wiki://spaces/eng/pages index and pick a listed slug."
},
"resource_uri": "wiki://spaces/eng/pages/deploy-runbok",
"request_id": "req_01J9WIKI7F"
}
```
A stale chunk read (`version` moved) returns `chunk_stale`, `temporary: false`, with `repair` pointing at re-reading the page URI to obtain the fresh chunk map — re-read-then-refetch, never guess.
## 7. Fallback for clients that handle resources poorly
Some clients expose resources badly or not at all, so the same indexed content is reachable through three read-only tools, **self-sufficient from `tools/list` alone** — callable without any value learnable only from resources (`[4.tool-fallback]`):
| Tool | Contract |
| --- | --- |
| `wiki_list_spaces` | No required args; returns the space index. This is the tool-reachable enumeration for the constrained `space` value. |
| `wiki_search_pages` | `query` required; `space`, `updated_after` optional (omission = all spaces / no time bound, declared per `[3.required-optional]`). Returns index entries (same shape as §2), paginated via a documented house `has_more`/`next_cursor` in `structuredContent` (`[8.house-pagination]`), with explicit truncation hints. |
| `wiki_get_page` | `space` + `slug` required; optional `chunk_id` (omission = full body for small pages, summary + chunk map for large — identical gating to the resource path, one contract, not two). |
All three: `readOnlyHint: true`, `openWorldHint: false` (internal wiki, closed world); `destructiveHint`/`idempotentHint` omitted because they are meaningful only when `readOnlyHint` is false (`[3.annotation-defaults]`). Each publishes an `outputSchema` and returns `structuredContent` as the authoritative payload with a text fallback in `content` (`[3.output-schema]`); tool-side failures carry the *same* envelope as §6, just as `isError: true` tool results with `code`/`message` unrenamed (`[6.tool-errors]`).
## 8. Capability summary (the first read)
Exposed at `wiki://server/capabilities`, mirrored in `instructions` as advisory only (`[2.instructions-advisory]`), and reachable via `wiki_list_spaces`-adjacent prose for resource-blind clients. It declares: what the server does (read/search/browse one wiki) and does **not** do (no page editing — writes would be tools, `[4.write-is-tool]` — no attachments over 10 MB, no cross-wiki federation); the revision target; the error carriers (`structuredContent` on tool results, `error.data` on RPC); and the negotiated-capability fallback table — `no_completions` → structured not-found errors with `repair` to `wiki_search_pages`; `no_subscriptions` → `ttlMs` + `lastModified` re-read guidance; `poor_resource_support` → the three tools above.
**Done criteria walk** (checklist §1–§9): §1 identity/caps declared in the summary; §2 compact catalog, index-not-bodies, completion; §3 fallback tools with honest annotations and closed schemas; §4 fully answered above; §5 not applicable — no prompts, none load-bearing (`[5.optional]`); §6 unified envelope, both carriers; §7 not applicable — all reads complete within a normal turn; §8 summary-first, chunking, honest cache hints, explicit truncation; §9 deterministic ordering, list-changed notifications, rename-as-remove-plus-add; a fingerprint is optional here and warranted only if a target client pins the surface (`[9.fingerprint]`).
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!