{{ message }}
Repository navigation
feat(mcp): add resource templates and a resources changed hook - #452
Open
erkamyaman wants to merge 4 commits into
Open
erkamyaman wants to merge 4 commits into
erkamyaman wants to merge 4 commits into
Conversation
|
👁️🗨️ Review this pull request with grouped, summarized diffs at: raw result💭 analyzed by {
"headSha": "61427efbcc5307381b1f1dd71c4ba4008e1d820b",
"result": {
"overallSummary": "Adds two MCP route options that let a host serve parameterized resources and notify agents of resource-list changes: `mcp.resourceTemplates` (RFC 6570 level-1 URI templates for `resources/templates/list`/`read`/`list`) and `mcp.onResourcesChanged` (a `notify()` hook sending `notifications/resources/list_changed`). The options are threaded through the existing MCP option path (`McpRouteOptions`, `resolveMcpConfig`, `mountMcpRoute`, hub `initHub`, `createMcpFetchHandler`, stdio `createMcpServer`, Next.js host), backed by a new DF0084 validation error, extensive tests, and docs.",
"groups": [
{
"key": "resource-templates-feature",
"label": "Resource templates & hook",
"summary": "Adds `mcp.resourceTemplates` (RFC 6570 level-1 URI templates served through `resources/templates/list`/`resources/read`/`resources/list`) and `mcp.onResourcesChanged` (a host hook that notifies `notifications/resources/list_changed`). The new options and types flow through the same path as other MCP route options: compile/validate in `build-server.ts` (new DF0084 diagnostic), wire into the fetch handler, the devframe route config/mount, the hub's aggregate route, and the Next.js host.",
"category": "api",
"filePaths": [
"packages/agentic/src/mcp/build-server.ts",
"packages/agentic/src/mcp/fetch.ts",
"packages/devframe/src/adapters/_shared.ts",
"packages/devframe/src/adapters/initiate.ts",
"packages/devframe/src/adapters/mcp.ts",
"packages/devframe/src/node/diagnostics.ts",
"packages/devframe/src/types/devframe.ts",
"packages/devframe/src/types/mcp.ts",
"packages/hub/src/node/initiate.ts",
"packages/next/src/host.ts"
],
"fileNotes": [
{
"path": "packages/agentic/src/mcp/build-server.ts",
"text": "Core logic: single-pass, non-backtracking URI template matcher (percent-decoding, no-`/` segment rule), template compilation/validation (DF0084), and the `bridgeListChanged` wiring for the new `onResourcesChanged` hook with unsubscribe-on-close semantics.",
"critical": true
}
]
},
{
"key": "tests",
"label": "Tests",
"summary": "Coverage for the new options: template listing/reading/matching edge cases (percent-decoding, multi-variable segments, ReDoS resistance, `__proto__`), DF0084 for invalid templates, `onResourcesChanged` notify/unsubscribe over in-memory, HTTP and hub transports.",
"category": "tests",
"filePaths": [
"packages/agentic/src/mcp/__tests__/mcp-http.test.ts",
"packages/agentic/src/mcp/__tests__/mcp-server.test.ts",
"packages/hub/src/node/__tests__/initiate.test.ts"
]
},
{
"key": "docs",
"label": "Documentation",
"summary": "Documents `resourceTemplates` and `onResourcesChanged`, and adds the DF0084 error page.",
"category": "docs",
"filePaths": [
"docs/content/1.guide/18.hub-initiate.md",
"docs/content/2.adapters/7.mcp.md",
"docs/content/6.errors/DF0084.md",
"docs/content/6.errors/index.md"
]
},
{
"key": "type-snapshots",
"label": "Type snapshots",
"summary": "Generated public-API type snapshots updated to reflect the new types and options.",
"category": "other",
"filePaths": [
"tests/__snapshots__/tsnapi/@devframes/next/hub.snapshot.d.ts",
"tests/__snapshots__/tsnapi/devframe/index.snapshot.d.ts",
"tests/__snapshots__/tsnapi/devframe/internal.snapshot.d.ts",
"tests/__snapshots__/tsnapi/devframe/types.snapshot.d.ts",
"tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts"
]
}
],
"schemaVersion": 1,
"source": "llm",
"generatedAt": "2026-10-20T19:19:35.309Z",
"model": "vercel-ai-gateway/anthropic/claude-sonnet-5",
"locale": "en"
}
} |
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Template matching can block the process and fails to preserve an accepted parameter name.
2 open findings
What changed in this PR
Adds parameterized MCP resources and resource-list change notifications across devframe, hub, and Next.js integrations.
Changes:
- Adds resource template listing, matching, reading, and DF0084 validation.
- Connects resource-change subscriptions to HTTP and stdio notifications.
- Adds integration tests, public types, API snapshots, and documentation.
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
6 tasks done
A devframe can pass `mcp.resourceTemplates` to serve resources whose URI holds parameters, such as one page's data, and `mcp.onResourcesChanged` to tell coding agents the resource list changed. An invalid template fails at mount with DF0084. Without them the server is unchanged.
Undefined fields drop out of the JSON result, so the listings pass them as is. The devframe and hub initiate tests that only repeated what the HTTP and in-memory tests already catch are removed.
…alue
A template with several values in one segment, such as `{a}-{b}-{c}`,
compiled to a regular expression with overlapping `([^/]+)` groups, so a
crafted URI could backtrack long enough to block the process. Templates
now match in one pass from left to right with no regular expression: each
value runs to the first place the next text appears. Two expressions
with nothing between them (`{a}{b}`) throw DF0084, since a URI cannot
show where one value ends.
The params object has no prototype, so a variable named `__proto__`
keeps its value.
A value ran to the first place the next literal appeared, even when that
place was inside a `%XX` escape of the value. With `{a}D{b}`, the URI
`a%2DbDc` split at the `D` of `%2D`, so `a` could not be decoded and a
valid URI matched no template. The search now skips places inside an
escape, so `a` reads `a-b` and `b` reads `c`.
erkamyaman
force-pushed
the
feat/mcp-resource-templates
branch
from
October 20, 2026 19:18
be9aa46 to
61427ef
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



What
Two new MCP route options.
mcp.resourceTemplatesserves resources whose URI holds parameters, such as one page's data:resources/templates/listreturns the templates andresources/listadds each template'slist()entries.resources/readtries the templates after the registered and shared-state resources, and before the existing unknown-resource error. The first matching template wins. A result istext, orjsonstringified like the other resources.{var}) only. Each variable matches one non-empty segment and is percent-decoded, so%2Fgives a/. Malformed percent-encoding does not match.{}, a stray brace, a repeated name) fails when the route mounts, with DF0084.readorlistthat throws reaches the client the same way a throwing resource read does today.mcp.onResourcesChanged(notify, ctx)lets the host tell coding agents that the resource list changed.notify()sendsnotifications/resources/list_changedover HTTP and stdio. The returned unsubscribe runs when the server closes, and anotify()after that does nothing.list,readand the hook take the devframe context as their last argument. Route options are resolved beforesetup()runs, so this is how they reach live state. In the hub they get the hub context.Without these options the server is unchanged:
resources/templates/liststays method-not-found andresources/listis the same.The options follow the same path as the other MCP route options:
McpRouteOptions,resolveMcpConfig,mountMcpRoute, the hub'sinitHub,createMcpFetchHandler, stdiocreateMcpServerand the Next.js host.New types:
McpResourceTemplate,McpResourceTemplateEntry,McpResourceTemplateContent,McpResourcesChangedHook.Why
Pangular Inspector lets coding agents read one browser tab's data as
pangular://page/{pageId}/<name>, and tells them when a tab connects or goes away. Today it does this through a local patch of the dist. This moves both options into devframe.Tests
mcp-server.test.ts: no change without templates,templates/list, reads with different parameters and several variables, percent-decoding, no match for an extra/, an empty segment or a malformed%, first match wins, registered resources first,list()entries, the context passed in, a throwing read, DF0084 for 8 invalid templates, andonResourcesChangedsendinglist_changedand unsubscribing on close.mcp-http.test.ts: templates over Streamable HTTP, andlist_changedon alistenstream.initiate.test.ts: the options reach the route, and the hook gets the hub context and unsubscribes on close.pnpm lint && pnpm knip && pnpm test && pnpm typecheck && pnpm buildpass.Note: DF0081 to DF0083 are left free because #451 and the prompts PR use them.