feat(mcp): expose bundled prompts as static MCP resources#2734
Conversation
Introduces `src/openhuman/mcp_server/resources.rs` — a compile-time `include_str!`-bundled catalog of OpenHuman's prompt assets (the three core identity files plus each built-in subagent's `prompt.md`) plus two pure builders, `list_resources_result` and `read_resource_result`, that shape the MCP `resources/list` and `resources/read` responses. The module is wired into the protocol layer in the next commit. Keeping the catalog as its own change lets the test suite cover catalog parity (against the `agent::agents::loader::BUILTINS` slice) and per-URI content distinctness independently of the protocol-handler wiring.
Hooks the static catalog module into the JSON-RPC dispatch so external MCP clients can pull OpenHuman's bundled prompts as conversation context. Three changes: * `initialize` now advertises a `resources` capability with `subscribe: false` and `listChanged: false` so clients know they can call `resources/list` but should not expect change notifications (the catalog is bundled at compile time). * `resources/list` returns the full catalog in one page; the `cursor` parameter is accepted for spec-compliance but ignored, and the response omits `nextCursor`. * `resources/read` looks up by `uri` and returns the markdown body. Missing-`uri` rejections surface as `-32602 Invalid params`; unknown-URI lookups surface as `-32002 Resource not found` per the MCP error-code convention. Four protocol tests cover the wired handlers plus the existing `initialize` test grows an assertion for the new capability.
Adds a Resources section to the MCP server gitbook listing the URI catalog and contract notes (`subscribe`/`listChanged` both false, `-32002` for unknown-URI lookups). Extends the smoke-test snippet to also exercise `resources/list` and `resources/read` against `openhuman://core/identity` and updates the expected-response sketch to match the new initialize capabilities block. Adds row 11.1.7 to the test-coverage matrix referencing `mcp_server/resources.rs` and `mcp_server/protocol.rs` for the seven catalog tests plus the four protocol-layer tests, and bumps the Covered total from 69 to 70.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds compile-time bundled MCP resources ( ChangesMCP Static Resources Surface
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/openhuman/mcp_server/resources.rs (1)
288-329: ⚡ Quick winMake BUILTINS parity assertion bidirectional.
This test only checks for missing expected URIs; it won’t fail on stale extra subagent URIs. Prefer exact set equality for subagent URIs.
Suggested test hardening
#[test] fn list_resources_advertises_every_subagent_prompt() { @@ - let expected = [ + let expected_uris = [ "openhuman://agents/archivist/prompt", @@ "openhuman://agents/trigger_triage/prompt", ]; let out = list_resources_result(None); let resources = out .get("resources") .and_then(Value::as_array) .expect("resources array"); - let uris: Vec<&str> = resources + use std::collections::BTreeSet; + let actual_subagent_uris: BTreeSet<&str> = resources .iter() .filter_map(|r| r.get("uri").and_then(Value::as_str)) + .filter(|uri| uri.starts_with("openhuman://agents/")) .collect(); - for uri in expected { - assert!( - uris.contains(&uri), - "missing subagent resource `{uri}` from resources/list — \ - catalog drift vs `agent::agents::loader::BUILTINS`" - ); - } + let expected_subagent_uris: BTreeSet<&str> = expected_uris.into_iter().collect(); + assert_eq!( + actual_subagent_uris, + expected_subagent_uris, + "subagent resource catalog drift vs `agent::agents::loader::BUILTINS`" + ); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/openhuman/mcp_server/resources.rs` around lines 288 - 329, The test list_resources_advertises_every_subagent_prompt only asserts that every item in the expected array appears in the discovered uris, so it misses extra/stale URIs; change the check to assert exact set equality between the expected set and the discovered set (built from out -> resources -> uris). Convert expected and uris into HashSet<String> (or sort and compare vectors) and assert they are equal (or assert that their symmetric difference is empty) so the test will fail on both missing and extra subagent URIs; refer to the existing symbols expected, uris, resources, and list_resources_result to locate where to replace the current loop/assert.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/openhuman/mcp_server/resources.rs`:
- Around line 288-329: The test list_resources_advertises_every_subagent_prompt
only asserts that every item in the expected array appears in the discovered
uris, so it misses extra/stale URIs; change the check to assert exact set
equality between the expected set and the discovered set (built from out ->
resources -> uris). Convert expected and uris into HashSet<String> (or sort and
compare vectors) and assert they are equal (or assert that their symmetric
difference is empty) so the test will fail on both missing and extra subagent
URIs; refer to the existing symbols expected, uris, resources, and
list_resources_result to locate where to replace the current loop/assert.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1dbd0768-4211-478a-9a1e-daebbbe6b261
📒 Files selected for processing (5)
docs/TEST-COVERAGE-MATRIX.mdgitbooks/developing/mcp-server.mdsrc/openhuman/mcp_server/mod.rssrc/openhuman/mcp_server/protocol.rssrc/openhuman/mcp_server/resources.rs
graycyrus
left a comment
There was a problem hiding this comment.
@obchain hey! the code looks good to me — solid implementation of the MCP resources surface. catalog parity tests lock in the subagent list, compile-time include_str! validation catches missing assets, and error codes follow the spec perfectly.
however, there's a CI failure on windows (Rust Core Tests — secrets ACL) that needs to be sorted first. once that's green, i'll come back and approve this.
Replace subset check with BTreeSet equality so the test also fails on stale catalog entries that have fallen out of agent::agents::loader::BUILTINS, not only on missing ones.
|
Done in |
|
@graycyrus rebased catalog parity test on |
Summary
resources/listandresources/readto the MCP stdio + HTTP server so external MCP clients (Claude Desktop, Cursor, Zed) can attach OpenHuman's bundled prompt assets as conversation context.IDENTITY.md,SOUL.md,USER.md) plus 18 subagentprompt.mdfiles (one perBUILTINSrow inagent::agents::loader).initializenow advertises aresourcescapability withsubscribe: falseandlistChanged: falsebecause the catalog isinclude_str!-bundled at compile time and cannot change at runtime.BUILTINS, per-URI content distinctness, the wired JSON-RPC happy paths, and the-32002 Resource not found/-32602 Invalid paramserror conventions.resources/listresponse: thecursorparameter is accepted for spec-compliance but ignored, and the response omitsnextCursor.Problem
The MCP server today only implements
initialize,ping,tools/list, andtools/call; every other spec method returns-32601 Method not found. External MCP clients connecting toopenhuman-coretherefore see a tools-only surface and have no way to pull OpenHuman's own prompt scaffolds into their own conversations — even though those scaffolds are already bundled into the binary at compile time and contain no per-user data.Concretely, a Claude Desktop user who connects OpenHuman as an MCP server cannot today attach
IDENTITY.md/SOUL.md/USER.mdor any subagent prompt without reading the OpenHuman source tree by hand. That's the smallest user-visible spec gap on the server-side MCP surface and lands without backend coordination.Solution
Three atomic commits:
75df6176feat(mcp): add static resources catalog modulesrc/openhuman/mcp_server/resources.rs+mod.rsregister. Catalog is aninclude_str!-bundled&[StaticResource]slice, so the asset paths are checked at compile time. Exposespub fn list_resources_result(cursor)andpub fn read_resource_result(uri)— both pure, no I/O, no async.b38f425afeat(mcp): wire resources/list + resources/read into protocolsrc/openhuman/mcp_server/protocol.rsadds twohandle_requestmatch arms, advertises theresourcescapability ininitialize_result, and updates the existinginitializetest to assert the new capability block. Missing-uri→-32602 Invalid params. Unknown-URI lookups →-32002 Resource not foundper the MCP error-code convention.d35c9d68docs(mcp): document the resources surface and coverage rowgitbooks/developing/mcp-server.mdadds a Resources section listing the URI catalog and contract notes, plus extends the smoke-test snippet to exercise both methods againstopenhuman://core/identity.docs/TEST-COVERAGE-MATRIX.mdadds row 11.1.7 and bumps the Covered total from 69 to 70.URI scheme
openhuman://core/identitysrc/openhuman/agent/prompts/IDENTITY.mdopenhuman://core/soulsrc/openhuman/agent/prompts/SOUL.mdopenhuman://core/usersrc/openhuman/agent/prompts/USER.mdopenhuman://agents/<id>/promptsrc/openhuman/agent/agents/<id>/prompt.md(one entry perBUILTINSrow)Out of scope (deliberately, follow-up tracking only)
openhuman://memory/recent,openhuman://memory/search?q=…) — needs per-user context plumbing andSecurityPolicygating; lands cleaner on top of this MVP.resources/subscribe/resources/unsubscribe/notifications/resources/list_changed— the static catalog cannot change at runtime, so subscriptions would be no-ops.resources/templates/list— no templated URIs in the MVP catalog.Submission Checklist
resources.rs(parity vsBUILTINS, per-URI distinctness, required-field shape, nonextCursor, unknown-URI rejection) + 4 protocol tests inprotocol.rs(list happy path, read happy path, unknown-URI-32002, missing-uri-32602).11.1.7 MCP resources (resources/list + resources/read)indocs/TEST-COVERAGE-MATRIX.mdand bumped the Covered total.## Related— feature ID11.1.7(new row).include_str!-bundled markdown.N/A: stdio/HTTP MCP server is opt-in and not on the release-cut smoke path.Closes #NNNin the## Relatedsection.Impact
const); request-time cost is aVec<Value>build of ≈21 entries forresources/listand a linear scan to find a single URI forresources/read.include_str!-bundled at compile time and contains only assets already in the repo; there is no async I/O, no filesystem traversal, and no user-context plumbing introduced.SecurityPolicyis unchanged.resourcescapability and never call the new methods continue to see the same surface.Related
docs/TEST-COVERAGE-MATRIX.md.openhuman://memory/…);resources/templates/listonce a templated URI is introduced.AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
Validation Run
pnpm --filter openhuman-app format:check— N/A: noapp/code changed.pnpm typecheck— N/A: no TS source changed.cargo test --lib openhuman::mcp_server::resources::tests(7/7 pass) andcargo test --lib openhuman::mcp_server::protocol::tests::resources(4/4 pass) pluscargo test --lib openhuman::mcp_server::protocol::tests::initialize(9/9 pass) to confirm the amendedinitializeassertion.cargo fmt --manifest-path Cargo.toml --checkclean on the touched files;cargo check --manifest-path Cargo.toml --libproduces no new warnings.Validation Blocked
command:N/Aerror:N/Aimpact:N/ABehavior Changes
resources/list+resources/read, and theinitializecapability response advertises the new surface.openhuman-corecan attach OpenHuman's bundled prompts as conversation context.Parity Contract
tools/list/tools/callpaths are untouched and the new methods are purely additive.mcp_server/changed; tool dispatch, security policy, and session lifecycle are unaffected.Duplicate / Superseded PR Handling
Summary by CodeRabbit
New Features
resources/listandresources/readto expose bundled prompt assets via stableopenhuman://URIs.resourcescapability.Documentation
Tests