Skip to content

feat(mcp): expose bundled prompts as static MCP resources#2734

Open
obchain wants to merge 5 commits into
tinyhumansai:mainfrom
obchain:feat/mcp-resources-mvp
Open

feat(mcp): expose bundled prompts as static MCP resources#2734
obchain wants to merge 5 commits into
tinyhumansai:mainfrom
obchain:feat/mcp-resources-mvp

Conversation

@obchain
Copy link
Copy Markdown
Contributor

@obchain obchain commented May 27, 2026

Summary

  • Adds resources/list and resources/read to the MCP stdio + HTTP server so external MCP clients (Claude Desktop, Cursor, Zed) can attach OpenHuman's bundled prompt assets as conversation context.
  • Catalog ships 21 static, read-only resources: 3 core identity files (IDENTITY.md, SOUL.md, USER.md) plus 18 subagent prompt.md files (one per BUILTINS row in agent::agents::loader).
  • initialize now advertises a resources capability with subscribe: false and listChanged: false because the catalog is include_str!-bundled at compile time and cannot change at runtime.
  • 11 new unit tests cover catalog parity vs BUILTINS, per-URI content distinctness, the wired JSON-RPC happy paths, and the -32002 Resource not found / -32602 Invalid params error conventions.
  • Single-page-per-resources/list response: the cursor parameter is accepted for spec-compliance but ignored, and the response omits nextCursor.

Problem

The MCP server today only implements initialize, ping, tools/list, and tools/call; every other spec method returns -32601 Method not found. External MCP clients connecting to openhuman-core therefore 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.md or 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:

SHA Title Files
75df6176 feat(mcp): add static resources catalog module new src/openhuman/mcp_server/resources.rs + mod.rs register. Catalog is an include_str!-bundled &[StaticResource] slice, so the asset paths are checked at compile time. Exposes pub fn list_resources_result(cursor) and pub fn read_resource_result(uri) — both pure, no I/O, no async.
b38f425a feat(mcp): wire resources/list + resources/read into protocol src/openhuman/mcp_server/protocol.rs adds two handle_request match arms, advertises the resources capability in initialize_result, and updates the existing initialize test to assert the new capability block. Missing-uri-32602 Invalid params. Unknown-URI lookups → -32002 Resource not found per the MCP error-code convention.
d35c9d68 docs(mcp): document the resources surface and coverage row gitbooks/developing/mcp-server.md adds a Resources section listing the URI catalog and contract notes, plus extends the smoke-test snippet to exercise both methods against openhuman://core/identity. docs/TEST-COVERAGE-MATRIX.md adds row 11.1.7 and bumps the Covered total from 69 to 70.

URI scheme

URI Source path
openhuman://core/identity src/openhuman/agent/prompts/IDENTITY.md
openhuman://core/soul src/openhuman/agent/prompts/SOUL.md
openhuman://core/user src/openhuman/agent/prompts/USER.md
openhuman://agents/<id>/prompt src/openhuman/agent/agents/<id>/prompt.md (one entry per BUILTINS row)

Out of scope (deliberately, follow-up tracking only)

  • Dynamic memory-as-resources (openhuman://memory/recent, openhuman://memory/search?q=…) — needs per-user context plumbing and SecurityPolicy gating; 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

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy — 7 catalog tests in resources.rs (parity vs BUILTINS, per-URI distinctness, required-field shape, no nextCursor, unknown-URI rejection) + 4 protocol tests in protocol.rs (list happy path, read happy path, unknown-URI -32002, missing-uri -32602).
  • Diff coverage ≥ 80% — changed Rust lines are 100% covered by the 11 new unit tests; docs lines are not eligible.
  • Coverage matrix updated — added row 11.1.7 MCP resources (resources/list + resources/read) in docs/TEST-COVERAGE-MATRIX.md and bumped the Covered total.
  • All affected feature IDs from the matrix are listed in the PR description under ## Related — feature ID 11.1.7 (new row).
  • No new external network dependencies introduced (mock backend used per Testing Strategy) — tests are pure in-memory; no HTTP, no socket, no fixtures beyond the include_str!-bundled markdown.
  • Manual smoke checklist updated if this touches release-cut surfaces — N/A: stdio/HTTP MCP server is opt-in and not on the release-cut smoke path.
  • Linked issue closed via Closes #NNN in the ## Related section.

Impact

  • Runtime/platform impact: none on existing tool-call flow; the new methods are additive and only fire when an MCP client invokes them.
  • Performance: zero overhead at startup (catalog is a const); request-time cost is a Vec<Value> build of ≈21 entries for resources/list and a linear scan to find a single URI for resources/read.
  • Security: the catalog is 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. SecurityPolicy is unchanged.
  • Migration / compatibility: backwards-compatible — older clients that ignore the resources capability and never call the new methods continue to see the same surface.

Related


AI Authored PR Metadata (required for Codex/Linear PRs)

Keep this section for AI-authored PRs. For human-only PRs, mark each field N/A.

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: N/A
  • Commit SHA: N/A

Validation Run

  • pnpm --filter openhuman-app format:check — N/A: no app/ code changed.
  • pnpm typecheck — N/A: no TS source changed.
  • Focused tests: ran cargo test --lib openhuman::mcp_server::resources::tests (7/7 pass) and cargo test --lib openhuman::mcp_server::protocol::tests::resources (4/4 pass) plus cargo test --lib openhuman::mcp_server::protocol::tests::initialize (9/9 pass) to confirm the amended initialize assertion.
  • Rust fmt/check (if changed): cargo fmt --manifest-path Cargo.toml --check clean on the touched files; cargo check --manifest-path Cargo.toml --lib produces no new warnings.
  • Tauri fmt/check (if changed): N/A — no Tauri code changed.

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: MCP server now exposes 21 static read-only resources via resources/list + resources/read, and the initialize capability response advertises the new surface.
  • User-visible effect: external MCP clients (Claude Desktop, Cursor, Zed) connected to openhuman-core can attach OpenHuman's bundled prompts as conversation context.

Parity Contract

  • Legacy behavior preserved: yes — existing tools/list / tools/call paths are untouched and the new methods are purely additive.
  • Guard/fallback/dispatch parity checks: no production code outside mcp_server/ changed; tool dispatch, security policy, and session lifecycle are unaffected.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none.
  • Canonical PR: this PR.
  • Resolution: N/A.

Summary by CodeRabbit

  • New Features

    • Added MCP resources surface with resources/list and resources/read to expose bundled prompt assets via stable openhuman:// URIs.
    • Initialization now advertises a resources capability.
  • Documentation

    • Added a Resources section describing supported URIs and error behavior.
    • Updated smoke test expectations to include resources results (four compact JSON lines).
  • Tests

    • Added unit and integration tests for listing, reading, not-found, and invalid-params paths.
    • Updated test-coverage matrix totals.

Review Change Stack

obchain added 3 commits May 27, 2026 12:49
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.
@obchain obchain requested a review from a team May 27, 2026 07:24
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: aa504545-5543-463d-a9a7-38011ad26833

📥 Commits

Reviewing files that changed from the base of the PR and between d35c9d6 and 4b53354.

📒 Files selected for processing (1)
  • src/openhuman/mcp_server/resources.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/openhuman/mcp_server/resources.rs

📝 Walkthrough

Walkthrough

Adds compile-time bundled MCP resources (resources/list, resources/read) exposing openhuman://... URIs, integrates handlers into the JSON-RPC dispatcher with -32002 not-found mapping, advertises capabilities.resources, and adds docs plus unit tests covering list/read success and error paths.

Changes

MCP Static Resources Surface

Layer / File(s) Summary
Resource catalog definition
src/openhuman/mcp_server/resources.rs (lines 1–208)
StaticResource and RESOURCES constant map openhuman:// URIs to metadata and markdown content bundled via include_str! at compile time.
Resource list and read operations
src/openhuman/mcp_server/resources.rs (lines 210–419)
list_resources_result returns the full catalog (no pagination). read_resource_result returns contents[0] with markdown for known URIs or ResourceError::NotFound for unknown URIs. Unit tests validate list/read behavior and error cases.
Module registration
src/openhuman/mcp_server/mod.rs
Adds mod resources; to register the new submodule.
Protocol JSON-RPC dispatch and capability
src/openhuman/mcp_server/protocol.rs (lines 3, 167–222, 315–331)
Imports resources, adds resources/list and resources/read handlers (param parsing, debug logs, error mapping to -32002), and updates initialize_result to advertise capabilities.resources with subscribe: false and listChanged: false.
Capability assertion and JSON-RPC tests
src/openhuman/mcp_server/protocol.rs (lines 414–798)
Extends initialize test to assert resources capability fields. Adds async tests: catalog listing, known-URI read, unknown-URI -32002 error, and missing-uri invalid-params.
Documentation and coverage
gitbooks/developing/mcp-server.md, docs/TEST-COVERAGE-MATRIX.md
Adds "Resources" docs, updates smoke-test example to include resources/list and resources/read, and inserts TEST-COVERAGE-MATRIX entry 11.1.7; Covered increases 69→70 and Total leaves 205→206.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • tinyhumansai/openhuman#1760: Added tools/* support in the same handle_request JSON-RPC dispatcher that this PR extends with resources/* handlers.

Suggested reviewers

  • senamakel

Poem

🐰 I bundled prompts in strings so neat,

openhuman:// URIs for each treat,
List them, read them, all compile-time bright,
Static and steady, a rabbit's delight! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat(mcp): expose bundled prompts as static MCP resources' clearly and concisely summarizes the primary change: adding a static MCP resources surface.
Linked Issues check ✅ Passed The PR comprehensively implements all coding requirements from issue #2732: resources/list and resources/read handlers with proper error codes, initialize capability advertising, catalog containing 21 URIs (3 core + 18 subagents), cursor handling, unit tests for catalog parity and protocol wiring, and documentation updates.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the static MCP resources surface as defined in #2732; no out-of-scope features like dynamic memory resources, subscriptions, or templated URIs are present.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. labels May 27, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/openhuman/mcp_server/resources.rs (1)

288-329: ⚡ Quick win

Make 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

📥 Commits

Reviewing files that changed from the base of the PR and between e6192e2 and d35c9d6.

📒 Files selected for processing (5)
  • docs/TEST-COVERAGE-MATRIX.md
  • gitbooks/developing/mcp-server.md
  • src/openhuman/mcp_server/mod.rs
  • src/openhuman/mcp_server/protocol.rs
  • src/openhuman/mcp_server/resources.rs

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 27, 2026
Copy link
Copy Markdown
Contributor

@graycyrus graycyrus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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.
@obchain
Copy link
Copy Markdown
Contributor Author

obchain commented May 27, 2026

Done in 4b533545. Replaced the subset loop with a BTreeSet equality assertion filtered to openhuman://agents/ URIs, so catalog drift now fails in both directions (missing and stale entries).

@obchain
Copy link
Copy Markdown
Contributor Author

obchain commented May 27, 2026

@graycyrus rebased catalog parity test on 4b533545 (set-equality + agents-only filter). The cancelled Windows secrets ACL job was an infra glitch, not a real failure — the new push will rerun it automatically.

@obchain obchain requested a review from graycyrus May 27, 2026 16:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose bundled prompts as static MCP resources (resources/list + resources/read)

2 participants