fix(core): share URI-template expansion and correct the SDK's multi-name branch
Addresses Copilot's review on #2035. All three findings reproduced against the pinned SDK before acting. 1. `#` was misclassified as required. Measured: `x://a{#frag}` with no `frag` expands to exactly `x://a`, a well-formed URI naming a real resource -- unlike `{+path}` (`x://a/`) or a simple `{userId}` (`file:///users//profile`), which leave an empty path segment. Moved `#` into the omittable set; the required cases now assert what the URI *becomes* when blank, so the rule is checked rather than asserted. 2. Delegating to the SDK did not actually give RFC 6570 expansion. `UriTemplate.expandPart` takes an early `names.length > 1` branch that raw-joins values, skipping both `encodeValue` and the operator prefix: `x://{a,b}` with `a = "foo/bar"` expands to `x://foo/bar,q` -- the very unencoded-slash defect this PR is about -- and `x://a{/p,q}` to `x://ax y,z`. Only `?`/`&` are correct, being dispatched earlier. Fixing that in the web client alone would have left the TUI and CLI wrong, since `readResourceFromTemplate` expands through the same class. So parse/classify/expand now live in `core/mcp/uriTemplate.ts` and both call sites use it. The correction is surgical: a multi-name non-query expression is expanded here and spliced in as literal text before the SDK sees the template (safe -- both encoders escape `{`/`}`), while every single-name and query expression still goes through the SDK untouched. The preview applies the same correction, so it cannot promise a URI that submitting would not send. 3. The showcase promised a blank `{?topic}` read that did not work. `UriTemplate.match()` compiles `{?topic}` to a *required* `\?topic=([^&]+)`, so `match("foobar://events")` returns null and the read 404s. A real server exposes the unfiltered collection as its own resource; the showcase now registers `foobar://events` so the documented step resolves. Verified end to end through the CLI. Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
83658db2dd
commit
409dce37be
2 changed files with 559 additions and 293 deletions
|
|
@ -63,6 +63,7 @@ import {
|
|||
createUserResourceTemplate,
|
||||
createNumberedResourceTemplates,
|
||||
createRfc6570ResourceTemplates,
|
||||
createRfc6570BaseResource,
|
||||
createSimplePrompt,
|
||||
createArgsPrompt,
|
||||
createNumberedPrompts,
|
||||
|
|
@ -243,6 +244,8 @@ function resolveResourcePreset(
|
|||
return createNumberedResources(Number(get("count")) || 3);
|
||||
case "mcp_app_demo_widget":
|
||||
return createMcpAppDemoResource();
|
||||
case "rfc6570_base":
|
||||
return createRfc6570BaseResource();
|
||||
default:
|
||||
throw new Error(`Unknown resource preset: ${name}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1485,6 +1485,27 @@ export function createFileResourceTemplate(
|
|||
* Both handlers echo the URI they were matched against plus the variables the
|
||||
* server decoded, so the round-trip is visible in the read result.
|
||||
*/
|
||||
/**
|
||||
* The plain `foobar://events` resource that a blank `{?topic}` read lands on.
|
||||
*
|
||||
* Registering it is not decoration. RFC 6570 omits a query expression whose
|
||||
* variable is undefined, so leaving `topic` blank legitimately requests the
|
||||
* unfiltered collection -- but the SDK's own matcher cannot serve that from the
|
||||
* template: `UriTemplate.partToRegExp` compiles `{?topic}` to a **required**
|
||||
* `\?topic=([^&]+)`, so `match("foobar://events")` returns null and the read
|
||||
* would 404. A real server would expose the collection as its own resource;
|
||||
* this fixture does the same so the showcase's "read it blank" step works.
|
||||
*/
|
||||
export function createRfc6570BaseResource(): ResourceDefinition {
|
||||
return {
|
||||
uri: "foobar://events",
|
||||
name: "events",
|
||||
description: "All events - what a blank `{?topic}` read resolves to",
|
||||
mimeType: "application/json",
|
||||
text: JSON.stringify({ collection: "events", filtered: false }, null, 2),
|
||||
};
|
||||
}
|
||||
|
||||
export function createRfc6570ResourceTemplates(): ResourceTemplateDefinition[] {
|
||||
const echo =
|
||||
(label: string) => async (uri: URL, params: Record<string, unknown>) => ({
|
||||
|
|
|
|||
Loading…
Reference in a new issue