@scenar/mcp-server 0.3.0 → 0.5.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 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.) scan a React app, author a scenario tour, narrate it, pack it, and
6
- serve or publish the embed. A thin, type-safe wrapper over the `@scenar/cli`
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
- | `scenar_preview_init` | Scan a React project → `.scenar/` registry + report |
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://registry/views`, `scenar://registry/report`,
47
- `scenar://registry/providers`, `scenar://scenario/{name}/steps`, and
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.0",
3
+ "version": "0.5.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.3.0",
29
- "@scenar/preview": "0.3.0",
28
+ "@scenar/cli": "0.5.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: scan components with `scenar
5
- preview`, write steps.ts + index.tsx (or use the createScenario SDK), add
6
- cursor/scroll/type interactions and narration, then pack, serve, and publish
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 preview/validate/narrate/pack/serve/publish (directly or via the
10
- @scenar/mcp-server tools scenar_preview_init, scenar_pack, scenar_serve,
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 scan the app, write the scenario, and run
18
- the pipeline. This skill is the source of truth for the model and the workflow.
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
- scan → wire providers → author → narrate → pack → serve / publish
24
+ install → wire providers → author → narrate → pack → serve / publish
24
25
  ```
25
26
 
26
27
  | Step | CLI | MCP tool |
27
28
  |------|-----|----------|
28
- | Scan a React app into a view registry | `scenar preview init` | `scenar_preview_init` |
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 preview init --source ./src`. It scans the project and writes a
44
- `.scenar/` registry (`views.ts`, `views.custom.tsx`, `providers.tsx`,
45
- `report.md`) plus an MSW service worker. You then author a scenario that
46
- renders the user's *actual* components. The hard part is wiring
47
- `.scenar/providers.tsx` (theme/router/data) and MSW handlers so the components
48
- render without a live backend — do this deliberately, with the user.
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 scan needed). The bundled
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
 
@@ -56,7 +59,7 @@ Both paths produce the same artifact: a **scenario directory**.
56
59
 
57
60
  ```
58
61
  my-tour/
59
- ├── steps.ts # the timeline (data + timing + captions + interactions)
62
+ ├── steps.ts # the timeline (data + timing + narration + interactions)
60
63
  ├── index.tsx # renderStep(data, stepIndex) → ReactNode
61
64
  └── narration/ # generated by `scenar narrate` (do not hand-write)
62
65
  ```
@@ -75,7 +78,6 @@ export interface StepData { url: string; screen: "login" | "dashboard"; }
75
78
  export const steps: ScenarioStep<StepData>[] = [
76
79
  {
77
80
  delayMs: 0, // dwell before reveal; first step is 0
78
- caption: "Sign in to Acme Cloud", // short label under the player
79
81
  narration: "Start by signing in to your workspace.", // TTS script (build-time)
80
82
  data: { url: "app.acme.cloud/login", screen: "login" },
81
83
  interactions: [ // optional timed actions (see below)
@@ -87,7 +89,7 @@ export const steps: ScenarioStep<StepData>[] = [
87
89
  ];
88
90
  ```
89
91
 
90
- Fields: `delayMs` (required), `data` (required), `caption?`, `narration?`,
92
+ Fields: `delayMs` (required), `data` (required), `narration?`,
91
93
  `interactions?`. Keep one idea per step.
92
94
 
93
95
  ### index.tsx — the render function
@@ -163,7 +165,8 @@ templates. Common shells: `BrowserView`, `TerminalView`, `CodeEditorView`,
163
165
  `FormCard`, `DataTable`, `StatusBadge`, `PulseHighlight`.
164
166
 
165
167
  See [reference.md](reference.md) for the full catalog, the `createScenario` SDK
166
- (type-safe Path B), provider/MSW wiring for Path A, and the bundle constraints.
168
+ (type-safe Path B), the provider + router-transport wiring for Path A, and the
169
+ bundle constraints.
167
170
 
168
171
  ## The one hard constraint: the bundle allowlist
169
172
 
@@ -175,15 +178,15 @@ CSS-drawn UI; import raster assets normally (Vite hashes them at pack time).
175
178
  ## Workflow checklist
176
179
 
177
180
  ```
178
- - [ ] Path A: scenar_preview_init, review report.md, wire providers.tsx + MSW
179
- - [ ] Write steps.ts (timeline, captions, narration, interactions)
181
+ - [ ] Path A: scenar_install, then wire the tour's .scenar/providers.tsx (provider + mock data)
182
+ - [ ] Write steps.ts (timeline, narration, interactions)
180
183
  - [ ] Write index.tsx (renderStep with shells + data-cursor-target hooks)
181
184
  - [ ] scenar_narrate to synthesize audio
182
185
  - [ ] scenar_pack to bundle
183
- - [ ] scenar_serve to preview, iterate on timing/captions
186
+ - [ ] scenar_serve to preview, iterate on timing/narration
184
187
  - [ ] scenar_publish for a public URL (or scenar_render for an MP4)
185
188
  ```
186
189
 
187
190
  Author for quality: realistic copy, sensible pacing (2–3s dwell per step),
188
- captions that read as a story, and interactions that mirror what a real user
191
+ narration that reads as a story, and interactions that mirror what a real user
189
192
  would do.
@@ -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/MSW wiring (Path A), the proto/YAML
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
 
@@ -89,10 +89,10 @@ export default createScenario({
89
89
  viewport: { width: 896, height: 480 },
90
90
  views: { login: LoginScreen, dashboard: Dashboard },
91
91
  steps: [
92
- { view: "login", delayMs: 0, caption: "Sign in",
92
+ { view: "login", delayMs: 0,
93
93
  narrationText: "Sign in to your workspace.",
94
94
  props: { email: "jordan@acme.cloud" } }, // typed to LoginScreen's props
95
- { view: "dashboard", delayMs: 2400, caption: "Your dashboard",
95
+ { view: "dashboard", delayMs: 2400,
96
96
  props: { userName: "Jordan" } },
97
97
  ],
98
98
  });
@@ -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 + MSW wiring
105
-
106
- `scenar preview init --source ./src` writes `.scenar/`:
107
-
108
- - `views.generated.ts` scanner-owned; never edit
109
- - `views.custom.tsx` — your hand-added views
110
- - `views.ts` the merged registry you author against
111
- - `providers.tsx` **you wire this**: wrap components in the theme/router/data
112
- providers they need to render in isolation
113
- - `report.md` what was discovered/skipped and why (read this first)
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
- Also generates an MSW service worker. Add MSW handlers so components that fetch
116
- data render with realistic fixtures (no live backend). This wiring is the one
117
- step that can't be automated do it explicitly with the user, using `report.md`
118
- to see what each component expects. Re-run `scenar preview sync` after code
119
- changes (it preserves `views.custom.tsx` and `providers.tsx`).
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 registry resources and scenario templates", async () => {
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 registry resource, returning guidance when absent", async () => {
64
+ it("reads a scenario steps resource, returning guidance when absent", async () => {
72
65
  const client = await connectClient();
73
- const result = await client.readResource({ uri: "scenar://registry/views" });
66
+ const result = await client.readResource({
67
+ uri: "scenar://scenario/nope/steps",
68
+ });
74
69
  expect(result.contents).toHaveLength(1);
75
- // With no .scenar/ in the test cwd, the body should guide the user.
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
@@ -1 +1 @@
1
- {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/project.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,8EAA8E;AAC9E,wBAAsB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMlF"}
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
@@ -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;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C;;;;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;AAED,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,YAAoB;IACxD,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,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
- }
@@ -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: the
4
- * generated view registry + scan report (in .scenar/), and per-scenario steps +
5
- * pack manifest. All are read relative to the project root; a missing file
6
- * returns a short explanatory body rather than an error so the client degrades
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
@@ -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;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA+EzD"}
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, readProjectFile } from "./project.js";
4
+ import { projectRoot } from "./project.js";
5
5
  /**
6
- * Register read-only resources the AI can inspect for authoring context: the
7
- * generated view registry + scan report (in .scenar/), and per-scenario steps +
8
- * pack manifest. All are read relative to the project root; a missing file
9
- * returns a short explanatory body rather than an error so the client degrades
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 {
@@ -1 +1 @@
1
- {"version":3,"file":"resources.js","sourceRoot":"","sources":["../../src/resources.ts"],"names":[],"mappings":"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"}
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, readProjectFile } from "./project.js";
4
+ import { projectRoot } from "./project.js";
5
5
 
6
6
  /**
7
- * Register read-only resources the AI can inspect for authoring context: the
8
- * generated view registry + scan report (in .scenar/), and per-scenario steps +
9
- * pack manifest. All are read relative to the project root; a missing file
10
- * returns a short explanatory body rather than an error so the client degrades
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 {
@@ -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,CAUrD"}
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"}