@magnolia/mgnl-ds-mcp-server 1.0.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/LICENSE.txt +27 -0
- package/README.md +42 -0
- package/bin/mcp.mjs +41 -0
- package/package.json +95 -0
- package/src/manifests.mjs +26 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Copyright (c) 2026 Magnolia International Ltd.
|
|
2
|
+
(http://www.magnolia-cms.com). All rights reserved.
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
The software is dual-licensed under both the Magnolia
|
|
6
|
+
Network Agreement and the GNU General Public License.
|
|
7
|
+
You may elect to use one or the other of these licenses.
|
|
8
|
+
|
|
9
|
+
The software is distributed in the hope that it will be
|
|
10
|
+
useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
|
|
11
|
+
implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
12
|
+
PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
|
|
13
|
+
Redistribution, except as permitted by whichever of the GPL
|
|
14
|
+
or MNA you select, is prohibited.
|
|
15
|
+
|
|
16
|
+
1. For the GPL license (GPL), you can redistribute and/or
|
|
17
|
+
modify this file under the terms of the GNU General
|
|
18
|
+
Public License, Version 3, as published by the Free Software
|
|
19
|
+
Foundation. You should have received a copy of the GNU
|
|
20
|
+
General Public License, Version 3 along with this program;
|
|
21
|
+
if not, write to the Free Software Foundation, Inc., 51
|
|
22
|
+
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
23
|
+
|
|
24
|
+
2. For the Magnolia Network Agreement (MNA), this file
|
|
25
|
+
and the accompanying materials are made available under the
|
|
26
|
+
terms of the MNA which accompanies this distribution, and
|
|
27
|
+
is available at http://www.magnolia-cms.com/mna.html
|
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Magnolia DS — Storybook MCP server
|
|
2
|
+
|
|
3
|
+
An [MCP](https://modelcontextprotocol.io/) server that exposes the [Magnolia Design System Storybook](https://ds.magnolia-cloud.com) to AI agents, so they build UI with **real** components, props, and usage instead of guessing. It runs locally via `npx` — your MCP client spawns it over stdio; there’s nothing to host.
|
|
4
|
+
|
|
5
|
+
It reuses the documentation tools from [`@storybook/mcp`](https://www.npmjs.com/package/@storybook/mcp) and points them at **published** manifests (`components.json` / `docs.json`) over HTTPS. So it holds **no state**, needs **no credentials**, and can never drift from what’s live.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
Claude Code:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
claude mcp add magnolia-ds -- npx -y @magnolia/mgnl-ds-mcp-server
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Cursor / VS Code / other clients — add to the `mcpServers` config:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"mcpServers": {
|
|
20
|
+
"magnolia-ds": {
|
|
21
|
+
"command": "npx",
|
|
22
|
+
"args": ["-y", "@magnolia/mgnl-ds-mcp-server"]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
That’s it. `npx` fetches the latest published version on each launch, the client manages the process over stdio, and the server reads the live manifests.
|
|
29
|
+
|
|
30
|
+
## MCP Tools
|
|
31
|
+
|
|
32
|
+
- `list-all-documentation` — list every component and docs entry (IDs + summaries).
|
|
33
|
+
- `get-documentation` — full docs for one component/entry (props, stories, code snippets).
|
|
34
|
+
- `get-documentation-for-story` — docs for a specific story variant.
|
|
35
|
+
|
|
36
|
+
Try it: ask your agent _"using the Magnolia design system, render a loading Button"_.
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
| Setting | env | default |
|
|
41
|
+
| --------------------------- | ---------------- | ----------------------------------------------- |
|
|
42
|
+
| manifests base (URL or dir) | `MANIFESTS_PATH` | `https://ds.magnolia-cloud.com/react/manifests` |
|
package/bin/mcp.mjs
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { McpServer } from 'tmcp';
|
|
4
|
+
import { ValibotJsonSchemaAdapter } from '@tmcp/adapter-valibot';
|
|
5
|
+
import { StdioTransport } from '@tmcp/transport-stdio';
|
|
6
|
+
import {
|
|
7
|
+
STORYBOOK_MCP_INSTRUCTIONS,
|
|
8
|
+
addListAllDocumentationTool,
|
|
9
|
+
addGetStoryDocumentationTool,
|
|
10
|
+
addGetDocumentationTool,
|
|
11
|
+
} from '@storybook/mcp';
|
|
12
|
+
import { createManifestProvider } from '../src/manifests.mjs';
|
|
13
|
+
|
|
14
|
+
const manifestsPath =
|
|
15
|
+
process.env.MANIFESTS_PATH ?? 'https://ds.magnolia-cloud.com/react/manifests';
|
|
16
|
+
|
|
17
|
+
const pkg = JSON.parse(
|
|
18
|
+
await readFile(new URL('../package.json', import.meta.url), 'utf8'),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
// Mirrors @storybook/mcp's createStorybookMcpHandler server construction.
|
|
22
|
+
const server = new McpServer(
|
|
23
|
+
{
|
|
24
|
+
name: 'magnolia-ds',
|
|
25
|
+
version: pkg.version,
|
|
26
|
+
description: 'Magnolia Design System Storybook documentation',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
adapter: new ValibotJsonSchemaAdapter(),
|
|
30
|
+
instructions: STORYBOOK_MCP_INSTRUCTIONS,
|
|
31
|
+
capabilities: { tools: { listChanged: true } },
|
|
32
|
+
},
|
|
33
|
+
).withContext();
|
|
34
|
+
|
|
35
|
+
await addListAllDocumentationTool(server);
|
|
36
|
+
await addGetStoryDocumentationTool(server);
|
|
37
|
+
await addGetDocumentationTool(server);
|
|
38
|
+
|
|
39
|
+
new StdioTransport(server).listen({
|
|
40
|
+
manifestProvider: createManifestProvider(manifestsPath),
|
|
41
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@magnolia/mgnl-ds-mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Magnolia Design System Storybook MCP server to be run locally via npx so AI agents build UI with real components.",
|
|
5
|
+
"author": "Magnolia International",
|
|
6
|
+
"license": "See license in LICENSE.txt",
|
|
7
|
+
"private": false,
|
|
8
|
+
"type": "module",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "ssh://git@gitlab.magnolia-platform.com:9022/ds/mgnl-ds-mcp-server.git"
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"mgnl-ds-mcp-server": "./bin/mcp.mjs"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"bin",
|
|
18
|
+
"src/manifests.mjs",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE.txt"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=22"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"start": "node bin/mcp.mjs",
|
|
30
|
+
"lint": "eslint . && prettier --check .",
|
|
31
|
+
"lint:prettier": "prettier . -w",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:integration": "cross-env RUN_INTEGRATION=1 vitest run",
|
|
34
|
+
"release": "semantic-release"
|
|
35
|
+
},
|
|
36
|
+
"release": {
|
|
37
|
+
"branches": [
|
|
38
|
+
"main"
|
|
39
|
+
],
|
|
40
|
+
"plugins": [
|
|
41
|
+
[
|
|
42
|
+
"@semantic-release/commit-analyzer",
|
|
43
|
+
{
|
|
44
|
+
"preset": "conventionalcommits"
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
"@semantic-release/release-notes-generator",
|
|
49
|
+
{
|
|
50
|
+
"preset": "conventionalcommits"
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
"@semantic-release/changelog",
|
|
54
|
+
[
|
|
55
|
+
"@semantic-release/npm",
|
|
56
|
+
{
|
|
57
|
+
"npmPublish": true
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
[
|
|
61
|
+
"@semantic-release/git",
|
|
62
|
+
{
|
|
63
|
+
"assets": [
|
|
64
|
+
"package.json",
|
|
65
|
+
"CHANGELOG.md"
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
],
|
|
70
|
+
"tagFormat": "${version}"
|
|
71
|
+
},
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"@storybook/mcp": "^0.7.0",
|
|
74
|
+
"@tmcp/adapter-valibot": "^0.1.6",
|
|
75
|
+
"@tmcp/transport-stdio": "^0.4.3",
|
|
76
|
+
"tmcp": "^1.19.4"
|
|
77
|
+
},
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"@eslint/js": "^10.0.1",
|
|
80
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
81
|
+
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
82
|
+
"@semantic-release/git": "^10.0.1",
|
|
83
|
+
"@semantic-release/npm": "^13.1.5",
|
|
84
|
+
"@semantic-release/release-notes-generator": "^14.1.1",
|
|
85
|
+
"conventional-changelog-conventionalcommits": "^9.3.1",
|
|
86
|
+
"cross-env": "^10.1.0",
|
|
87
|
+
"eslint": "^10.6.0",
|
|
88
|
+
"eslint-config-prettier": "^10.1.8",
|
|
89
|
+
"eslint-plugin-prettier": "^5.5.6",
|
|
90
|
+
"globals": "^17.7.0",
|
|
91
|
+
"prettier": "^3.9.4",
|
|
92
|
+
"semantic-release": "^25.0.5",
|
|
93
|
+
"vitest": "^4.1.9"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Manifest provider for the Magnolia DS Storybook MCP server.
|
|
2
|
+
//
|
|
3
|
+
// `@storybook/mcp` is a LIBRARY (no CLI). Its tools read the Storybook manifests (`components.json` / `docs.json`) through a `manifestProvider` we supply via the server context. This builds that provider for two sources, picked from `manifestsPath`:
|
|
4
|
+
// - a remote base URL (e.g. https://ds.magnolia-cloud.com/react/manifests) — the default: it `fetch`es the already-public manifests, so it needs no credentials and can never drift from what's deployed.
|
|
5
|
+
// - a local directory — handy for testing against `storybook-static/manifests`.
|
|
6
|
+
import { readFile } from 'node:fs/promises';
|
|
7
|
+
import { basename, resolve } from 'node:path';
|
|
8
|
+
|
|
9
|
+
export function createManifestProvider(manifestsPath) {
|
|
10
|
+
const base = manifestsPath.replace(/\/+$/, ''); // no trailing slash → no `//file.json`
|
|
11
|
+
const isRemote = base.startsWith('http://') || base.startsWith('https://');
|
|
12
|
+
|
|
13
|
+
return async (_request, path) => {
|
|
14
|
+
const fileName = basename(path);
|
|
15
|
+
if (isRemote) {
|
|
16
|
+
const res = await fetch(`${base}/${fileName}`);
|
|
17
|
+
if (!res.ok) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`Failed to fetch manifest ${base}/${fileName}: ${res.status} ${res.statusText}`,
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
return await res.text();
|
|
23
|
+
}
|
|
24
|
+
return await readFile(resolve(base, fileName), 'utf-8');
|
|
25
|
+
};
|
|
26
|
+
}
|