Skip to content

feat(mcp): unified MCP server registration for tool and resource access (RFC-0058) - #372

Merged
Million-mo merged 11 commits into
wolf1069b:mainfrom
Million-mo:feat/mcp-resource-unified-registration
Aug 17, 2026
Merged

feat(mcp): unified MCP server registration for tool and resource access (RFC-0058)#372
Million-mo merged 11 commits into
wolf1069b:mainfrom
Million-mo:feat/mcp-resource-unified-registration

Conversation

@Million-mo

Copy link
Copy Markdown
Collaborator

Summary

Implements RFC-0058: Unified MCP Server Registration. Top-level MCP servers were previously represented as a child of SkillManagerCap, making their resources invisible to @ mention (/experimental/resource) while their tools flowed through a separate get_capabilities() path — the "dual-object problem".

Now each top-level McpServerCap is registered independently at POOL scope in the ExtensionRegistry and injected directly into agent tool capabilities, unifying tool + resource access behind one object per MCP server.

Changes

Implementation

  • capabilities/mcp_server_cap.py: adds tool_prefix (model-visible namespace from display_name); get_toolset() wraps tools in PrefixedToolset; list_resources() prefers server title over raw name
  • capabilities/skill_manager_cap.py: implements ResourceAccess, delegating to skill-level MCP children
  • delegation/pool.py: registers each top-level McpServerCap independently at POOL scope in _rebuild_skill_capabilities()
  • mcp_server/manager.py: de-duplicates display_name tool prefixes (kbkb_2); adds exclude_global flag to get_capabilities()
  • agents/native_agent/agent.py: get_agentlet() injects top-level McpServerCap directly into tool_capabilities; session/skill configs use get_capabilities(exclude_global=True)
  • routes/agent_routes.py: /experimental/resource lists resources by URI so opencode @ mention surfaces them consistently

Tests (74 passed)

  • New: test_skill_manager_cap_resource_access.py — 9 ResourceAccess delegation cases
  • test_pool_skills.py — top-level McpServerCap POOL registration + tool_prefix de-dup
  • test_mcp_server_cap.py — resource title preference + prefixed toolset
  • test_factory.py — updated scope assertion

Docs & Examples

  • RFC-0058 design doc (decision lineage RFC-0051 → 0052 → 0058, open questions)
  • examples/kb_mcp_server_example.py — demo KB server with dynamic resources/list (background scan of kb_data/, no restart needed)

Test Evidence

74 passed, 2 skipped in 2.79s

Notes

Million-mo and others added 11 commits August 15, 2026 15:00
…ion to SkillManagerCap

McpServerCap gains a model-visible tool namespace (derived from display_name)
so prefixed MCP tool names are predictable. list_resources() now prefers the
server-provided title over the raw name. SkillManagerCap implements
ResourceAccess, delegating to its skill-level MCP children, so top-level
resources are discoverable through the ExtensionRegistry (RFC-0058).

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ools directly

AgentPool._rebuild_skill_capabilities() now registers each top-level
McpServerCap independently in the ExtensionRegistry at POOL scope, making
them discoverable via get_resource_access() for @ mention. get_agentlet()
injects these providers directly into tool_capabilities and passes
exclude_global=True to get_capabilities() so session/skill configs are not
double-processed. MCPManager de-duplicates display_name tool prefixes and
supports the exclude_global flag. /experimental/resource lists resources by
URI so opencode @ mention surfaces them consistently (RFC-0058).

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…eAccess delegation

Adds test_skill_manager_cap_resource_access.py (9 delegation cases for the
new ResourceAccess implementation), McpServerCap resource title preference
and prefixed toolset cases, top-level MCP pool registration + tool_prefix
de-duplication in test_pool_skills.py, and updates the factory scope
assertion to filter ResourceAccess caps correctly (RFC-0058).

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Describes the dual-object problem (tools via get_capabilities, resources via
SkillManagerCap.children) and the decision to register each top-level
McpServerCap independently at POOL scope with direct tool injection.
Documents the RFC-0051 -> RFC-0052 -> RFC-0058 decision lineage and the
open question on top-level vs skill-MCP prefix convention divergence.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…list

Demo server exposing kb_data/ files as kb:// static resources plus
resource templates. resources/list is re-scanned in the background
(--scan-interval) so files added or removed from kb_data appear in
@ mention without a server restart (RFC-0058).

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…el providers

exclude_global=True skipped ALL of the agent's global MCP configs even
when the agent owns a dedicated MCPManager (NativeAgentConfig with
mcp_servers=[]). Such agents never have their servers surfaced via
pool.mcp.providers, so the exclude dropped them entirely and MCP tools
disappeared from the model (test_mcp_tool_with_progress KeyError).

Only exclude when the agent shares the pool's MCPManager, whose providers
are injected directly above.

Fixes the Integration tests failure on wolf1069b#372.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…mantics

MCPClient gains a public set_notification_callbacks() API replacing the
private attribute writes in McpServerCap.\_ensure_client. The message
handler reads callbacks dynamically from the client on each notification,
so a handler created before callbacks are bound still observes them
(\_rebind_session_message_handler after transport connect).

McpServerCap.tool_prefix now defaults to None instead of falling back to
config.display_name. The manager already passes an explicit prefix for
POOL-scope servers, and skill-level MCP wraps its children itself;
falling back to display_name silently prefixed dedicated-agent servers
and broke their raw MCP tool names.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
FakeMCPClient in test_mcp_server_cap and test_review_fixes gains the
public set_notification_callbacks() method and missing callback fields so
McpServerCap._ensure_client can bind through the public API.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Capture connected sessions via a FastMCP middleware instead of a manual
caller_register tool. A background scan loop detects kb_data changes
and broadcasts notifications/resources/list_changed to every active
session, so @-mention resource listings stay fresh without a server
restart. FileResource entries carry annotations.lastModified; add a
kb://docs/ directory listing resource and name/describe the resource
templates.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Spawn the kb MCP server as a real subprocess against a temp kb-dir, add a
file, and assert the McpServerCap receives a
ChangeEvent(kind=resource_list_changed). Exercises the full chain: server
broadcast -> MCP transport -> MCPMessageHandler -> McpServerCap.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
McpServerCap.tool_prefix is str | None now that tool_prefix no longer
falls back to display_name. The attr-defined ignore on ClientSession.
_message_handler became unused after the mcp SDK gained the attribute.

Fixes remaining mypy failures on wolf1069b#372.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@Million-mo
Million-mo merged commit 4add8c7 into wolf1069b:main Aug 17, 2026
12 checks passed
Million-mo added a commit to Million-mo/agentpool that referenced this pull request Aug 19, 2026
…ss (RFC-0058) (wolf1069b#372)

* feat(mcp): add tool_prefix to McpServerCap and ResourceAccess delegation to SkillManagerCap

McpServerCap gains a model-visible tool namespace (derived from display_name)
so prefixed MCP tool names are predictable. list_resources() now prefers the
server-provided title over the raw name. SkillManagerCap implements
ResourceAccess, delegating to its skill-level MCP children, so top-level
resources are discoverable through the ExtensionRegistry (RFC-0058).

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* feat(mcp): register top-level McpServerCap at POOL scope and inject tools directly

AgentPool._rebuild_skill_capabilities() now registers each top-level
McpServerCap independently in the ExtensionRegistry at POOL scope, making
them discoverable via get_resource_access() for @ mention. get_agentlet()
injects these providers directly into tool_capabilities and passes
exclude_global=True to get_capabilities() so session/skill configs are not
double-processed. MCPManager de-duplicates display_name tool prefixes and
supports the exclude_global flag. /experimental/resource lists resources by
URI so opencode @ mention surfaces them consistently (RFC-0058).

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* test(mcp): cover top-level McpServerCap POOL registration and ResourceAccess delegation

Adds test_skill_manager_cap_resource_access.py (9 delegation cases for the
new ResourceAccess implementation), McpServerCap resource title preference
and prefixed toolset cases, top-level MCP pool registration + tool_prefix
de-duplication in test_pool_skills.py, and updates the factory scope
assertion to filter ResourceAccess caps correctly (RFC-0058).

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* docs(rfc): add RFC-0058 unified MCP server registration design

Describes the dual-object problem (tools via get_capabilities, resources via
SkillManagerCap.children) and the decision to register each top-level
McpServerCap independently at POOL scope with direct tool injection.
Documents the RFC-0051 -> RFC-0052 -> RFC-0058 decision lineage and the
open question on top-level vs skill-MCP prefix convention divergence.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* feat(examples): add knowledge base MCP server with dynamic resources/list

Demo server exposing kb_data/ files as kb:// static resources plus
resource templates. resources/list is re-scanned in the background
(--scan-interval) so files added or removed from kb_data appear in
@ mention without a server restart (RFC-0058).

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix(mcp): preserve dedicated-agent MCP servers when injecting top-level providers

exclude_global=True skipped ALL of the agent's global MCP configs even
when the agent owns a dedicated MCPManager (NativeAgentConfig with
mcp_servers=[]). Such agents never have their servers surfaced via
pool.mcp.providers, so the exclude dropped them entirely and MCP tools
disappeared from the model (test_mcp_tool_with_progress KeyError).

Only exclude when the agent shares the pool's MCPManager, whose providers
are injected directly above.

Fixes the Integration tests failure on wolf1069b#372.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* feat(mcp): dynamic notification callbacks and explicit tool_prefix semantics

MCPClient gains a public set_notification_callbacks() API replacing the
private attribute writes in McpServerCap.\_ensure_client. The message
handler reads callbacks dynamically from the client on each notification,
so a handler created before callbacks are bound still observes them
(\_rebind_session_message_handler after transport connect).

McpServerCap.tool_prefix now defaults to None instead of falling back to
config.display_name. The manager already passes an explicit prefix for
POOL-scope servers, and skill-level MCP wraps its children itself;
falling back to display_name silently prefixed dedicated-agent servers
and broke their raw MCP tool names.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* test(mcp): sync test doubles with set_notification_callbacks API

FakeMCPClient in test_mcp_server_cap and test_review_fixes gains the
public set_notification_callbacks() method and missing callback fields so
McpServerCap._ensure_client can bind through the public API.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* feat(examples): broadcast resources/list_changed from kb MCP server

Capture connected sessions via a FastMCP middleware instead of a manual
caller_register tool. A background scan loop detects kb_data changes
and broadcasts notifications/resources/list_changed to every active
session, so @-mention resource listings stay fresh without a server
restart. FileResource entries carry annotations.lastModified; add a
kb://docs/ directory listing resource and name/describe the resource
templates.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* test(e2e): verify kb server list_changed reaches McpServerCap

Spawn the kb MCP server as a real subprocess against a temp kb-dir, add a
file, and assert the McpServerCap receives a
ChangeEvent(kind=resource_list_changed). Exercises the full chain: server
broadcast -> MCP transport -> MCPMessageHandler -> McpServerCap.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* fix(mcp): align tool_prefix return type and drop stale type ignore

McpServerCap.tool_prefix is str | None now that tool_prefix no longer
falls back to display_name. The attr-defined ignore on ClientSession.
_message_handler became unused after the mcp SDK gained the attribute.

Fixes remaining mypy failures on wolf1069b#372.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

---------

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant