Skip to content

CLI: Add storybook ai <tool> MCP passthrough behind STORYBOOK_FEATURE_AI_CLI#35125

Merged
kasperpeulen merged 17 commits into
nextfrom
kasper/ai-cli-mcp-passthrough
Jun 11, 2026
Merged

CLI: Add storybook ai <tool> MCP passthrough behind STORYBOOK_FEATURE_AI_CLI#35125
kasperpeulen merged 17 commits into
nextfrom
kasper/ai-cli-mcp-passthrough

Conversation

@kasperpeulen

@kasperpeulen kasperpeulen commented Jun 10, 2026

Copy link
Copy Markdown
Member

Closes #35124

This pull request has been released as version 0.0.0-pr-35125-sha-838d82b6. Try it out in a new sandbox by running npx storybook@0.0.0-pr-35125-sha-838d82b6 sandbox or in an existing project with npx storybook@0.0.0-pr-35125-sha-838d82b6 upgrade.

Published version 0.0.0-pr-35125-sha-838d82b6

What I did

POC for a skill + CLI based agent integration: expose the commands of the locally running Storybook's server (@storybook/addon-mcp) as real CLI commands, so agent skills can call them directly instead of going through the stdio MCP proxy.

  • storybook ai <command> — generic passthrough: resolves the running Storybook for the target cwd and forwards the call via MCP tools/call over HTTP. No per-command subcommands; new addon commands work for free.
  • Discovery via --help: storybook ai --help (and bare storybook ai) appends a "Storybook commands" section fetched live from the running Storybook to the regular commander help — including the instance URL, its Storybook version, and a note listing sibling instances/ports when several run at the same cwd — and storybook ai <command> --help prints that command's full description and arguments. Unknown command names produce an error listing the available commands.
  • Naming: all user-facing copy says "commands" — MCP and "tools" never appear in CLI output; the protocol is an implementation detail. (The descriptions of the commands themselves come from @storybook/addon-mcp and are updated separately in storybookjs/mcp.)
  • Feature flag: the new commands are only registered when STORYBOOK_FEATURE_AI_CLI=1 is set. Without it, storybook ai behaves exactly as today (setup only), verified by unit tests and manual QA.
  • Discovery/routing: registry resolution (~/.storybook/instances/*.json), cwd matching, PID liveness, most-recently-started instance selection, optional --port targeting (with a port-mismatch intercept) and repair-instruction markdown are copied from the current @storybook/mcp-proxy (storybookjs/mcp) into code/lib/cli-storybook/src/ai/mcp/ — no cross-repo dependency, consolidate later if the approach wins. Intercept conditions print the proxy's repair markdown and exit non-zero. The proxy's version/installed checks were dropped per review (huang-julien): the CLI is invoked as bare npx storybook, so the fact that it executes already proves the project has a compatible Storybook.
  • Arguments: --cwd <path> optional (defaults to process.cwd()); --key value / --key=value flags become command arguments with JSON-parse coercion (string fallback); --json '<object>' escape hatch for raw argument objects; -o/--output <path> writes the result to a file. Everything after the command name is passed through verbatim (passThroughOptions), except --cwd/--port/--json/--help, which the CLI consumes.
  • Transport: a deliberately stateless one-shot JSON-RPC POST (no MCP initialize/session) with a request timeout — the same local shortcut @storybook/mcp-proxy takes against @storybook/addon-mcp's stateless tmcp HTTP transport; documented as such in client.ts. The JSON-RPC envelope and result payloads are validated with loose valibot schemas (the endpoint comes from a world-writable registry file), with the types derived from the schemas.
  • Telemetry: deferred, explicitly out of scope for this POC.

Follow-ups happen in storybookjs/mcp: converting the Claude Code plugin skills to use these commands, the eval comparison against the mcp-proxy plugin, and skill-level handling of old-CLI errors (Invalid command: ai / too many arguments) as the "upgrade Storybook" signal.

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

138 unit tests in code/lib/cli-storybook/src/ai/mcp/*.test.ts cover flag gating, arg mapping/coercion, registry reading (memfs), PID liveness, instance resolution (incl. most-recent selection and port targeting), the HTTP/SSE client incl. payload-shape validation, repair-instruction intercepts, the help surfaces, and the command orchestration.

Manual testing

  1. In a project with @storybook/addon-mcp installed and Storybook >= 10.5 (e.g. the internal-storybook app in storybookjs/mcp), run storybook dev.
  2. STORYBOOK_FEATURE_AI_CLI=1 npx storybook ai --help → regular help plus the commands of the running Storybook (URL, version, sibling instances when applicable).
  3. STORYBOOK_FEATURE_AI_CLI=1 npx storybook ai get-documentation --help → that command's description and arguments.
  4. STORYBOOK_FEATURE_AI_CLI=1 npx storybook ai list-all-documentation --withStoryIds true → markdown docs index.
  5. STORYBOOK_FEATURE_AI_CLI=1 npx storybook ai get-documentation --id <id> → component docs (also try --json '{"id":"<id>"}' and -o out.md).
  6. STORYBOOK_FEATURE_AI_CLI=1 npx storybook ai not-a-real-command → error listing the available commands, exit 1.
  7. STORYBOOK_FEATURE_AI_CLI=1 npx storybook ai list-all-documentation --port 9999 → port-mismatch repair listing the running ports, exit 1.
  8. Stop storybook dev, rerun a command → repair instructions ("start storybook dev"), exit 1; ai --help still works with an "unavailable" note.
  9. Run from a cwd without a Storybook → repair instructions listing running candidates, exit 1.
  10. Without the env var: npx storybook ai list-all-documentation → "too many arguments" error as before; npx storybook ai --help shows only setup.

All of the above were performed against the internal-storybook app from storybookjs/mcp, and re-verified end-to-end on the published canary (see the canary QA report), including the failure paths (SIGKILLed dev server with stale registry record → dead-PID detection + stale file cleanup).

Documentation

  • Add or update documentation reflecting your changes — intentionally none: the feature is behind an experimental feature flag.
  • If you are deprecating/removing a feature, make sure to update MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts
  • Declare whether manual QA will be needed for this PR during the next release, through qa:needed or qa:skip

…_FEATURE_AI_CLI

Generic passthrough that forwards `storybook ai <tool>` calls to the MCP
server of the locally running Storybook via tools/call, plus an
`ai list-tools` helper. Registry resolution (~/.storybook/instances), PID
liveness, version check and repair instructions are copied from
@storybook/mcp-proxy (storybookjs/mcp) so the CLI behaves like the proxy.

Without STORYBOOK_FEATURE_AI_CLI, `storybook ai` is unchanged (setup only).

Part of #35124
… error result

addon-mcp (tmcp) returns unknown-tool failures as an isError tool result
rather than a JSON-RPC error, so the unknown-tool listing must also check
error results against tools/list.
…rough

- exhaustive never guards on the status/reason switches
- placeholder output when a tool returns no content
- precise undefined check for JSON-RPC results
- document why the registry dir constant and extra MCP statuses are duplicated
- trim provenance prose from comments
@kasperpeulen kasperpeulen added feature request cli ci:normal Run our default set of CI jobs (choose this for most PRs). qa:skip Pull Requests that do not need any QA. qa:needed Pull Requests that will need manual QA prior to release. and removed qa:skip Pull Requests that do not need any QA. labels Jun 10, 2026
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an experimental storybook ai CLI passthrough backed by a local MCP: shared types, registry reading with liveness and memfs tests, precise cwd+port instance resolution, Storybook version checks, JSON-RPC client (JSON + SSE), CLI token parsing, tool invocation/help formatting, feature-flagged Commander wiring, and tests.

Changes

MCP AI CLI execution passthrough

Layer / File(s) Summary
Type contracts and data shapes
code/lib/cli-storybook/src/ai/mcp/types.ts
Defines MCP status, Storybook instance record schema, intercept reason codes, and tool descriptor/result shapes.
MCP HTTP JSON-RPC client
code/lib/cli-storybook/src/ai/mcp/client.ts, code/lib/cli-storybook/src/ai/mcp/client.test.ts
Implements JSON-RPC POSTs to instance.mcp.endpoint with proxy header and timeout, negotiates application/json and text/event-stream (SSE) responses, parses SSE data: payloads, and maps JSON-RPC error to McpJsonRpcError; tests cover headers, body shape, SSE parsing, and error cases.
Storybook version validation
code/lib/cli-storybook/src/ai/mcp/version-check.ts, code/lib/cli-storybook/src/ai/mcp/version-check.test.ts
Adds STORYBOOK_MIN_VERSION (10.5.0), classifies versions (including prerelease/canary rules), and reads Storybook's package.json via createRequire.resolve.paths with tests for resolution and malformed files.
Instance registry reading
code/lib/cli-storybook/src/ai/mcp/registry.ts, code/lib/cli-storybook/src/ai/mcp/registry.test.ts, code/lib/cli-storybook/package.json
Reads ~/.storybook/instances JSON records, validates shapes/ports/pids, checks process liveness (process.kill 0), removes stale files, and tests with memfs; memfs added to dependencies.
Instance resolution by cwd
code/lib/cli-storybook/src/ai/mcp/resolve-instance.ts, code/lib/cli-storybook/src/ai/mcp/resolve-instance.test.ts
Selects instance via exact-normalized cwd (optional port), sorts by startedAt then pid, prefers ready, and returns instance or intercept with reason and matches.
Tool argument parsing
code/lib/cli-storybook/src/ai/mcp/tool-args.ts, code/lib/cli-storybook/src/ai/mcp/tool-args.test.ts
Parses passthrough tokens, consumes --help/--cwd/--port/--json, coerces JSON-like values, validates ports, and merges JSON escape hatch with explicit flags.
Intercept messaging
code/lib/cli-storybook/src/ai/mcp/intercepts.ts, code/lib/cli-storybook/src/ai/mcp/intercepts.test.ts
Generates repair/diagnostic markdown for intercept reasons (no-instance, port-mismatch, not-installed, addon-missing, starting, error, too-old) with contextual details.
Tool execution and result formatting
code/lib/cli-storybook/src/ai/mcp/run-tool.ts, code/lib/cli-storybook/src/ai/mcp/run-tool.test.ts
Orchestrates runAiTool: parse args, resolve ready instance (registry + version check), call MCP tool, format results as markdown, handle unknown-tool and connectivity errors, and include multi-instance warnings.
CLI command registration
code/lib/cli-storybook/src/ai/mcp/register.ts, code/lib/cli-storybook/src/ai/mcp/register.test.ts
Adds registerAiMcpPassthrough to wire ai [tool] [toolArgs...] with options, custom help handling, output-to-file, and process.exitCode propagation; feature-flag gated.
CLI entrypoint integration
code/lib/cli-storybook/src/bin/run.ts
Conditionally registers the MCP AI passthrough on startup when STORYBOOK_FEATURE_AI_CLI is enabled.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

  • storybookjs/storybook#34863: Adds the server-side runtime instance registry writer that this PR reads; formats and registry contract align with this change.
  • storybookjs/storybook#34345: Related CLI ai wiring and help behavior changes that interact with passthrough registration.

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
code/lib/cli-storybook/src/ai/mcp/version-check.test.ts (1)

7-9: ⚡ Quick win

Use Vitest spy-mock mode for the module mock.

This file-level node:module mock should use the repository’s spy-mocking pattern.

Suggested fix
-vi.mock('node:module', () => ({
-  createRequire: vi.fn(),
-}));
+vi.mock('node:module', { spy: true });

As per coding guidelines, "Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/lib/cli-storybook/src/ai/mcp/version-check.test.ts` around lines 7 - 9,
Replace the file-level Vitest mock for 'node:module' to use spy-mock mode;
specifically update the vi.mock invocation that defines createRequire so it
includes the { spy: true } option (i.e. call vi.mock('node:module', () => ({
createRequire: vi.fn() }), { spy: true })), leaving the createRequire vi.fn()
factory intact.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@code/lib/cli-storybook/src/ai/mcp/client.test.ts`:
- Around line 89-100: The test 'joins multi-line SSE data correctly' currently
uses JSON.stringify(envelope) which emits escaped \n sequences, so the string
stays single-line; change the serialization to produce actual newlines (e.g.,
use JSON.stringify(envelope, null, 2) or replace escaped "\\n" with real "\n")
before splitting into data lines so the dataLines construction tests the
multi-line branch (update the variable dataLines / sseBody in the test that
creates the SSE body and leave fetchImpl/sseResponse usage unchanged).

In `@code/lib/cli-storybook/src/ai/mcp/register.test.ts`:
- Around line 8-11: Update the existing vi.mock call for './run-tool.ts' to
include the spy: true option so the mock follows Vitest spy mocking guidelines;
specifically modify the vi.mock invocation that currently registers runAiTool
and runAiListTools to pass { spy: true } as the second argument, leaving the
mock implementations for runAiTool and runAiListTools unchanged.

In `@code/lib/cli-storybook/src/ai/mcp/run-tool.test.ts`:
- Around line 9-24: The vitest mocks in run-tool.test.ts are missing spy: true
and per-test mock setups live inside individual it blocks; update the three
vi.mock(...) calls that wrap './registry.ts', './client.ts', and
'./version-check.ts' to include { spy: true } (e.g. vi.mock('./registry.ts', {
spy: true }, async (importOriginal) => ...)), and move any per-test overrides
that call vi.mocked(readRegistry).mockResolvedValue(...),
vi.mocked(callMcpTool).mockResolvedValue/...mockRejectedValue(...), and
vi.mocked(listMcpTools).mockResolvedValue(...) out of individual it(...) cases
into a shared beforeEach (or top-level setup) so tests follow the repository
mocking convention and each test can adjust those mocked return values as
needed.
- Around line 62-266: Tests mix scenario-specific vi.mocked(...) setups inside
many it blocks; extract these into focused beforeEach hooks per scenario. Create
nested describe blocks for related scenarios (e.g., "version checks", "unknown
tool handling", "registry permutations") and move inline mocks for readRegistry,
checkStorybookVersion, callMcpTool, and listMcpTools into those
describe-specific beforeEach hooks so each it only contains assertions;
alternatively add small helper functions that configure the mocks (e.g.,
setupRegistryReady(), setupMcpError(), setupCallMcpToolReturns(...)) and call
those from the it blocks to keep tests declaration-focused while still using the
existing top-level beforeEach baseline. Ensure you update tests referencing
runAiTool and runAiListTools to rely on the scenario beforeEach mocks and remove
duplicated inline vi.mocked(...) calls.

---

Nitpick comments:
In `@code/lib/cli-storybook/src/ai/mcp/version-check.test.ts`:
- Around line 7-9: Replace the file-level Vitest mock for 'node:module' to use
spy-mock mode; specifically update the vi.mock invocation that defines
createRequire so it includes the { spy: true } option (i.e. call
vi.mock('node:module', () => ({ createRequire: vi.fn() }), { spy: true })),
leaving the createRequire vi.fn() factory intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f84a35ec-c848-4630-97b1-1d6211867c1f

📥 Commits

Reviewing files that changed from the base of the PR and between 514a19b and a8b028c.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (19)
  • code/lib/cli-storybook/package.json
  • code/lib/cli-storybook/src/ai/mcp/client.test.ts
  • code/lib/cli-storybook/src/ai/mcp/client.ts
  • code/lib/cli-storybook/src/ai/mcp/intercepts.test.ts
  • code/lib/cli-storybook/src/ai/mcp/intercepts.ts
  • code/lib/cli-storybook/src/ai/mcp/register.test.ts
  • code/lib/cli-storybook/src/ai/mcp/register.ts
  • code/lib/cli-storybook/src/ai/mcp/registry.test.ts
  • code/lib/cli-storybook/src/ai/mcp/registry.ts
  • code/lib/cli-storybook/src/ai/mcp/resolve-instance.test.ts
  • code/lib/cli-storybook/src/ai/mcp/resolve-instance.ts
  • code/lib/cli-storybook/src/ai/mcp/run-tool.test.ts
  • code/lib/cli-storybook/src/ai/mcp/run-tool.ts
  • code/lib/cli-storybook/src/ai/mcp/tool-args.test.ts
  • code/lib/cli-storybook/src/ai/mcp/tool-args.ts
  • code/lib/cli-storybook/src/ai/mcp/types.ts
  • code/lib/cli-storybook/src/ai/mcp/version-check.test.ts
  • code/lib/cli-storybook/src/ai/mcp/version-check.ts
  • code/lib/cli-storybook/src/bin/run.ts

Comment thread code/lib/cli-storybook/src/ai/mcp/client.test.ts
Comment thread code/lib/cli-storybook/src/ai/mcp/register.test.ts Outdated
Comment thread code/lib/cli-storybook/src/ai/mcp/run-tool.test.ts Outdated
Comment thread code/lib/cli-storybook/src/ai/mcp/run-tool.test.ts
@storybook-app-bot

storybook-app-bot Bot commented Jun 10, 2026

Copy link
Copy Markdown

Package Benchmarks

Commit: 5a207e0, ran on 10 June 2026 at 16:22:22 UTC

The following packages have significant changes to their size or dependencies:

@storybook/cli

Before After Difference
Dependency count 203 203 0
Self size 908 KB 948 KB 🚨 +39 KB 🚨
Dependency size 88.99 MB 88.99 MB 🚨 +30 B 🚨
Bundle Size Analyzer Link Link

…p the discovery surface

Review follow-ups on #35125:

- copy the current @storybook/mcp-proxy resolver: most-recently-started
  instance selection, optional --port targeting with a port-mismatch
  intercept, and version checks that prefer the registry record's
  storybookVersion over disk resolution (resolve.paths based lookup)
- replace the list-tools subcommand with help: `storybook ai --help`
  appends the running Storybook's tool commands to the regular help, and
  `storybook ai <tool> --help` shows a tool's description and arguments
- document the intentionally stateless JSON-RPC shortcut and add a
  request timeout so a hung server cannot stall the CLI
- honor -o/--output for tool results instead of silently ignoring it

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
code/lib/cli-storybook/src/ai/mcp/register.test.ts (1)

10-14: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add spy: true to ./run-tool.ts mock.

The vi.mock('./run-tool.ts') call should include { spy: true } to align with the repository's Vitest mocking convention.

♻️ Proposed fix
-vi.mock('./run-tool.ts', () => ({
-  runAiTool: vi.fn(),
-  runAiToolHelp: vi.fn(),
-  buildToolCommandsHelp: vi.fn(),
-}));
+vi.mock('./run-tool.ts', { spy: true });

The mock implementations in beforeEach (lines 66-68) will continue to work with this change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/lib/cli-storybook/src/ai/mcp/register.test.ts` around lines 10 - 14, The
vi.mock call for './run-tool.ts' is missing the spy option; update the mock
invocation vi.mock('./run-tool.ts', ...) to include the { spy: true } option so
it follows the repository's Vitest convention and allows the existing beforeEach
mock implementations for runAiTool, runAiToolHelp, and buildToolCommandsHelp to
continue working as spies.

Source: Coding guidelines

code/lib/cli-storybook/src/ai/mcp/run-tool.test.ts (1)

9-24: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add spy: true to all three vi.mock() calls.

All three mocks (./registry.ts, ./client.ts, ./version-check.ts) should include { spy: true } to align with the repository's Vitest mocking convention.

♻️ Proposed fix
-vi.mock('./registry.ts', async (importOriginal) => ({
-  ...(await importOriginal<typeof import('./registry.ts')>()),
-  readRegistry: vi.fn(),
-}));
+vi.mock('./registry.ts', { spy: true });

-vi.mock('./client.ts', async (importOriginal) => ({
-  ...(await importOriginal<typeof import('./client.ts')>()),
-  callMcpTool: vi.fn(),
-  listMcpTools: vi.fn(),
-}));
+// Preserve the McpJsonRpcError class identity so `instanceof` checks keep working.
+vi.mock('./client.ts', { spy: true });

-vi.mock('./version-check.ts', async (importOriginal) => ({
-  ...(await importOriginal<typeof import('./version-check.ts')>()),
-  checkStorybookVersion: vi.fn(),
-}));
+vi.mock('./version-check.ts', { spy: true });

The existing beforeEach mock setups will continue to work with this change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/lib/cli-storybook/src/ai/mcp/run-tool.test.ts` around lines 9 - 24, The
three vi.mock calls that mock './registry.ts', './client.ts', and
'./version-check.ts' need the Vitest spy option added so they follow repository
convention; update each vi.mock invocation to pass { spy: true } as the second
argument (preserving the existing async importOriginal factory), ensuring the
mocks for readRegistry (from './registry.ts'), callMcpTool and listMcpTools
(from './client.ts'), and checkStorybookVersion (from './version-check.ts') are
created with spy:true so existing beforeEach spy setups continue to work.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@code/lib/cli-storybook/src/ai/mcp/version-check.test.ts`:
- Around line 13-15: Update the vitest mock for 'node:module' to include the spy
option (vi.mock('node:module', () => ({ createRequire: vi.fn() }), { spy: true
})) and then change the mockSearchPaths helper to call vi.mocked(createRequire)
when accessing the mocked createRequire for type-safe usage; locate the
vi.mock('node:module') declaration and the mockSearchPaths helper and replace
direct createRequire casts/usages with vi.mocked(createRequire).

---

Duplicate comments:
In `@code/lib/cli-storybook/src/ai/mcp/register.test.ts`:
- Around line 10-14: The vi.mock call for './run-tool.ts' is missing the spy
option; update the mock invocation vi.mock('./run-tool.ts', ...) to include the
{ spy: true } option so it follows the repository's Vitest convention and allows
the existing beforeEach mock implementations for runAiTool, runAiToolHelp, and
buildToolCommandsHelp to continue working as spies.

In `@code/lib/cli-storybook/src/ai/mcp/run-tool.test.ts`:
- Around line 9-24: The three vi.mock calls that mock './registry.ts',
'./client.ts', and './version-check.ts' need the Vitest spy option added so they
follow repository convention; update each vi.mock invocation to pass { spy: true
} as the second argument (preserving the existing async importOriginal factory),
ensuring the mocks for readRegistry (from './registry.ts'), callMcpTool and
listMcpTools (from './client.ts'), and checkStorybookVersion (from
'./version-check.ts') are created with spy:true so existing beforeEach spy
setups continue to work.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 00128f61-fa3e-4a20-b42b-bd7fd75ef39b

📥 Commits

Reviewing files that changed from the base of the PR and between a8b028c and 3c2dbda.

📒 Files selected for processing (15)
  • code/lib/cli-storybook/src/ai/mcp/client.test.ts
  • code/lib/cli-storybook/src/ai/mcp/client.ts
  • code/lib/cli-storybook/src/ai/mcp/intercepts.test.ts
  • code/lib/cli-storybook/src/ai/mcp/intercepts.ts
  • code/lib/cli-storybook/src/ai/mcp/register.test.ts
  • code/lib/cli-storybook/src/ai/mcp/register.ts
  • code/lib/cli-storybook/src/ai/mcp/resolve-instance.test.ts
  • code/lib/cli-storybook/src/ai/mcp/resolve-instance.ts
  • code/lib/cli-storybook/src/ai/mcp/run-tool.test.ts
  • code/lib/cli-storybook/src/ai/mcp/run-tool.ts
  • code/lib/cli-storybook/src/ai/mcp/tool-args.test.ts
  • code/lib/cli-storybook/src/ai/mcp/tool-args.ts
  • code/lib/cli-storybook/src/ai/mcp/types.ts
  • code/lib/cli-storybook/src/ai/mcp/version-check.test.ts
  • code/lib/cli-storybook/src/ai/mcp/version-check.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • code/lib/cli-storybook/src/ai/mcp/client.test.ts
  • code/lib/cli-storybook/src/ai/mcp/intercepts.test.ts
  • code/lib/cli-storybook/src/ai/mcp/intercepts.ts
  • code/lib/cli-storybook/src/ai/mcp/version-check.ts
  • code/lib/cli-storybook/src/ai/mcp/tool-args.test.ts
  • code/lib/cli-storybook/src/ai/mcp/resolve-instance.test.ts
  • code/lib/cli-storybook/src/ai/mcp/types.ts
  • code/lib/cli-storybook/src/ai/mcp/client.ts

Comment thread code/lib/cli-storybook/src/ai/mcp/version-check.test.ts Outdated
Comment thread code/lib/cli-storybook/src/ai/mcp/register.ts Outdated
Review feedback from #35125: use the repository's vi.mock(..., { spy: true })
convention instead of mock factories, and serialize the SSE fixture with
indentation so the multi-line data: join branch is actually exercised.
…entation detail

User-facing output no longer mentions tools or MCP: the help section is
'Storybook commands', errors say 'Storybook server', the usage line is
'ai [options] [command] [args...]', and repair instructions reference the
addon without protocol jargon. Internal names keep Tool/MCP since the
protocol is the implementation.
@kasperpeulen

Copy link
Copy Markdown
Member Author

Canary QA report (0.0.0-pr-35125-sha-eeb0bba2)

Verified end-to-end against the published canary packages in a fresh Vite + React project (npm create vitenpx storybook@0.0.0-pr-35125-sha-eeb0bba2 initnpx storybook add @storybook/addon-mcpstorybook dev), so this exercises the real npx storybook dispatcher → @storybook/cli path:

# Scenario Result
1 STORYBOOK_FEATURE_AI_CLI=1 npx storybook ai --help ✅ regular help + "Storybook commands" section listing all 7 commands fetched live
2 ai list-all-documentation --withStoryIds true ✅ markdown docs index, boolean coerced
3 ai get-documentation --help ✅ per-command usage, description, arguments
4 ai get-documentation --id example-button and --json '{"id":"example-button"}' ✅ component docs, exit 0
5 ai not-a-command ✅ "Unknown command" error listing available commands, exit 1
6 Without the env var ✅ unchanged: "too many arguments" error, exit 1; ai --help shows setup only
7 Wrong cwd (Storybook running elsewhere) ✅ repair instructions listing running cwds, exit 1
8 Server stopped ✅ "Storybook is not running at this cwd…" repair, exit 1
9 ai --help with server stopped ✅ help still renders with "(unavailable — … start `storybook dev` …)" note, exit 0

No "MCP" or "tool" wording appears in any CLI output — the remaining "Use this tool to…" phrases come from @storybook/addon-mcp's own command descriptions (follow-up in storybookjs/mcp).

Comment thread code/lib/cli-storybook/src/ai/mcp/registry.ts Outdated
Restores parity with @storybook/mcp-proxy's registry schema instead of a
hand-rolled type guard; valibot was already a monorepo dependency.
Found by a second thermo-nuclear review pass: the help path re-parsed from
the pre-command options and dropped a token-provided --port. Both help
entry points now delegate to one post-parse helper, the duplicated
server-unreachable error is a single formatter, and the multi-instance
warning says 'status' instead of 'mcp'.
When several instances run at the cwd, the help section names the chosen
port, lists sibling ports and points at --port. The unavailable note now
reflects the actual intercept (port mismatch with running ports, starting
up, addon missing, too old) instead of always claiming nothing is running.
The section header includes the version reported by the running instance,
and the too-old note names the detected version next to the minimum.
@kasperpeulen kasperpeulen enabled auto-merge June 10, 2026 14:41
The endpoint comes from a world-writable registry file, so the response
deserves the same scrutiny as the transport: the envelope and the
tools/call / tools/list results are now parsed with loose valibot schemas
(deriving the types, deleting the boundary casts). Malformed payloads
produce 'unexpected response shape' instead of confusing downstream
errors; extra MCP fields still pass through.
Comment thread code/lib/cli-storybook/src/ai/mcp/client.ts
Comment thread code/lib/cli-storybook/src/ai/mcp/version-check.ts Outdated
@huang-julien huang-julien self-requested a review June 10, 2026 15:24
… project's version

Review feedback (huang-julien): the CLI is invoked as bare 'npx storybook',
so it is always the project's own Storybook and a semver floor can never
disagree with the running instance. Replaced with a minimal installed-check
so projects without Storybook still get the setup repair instead of 'start
storybook dev'. The help header still shows the version from the registry
record.
@kasperpeulen kasperpeulen disabled auto-merge June 10, 2026 15:36
If this code executes via 'npx storybook', npx already resolved Storybook
from the project — so 'is storybook installed' is answered by the CLI
running at all. Resolution is now purely registry-based; a cwd without a
running instance always gets the 'start storybook dev' repair.
@kasperpeulen kasperpeulen merged commit 4730339 into next Jun 11, 2026
143 checks passed
@kasperpeulen kasperpeulen deleted the kasper/ai-cli-mcp-passthrough branch June 11, 2026 08:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:normal Run our default set of CI jobs (choose this for most PRs). cli feature request qa:needed Pull Requests that will need manual QA prior to release.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

POC: storybook ai <tool> CLI passthrough to local Storybook MCP (behind STORYBOOK_FEATURE_AI_CLI)

2 participants