@scenar/mcp-server 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -9
- package/package.json +2 -3
- package/skill/SKILL.md +25 -21
- package/skill/reference.md +49 -17
- package/src/__tests__/server.test.ts +7 -12
- package/src/project.d.ts +0 -2
- package/src/project.d.ts.map +1 -1
- package/src/project.js +0 -10
- package/src/project.js.map +1 -1
- package/src/project.ts +0 -10
- package/src/resources.d.ts +4 -5
- package/src/resources.d.ts.map +1 -1
- package/src/resources.js +5 -23
- package/src/resources.js.map +1 -1
- package/src/resources.ts +5 -57
- package/src/tools.d.ts.map +1 -1
- package/src/tools.js +56 -65
- package/src/tools.js.map +1 -1
- package/src/tools.ts +59 -73
- package/tsconfig.tsbuildinfo +1 -1
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
The [Model Context Protocol](https://modelcontextprotocol.io) server for
|
|
4
4
|
[Scenar](https://github.com/stigmer/scenar) — it lets AI editors (Cursor, Claude
|
|
5
|
-
Desktop, etc.)
|
|
6
|
-
serve or publish the embed. A thin, type-safe wrapper over the
|
|
7
|
-
programmatic API.
|
|
5
|
+
Desktop, etc.) bootstrap a demos project, author a scenario tour, narrate it,
|
|
6
|
+
pack it, and serve or publish the embed. A thin, type-safe wrapper over the
|
|
7
|
+
`@scenar/cli` programmatic API.
|
|
8
8
|
|
|
9
9
|
## Install in Cursor
|
|
10
10
|
|
|
@@ -31,8 +31,7 @@ Restart your editor.
|
|
|
31
31
|
|
|
32
32
|
| Tool | What it does |
|
|
33
33
|
|------|-------------|
|
|
34
|
-
| `
|
|
35
|
-
| `scenar_preview_sync` | Re-scan after code changes (preserves your edits) |
|
|
34
|
+
| `scenar_install` | Bootstrap a demos project → scaffold + add deps + starter tour |
|
|
36
35
|
| `scenar_validate` | Validate a scenario YAML or directory |
|
|
37
36
|
| `scenar_narrate` | Synthesize TTS audio for a scenario |
|
|
38
37
|
| `scenar_pack` | Bundle a scenario into a static embed |
|
|
@@ -43,10 +42,8 @@ Restart your editor.
|
|
|
43
42
|
|
|
44
43
|
## Resources
|
|
45
44
|
|
|
46
|
-
`scenar://
|
|
47
|
-
|
|
48
|
-
`scenar://scenario/{name}/manifest` expose read-only context the model can
|
|
49
|
-
inspect.
|
|
45
|
+
`scenar://scenario/{name}/steps` and `scenar://scenario/{name}/manifest` expose
|
|
46
|
+
read-only context the model can inspect.
|
|
50
47
|
|
|
51
48
|
See the [Scenar docs](https://github.com/stigmer/scenar/blob/main/docs/mcp-server.md)
|
|
52
49
|
for configuration in other editors.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scenar/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Model Context Protocol server for Scenar — lets AI editors scan, author, narrate, pack, serve, and publish scenario tours.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -25,8 +25,7 @@
|
|
|
25
25
|
"types": "./src/index.d.ts",
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
28
|
-
"@scenar/cli": "0.
|
|
29
|
-
"@scenar/preview": "0.2.1",
|
|
28
|
+
"@scenar/cli": "0.4.0",
|
|
30
29
|
"zod": "^3.23.8"
|
|
31
30
|
}
|
|
32
31
|
}
|
package/skill/SKILL.md
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: scenar
|
|
3
3
|
description: >-
|
|
4
|
-
Author Scenar scenario tours from a React app:
|
|
5
|
-
|
|
6
|
-
cursor/scroll/type interactions and narration, then pack, serve, and
|
|
7
|
-
the embed. Use when the user wants to build a product tour, demo, or
|
|
4
|
+
Author Scenar scenario tours from a React app: bootstrap a demos project with
|
|
5
|
+
`scenar install`, write steps.ts + index.tsx (or use the createScenario SDK),
|
|
6
|
+
add cursor/scroll/type interactions and narration, then pack, serve, and
|
|
7
|
+
publish the embed. Use when the user wants to build a product tour, demo, or
|
|
8
8
|
interactive embed with Scenar; mentions the @scenar/* packages; or asks to run
|
|
9
|
-
scenar
|
|
10
|
-
@scenar/mcp-server tools
|
|
9
|
+
scenar install/validate/narrate/pack/serve/publish (directly or via the
|
|
10
|
+
@scenar/mcp-server tools scenar_install, scenar_pack, scenar_serve,
|
|
11
11
|
scenar_publish, etc.).
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
# Authoring Scenar Scenarios
|
|
15
15
|
|
|
16
16
|
Scenar turns a React app into a narrated, interactive embed (and an MP4). You,
|
|
17
|
-
the AI, are the authoring engine: you
|
|
18
|
-
the pipeline. This skill is the source of truth for the model and the
|
|
17
|
+
the AI, are the authoring engine: you bootstrap the project, write the scenario,
|
|
18
|
+
and run the pipeline. This skill is the source of truth for the model and the
|
|
19
|
+
workflow.
|
|
19
20
|
|
|
20
21
|
## The pipeline
|
|
21
22
|
|
|
22
23
|
```
|
|
23
|
-
|
|
24
|
+
install → wire providers → author → narrate → pack → serve / publish
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
| Step | CLI | MCP tool |
|
|
27
28
|
|------|-----|----------|
|
|
28
|
-
|
|
|
29
|
-
| Re-scan after code changes | `scenar preview sync` | `scenar_preview_sync` |
|
|
29
|
+
| Bootstrap a demos project + starter tour | `scenar install` | `scenar_install` |
|
|
30
30
|
| Validate a scenario YAML | `scenar validate` | `scenar_validate` |
|
|
31
31
|
| Generate narration audio (TTS) | `scenar narrate` | `scenar_narrate` |
|
|
32
32
|
| Bundle into a static embed | `scenar pack` | `scenar_pack` |
|
|
@@ -40,15 +40,18 @@ structured results. Paths are resolved against the project root.
|
|
|
40
40
|
## Two authoring paths
|
|
41
41
|
|
|
42
42
|
- **Path A — real components (recommended for real tours).** Run
|
|
43
|
-
`scenar
|
|
44
|
-
|
|
45
|
-
`
|
|
46
|
-
|
|
47
|
-
`.scenar/providers.tsx` (
|
|
48
|
-
render without a live backend — do this
|
|
43
|
+
`scenar install <your-component-package>`. It scaffolds a demos project, adds
|
|
44
|
+
the component package as a dependency, and writes a runnable starter tour under
|
|
45
|
+
`tours/example-tour/`. You then author a scenario whose `index.tsx`
|
|
46
|
+
**imports the user's actual components directly** (no registry, no scan). The
|
|
47
|
+
hard part is wiring the tour's `.scenar/providers.tsx` (client + provider +
|
|
48
|
+
mock data) so the components render without a live backend — do this
|
|
49
|
+
deliberately, with the user. The recommended mock for Connect-RPC SDKs is an
|
|
50
|
+
in-process router transport (`createRouterTransport`); resolve the embed theme
|
|
51
|
+
with `getEmbedColorMode()` from `@scenar/react`. See [reference.md](reference.md).
|
|
49
52
|
- **Path B — illustrative components (fastest for generic flows).** Compose the
|
|
50
|
-
`@scenar/react` shells and page templates (no
|
|
51
|
-
`welcome-tour` example is Path B; study it before authoring.
|
|
53
|
+
`@scenar/react` shells and page templates (no product dependency needed). The
|
|
54
|
+
bundled `welcome-tour` example is Path B; study it before authoring.
|
|
52
55
|
|
|
53
56
|
Both paths produce the same artifact: a **scenario directory**.
|
|
54
57
|
|
|
@@ -163,7 +166,8 @@ templates. Common shells: `BrowserView`, `TerminalView`, `CodeEditorView`,
|
|
|
163
166
|
`FormCard`, `DataTable`, `StatusBadge`, `PulseHighlight`.
|
|
164
167
|
|
|
165
168
|
See [reference.md](reference.md) for the full catalog, the `createScenario` SDK
|
|
166
|
-
(type-safe Path B), provider
|
|
169
|
+
(type-safe Path B), the provider + router-transport wiring for Path A, and the
|
|
170
|
+
bundle constraints.
|
|
167
171
|
|
|
168
172
|
## The one hard constraint: the bundle allowlist
|
|
169
173
|
|
|
@@ -175,7 +179,7 @@ CSS-drawn UI; import raster assets normally (Vite hashes them at pack time).
|
|
|
175
179
|
## Workflow checklist
|
|
176
180
|
|
|
177
181
|
```
|
|
178
|
-
- [ ] Path A:
|
|
182
|
+
- [ ] Path A: scenar_install, then wire the tour's .scenar/providers.tsx (provider + mock data)
|
|
179
183
|
- [ ] Write steps.ts (timeline, captions, narration, interactions)
|
|
180
184
|
- [ ] Write index.tsx (renderStep with shells + data-cursor-target hooks)
|
|
181
185
|
- [ ] scenar_narrate to synthesize audio
|
package/skill/reference.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Scenar Authoring Reference
|
|
2
2
|
|
|
3
3
|
Deep reference for the [scenar skill](SKILL.md): the shell catalog, page parts,
|
|
4
|
-
the `createScenario` SDK (Path B), provider
|
|
5
|
-
shape, and bundle constraints.
|
|
4
|
+
the `createScenario` SDK (Path B), provider + router-transport wiring (Path A),
|
|
5
|
+
the proto/YAML shape, and bundle constraints.
|
|
6
6
|
|
|
7
7
|
## Canonical example
|
|
8
8
|
|
|
@@ -101,22 +101,54 @@ export default createScenario({
|
|
|
101
101
|
`StepInput` uses `narrationText` (not `narration`) and carries `props` instead of
|
|
102
102
|
a free-form `data`. The output plugs straight into `<ScenarioPlayer>`.
|
|
103
103
|
|
|
104
|
-
## Path A — real components: provider +
|
|
105
|
-
|
|
106
|
-
`scenar
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
104
|
+
## Path A — real components: provider + router-transport wiring
|
|
105
|
+
|
|
106
|
+
`scenar install <your-component-package>` adds the package as a dependency and
|
|
107
|
+
scaffolds a runnable starter tour. There is **no registry and no scan**: your
|
|
108
|
+
`index.tsx` imports the real components directly, exactly like any React app.
|
|
109
|
+
|
|
110
|
+
The one piece to wire by hand is each tour's `.scenar/providers.tsx`, which
|
|
111
|
+
exports `PreviewProviders`. `scenar pack` (embed) and `scenar render` (video)
|
|
112
|
+
both wrap every step of the tour in it, so this is where the components get the
|
|
113
|
+
provider and the data they need to render with no live backend.
|
|
114
|
+
|
|
115
|
+
For **Connect-RPC SDKs** (e.g. `@stigmer/react`), mock the RPCs your components
|
|
116
|
+
call with an in-process router transport — no network, no service worker — and
|
|
117
|
+
resolve the embed's light/dark mode with `getEmbedColorMode()` from
|
|
118
|
+
`@scenar/react`:
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
import type { ReactNode } from "react";
|
|
122
|
+
import "@your-org/ui/styles.css"; // pack bundles JS, not Tailwind
|
|
123
|
+
import { createRouterTransport } from "@connectrpc/connect";
|
|
124
|
+
import { getEmbedColorMode } from "@scenar/react";
|
|
125
|
+
import { YourClient } from "@your-org/sdk";
|
|
126
|
+
import { YourProvider } from "@your-org/ui";
|
|
127
|
+
import { ProjectController } from "@your-org/protos/project/v1/query_pb";
|
|
128
|
+
|
|
129
|
+
const transport = createRouterTransport((router) => {
|
|
130
|
+
router.service(ProjectController, {
|
|
131
|
+
list: () => ({ projects: [{ id: "1", name: "checkout-api" }] }),
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
const client = new YourClient({ baseUrl: "/", customTransport: transport });
|
|
135
|
+
|
|
136
|
+
export function PreviewProviders({ children }: { readonly children: ReactNode }) {
|
|
137
|
+
return (
|
|
138
|
+
<YourProvider client={client} colorMode={getEmbedColorMode()}>
|
|
139
|
+
{children}
|
|
140
|
+
</YourProvider>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
```
|
|
114
144
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
145
|
+
If the SDK isn't Connect-RPC based, use whatever in-process mock the client
|
|
146
|
+
accepts — the only contract is that `PreviewProviders` renders the components
|
|
147
|
+
without a backend. The product glue (client + provider + stylesheet) repeats
|
|
148
|
+
across tours, so factor it into one local helper and let each tour's
|
|
149
|
+
`providers.tsx` pass only its fixtures. This wiring is the one step that can't be
|
|
150
|
+
automated — do it explicitly with the user. Re-running `scenar install` never
|
|
151
|
+
overwrites it.
|
|
120
152
|
|
|
121
153
|
## YAML / proto scenarios
|
|
122
154
|
|
|
@@ -21,10 +21,9 @@ describe("Scenar MCP server", () => {
|
|
|
21
21
|
const names = tools.map((t) => t.name).sort();
|
|
22
22
|
expect(names).toEqual(
|
|
23
23
|
[
|
|
24
|
+
"scenar_install",
|
|
24
25
|
"scenar_narrate",
|
|
25
26
|
"scenar_pack",
|
|
26
|
-
"scenar_preview_init",
|
|
27
|
-
"scenar_preview_sync",
|
|
28
27
|
"scenar_publish",
|
|
29
28
|
"scenar_render",
|
|
30
29
|
"scenar_serve",
|
|
@@ -34,14 +33,8 @@ describe("Scenar MCP server", () => {
|
|
|
34
33
|
);
|
|
35
34
|
});
|
|
36
35
|
|
|
37
|
-
it("exposes the
|
|
36
|
+
it("exposes the per-scenario resource templates", async () => {
|
|
38
37
|
const client = await connectClient();
|
|
39
|
-
const { resources } = await client.listResources();
|
|
40
|
-
const uris = resources.map((r) => r.uri);
|
|
41
|
-
expect(uris).toContain("scenar://registry/views");
|
|
42
|
-
expect(uris).toContain("scenar://registry/report");
|
|
43
|
-
expect(uris).toContain("scenar://registry/providers");
|
|
44
|
-
|
|
45
38
|
const { resourceTemplates } = await client.listResourceTemplates();
|
|
46
39
|
const templates = resourceTemplates.map((t) => t.uriTemplate);
|
|
47
40
|
expect(templates).toContain("scenar://scenario/{name}/steps");
|
|
@@ -68,11 +61,13 @@ describe("Scenar MCP server", () => {
|
|
|
68
61
|
}
|
|
69
62
|
});
|
|
70
63
|
|
|
71
|
-
it("reads a
|
|
64
|
+
it("reads a scenario steps resource, returning guidance when absent", async () => {
|
|
72
65
|
const client = await connectClient();
|
|
73
|
-
const result = await client.readResource({
|
|
66
|
+
const result = await client.readResource({
|
|
67
|
+
uri: "scenar://scenario/nope/steps",
|
|
68
|
+
});
|
|
74
69
|
expect(result.contents).toHaveLength(1);
|
|
75
|
-
// With no
|
|
70
|
+
// With no such scenario in the test cwd, the body should guide the user.
|
|
76
71
|
const text = String(result.contents[0]!.text);
|
|
77
72
|
expect(text.length).toBeGreaterThan(0);
|
|
78
73
|
});
|
package/src/project.d.ts
CHANGED
|
@@ -6,6 +6,4 @@
|
|
|
6
6
|
export declare function projectRoot(): string;
|
|
7
7
|
/** Resolve a possibly-relative path against the project root. */
|
|
8
8
|
export declare function resolveInProject(path: string): string;
|
|
9
|
-
/** Read a project-relative text file, or null if it is missing/unreadable. */
|
|
10
|
-
export declare function readProjectFile(relativePath: string): Promise<string | null>;
|
|
11
9
|
//# sourceMappingURL=project.d.ts.map
|
package/src/project.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/project.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/project.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD"}
|
package/src/project.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { resolve, isAbsolute, join } from "node:path";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
2
|
/**
|
|
4
3
|
* The project root the server reads relative paths against. Cursor launches the
|
|
5
4
|
* MCP server with its working directory set to the workspace, but an explicit
|
|
@@ -12,13 +11,4 @@ export function projectRoot() {
|
|
|
12
11
|
export function resolveInProject(path) {
|
|
13
12
|
return isAbsolute(path) ? path : join(projectRoot(), path);
|
|
14
13
|
}
|
|
15
|
-
/** Read a project-relative text file, or null if it is missing/unreadable. */
|
|
16
|
-
export async function readProjectFile(relativePath) {
|
|
17
|
-
try {
|
|
18
|
-
return await readFile(join(projectRoot(), relativePath), "utf-8");
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
14
|
//# sourceMappingURL=project.js.map
|
package/src/project.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtD;;;;GAIG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;AACnE,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,CAAC;AAC7D,CAAC"}
|
package/src/project.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { resolve, isAbsolute, join } from "node:path";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* The project root the server reads relative paths against. Cursor launches the
|
|
@@ -14,12 +13,3 @@ export function projectRoot(): string {
|
|
|
14
13
|
export function resolveInProject(path: string): string {
|
|
15
14
|
return isAbsolute(path) ? path : join(projectRoot(), path);
|
|
16
15
|
}
|
|
17
|
-
|
|
18
|
-
/** Read a project-relative text file, or null if it is missing/unreadable. */
|
|
19
|
-
export async function readProjectFile(relativePath: string): Promise<string | null> {
|
|
20
|
-
try {
|
|
21
|
-
return await readFile(join(projectRoot(), relativePath), "utf-8");
|
|
22
|
-
} catch {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
}
|
package/src/resources.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
/**
|
|
3
|
-
* Register read-only resources the AI can inspect for authoring context:
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* gracefully.
|
|
3
|
+
* Register read-only resources the AI can inspect for authoring context: a
|
|
4
|
+
* scenario's steps and its packed bundle's manifest. Both are read relative to
|
|
5
|
+
* the project root; a missing file returns a short explanatory body rather than
|
|
6
|
+
* an error so the client degrades gracefully.
|
|
8
7
|
*/
|
|
9
8
|
export declare function registerResources(server: McpServer): void;
|
|
10
9
|
//# sourceMappingURL=resources.d.ts.map
|
package/src/resources.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/resources.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAoB,MAAM,yCAAyC,CAAC;AAGtF
|
|
1
|
+
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/resources.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAoB,MAAM,yCAAyC,CAAC;AAGtF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAmDzD"}
|
package/src/resources.js
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
-
import { projectRoot
|
|
4
|
+
import { projectRoot } from "./project.js";
|
|
5
5
|
/**
|
|
6
|
-
* Register read-only resources the AI can inspect for authoring context:
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* gracefully.
|
|
6
|
+
* Register read-only resources the AI can inspect for authoring context: a
|
|
7
|
+
* scenario's steps and its packed bundle's manifest. Both are read relative to
|
|
8
|
+
* the project root; a missing file returns a short explanatory body rather than
|
|
9
|
+
* an error so the client degrades gracefully.
|
|
11
10
|
*/
|
|
12
11
|
export function registerResources(server) {
|
|
13
|
-
registerRegistryFile(server, "scenar-views", "scenar://registry/views", "Scenar view registry", "The generated .scenar/views.ts — the components available to scenarios.", ".scenar/views.ts", "text/plain");
|
|
14
|
-
registerRegistryFile(server, "scenar-report", "scenar://registry/report", "Scenar scan report", "The .scenar/report.md — what the scanner discovered, skipped, and why.", ".scenar/report.md", "text/markdown");
|
|
15
|
-
registerRegistryFile(server, "scenar-providers", "scenar://registry/providers", "Scenar provider wiring", "The .scenar/providers.tsx — how real components are wrapped for preview.", ".scenar/providers.tsx", "text/plain");
|
|
16
12
|
// Per-scenario resources: {name} is a scenario directory under the project.
|
|
17
13
|
server.registerResource("scenar-scenario-steps", new ResourceTemplate("scenar://scenario/{name}/steps", { list: undefined }), {
|
|
18
14
|
title: "Scenario steps",
|
|
@@ -53,20 +49,6 @@ export function registerResources(server) {
|
|
|
53
49
|
};
|
|
54
50
|
});
|
|
55
51
|
}
|
|
56
|
-
function registerRegistryFile(server, id, uri, title, description, relativePath, mimeType) {
|
|
57
|
-
server.registerResource(id, uri, { title, description, mimeType }, async (u) => {
|
|
58
|
-
const body = await readProjectFile(relativePath);
|
|
59
|
-
return {
|
|
60
|
-
contents: [
|
|
61
|
-
{
|
|
62
|
-
uri: u.href,
|
|
63
|
-
mimeType,
|
|
64
|
-
text: body ?? `No ${relativePath} yet. Run scenar_preview_init to generate it.`,
|
|
65
|
-
},
|
|
66
|
-
],
|
|
67
|
-
};
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
52
|
async function readFirstExisting(paths) {
|
|
71
53
|
for (const path of paths) {
|
|
72
54
|
try {
|
package/src/resources.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../../src/resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAa,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AACtF,OAAO,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../../src/resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAa,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AACtF,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAiB;IACjD,4EAA4E;IAC5E,MAAM,CAAC,gBAAgB,CACrB,uBAAuB,EACvB,IAAI,gBAAgB,CAAC,gCAAgC,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAC3E;QACE,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,oEAAoE;KAClF,EACD,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE;QACvB,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC;YACnC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,UAAU,CAAC;YACrC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,WAAW,CAAC;SACvC,CAAC,CAAC;QACH,OAAO;YACL,QAAQ,EAAE;gBACR;oBACE,GAAG,EAAE,GAAG,CAAC,IAAI;oBACb,QAAQ,EAAE,YAAY;oBACtB,IAAI,EAAE,IAAI,IAAI,mCAAmC,IAAI,WAAW,WAAW,EAAE,GAAG;iBACjF;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,gBAAgB,CACrB,0BAA0B,EAC1B,IAAI,gBAAgB,CAAC,mCAAmC,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAC9E;QACE,KAAK,EAAE,wBAAwB;QAC/B,WAAW,EAAE,8EAA8E;KAC5F,EACD,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE;QACvB,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC;YACnC,IAAI,CAAC,WAAW,EAAE,EAAE,GAAG,IAAI,SAAS,EAAE,oBAAoB,CAAC;YAC3D,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,oBAAoB,CAAC;SAChD,CAAC,CAAC;QACH,OAAO;YACL,QAAQ,EAAE;gBACR;oBACE,GAAG,EAAE,GAAG,CAAC,IAAI;oBACb,QAAQ,EAAE,kBAAkB;oBAC5B,IAAI,EAAE,IAAI,IAAI,oCAAoC,IAAI,2BAA2B;iBAClF;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,KAAe;IAC9C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,WAAW;QACb,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/src/resources.ts
CHANGED
|
@@ -1,44 +1,15 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
-
import { projectRoot
|
|
4
|
+
import { projectRoot } from "./project.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Register read-only resources the AI can inspect for authoring context:
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* gracefully.
|
|
7
|
+
* Register read-only resources the AI can inspect for authoring context: a
|
|
8
|
+
* scenario's steps and its packed bundle's manifest. Both are read relative to
|
|
9
|
+
* the project root; a missing file returns a short explanatory body rather than
|
|
10
|
+
* an error so the client degrades gracefully.
|
|
12
11
|
*/
|
|
13
12
|
export function registerResources(server: McpServer): void {
|
|
14
|
-
registerRegistryFile(
|
|
15
|
-
server,
|
|
16
|
-
"scenar-views",
|
|
17
|
-
"scenar://registry/views",
|
|
18
|
-
"Scenar view registry",
|
|
19
|
-
"The generated .scenar/views.ts — the components available to scenarios.",
|
|
20
|
-
".scenar/views.ts",
|
|
21
|
-
"text/plain",
|
|
22
|
-
);
|
|
23
|
-
registerRegistryFile(
|
|
24
|
-
server,
|
|
25
|
-
"scenar-report",
|
|
26
|
-
"scenar://registry/report",
|
|
27
|
-
"Scenar scan report",
|
|
28
|
-
"The .scenar/report.md — what the scanner discovered, skipped, and why.",
|
|
29
|
-
".scenar/report.md",
|
|
30
|
-
"text/markdown",
|
|
31
|
-
);
|
|
32
|
-
registerRegistryFile(
|
|
33
|
-
server,
|
|
34
|
-
"scenar-providers",
|
|
35
|
-
"scenar://registry/providers",
|
|
36
|
-
"Scenar provider wiring",
|
|
37
|
-
"The .scenar/providers.tsx — how real components are wrapped for preview.",
|
|
38
|
-
".scenar/providers.tsx",
|
|
39
|
-
"text/plain",
|
|
40
|
-
);
|
|
41
|
-
|
|
42
13
|
// Per-scenario resources: {name} is a scenario directory under the project.
|
|
43
14
|
server.registerResource(
|
|
44
15
|
"scenar-scenario-steps",
|
|
@@ -91,29 +62,6 @@ export function registerResources(server: McpServer): void {
|
|
|
91
62
|
);
|
|
92
63
|
}
|
|
93
64
|
|
|
94
|
-
function registerRegistryFile(
|
|
95
|
-
server: McpServer,
|
|
96
|
-
id: string,
|
|
97
|
-
uri: string,
|
|
98
|
-
title: string,
|
|
99
|
-
description: string,
|
|
100
|
-
relativePath: string,
|
|
101
|
-
mimeType: string,
|
|
102
|
-
): void {
|
|
103
|
-
server.registerResource(id, uri, { title, description, mimeType }, async (u) => {
|
|
104
|
-
const body = await readProjectFile(relativePath);
|
|
105
|
-
return {
|
|
106
|
-
contents: [
|
|
107
|
-
{
|
|
108
|
-
uri: u.href,
|
|
109
|
-
mimeType,
|
|
110
|
-
text: body ?? `No ${relativePath} yet. Run scenar_preview_init to generate it.`,
|
|
111
|
-
},
|
|
112
|
-
],
|
|
113
|
-
};
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
65
|
async function readFirstExisting(paths: string[]): Promise<string | null> {
|
|
118
66
|
for (const path of paths) {
|
|
119
67
|
try {
|
package/src/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/tools.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAsCzE,gDAAgD;AAChD,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/tools.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAsCzE,gDAAgD;AAChD,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CASrD"}
|