Skip to content

feat(mcp): serve the agent resources surface that only dappcore/mcp had - #20

Merged
Snider merged 1 commit into
mainfrom
feat/mcp-agent-resources
Aug 8, 2026
Merged

feat(mcp): serve the agent resources surface that only dappcore/mcp had#20
Snider merged 1 commit into
mainfrom
feat/mcp-agent-resources

Conversation

@Snider

@Snider Snider commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

PR 1 of 2 in the dependency-cycle break. This ports the capability into agent; mcp's copy is deleted in PR 2, after this merges — so nothing is missing from both at once.

The gap

The agent MCP server answered resources/list with a hardcoded ['resources' => []] and implemented no resources/read at all. The real surface lived only in dappcore/mcp's McpAgentServerCommand — which is also where the cycle came from: a shared package importing Core\Mod\Agentic\Models\*.

The 46-method diff that gated this found the 29 tool* methods fully covered by agent's extracted tool classes and reachable (Boot::onMcpTools registers all of them). The 5 resource* methods were covered by nothing. A straight delete would have lost them silently.

What lands

Five URI shapes, one class each — matching how Mcp\Tools\Agent already splits tools out rather than carrying them as methods on a 2000-line command:

plans://all                     AllPlansResource
plans://{slug}                  PlanDocumentResource
plans://{slug}/phases/{order}   PhaseChecklistResource
plans://{slug}/state/{key}      StateValueResource
sessions://{id}/context         SessionContextResource

AgentResourceRegistry lists and routes them — the counterpart to AgentToolRegistry. Resources contribute their own list entries, so the plan document resource enumerates every non-archived plan, while the unbounded per-key ones (phase checklists, state values) advertise nothing and stay directly addressable.

Two deliberate departures from the ported code

A missing target answers -32602, not the string "Plan not found: {slug}" returned as the resource body. A client could not distinguish that from a real document whose text happens to say so.

initialize advertises the resources capability. Implementing the surface without declaring it leaves it undiscoverable — a client that isn't told the server has resources never issues resources/list. It would have been shipped and never called, which is the exact failure this sweep exists to remove.

Registration

A container singleton in register(), not hung off an event the way onMcpTools is. There's no McpResourcesRegistering to listen for, and $listens is populated by ModuleScanner from app/Core|Mod|Website only — dead once this package sits under vendor/ — so a binding is what actually resolves in a host application.

Verification

result
new registry tests 6 passed
new command tests (real stdio, end to end) 4 passed
full suite 156 failed, 1165 passed — was 156 failed, 1155 passed

Same failures as main, plus exactly these ten. The one failing test whose name contains "Resource" is ContentResource, a pre-existing failure in a file this branch does not touch.

🤖 Generated with Claude Code
Co-Authored-By: Virgil virgil@lethean.io

Summary by CodeRabbit

  • New Features

    • Added MCP resource discovery and reading capabilities.
    • Browse all active agent plans, including statuses, progress, titles and recent updates.
    • Open individual plan documents and phase checklists in Markdown.
    • Retrieve plan state values and session context, including status, notes and artefacts.
    • Added validation and clear errors for unavailable or invalid resource addresses.
  • Tests

    • Added coverage for resource listing, reading, routing, dynamic plan data and error handling.

The agent MCP server answered resources/list with a hardcoded
['resources' => []] and implemented no resources/read at all. The real
surface — plans, phase checklists, plan state, session context — lived only
in dappcore/mcp's McpAgentServerCommand, which is also where the dependency
cycle came from: a shared package importing Core\Mod\Agentic\Models\*.

This ports the capability across so mcp's copy can be deleted afterwards
rather than before. Nothing is missing from both at once.

Five URI shapes, one class each, matching how Mcp\Tools\Agent already splits
tools out rather than carrying them as methods on a 2000-line command:

  plans://all                     AllPlansResource
  plans://{slug}                  PlanDocumentResource
  plans://{slug}/phases/{order}   PhaseChecklistResource
  plans://{slug}/state/{key}      StateValueResource
  sessions://{id}/context         SessionContextResource

AgentResourceRegistry lists and routes them, the counterpart to
AgentToolRegistry. Resources contribute their own list entries, so the plan
document resource enumerates every non-archived plan while the unbounded
per-key ones advertise nothing and stay directly addressable.

Two deliberate departures from the code being ported.

A missing target now answers a JSON-RPC -32602 instead of returning the
string "Plan not found: {slug}" as the resource body — a client could not
distinguish that from a real document whose text happens to say so.

And initialize advertises the resources capability. Implementing the surface
without declaring it would leave it undiscoverable: a client that is not told
the server has resources never issues resources/list, so it would have been
shipped and never called, which is the failure this whole sweep exists to
remove.

Registered as a container singleton in register(), not hung off an event like
onMcpTools. There is no McpResourcesRegistering to listen for, and $listens
is populated by ModuleScanner from app/Core|Mod|Website only — dead once this
package sits under vendor/ — so a binding is what actually resolves in a host
application.

Ten new tests: six over the registry (routing, declining near-miss shapes,
reading, dynamic listing, and returning null rather than a document for a
missing target) and four driving the real stdio command end to end for the
initialize advertisement, resources/list, resources/read and its error path.
Suite: 156 failed, 1165 passed, from 156 failed, 1155 passed — the same
failures, plus exactly these ten.

Co-Authored-By: Virgil <virgil@lethean.io>
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c574605c-b6d6-4aa2-afd7-7fe0bb5b2bca

📥 Commits

Reviewing files that changed from the base of the PR and between 1fd5971 and 12b924e.

📒 Files selected for processing (11)
  • php/Boot.php
  • php/Mcp/Console/McpAgentServerCommand.php
  • php/Mcp/Resources/Agent/AgentResource.php
  • php/Mcp/Resources/Agent/AllPlansResource.php
  • php/Mcp/Resources/Agent/PhaseChecklistResource.php
  • php/Mcp/Resources/Agent/PlanDocumentResource.php
  • php/Mcp/Resources/Agent/SessionContextResource.php
  • php/Mcp/Resources/Agent/StateValueResource.php
  • php/Services/AgentResourceRegistry.php
  • php/tests/Feature/Mcp/Console/McpAgentServerCommandTest.php
  • php/tests/Feature/Mcp/Resources/AgentResourcesTest.php

📝 Walkthrough

Walkthrough

Changes

Agent resource support

Layer / File(s) Summary
Resource contracts and renderers
php/Mcp/Resources/Agent/*
Adds the AgentResource contract and resources for plans, phases, state values, and session context. Each resource validates URIs and renders Markdown content where applicable.
Registry and service wiring
php/Services/AgentResourceRegistry.php, php/Boot.php
Adds resource registration, URI resolution, entry aggregation, and content reading. Boot registers five agent resources and updates Agentic service references.
MCP resource operations
php/Mcp/Console/McpAgentServerCommand.php
Adds resource capability advertisement, resources/list, and resources/read handling. The command returns resource content or JSON-RPC -32602 errors.
Resource integration coverage
php/tests/Feature/Mcp/Resources/AgentResourcesTest.php, php/tests/Feature/Mcp/Console/McpAgentServerCommandTest.php
Tests resource registration, URI routing, listing, Markdown reads, missing resources, and MCP capability responses.

Sequence Diagram(s)

sequenceDiagram
  participant MCPClient
  participant McpAgentServerCommand
  participant AgentResourceRegistry
  participant AgentResource
  MCPClient->>McpAgentServerCommand: resources/list or resources/read
  McpAgentServerCommand->>AgentResourceRegistry: entries() or read(uri)
  AgentResourceRegistry->>AgentResource: matches(uri) and read(uri)
  AgentResource-->>AgentResourceRegistry: metadata or Markdown content
  AgentResourceRegistry-->>McpAgentServerCommand: resource response data
  McpAgentServerCommand-->>MCPClient: JSON-RPC result or -32602 error
Loading

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


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.

@Snider
Snider merged commit a0debd8 into main Aug 8, 2026
3 of 5 checks passed
@Snider
Snider deleted the feat/mcp-agent-resources branch August 8, 2026 10:45
@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Snider added a commit to dAppCore/mcp that referenced this pull request Aug 8, 2026
…dy owns (#19)

This package imported Core\Mod\Agentic\Models\* in four files while
dappcore/agent is meant to depend on it — a library importing the thing that
imports it. AX-8: the arrow points one way.

Three files go because agent already owns them, verified rather than assumed:

  AgentToolRegistry     agent's has all 13 shared methods plus 6 more, none
                        only here. Unreferenced in this package — even
                        McpApiController reaches for agent's FQCN, not this.
  AgentSessionService   agent's has all 20 shared plus replay and
                        getReplayContext, none only here. Zero references.
  McpAgentServerCommand 2064 lines. Its 29 tool methods each map to an
                        extracted class in agent's Mcp\Tools\Agent, and
                        agent's Boot::onMcpTools registers every one — checked
                        for registration, not just for the files existing.

Its five resource handlers were the one thing agent did NOT have: agent's
server answered resources/list with a hardcoded empty array and had no
resources/read. Deleting this first would have silently dropped
plans://all, plans://{slug}, plans://{slug}/phases/{order},
plans://{slug}/state/{key} and sessions://{id}/context. They were ported to
agent first, in dAppCore/agent#20, and only then is this removed.

McpApiController carried a third copy of the same rendering, and that is what
made the cycle a compile-time one. The plan and session renderers are replaced
by AgentResourceProvider, an interface this package owns and the module that
owns the data implements, resolved from the container and absent without
complaint when agent is not installed. Both model imports go with them.

Interim worth stating plainly: nothing is bound to AgentResourceProvider yet,
so this package's HTTP plans:// and sessions:// endpoints answer "not found"
until agent binds its registry to it. Agent implementing the interface needs
agent to depend on dappcore/mcp, which is still blocked on the Core\Mcp\ PSR-4
root being claimed by both packages. The data itself is not lost — agent's own
MCP server serves all five URIs as of #20; it is the HTTP mirror here that is
degraded until that lands.

Two soft couplings remain by design, both container lookups by string with no
import and a guard: AgentToolRegistry in executeTool and AgentApiKeyService in
McpAuthenticate. Neither is a compile-time dependency and neither creates a
composer cycle; both would be better behind interfaces, and are left for when
the namespace collision is resolved.

Suite unchanged: 21 failed, 299 passed. 2786 deletions, 102 insertions.
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