Story UIBLOCK
AI-powered Storybook story generator — works with any framework, any design system, any LLM provider
Overview
From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.
[](https://www.npmjs.com/package/@tpitre/story-ui) [](LICENSE)
Design screens in your own design system, by describing them.
Story UI adds a workspace to the Storybook you already have. You type what you want — "a settings page with a sidebar and a sticky save bar" — and it writes a real Storybook story using the components your team actually ships. The result appears in Storybook's own preview, with your providers, your theme and your tokens. It is not a mockup and not a sandbox. It is a file in your repository that a developer can open, review and keep.
Why it exists
Design systems are full of components that nobody has time to assemble into real screens. So exploration happens in a design tool, in a vocabulary the code does not share, and every idea has to be rebuilt before anyone can judge it.
Story UI closes that gap from the other side.
- It only uses what you have. Components are discovered from your project:
the package's type declarations, your local source, and Storybook's own index. It cannot invent a component you do not own, and every prop is checked against that component's real type.
- It composes, so you can judge. Buttons and cards in isolation tell you
nothing. Whole screens do.
- The output is real work. Every generation is a
.stories.tsxfile in
your repository, in your Storybook, ready for a pull request.
- It checks itself before you see it. Each story is rendered in a real
browser and measured — content inside its box, nothing overlapping, nothing blank, nothing broken — and generated again if it fails. See How Story UI checks its own work.
**
c8c62f93525eOBSERVED · 2026-10-12Connect
Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.
claude mcp add story-ui --env ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} --env CLAUDE_API_KEY=${CLAUDE_API_KEY} --env GEMINI_API_KEY=${GEMINI_API_KEY} --env GOOGLE_API_KEY=${GOOGLE_API_KEY} -- npx -y @tpitre/[email protected]{
"mcpServers": {
"story-ui": {
"command": "npx",
"args": [
"-y",
"@tpitre/[email protected]"
],
"env": {
"ANTHROPIC_API_KEY": "${ANTHROPIC_API_KEY}",
"CLAUDE_API_KEY": "${CLAUDE_API_KEY}",
"GEMINI_API_KEY": "${GEMINI_API_KEY}",
"GOOGLE_API_KEY": "${GOOGLE_API_KEY}"
}
}
}
}Exposed tools (21)
18 read · 2 write · 1 destructive. Blast radius: 1 tool can delete or overwrite — an agent that can be talked into calling a tool can be talked into calling this one.
| Tool | Risk | Description |
|---|---|---|
Alert | read | |
Button | read | Interactive button |
Card | read | Container for grouped content |
Checkbox | read | Checkbox input |
Input | read | Text input field |
SimpleGrid | read | Lays children out in columns |
Stack | read | Stacks children vertically |
Text | read | Body text |
Tile | read | Tile component from Housekit |
Title | read | A heading |
delete-story | destructive | Delete a generated story |
generate-story | read | Generate a Storybook story from a natural language prompt |
get-component-props | read | Get detailed prop information for a specific component |
get-story | read | Get the content of a specific generated story |
left-pad | read | String left pad |
list-components | read | List all available components that can be used in stories |
list-stories | read | List all generated stories |
pictos | write | A glyph set |
test-connection | read | Test if MCP connection is working |
theme | read | Global theme |
update-story | write | Update an existing Storybook story with modifications. If no storyId is provided, I |
Trust audit
BLOCKgrade F · trust 54/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | WARN |
| L1 | Static analysis of the code | FAIL |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | WARN |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- declared (2 observation(s))
- Network
- declared (8 observation(s))
- Shell
- declared (10 observation(s))
- Dependencies
- not all pinned
- Secrets in source
- none-found
Findings (25)
{ pattern: /\beval\s*\(/g, label: 'eval()', replacement: '/* [sanitized: eval] */void(' },{ pattern: /\bnew\s+Function\s*\(/g, label: 'new Function()', replacement: '/* [sanitized: new Function] */void(' },const dynamicImport = new Function('specifier', 'return import(specifier)');const componentFrom = eval(`(${componentFromMarkupSrc})`);const orderCandidates = eval(`(${orderCandidatesSrc})`);fileAttachments.ts
health = await fetch(`http://127.0.0.1:${spec.split(':')[1]}/health`, { signal: AbortSignal.timeout(4000) }).then(r => r.ok).catch(() => false);index = await fetch(`http://127.0.0.1:${spec.split(':')[2]}/index.json`, { signal: AbortSignal.timeout(8000) }).then(r => r.ok).catch(() => false);.replace(/[\s]/g, '');
delete-story
.env.sample
.releaserc
?? crypto.createHash('sha1').update(result.fileName).digest('hex').slice(0, 8);`fallback-${crypto.createHash('sha1').update(`${prompt}${startedAt}`).digest('hex').slice(0, 8)}`,hash = hashMatch ? hashMatch[1] : crypto.createHash('sha1').update(prompt).digest('hex').slice(0, 8);hash = hashMatch ? hashMatch[1] : crypto.createHash('sha1').update(prompt).digest('hex').slice(0, 8);hash = crypto.createHash('sha1').update(prompt + timestamp).digest('hex').slice(0, 8);'/etc/passwd',
Button: { specifier: '../../components/Button/Button', defaultExport: false },Card: { specifier: '../../components/Card/Card', defaultExport: false },CardHeader: { specifier: '../../components/Card/Card', defaultExport: false },"import { Button } from '../../components/Button/Button';","import { Card, CardHeader } from '../../components/Card/Card';",process.env.STORYBOOK_URL = 'http://127.0.0.1:1';
process.env.STORYBOOK_URL = 'http://127.0.0.1:1';
Gates applied: no_behavioural_pass.
c8c62f93525efull audit observations/trust-audit/mcp-server/southleft__story-ui.json · Report an issue / request a re-scanAudit history
Every audit this server has had. A grade with a past is a grade somebody is still checking.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-12 | c8c62f93525e | BLOCK | F | 54 | first audit |
Questions
What is the @tpitre/story-ui MCP server?
AI-powered Storybook story generator — works with any framework, any design system, any LLM provider
What tools does @tpitre/story-ui expose?
21 in total: 18 read-only, 2 that write, and 1 that can delete or overwrite (delete-story). Every one is listed on this page with its risk.
Is @tpitre/story-ui safe to connect to an agent?
No — not without reading the findings first. The audit graded it F (54/100) and found 5 critical or high issues in the source. Each one is listed on this page with the file and line it is on. Separately from the audit: 1 of its tools can destroy data, so scope the token you give it to what you actually need.
What credentials does @tpitre/story-ui need?
It reads ANTHROPIC_API_KEY, CLAUDE_API_KEY, GEMINI_API_KEY, GOOGLE_API_KEY, OPENAI_API_KEY and TYPESAFE_API_KEY from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.
How does @tpitre/story-ui run?
It speaks sse, stdio and streamable-http, so it runs as a local process your client starts. It is published on npm as @tpitre/story-ui at 5.21.2.
How current is this page?
The grade is for one exact copy of the source (c8c62f93525e), read on 2026-10-12. The repository is watched and re-audited when it changes.