Skip to content

Skills M4: Run addon-mcp and @storybook/mcp on the shared core toolsets - #35854

Closed
JReinhold wants to merge 7 commits into
m2b-core-toolsetsfrom
m4-addon-mcp-core-services
Closed

JReinhold wants to merge 7 commits into
m2b-core-toolsetsfrom
m4-addon-mcp-core-services

Conversation

@JReinhold

Copy link
Copy Markdown
Contributor

Replaces accidentally closed #35677 (a mergeability probe via the GitHub merges API marked that PR merged and deleted this branch; the merge commit on m2b-core-toolsets was reverted and this branch tip was restored to 0a5ad88a6a4).

No intentional code change versus #35677.


Closes #35673 · Closes #35725

Stacked: this PR's base is #35726 — the core toolsets layer this PR consumes; review that one first. #35719 (the storybook tools CLI) stacks on this one. Merge #35726 first; GitHub retargets this PR to next automatically.

What I did

#35726 built Storybook's agent-facing capabilities as core toolsets. This PR is the switch: both MCP surfaces — @storybook/addon-mcp (the dev-server MCP) and @storybook/mcp (the hosted/composition MCP) — now render their tools from those shared definitions, and the engines they each carried are deleted. Net: −11,199 lines.

What remains in each package is an adapter. It resolves a toolset method and maps the outcome onto the MCP reply mechanically: schemas and prose come from the definition, markdown becomes the text blocks, data becomes structuredContent, ok becomes isError, and agent-facing errors surface verbatim. Meaning lives in the toolset — the adapter is not allowed to re-derive it from the data.

Concretely, an MCP tool is now a registry row pointing at a toolset method (real code):

function docsRow(method: 'docs.list' | 'docs.show' | 'docs.showStory'): AddonToolDefinition {
  const forContext = (context: AddonToolRegistryContext): ToolsetToolOptions =>
    context.multiSource
      ? { method, resolveToolset: (server) => compositionDocsToolset(server) }
      : { method };

  return {
    name: MCP_TOOL_NAMES[method],
    toolset: 'docs',
    available: ({ availability }) => availability.docsEnabled,
    getMetadata: (context) => getToolsetToolMetadata(forContext(context)),
    register: async (server, context, enabled) => {
      registerToolsetTool(server, forContext(context), enabled);
    },
  };
}

The one review question

Does the way the MCP uses the toolsets make sense — and is there business logic still in the MCP that belongs in a toolset? Everything in commits 1–3 should read as mechanical: naming, gating, unwrapping, transport. If you find an adapter interpreting a result — branching on domain data, rewriting prose, computing anything the future CLI consumer would have to duplicate — that is the review finding to raise.

You do not need to review the deleted engines: commit 4/7 is −12,570 lines of pure deletions, and behavioral equality with them is pinned by the e2e wire snapshots (evidence below), not by reading them.

How to review (~1½ hours)

The branch's seven commits are these seven blocks, in this order. Each block heading links straight to its commit's diff — review one commit at a time, and read the block's intro here before opening any file.

  1. The addon adapter — 30 min
  2. The hosted adapter — 25 min
  3. Addon rewiring — 15 min
  4. Delete the replaced engines — 0 min
  5. Retire the PUSH_REVIEW channel adapter — 5 min
  6. The adapter tests and the e2e proof — 15 min
  7. Docs and bookkeeping — 5 min

Block 1 · The addon adapter — 30 min

Files: toolset-tools.ts, tool-registry.ts, ui-root.ts, tool-names.ts (4 files, +425 −109)

The entire addon-side integration is two ideas. The first is one generic unwrap that turns any toolset outcome into an MCP result — toolset-tools.ts, 187 lines, read it in full:

callToolsetMethod(server, { method }, input):
  toolset = getToolset(id)                       // throws loudly if missing
  outcome = method.handler(input, ctx(server))   // ctx carries origin, uiRoot, telemetry, consumer:'mcp'
  return {
    content:           one text block per outcome.markdown entry
    structuredContent: outputSchema ? narrowed(outcome.data) : undefined
    isError:           outcome.ok ? undefined : true
  }
  on throw: error.agentFacing ? its message, verbatim
                              : a generic message + logger.error on the server

The second is a declarative registry where every MCP tool is a row pointing at a toolset method — the docsRow snippet above is one real row. tool-registry.ts walks the rows; the availability gate × the toolsets config decides what registers.

What to check: nothing in this block may interpret data — no branching on domain fields, no re-deriving prose from the payload. If you find the adapter deciding what a result means, that is the finding to raise.

Block 2 · The hosted adapter — 25 min

Files: register.ts, error-to-mcp-content.ts, multi-source-manifests.ts, index.ts, types.ts, bin.ts, package.json (7 files, +497 −225)

The same unwrap for @storybook/mcp, with one structural difference: the hosted server builds its docs toolset per request, because the provider and sources belong to the request:

handle(request):
  sources = resolveSources(request)          // which Storybooks, which URLs, which auth
  toolset = createDocsToolset({ sources })   // the portable core factory — no second engine here
  register each method → the same mechanical unwrap as block 1

packaging: 'storybook' becomes a bundled devDependency —
           the published dependencies stay free of it (dist-contract.test.ts pins this)

What to check: register.ts (346 lines) in full — the per-request construction, the twin error mapping, and that the package boundary holds.

Block 3 · Addon rewiring — 15 min

Files: preset, mcp-handler, availability, telemetry, instructions, auth (16 files, +131 −386)

Mechanical re-pointing: everything that used to call an engine now consults the registry. Net −255 lines. Skim with one question: did any logic sneak in here, or is it all naming and plumbing?

Block 4 · Delete the replaced engines — 0 min

Files: the old tools, manifest pipeline, formatter and utils of both packages (47 files, −12,570, zero insertions)

Don't review. This is the point of the PR: every deleted line is an engine the core toolsets replaced. Behavioral equality is proven by block 6, not by reading these.

Block 5 · Retire the PUSH_REVIEW channel adapter — 5 min

Files: common-preset.ts, review-channel.ts (+ test), events.ts (4 files, +3 −200)

before: the addon emits 'storybook/review/push-review' on the channel; a core adapter listens
after : the review toolset method is called directly — the channel surface is deleted

#35726 deliberately kept this adapter alive so released addon-mcp versions kept working against the new core; with the addon switched over in this PR, it retires. One glance.

Block 6 · The adapter tests and the e2e proof — 15 min

Files: adapter unit tests, dist-contract.test.ts, test-storybooks/mcp (22 files, +1,569 −366)

Skim as evidence, not as code under review: the contract tests pin the unwrap (outcome → isError, structuredContent narrowing), gating (including the preview app resource), telemetry, and agentFacing error surfacing; the e2e suite exercises the real wire against real servers.

Block 7 · Docs and bookkeeping — 5 min

Files: AGENTS.md, agent-eval templates, yarn.lock (4 files, +89 −57)

The AGENTS.md architecture section documents the end state of the whole layer — worth an actual read: it is also the text future agents working in this repo obey.

Why skipping the deletions is safe:

Claim Evidence
The wire behavior did not change The MCP e2e suite (46/46 — real dev servers and real hosted manifests) passes with byte-identical inline snapshots before and after the swap
The adapters' contracts are pinned Unit tests cover the outcome→isError mapping, structuredContent narrowing, telemetry, agentFacing error surfacing, and toolset gating (including the preview app resource)
Nothing else regressed The full 9,212-test unit suite, TS 7 per-package checks across 47 projects plus workspace TS 6, and manual QA of the preview, test, docs and review flows
@storybook/mcp ships without storybook at runtime dist-contract.test.ts pins the published dependency surface; the docs toolset arrives through the portable storybook/internal/toolsets-docs entry

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

Manual testing

  1. yarn nx run-many -t compile
  2. cd test-storybooks/mcp && yarn install && yarn vitest run --project=e2e — 46 tests, inline snapshots byte-identical to the old engines
  3. yarn storybook in test-storybooks/mcp, connect an MCP client to http://localhost:6006/mcp — the tool list and every tool response match the released addon
  4. The review flow end-to-end (display-review → UI) — now served by the review toolset; the PUSH_REVIEW channel event no longer exists
  5. Composition: the hosted @storybook/mcp against the Chromatic-hosted Storybooks — listing groups per source, lookups take storybookId

Documentation

  • Add or update documentation reflecting your changes — the AGENTS.md architecture section in this PR documents the end state of the toolset layer and its consumers
  • 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

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

Made with Cursor

Made with Cursor

@github-actions

Copy link
Copy Markdown
Contributor
Fails
🚫

PR is not labeled with one of: ["cleanup","BREAKING CHANGE","feature request","bug","documentation","maintenance","build","dependencies"]

🚫

PR is not labeled with one of: ["ci:normal","ci:merged","ci:daily","ci:docs"]

🚫

PR is not labeled with one of: ["qa:needed","qa:skip","qa:success"]

🚫 This PR needs an approving review from a Storybook Core or Developer Experience team member before it can be merged.
Warnings
⚠️

This PR targets m2b-core-toolsets. The default branch for contributions is next. Please make sure you are targeting the correct branch.

Generated by 🚫 dangerJS against b0ac819

@JReinhold

Copy link
Copy Markdown
Contributor Author

Superseding with a fresh branch name to clear false CONFLICTING state left over from the accidental #35677 merge on this head branch name.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 22f76cb6-3a91-4853-82e5-b702a658aaab

📥 Commits

Reviewing files that changed from the base of the PR and between a5ab56c and b0ac819.

⛔ Files ignored due to path filters (3)
  • code/lib/mcp/src/utils/manifest-formatter/__snapshots__/markdown.test.ts.snap is excluded by !**/*.snap
  • test-storybooks/mcp/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (101)
  • AGENTS.md
  • agent-eval/lib/templates.test.ts
  • agent-eval/lib/templates.ts
  • code/addons/mcp/package.json
  • code/addons/mcp/scripts/generate-tools-api-doc.ts
  • code/addons/mcp/src/auth/composition-auth.test.ts
  • code/addons/mcp/src/auth/composition-auth.ts
  • code/addons/mcp/src/auth/resolve-composition-sources.ts
  • code/addons/mcp/src/constants.ts
  • code/addons/mcp/src/instructions/build-server-instructions.ts
  • code/addons/mcp/src/manifests/__fixtures__/core-manifest.fixture.json
  • code/addons/mcp/src/manifests/core-format-contract.test.ts
  • code/addons/mcp/src/manifests/in-process-provider.test.ts
  • code/addons/mcp/src/manifests/in-process-provider.ts
  • code/addons/mcp/src/manifests/service-access.test.ts
  • code/addons/mcp/src/manifests/service-access.ts
  • code/addons/mcp/src/manifests/vendored.ts
  • code/addons/mcp/src/mcp-handler.test.ts
  • code/addons/mcp/src/mcp-handler.ts
  • code/addons/mcp/src/preset.test.ts
  • code/addons/mcp/src/preset.ts
  • code/addons/mcp/src/storybook-ai-metadata.test.ts
  • code/addons/mcp/src/telemetry.test.ts
  • code/addons/mcp/src/telemetry.ts
  • code/addons/mcp/src/test-support/register-core-toolsets.ts
  • code/addons/mcp/src/tools/display-review.test.ts
  • code/addons/mcp/src/tools/display-review.ts
  • code/addons/mcp/src/tools/get-changed-stories.test.ts
  • code/addons/mcp/src/tools/get-changed-stories.ts
  • code/addons/mcp/src/tools/get-stories-by-component.test.ts
  • code/addons/mcp/src/tools/get-stories-by-component.ts
  • code/addons/mcp/src/tools/get-storybook-story-instructions.test.ts
  • code/addons/mcp/src/tools/get-storybook-story-instructions.ts
  • code/addons/mcp/src/tools/preview-stories.test.ts
  • code/addons/mcp/src/tools/preview-stories.ts
  • code/addons/mcp/src/tools/preview-stories/preview-stories-app-script.ts
  • code/addons/mcp/src/tools/run-story-tests.test.ts
  • code/addons/mcp/src/tools/run-story-tests.ts
  • code/addons/mcp/src/tools/tool-names.ts
  • code/addons/mcp/src/tools/tool-registry-composition.test.ts
  • code/addons/mcp/src/tools/tool-registry.test.ts
  • code/addons/mcp/src/tools/tool-registry.ts
  • code/addons/mcp/src/tools/toolset-tools.test.ts
  • code/addons/mcp/src/tools/toolset-tools.ts
  • code/addons/mcp/src/tools/ui-root.ts
  • code/addons/mcp/src/types.ts
  • code/addons/mcp/src/utils/addon-vitest.ts
  • code/addons/mcp/src/utils/build-args-param.test.ts
  • code/addons/mcp/src/utils/build-args-param.ts
  • code/addons/mcp/src/utils/detect-unreachable-changes.test.ts
  • code/addons/mcp/src/utils/detect-unreachable-changes.ts
  • code/addons/mcp/src/utils/estimate-tokens.test.ts
  • code/addons/mcp/src/utils/estimate-tokens.ts
  • code/addons/mcp/src/utils/find-story-ids.test.ts
  • code/addons/mcp/src/utils/find-story-ids.ts
  • code/addons/mcp/src/utils/get-tool-availability.ts
  • code/addons/mcp/src/utils/module-graph.ts
  • code/addons/mcp/src/utils/resolve-component-stories.test.ts
  • code/addons/mcp/src/utils/resolve-component-stories.ts
  • code/addons/mcp/src/utils/slash.ts
  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/core-server/server-channel/review-channel.test.ts
  • code/core/src/core-server/server-channel/review-channel.ts
  • code/core/src/shared/review/events.ts
  • code/lib/mcp/bin.ts
  • code/lib/mcp/package.json
  • code/lib/mcp/src/dist-contract.test.ts
  • code/lib/mcp/src/globals.d.ts
  • code/lib/mcp/src/index.ts
  • code/lib/mcp/src/instructions.md
  • code/lib/mcp/src/tools/get-documentation-for-story.test.ts
  • code/lib/mcp/src/tools/get-documentation-for-story.ts
  • code/lib/mcp/src/tools/get-documentation.test.ts
  • code/lib/mcp/src/tools/get-documentation.ts
  • code/lib/mcp/src/tools/list-all-documentation.test.ts
  • code/lib/mcp/src/tools/list-all-documentation.ts
  • code/lib/mcp/src/tools/outcome-unwrap.test.ts
  • code/lib/mcp/src/tools/register.ts
  • code/lib/mcp/src/types.ts
  • code/lib/mcp/src/utils/adapt-core-manifest.test.ts
  • code/lib/mcp/src/utils/adapt-core-manifest.ts
  • code/lib/mcp/src/utils/dedent.ts
  • code/lib/mcp/src/utils/error-to-mcp-content.test.ts
  • code/lib/mcp/src/utils/error-to-mcp-content.ts
  • code/lib/mcp/src/utils/get-manifest.test.ts
  • code/lib/mcp/src/utils/get-manifest.ts
  • code/lib/mcp/src/utils/manifest-formatter/extract-docs-summary.test.ts
  • code/lib/mcp/src/utils/manifest-formatter/extract-docs-summary.ts
  • code/lib/mcp/src/utils/manifest-formatter/markdown.test.ts
  • code/lib/mcp/src/utils/manifest-formatter/markdown.ts
  • code/lib/mcp/src/utils/map-with-concurrency.ts
  • code/lib/mcp/src/utils/multi-source-manifests.ts
  • code/lib/mcp/src/utils/parse-react-docgen.test.ts
  • code/lib/mcp/src/utils/parse-react-docgen.ts
  • code/lib/mcp/src/utils/requires-own-mcp.test.ts
  • code/lib/mcp/src/utils/requires-own-mcp.ts
  • test-storybooks/mcp/.storybook-no-vitest/main.ts
  • test-storybooks/mcp/.storybook-no-vitest/preview.ts
  • test-storybooks/mcp/tests/helpers.ts
  • test-storybooks/mcp/tests/mcp-addon-vitest-not-enabled.e2e.test.ts
  • test-storybooks/mcp/tests/mcp-git-unusable.e2e.test.ts

Walkthrough

The PR moves addon and hosted MCP tools onto shared Storybook toolsets. It adds thin MCP adapters, request-scoped documentation access, outcome and error handling, availability and telemetry wiring, package-boundary tests, and end-to-end coverage. Obsolete MCP implementations and review-channel plumbing are removed.

Changes

Shared MCP toolset integration

Layer / File(s) Summary
Toolset contracts and hosted documentation
AGENTS.md, code/lib/mcp/src/tools/register.ts, code/lib/mcp/src/types.ts, code/lib/mcp/src/utils/*
Shared toolset contracts, documentation types, hosted registration, output validation, error conversion, and multi-source manifest access are defined.
Addon toolset adapter and registry
code/addons/mcp/src/tools/*, code/addons/mcp/src/mcp-handler.ts, code/addons/mcp/src/types.ts
Addon MCP tools delegate to shared toolsets. The adapter maps metadata, outcomes, structured output, errors, telemetry, and UI context.
Addon documentation access and composition
code/addons/mcp/src/auth/*, code/addons/mcp/src/preset.ts, code/lib/mcp/src/tools/*documentation*.test.ts
Local documentation access is separated from remote composition providers. Tests use request-context manifest providers and validate source routing and failure handling.
Availability and telemetry wiring
code/addons/mcp/src/utils/*, code/addons/mcp/src/telemetry.ts, code/addons/mcp/src/storybook-ai-metadata.test.ts
Vitest availability follows enabled preset state. Tool metadata follows registered toolsets. Telemetry identifies MCP and CLI consumers.
Cleanup and package validation
code/core/src/shared/review/events.ts, code/core/src/core-server/presets/common-preset.ts, code/lib/mcp/src/dist-contract.test.ts, agent-eval/lib/templates.ts
The legacy review channel and obsolete MCP engines are removed. Package dependency rewriting and published artifact constraints are updated and tested.
Integration coverage
code/addons/mcp/src/tools/*.test.ts, code/lib/mcp/src/tools/outcome-unwrap.test.ts, test-storybooks/mcp/tests/*
Registry, adapter, error, schema, disabled-Vitest, and unusable-Git scenarios receive coverage.

Sequence Diagram(s)

sequenceDiagram
  participant MCPClient
  participant MCPRegistry
  participant ToolsetAdapter
  participant CoreToolset
  participant ManifestProvider
  MCPClient->>MCPRegistry: invoke MCP tool
  MCPRegistry->>ToolsetAdapter: resolve registered method
  ToolsetAdapter->>CoreToolset: execute with request context
  CoreToolset->>ManifestProvider: read documentation manifest when required
  ManifestProvider-->>CoreToolset: return manifest or failure
  CoreToolset-->>ToolsetAdapter: return outcome
  ToolsetAdapter-->>MCPClient: return MCP content and structured result
Loading
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants