@via-ds/mcp
v0.0.1
Published
MCP server for the Via Design System
Readme
@via-ds/mcp
An MCP (Model Context Protocol) server for the Via Design System. It exposes component documentation and interactive story previews from Storybook, making Via components discoverable and usable by AI tools.
How it works
The server wraps the Via Storybook instance and surfaces its component manifest over HTTP as MCP tools. On startup it waits for Storybook's manifest to be reachable, then begins accepting JSON-RPC requests at http://{host}:{port}/mcp.
AI client → MCP HTTP endpoint → tool handlers → Storybook manifest / iframeTools
| Tool | Description |
| ----------------------------- | -------------------------------------------------- |
| list-all-documentation | Lists all available Via components |
| get-documentation | Returns documentation for a component |
| get-documentation-for-story | Returns documentation for a specific story variant |
| get-story-ui | Returns an interactive iframe preview of a story |
The Storybook tools (list-all-documentation, get-documentation, get-documentation-for-story) come from @storybook/mcp. get-story-ui is a custom tool that generates an HTML resource (ui://get-story-ui/{storyId}) pointing to the Storybook iframe.
Resources
| URI template | MIME type | Description |
| ----------------------------- | ----------- | --------------------------------------- |
| ui://get-story-ui/{storyId} | text/html | Sandboxed iframe rendering a live story |
Running locally
The MCP server requires a running Storybook instance (default: http://127.0.0.1:6006).
Development (auto-reloads on source changes):
# In repo root — start Storybook
pnpm storybook
# In packages/mcp — start the MCP server
pnpm devTesting with an MCP client:
We recommend using MCPJam to inspect and interact with the server during local development. It provides a visual interface for browsing tools, resources, and sending requests — useful for verifying changes without wiring up a full AI client.
Adding to Claude Code
To register the staging deployment as an MCP server in Claude Code (user scope). This will prompt you to sign in to Dex using your Okta credentials.
claude mcp add --scope user via \
--transport stdio \
-- devprod-mcp-proxy \
--gateway https://via-mcp.ux-foundations.staging.corp.mongodb.com/mcpDeployment
Deployments are managed by Drone CI and run automatically on every push to main.
Staging and production builds are deployed using Kubernetes on Kanopy. Helm values for each environment live alongside the package (e.g. helm.staging.yml).
The staging deployment is reachable at:
https://via-mcp.ux-foundations.staging.corp.mongodb.com/mcpThe server exposes a /healthz endpoint used by Kubernetes liveness and readiness probes.
Evals
The evals/ directory contains agentic evaluations that measure how well an LLM uses the MCP server to build real Via components. Results are tracked in Braintrust under the "Via MCP" project.
There are two eval types:
- Coding — an agent receives a UI task, consults the Via MCP documentation tools, and writes a working component. Scored on MCP tool usage, import correctness, build quality, docs adherence, and design correctness.
- Quiz — an agent answers design-guidance questions using the MCP server. Scored on semantic similarity to hand-authored expected answers.
Seed datasets (one-time, or when prompts change):
npx braintrust eval packages/mcp/evals/coding.dataset.ts
npx braintrust eval packages/mcp/evals/quiz.dataset.tsRun evals:
# Requires GROVE_API_KEY, BRAINTRUST_API_KEY, and VIA_MCP_URL to be set
pnpm evalThe shared eval harness (runCodingAgent, runAgenticEval, scorers) lives in @via-ds/eval-library so it can be reused across packages.
High-level flow
flowchart TD
Datasets["Braintrust Datasets\ncoding.dataset.ts · quiz.dataset.ts"]
Datasets --> CodingEval["coding.eval.ts"]
Datasets --> QuizEval["quiz.eval.ts"]
MCP["Via MCP server"]
subgraph CodingPath ["Coding eval"]
CodingEval --> CodingAgent["runCodingAgent\nopencode in Docker"]
CodingAgent --> Output["IsolatedEvalOutput"]
Output --> Scorers["Scorers\ntool-calls · code-imports\ncode-quality · docs-adherence\ndesign-correctness"]
end
subgraph QuizPath ["Quiz eval"]
QuizEval --> AgenticEval["runAgenticEval\nGrove agentic loop"]
AgenticEval --> TextOut["text response"]
TextOut --> AnsCorrectness["Scorers\nAnswerCorrectness\nsemantic similarity"]
end
CodingAgent <-->|MCP tool calls| MCP
AgenticEval <-->|MCP tool calls| MCP
Scorers --> Braintrust["Braintrust\nexperiment results + traces"]
AnsCorrectness --> BraintrustSetup
Required environment variables
| Variable | Required | Purpose |
| ------------------------- | ----------------------------------------- | ---------------------------------------------------- |
| GROVE_API_KEY | Yes (or GROVE_API_KEY_SECONDARY) | Authenticates requests to Grove and Voyage AI |
| GROVE_API_KEY_SECONDARY | Fallback for GROVE_API_KEY | Same as above |
| VOYAGE_API_KEY | Yes (quiz evals only) | Authenticates embedding calls to the Voyage AI proxy |
| BRAINTRUST_API_KEY | Yes | Authenticates with Braintrust |
| VIA_MCP_URL | No (default: http://127.0.0.1:3333/mcp) | Override the Via MCP server URL |
Grove and Voyage AI
LLM calls are routed through MongoDB's internal AI gateway (Grove), which presents an OpenAI-compatible API. Embeddings (used by the quiz AnswerCorrectness scorer) are routed through MongoDB's Voyage AI proxy (voyage-3-large), since Grove has no embeddings endpoint. The setupAutoevals() helper in @via-ds/eval-library wires this up for quiz.eval.ts.
MCP server health check
Before running agents, the runner fetches /healthz on the Via MCP server. If the server is not reachable, the eval fails immediately. Start the server with pnpm start:mcp.
Datasets
Eval inputs live in Braintrust rather than inline in the eval files. The .dataset.ts files seed them.
Once seeded, datasets appear in the Braintrust web UI under the Via MCP project and are referenced by name in the eval files via initDataset().
Coding scenarios
| Scenario | withVia | Description |
| -------------------- | --------- | --------------------------------------------------- |
| welcome-banner | false | Fresh project — agent must discover and install Via |
| status-dashboard | true | Via pre-installed — build a multi-card dashboard |
Quiz scenarios
The quiz dataset covers design-guidance questions on component selection, variant rules, and accessibility. Expected answers are hand-authored against the Via documentation.
Eval definition
Each eval file calls Braintrust's Eval() with a dataset, a task function, and a list of scorers:
// coding.eval.ts
Eval(BRAINTRUST_PROJECT, {
experimentName: 'Coding',
data: initDataset({ project: BRAINTRUST_PROJECT, dataset: 'Coding' }),
task: (input: string) => {
const { outputFiles, prompt, sandbox } = JSON.parse(input) as CodingInput;
return runCodingAgent({ input: prompt, outputFiles, sandbox });
},
scores: [
toolCallsScorer,
codeImportsScorer,
codeQualityScorer,
docsAdherenceScorer,
designCorrectnessScorer,
],
timeout: 360_000,
});// quiz.eval.ts
setupAutoevals();
Eval(BRAINTRUST_PROJECT, {
experimentName: 'Quiz',
data: initDataset({ project: BRAINTRUST_PROJECT, dataset: 'Quiz' }),
task: (input: string) => runAgenticEval({ input }),
scores: [AnswerCorrectness],
timeout: 60_000,
});Running agents
runCodingAgent — opencode in Docker
runCodingAgent scaffolds a fresh Vite React-TS workspace, writes an opencode.json config pointing at Grove and the Via MCP server, then launches opencode inside a Docker container (via-eval-opencode:latest) mounted on that workspace. The JSON event stream from opencode is parsed to build a tool-call transcript and extract the final response. After the agent finishes, configured checks (build, type-check) and output files are read back for scoring.
Workspace directories are left in place under .viteval/sandbox/ after each run for debugging.
runAgenticEval — Grove agentic loop
runAgenticEval drives an agentic loop directly — no subprocess. It connects to the Via MCP server, initialises an MCP session, and sends messages to Grove with MCP tools available until the model stops making tool calls. The quiz eval uses this runner without a sandbox and returns the model's plain text response.
IsolatedEvalOutput
Coding runners return a JSON-serialised IsolatedEvalOutput:
interface IsolatedEvalOutput {
evalTimestamp: number;
response: string;
transcript: ToolCallEntry[]; // every tool call the agent made
files: Record<string, string>; // output files after the agent finished
checks: {
build?: CommandResult;
typeCheck?: CommandResult;
};
error?: string;
}Scoring
Code-based scorers
Deterministic — no LLM calls.
tool-calls-scorer — Analyses the transcript to reward agents that use the Via MCP documentation tools.
| Base condition | Base score |
| ------------------------------------------------------------------- | ---------- |
| via_get-documentation or via_get-documentation-for-story called | 1.0 |
| via_list-all-documentation called only | 0.8 |
| No via_* tools called | 0.3 |
Each of the following counts as an offense and multiplies the base score by 0.67:
- A bash tool call that reads from
node_modules(e.g.cat node_modules/@via-ds/...orls node_modules/@via-ds/...) - A web fetch to
npmjs.comwith "via" in the URL
code-imports-scorer — Static import analysis on all .ts/.tsx/.js/.jsx files the agent wrote.
Banned prefixes: @leafygreen-ui/, react-aria-components, @react-aria/, tailwind, shadcn, @mui/
Score: max(0, 1 − violations × 0.3)
code-quality-scorer — Runs post-loop checks (build + type-check by default) in the workspace.
Score: passing_checks / total_checks
Model-based scorers
These send a rubric prompt to Grove and parse a score (0–1) and reason from the response.
docs-adherence-scorer — Checks whether the agent followed the Via documentation it retrieved — correct props, required props, and usage patterns.
design-correctness-scorer — Checks whether the generated UI matches the task description — right components, correct hierarchy, and overall design intent.
Eval key files
| File | Role |
| -------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
| evals/coding.eval.ts | Coding agent eval (Braintrust Eval) |
| evals/quiz.eval.ts | Quiz eval (Braintrust Eval) |
| evals/coding.dataset.ts | Seeds the Coding dataset in Braintrust |
| evals/quiz.dataset.ts | Seeds the Quiz dataset in Braintrust |
| packages/tools/eval-library/src/runCodingAgent.ts | opencode-in-Docker runner |
| packages/tools/eval-library/src/runEval.ts | runAgenticEval — Grove agentic loop |
| packages/tools/eval-library/src/scorers/ | All scorer implementations |
| packages/tools/eval-library/src/utils/autoevals-setup.ts | Wires Grove + Voyage AI into autoevals |
| packages/tools/eval-library/src/utils/grove-client.ts | Grove OpenAI client |
| packages/tools/eval-library/src/utils/scaffold.ts | Scaffolds the Vite React-TS workspace |
| packages/tools/eval-library/src/utils/opencode-config.ts | Writes opencode.json into the workspace |
| packages/tools/eval-library/src/mcp-client.ts | Lightweight MCP HTTP client |
Environment variables
| Variable | Default | Description |
| ------------------------- | ----------------------- | ----------------------------------------------------------------------------- |
| VIA_STORYBOOK_URL | http://127.0.0.1:6006 | Base URL of the Storybook instance |
| VIA_MCP_HOST | 127.0.0.1 | Bind address for the MCP HTTP server |
| PORT | — | Fallback port (used by Kubernetes); takes effect when VIA_MCP_PORT is unset |
| VIA_MCP_PORT | 3333 | Port for the MCP HTTP server |
| VIA_MCP_PATH | /mcp | HTTP path for the MCP endpoint |
| NODE_ENV | — | Set to production to disable interactive Storybook retry prompts |
| VIA_MCP_URL | — | MCP endpoint URL used by evals (e.g. http://127.0.0.1:3333/mcp) |
| GROVE_API_KEY | — | MongoDB Grove API key used by evals |
| GROVE_API_KEY_SECONDARY | — | Fallback for GROVE_API_KEY |
| VOYAGE_API_KEY | — | Voyage AI key used by the quiz eval's answer-correctness scorer |
| BRAINTRUST_API_KEY | — | Braintrust API key for logging eval results |
Copy .env.example to .env to configure locally.
Package structure
src/
├── server.ts # CLI entry point — starts the HTTP server
├── index.ts # Library export (createViaMCPHandler)
├── createViaMcpHandler.ts # MCP server factory — registers tools and resources
├── storybookManifestPreflight.ts # Blocks startup until Storybook manifest is reachable
└── tools/
└── get-story-ui.ts # Custom tool for interactive story previews
evals/
├── coding.eval.ts # Coding agent eval (Braintrust)
├── coding.dataset.ts # Seeds the Coding dataset in Braintrust
├── quiz.eval.ts # Quiz eval — design-guidance Q&A (Braintrust)
└── quiz.dataset.ts # Seeds the Quiz dataset in Braintrust