@volue/wave-mcp 0.1.0-next.3 → 0.1.0-next.5
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/README.md +27 -11
- package/dist/index-Bz3fxPpO.js +71 -0
- package/dist/index.js +1 -31
- package/dist/server-DrYJkNtU.js +1 -0
- package/dist/transports/http.js +1 -109
- package/dist/transports/stdio.js +1 -26
- package/package.json +11 -11
- package/dist/index-EJr2aPNi.js +0 -619
- package/dist/server-DtR5gJns.js +0 -45
package/README.md
CHANGED
|
@@ -4,20 +4,24 @@
|
|
|
4
4
|
|
|
5
5
|
## Available Tools
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
| Tool Name | Description |
|
|
8
|
+
| ----------------- | -------------------------------------------------------- |
|
|
9
|
+
| `init` | Initialize the Wave Design System in your project |
|
|
10
|
+
| `list_components` | List all available components grouped by category |
|
|
11
|
+
| `get_component` | Get comprehensive documentation for a specific component |
|
|
12
|
+
| `list_patterns` | List all UX patterns grouped by category |
|
|
13
|
+
| `get_pattern` | Retrieve detailed guidance for a specific pattern |
|
|
14
|
+
| `get_color_usage` | Get color token usage and guidelines |
|
|
15
|
+
| `list_icons` | List all available icons in the Wave icon library |
|
|
16
|
+
| `get_icon` | Get SVG code and usage information for a specific icon |
|
|
15
17
|
|
|
16
18
|
## Setup
|
|
17
19
|
|
|
20
|
+
Add configuration code that tells your MCP-compatible client how to connect to the Wave MCP server.
|
|
21
|
+
|
|
18
22
|
### VS Code
|
|
19
23
|
|
|
20
|
-
[](https://insiders.vscode.dev/redirect/mcp/install?name=Wave%20Design%20System&config=%7B%22type%22%3A%22stdio%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40volue%2Fwave-mcp%40latest%22%5D%7D)
|
|
21
25
|
|
|
22
26
|
Use the button above or manually add to your `.vscode/mcp.json`:
|
|
23
27
|
|
|
@@ -36,7 +40,17 @@ Use the button above or manually add to your `.vscode/mcp.json`:
|
|
|
36
40
|
|
|
37
41
|
### Claude Code
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
Use the CLI:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# 1. Navigate to your project
|
|
47
|
+
cd your-awesome-project
|
|
48
|
+
|
|
49
|
+
# 2. Add Wave MCP server
|
|
50
|
+
claude mcp add wave -- npx -y @volue/wave-mcp@latest
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or manually add to your Claude Code MCP servers configuration:
|
|
40
54
|
|
|
41
55
|
```json
|
|
42
56
|
{
|
|
@@ -51,7 +65,9 @@ Add to your Claude Code MCP servers configuration:
|
|
|
51
65
|
|
|
52
66
|
### Cursor
|
|
53
67
|
|
|
54
|
-
|
|
68
|
+
[](https://cursor.com/en/install-mcp?name=wave&config=eyJ0eXBlIjoic3RkaW8iLCJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkB2b2x1ZS93YXZlLW1jcEBsYXRlc3QiXX0=)
|
|
69
|
+
|
|
70
|
+
Use the button above or manually add to your Cursor MCP configuration:
|
|
55
71
|
|
|
56
72
|
```json
|
|
57
73
|
{
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
var N=Object.defineProperty;var r=(e,t)=>N(e,"name",{value:t,configurable:!0});import{a as u,f as p,u as f,g as m}from"./server-DrYJkNtU.js";import{createRequire as W}from"node:module";import I from"remark-parse";import A from"unified";import{z as g}from"zod";const j=A().use(I),$={Components:"component","UX patterns":"pattern"};function F(e,t){const o=j.parse(e);let n=null;return o.children.reduce((a,i)=>{if(u(i,"heading")&&i.depth===2)return n=B(i),a;if(!n||!u(i,"list"))return a;const s=n,c=i.children.map(l=>P(l,s,t)).filter(l=>l!==null);return a.concat(c)},[])}r(F,"parseLlmsIndex");function B(e){const[t]=e.children;if(!t||!u(t,"text"))return null;const o=t.value.trim();if(!(o in $))return null;const n=$[o];return{title:o,type:n}}r(B,"extractSectionContext");function P(e,t,o){const[n]=e.children;if(!n||!u(n,"paragraph")||n.children.length!==2)return null;const[a,i]=n.children;if(!u(a,"link")||!u(i,"text")||a.children.length!==1)return null;const s=a.children[0];if(!u(s,"text"))return null;const c=s.value.trim(),l=a.url.replace(/^\//,""),L=new URL(l,`${o}/`).toString(),D=O(l),U=M(t.title,l,t.type),k=z(i.value);return{name:c,slug:D,category:U,section:t.title,type:t.type,url:L,description:k??void 0}}r(P,"listItemToResource");function O(e){return e.replace(/\.md$/,"").split("/").pop()??""}r(O,"deriveSlug");function M(e,t,o){const n=t.replace(/\.md$/,"").split("/");if(o==="component"||o==="pattern"){const a=n.length>=3?n[n.length-2]:null;return a?`${e} / ${V(a)}`:e}return e}r(M,"deriveCategory");function V(e){return e.split("-").filter(Boolean).map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")}r(V,"formatSegment");function z(e){if(!e)return null;const t=e.replace(/^\s*:\s*/,"").trim();return t.toLowerCase()==="no description available"?null:t}r(z,"normalizeDescription");const E=W(import.meta.url),H=E("@volue/design-colors/generic"),X=E("@volue/design-icons"),h=process.env.USE_STABLE_DOCS==="true"||process.env.USE_STABLE_DOCS==="1"?"https://wave.volue.com":"https://wave-design-system.vercel.app",G=`${h}/llms.txt`,q=1e3*60*15,J=process.env.WAVE_RESOURCES_CACHE_TIME?Number.parseInt(process.env.WAVE_RESOURCES_CACHE_TIME,10):q;function y(e){const t=new URL(e,h);return t.pathname.endsWith(".md")||(t.pathname+=".md"),t.toString()}r(y,"createUrl");let v=null,C=0;async function Y(){if(v!==null&&Date.now()<C)return v;const e=await K();return v=e,C=Date.now()+J,e}r(Y,"listCachedResources");let d=null;async function K(){return d||(d=Q().finally(()=>{d=null})),d}r(K,"idempotentLoadResourcesFromLlms");async function Q(){const e=await p(G,void 0,"fetch llms.txt");return F(e,h)}r(Q,"loadResourcesFromLlms");async function Z(e){return p(e.url,void 0,`fetch ${e.name} documentation`)}r(Z,"fetchComponent");function ee(e){return p(e.url,void 0,`fetch ${e.name} pattern`)}r(ee,"fetchPattern");async function te(){return p(y("get-started/developing"),void 0,"fetch developing guide")}r(te,"fetchDevelopingGuide");async function ne(){return p(y("tokens/colors"),void 0,"fetch color usage guidelines")}r(ne,"fetchColorUsageGuidelines");function x(){return Object.entries(X).map(([e,t])=>({name:e,svg:t}))}r(x,"listIcons");function oe(){const e=x();return f(e.map(t=>t.name))}r(oe,"getIconNames");function re(e,t){const o=t.trim().toLowerCase();return e.find(n=>n.name.toLowerCase()===o)}r(re,"findIconByName");async function T(e){return(await Y()).filter(o=>o.type===e)}r(T,"listResourcesByType");function b(e,t){const o=t.trim().toLowerCase();return e.find(n=>n.name.toLowerCase()===o)}r(b,"findResourceByName");function w(){return T("component")}r(w,"listComponents");async function ae(){const e=await w();return f(e.map(t=>t.name))}r(ae,"getComponentNames");function S(){return T("pattern")}r(S,"listPatterns");async function ie(){const e=await S();return f(e.map(t=>t.name))}r(ie,"getPatternNames");function _(e){return e.reduce((t,o)=>(t[o.category]||(t[o.category]=[]),t[o.category].push(o),t),{})}r(_,"groupResourcesByCategory");const se={name:"get_color_usage",description:"Retrieve the Wave Design System color tokens and guidelines for applying color in user interfaces.",exec(e,{name:t,description:o}){e.tool(t,o,async()=>{try{const n=await ne();return{content:[{type:"text",text:JSON.stringify(H)},{type:"text",text:`Here are the Wave Design System color usage guidelines:
|
|
2
|
+
|
|
3
|
+
${n}
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
Source: ${y("tokens/colors")}`}]}}catch(n){return{isError:!0,content:[{type:"text",text:`Failed to retrieve color usage guidelines: ${m(n)}`}]}}})}},ce={name:"get_component",description:"Retrieve comprehensive documentation for a specific Wave Design System component by name. This tool retrieves the import instructions, basic usage and advanced configuration examples, code snippets for common scenarios, available props/properties table and some general guidelines for a given Wave Design System component.",async ctx(){try{return{componentNames:await ae()}}catch(e){throw new Error(`Failed to initialize component tool: ${m(e)}`)}},exec(e,{ctx:t,name:o,description:n}){e.tool(o,n,{name:g.enum(t.componentNames).describe("The name of the component to retrieve documentation for. This can be derived from calling the `get_components` tool.")},async({name:a})=>{try{const i=await w(),s=b(i,a);if(!s)throw new Error("Component not found");const c=await Z(s);return c?{content:[{type:"text",text:`Here is the documentation for the \`${s.name}\` component from the Wave Design System:
|
|
7
|
+
|
|
8
|
+
${c}
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
Source: ${s.url}`}]}:{content:[{type:"text",text:`No documentation content found for the \`${s.name}\` component.`}]}}catch(i){return{isError:!0,content:[{type:"text",text:`Failed to retrieve documentation for \`${a}\`: ${m(i)}`}]}}})}},le={name:"get_icon",description:"Retrieve a specific icon information by name from Wave Design System.",exec(e,{name:t,description:o}){e.tool(t,o,{name:g.enum(oe()).describe("The icon name to retrieve. This can be derived from calling the `list_icon` tool.")},async({name:n})=>{try{const a=x(),i=re(a,n);if(!i)throw new Error("Icon not found.");const s=`<SvgIcon iconName="${i.name}" />`,c=`<div class="svgIcon svgIcon--stroked">
|
|
12
|
+
<svg focusable="false" aria-hidden="true">
|
|
13
|
+
<use xlink:href="#svg--${i.name}"></use>
|
|
14
|
+
</svg>
|
|
15
|
+
</div>`;return{content:[{type:"text",text:`# ${i.name} icon
|
|
16
|
+
|
|
17
|
+
## React component:
|
|
18
|
+
|
|
19
|
+
\`\`\`tsx
|
|
20
|
+
${s}
|
|
21
|
+
\`\`\`
|
|
22
|
+
|
|
23
|
+
## HTML markup:
|
|
24
|
+
|
|
25
|
+
\`\`\`html
|
|
26
|
+
${c}
|
|
27
|
+
\`\`\`
|
|
28
|
+
|
|
29
|
+
## Raw SVG code:
|
|
30
|
+
|
|
31
|
+
\`\`\`svg
|
|
32
|
+
${i.svg}
|
|
33
|
+
\`\`\``}]}}catch(a){return{isError:!0,content:[{type:"text",text:`Failed to retrieve icon \`${n}\`: ${m(a)}`}]}}})}},me={name:"get_pattern",description:"Retrieve documentation for a specific Wave Design System UX pattern by name. This tool provides guidance, examples and components that implement the selected pattern.",async ctx(){try{return{patternNames:await ie()}}catch(e){throw new Error(`Failed to initialize pattern tool: ${m(e)}`)}},exec(e,{ctx:t,name:o,description:n}){e.tool(o,n,{name:g.enum(t.patternNames).describe("The name of the UX pattern to retrieve documentation for. This can be derived from calling the `list_patterns` tool.")},async({name:a})=>{try{const i=await S(),s=b(i,a);if(!s)throw new Error("Pattern not found");const c=await ee(s);return{content:[{type:"text",text:`Here are the guidelines for the \`${s.name}\` UX pattern from the Wave Design System:
|
|
34
|
+
|
|
35
|
+
${c}
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
Source: ${s.url}`}]}}catch(i){return{isError:!0,content:[{type:"text",text:`Failed to retrieve documentation for \`${a}\`: ${m(i)}`}]}}})}},ue={name:"init",description:"Get guide on how to setup or create a project that includes Wave Design System.",exec(e,{name:t,description:o}){e.tool(t,o,async()=>{try{return{content:[{type:"text",text:`The getting started documentation for developing with Wave Design System is included below.
|
|
39
|
+
|
|
40
|
+
It's **important** that if the project is using a tool like Vite or Next.js, one of the pre-configured starter templates should be used instead of manual configuration:
|
|
41
|
+
|
|
42
|
+
- https://github.com/Volue/wave-vite-example (Vite template)
|
|
43
|
+
- https://github.com/Volue/wave-nextjs-app-router-example (Next.js App Router template)
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
${await te()}
|
|
48
|
+
`}]}}catch(n){return{isError:!0,content:[{type:"text",text:`Failed to retrieve getting started documentation: ${m(n)}`}]}}})}},pe={name:"list_components",description:"List all components available from the Wave Design System. This tool retrieves the names of all available Wave Design System components grouped by category.",exec(e,{name:t,description:o}){e.tool(t,o,async()=>{try{const n=await w(),a=_(n);return{content:[{type:"text",text:`The following components are available in the Wave Design System:
|
|
49
|
+
|
|
50
|
+
${Object.entries(a).map(([s,c])=>`## ${s}
|
|
51
|
+
${c.map(l=>`- ${l.name}`).join(`
|
|
52
|
+
`)}`).join(`
|
|
53
|
+
|
|
54
|
+
`)}
|
|
55
|
+
|
|
56
|
+
Use the \`get_component\` tool to get more information about a specific component.
|
|
57
|
+
|
|
58
|
+
Use these components from the @volue/wave-react package.`}]}}catch(n){return{isError:!0,content:[{type:"text",text:`Failed to retrieve the component index: ${m(n)}`}]}}})}},de={name:"list_icons",description:"List all icons available from the Wave Design System. Icons are sourced directly from the `@volue/design-icons` package.",exec(e,{name:t,description:o}){e.tool(t,o,async()=>{try{return{content:[{type:"text",text:`The following icons are available in the Wave Design System:
|
|
59
|
+
|
|
60
|
+
${x().map(i=>`- ${i.name}`).join(`
|
|
61
|
+
`)}
|
|
62
|
+
|
|
63
|
+
Use the \`get_icon\` tool to retrieve the SVG markup and usage guidelines for a specific icon.`}]}}catch(n){return{isError:!0,content:[{type:"text",text:`Failed to retrieve the icon data: ${m(n)}`}]}}})}},fe={name:"list_patterns",description:"List all UX patterns available from the Wave Design System. This tool retrieves the names of all available Wave Design System patterns grouped by category.",exec(e,{name:t,description:o}){e.tool(t,o,async()=>{try{const n=await S(),a=_(n);return{content:[{type:"text",text:`The following UX patterns are available in the Wave Design System:
|
|
64
|
+
|
|
65
|
+
${Object.entries(a).map(([s,c])=>`## ${s}
|
|
66
|
+
${c.map(l=>`- ${l.name}`).join(`
|
|
67
|
+
`)}`).join(`
|
|
68
|
+
|
|
69
|
+
`)}
|
|
70
|
+
|
|
71
|
+
Use the \`get_pattern\` tool to access detailed guidance for a specific pattern.`}]}}catch(n){return{isError:!0,content:[{type:"text",text:`Failed to retrieve the pattern index: ${m(n)}`}]}}})}},ge=[ue,pe,ce,fe,me,se,de,le],R=new Set;async function he(e){await Promise.all(ge.map(async t=>{if(R.has(t.name))return;const o=await t.ctx?.();R.add(t.name),t.exec(e,{name:t.name,description:t.description,ctx:o})}))}r(he,"initializeTools");export{he as i};
|
package/dist/index.js
CHANGED
|
@@ -1,32 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
export { s as server } from './server-DtR5gJns.js';
|
|
4
|
-
import 'node:fs';
|
|
5
|
-
import 'node:url';
|
|
6
|
-
import '@modelcontextprotocol/sdk/server/mcp.js';
|
|
7
|
-
|
|
8
|
-
if (isEntryFile(import.meta.url)) {
|
|
9
|
-
const args = process.argv.slice(2);
|
|
10
|
-
const transport = args[0] || "stdio";
|
|
11
|
-
async function run() {
|
|
12
|
-
try {
|
|
13
|
-
switch (transport) {
|
|
14
|
-
case "stdio":
|
|
15
|
-
await import('./transports/stdio.js');
|
|
16
|
-
break;
|
|
17
|
-
case "http":
|
|
18
|
-
process.argv.push("--auto-run");
|
|
19
|
-
await import('./transports/http.js');
|
|
20
|
-
break;
|
|
21
|
-
default:
|
|
22
|
-
console.error(`Unknown transport: ${transport}`);
|
|
23
|
-
console.error("Available transports: stdio, http");
|
|
24
|
-
process.exit(1);
|
|
25
|
-
}
|
|
26
|
-
} catch (error) {
|
|
27
|
-
console.error("Error running transport:", error);
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
run();
|
|
32
|
-
}
|
|
2
|
+
var e=Object.defineProperty;var t=(o,r)=>e(o,"name",{value:r,configurable:!0});import{i}from"./server-DrYJkNtU.js";import{s as g}from"./server-DrYJkNtU.js";import"node:fs";import"node:url";import"@modelcontextprotocol/sdk/server/mcp.js";if(i(import.meta.url)){const r=process.argv.slice(2)[0]||"stdio";async function s(){try{switch(r){case"stdio":await import("./transports/stdio.js");break;case"http":process.argv.push("--auto-run"),await import("./transports/http.js");break;default:console.error(`Unknown transport: ${r}`),console.error("Available transports: stdio, http"),process.exit(1)}}catch(a){console.error("Error running transport:",a),process.exit(1)}}t(s,"run"),s()}export{g as server};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var i=Object.defineProperty;var t=(e,r)=>i(e,"name",{value:r,configurable:!0});import{realpathSync as c}from"node:fs";import{pathToFileURL as f}from"node:url";import{McpServer as p}from"@modelcontextprotocol/sdk/server/mcp.js";async function u(e,r,n){const s=await fetch(e,r);if(!s.ok){const a=n??`fetch ${e}`;throw new Error(`Failed to ${a}: ${s.status} ${s.statusText}`)}return s.text()}t(u,"fetchMarkdown");function m(e,r){return e.type===r}t(m,"isNodeType");function v(e){return Array.from(new Set(e))}t(v,"uniq");function h(e){return e instanceof Error?e.message:"Unknown error"}t(h,"getErrorMessage");function g(e){if(!process.argv[1])return!1;const r=c(process.argv[1]),n=f(r);return e===n.href}t(g,"isEntryFile");var l="0.1.0-next.5",o={version:l};const w=new p({name:"Wave Design System",version:o.version,capabilities:{prompts:{},resources:{},tools:{}}});export{m as a,u as f,h as g,g as i,o as p,w as s,v as u};
|
package/dist/transports/http.js
CHANGED
|
@@ -1,110 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
-
import cors from 'cors';
|
|
5
|
-
import express from 'express';
|
|
6
|
-
import { randomUUID } from 'node:crypto';
|
|
7
|
-
import { s as server, p as packageJson, i as isEntryFile } from '../server-DtR5gJns.js';
|
|
8
|
-
import { i as initializeTools } from '../index-EJr2aPNi.js';
|
|
9
|
-
import 'node:fs';
|
|
10
|
-
import 'node:url';
|
|
11
|
-
import '@modelcontextprotocol/sdk/server/mcp.js';
|
|
12
|
-
import 'node:module';
|
|
13
|
-
import 'remark-parse';
|
|
14
|
-
import 'unified';
|
|
15
|
-
import 'zod';
|
|
16
|
-
|
|
17
|
-
const app = express();
|
|
18
|
-
const corsOrigin = process.env.CORS_ORIGIN?.split(",") ?? "*";
|
|
19
|
-
app.use(
|
|
20
|
-
cors({
|
|
21
|
-
origin: corsOrigin,
|
|
22
|
-
exposedHeaders: ["Mcp-Session-Id"],
|
|
23
|
-
allowedHeaders: ["Content-Type", "mcp-session-id"]
|
|
24
|
-
})
|
|
25
|
-
);
|
|
26
|
-
app.use(express.json());
|
|
27
|
-
const transports = /* @__PURE__ */ new Map();
|
|
28
|
-
app.post("/mcp", async (req, res) => {
|
|
29
|
-
const sessionId = req.headers["mcp-session-id"];
|
|
30
|
-
let transport;
|
|
31
|
-
if (sessionId && transports.has(sessionId)) {
|
|
32
|
-
transport = transports.get(sessionId);
|
|
33
|
-
} else if (!sessionId && isInitializeRequest(req.body)) {
|
|
34
|
-
transport = new StreamableHTTPServerTransport({
|
|
35
|
-
sessionIdGenerator: () => randomUUID(),
|
|
36
|
-
onsessioninitialized: (sessionId2) => {
|
|
37
|
-
transports.set(sessionId2, transport);
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
transport.onclose = () => {
|
|
41
|
-
if (transport.sessionId && transports.has(transport.sessionId)) {
|
|
42
|
-
transports.delete(transport.sessionId);
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
await initializeTools(server);
|
|
46
|
-
await server.connect(transport);
|
|
47
|
-
} else {
|
|
48
|
-
return res.status(400).json({
|
|
49
|
-
error: { message: "Bad Request: No valid session ID provided" }
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
await transport.handleRequest(req, res, req.body);
|
|
53
|
-
});
|
|
54
|
-
const handleSessionRequest = async (req, res) => {
|
|
55
|
-
const sessionId = req.headers["mcp-session-id"];
|
|
56
|
-
if (!sessionId || !transports.has(sessionId)) {
|
|
57
|
-
return res.status(404).send("Invalid or missing session ID");
|
|
58
|
-
}
|
|
59
|
-
const transport = transports.get(sessionId);
|
|
60
|
-
await transport.handleRequest(req, res);
|
|
61
|
-
};
|
|
62
|
-
app.get("/mcp", handleSessionRequest);
|
|
63
|
-
app.delete("/mcp", handleSessionRequest);
|
|
64
|
-
app.get("/", (_req, res) => {
|
|
65
|
-
res.json({
|
|
66
|
-
name: "Wave MCPc Server",
|
|
67
|
-
version: packageJson.version,
|
|
68
|
-
description: "MCP server for Wave Design System",
|
|
69
|
-
endpoints: {
|
|
70
|
-
"/": "Server information (this response)",
|
|
71
|
-
"/mcp": "Streamable HTTP endpoint for MCP connection",
|
|
72
|
-
"/health": "Health check endpoint"
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
app.get("/health", (_req, res) => {
|
|
77
|
-
res.json({
|
|
78
|
-
status: "healthy",
|
|
79
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
80
|
-
uptime: process.uptime()
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
const host = process.env.HOST ?? "localhost";
|
|
84
|
-
const port = process.env.PORT ? Number(process.env.PORT) : 3e3;
|
|
85
|
-
let runningServer = null;
|
|
86
|
-
if (process.argv.includes("--auto-run") || isEntryFile(import.meta.url)) {
|
|
87
|
-
runningServer = app.listen(port, host, (error) => {
|
|
88
|
-
if (error) {
|
|
89
|
-
console.error("\u274C Failed to start server:", error);
|
|
90
|
-
process.exit(1);
|
|
91
|
-
}
|
|
92
|
-
console.log(`\u{1F680} Wave MCP Server running on http://${host}:${port}`);
|
|
93
|
-
console.log(`\u{1F517} Connect via http://${host}:${port}/mcp`);
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
const cleanup = async () => {
|
|
97
|
-
const closeTasks = Array.from(transports.values()).map((t) => t.close());
|
|
98
|
-
if (runningServer) {
|
|
99
|
-
closeTasks.push(
|
|
100
|
-
new Promise((resolve) => runningServer.close(() => resolve()))
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
await Promise.allSettled(closeTasks);
|
|
104
|
-
console.log("\u{1F44B} Server shut down gracefully");
|
|
105
|
-
process.exit(0);
|
|
106
|
-
};
|
|
107
|
-
process.on("SIGTERM", cleanup);
|
|
108
|
-
process.on("SIGINT", cleanup);
|
|
109
|
-
|
|
110
|
-
export { app as default };
|
|
2
|
+
var h=Object.defineProperty;var i=(e,s)=>h(e,"name",{value:s,configurable:!0});import{StreamableHTTPServerTransport as f}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{isInitializeRequest as g}from"@modelcontextprotocol/sdk/types.js";import I from"cors";import l from"express";import{randomUUID as S}from"node:crypto";import{s as m,p as T,i as w}from"../server-DrYJkNtU.js";import{i as y}from"../index-Bz3fxPpO.js";import"node:fs";import"node:url";import"@modelcontextprotocol/sdk/server/mcp.js";import"node:module";import"remark-parse";import"unified";import"zod";const t=l(),P=process.env.CORS_ORIGIN?.split(",")??"*";t.use(I({origin:P,exposedHeaders:["Mcp-Session-Id"],allowedHeaders:["Content-Type","mcp-session-id"]})),t.use(l.json());const r=new Map;t.post("/mcp",async(e,s)=>{const n=e.headers["mcp-session-id"];let o;if(n&&r.has(n))o=r.get(n);else if(!n&&g(e.body))o=new f({sessionIdGenerator:i(()=>S(),"sessionIdGenerator"),onsessioninitialized:i(v=>{r.set(v,o)},"onsessioninitialized")}),o.onclose=()=>{o.sessionId&&r.has(o.sessionId)&&r.delete(o.sessionId)},await y(m),await m.connect(o);else return s.status(400).json({error:{message:"Bad Request: No valid session ID provided"}});await o.handleRequest(e,s,e.body)});const d=i(async(e,s)=>{const n=e.headers["mcp-session-id"];if(!n||!r.has(n))return s.status(404).send("Invalid or missing session ID");await r.get(n).handleRequest(e,s)},"handleSessionRequest");t.get("/mcp",d),t.delete("/mcp",d),t.get("/",(e,s)=>{s.json({name:"Wave MCPc Server",version:T.version,description:"MCP server for Wave Design System",endpoints:{"/":"Server information (this response)","/mcp":"Streamable HTTP endpoint for MCP connection","/health":"Health check endpoint"}})}),t.get("/health",(e,s)=>{s.json({status:"healthy",timestamp:new Date().toISOString(),uptime:process.uptime()})});const a=process.env.HOST??"localhost",p=process.env.PORT?Number(process.env.PORT):3e3;let c=null;(process.argv.includes("--auto-run")||w(import.meta.url))&&(c=t.listen(p,a,e=>{e&&(console.error("\u274C Failed to start server:",e),process.exit(1)),console.log(`\u{1F680} Wave MCP Server running on http://${a}:${p}`),console.log(`\u{1F517} Connect via http://${a}:${p}/mcp`)}));const u=i(async()=>{const e=Array.from(r.values()).map(s=>s.close());c&&e.push(new Promise(s=>c.close(()=>s()))),await Promise.allSettled(e),console.log("\u{1F44B} Server shut down gracefully"),process.exit(0)},"cleanup");process.on("SIGTERM",u),process.on("SIGINT",u);export{t as default};
|
package/dist/transports/stdio.js
CHANGED
|
@@ -1,27 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { s as server } from '../server-DtR5gJns.js';
|
|
4
|
-
import { i as initializeTools } from '../index-EJr2aPNi.js';
|
|
5
|
-
import 'node:fs';
|
|
6
|
-
import 'node:url';
|
|
7
|
-
import '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
|
-
import 'node:module';
|
|
9
|
-
import 'remark-parse';
|
|
10
|
-
import 'unified';
|
|
11
|
-
import 'zod';
|
|
12
|
-
|
|
13
|
-
async function main() {
|
|
14
|
-
await initializeTools(server);
|
|
15
|
-
const transport = new StdioServerTransport();
|
|
16
|
-
await server.connect(transport);
|
|
17
|
-
const cleanup = async () => {
|
|
18
|
-
await transport.close();
|
|
19
|
-
process.exit(0);
|
|
20
|
-
};
|
|
21
|
-
process.on("SIGTERM", cleanup);
|
|
22
|
-
process.on("SIGINT", cleanup);
|
|
23
|
-
}
|
|
24
|
-
main().catch((error) => {
|
|
25
|
-
console.error("\u274C Failed to start server:", error);
|
|
26
|
-
process.exit(1);
|
|
27
|
-
});
|
|
2
|
+
var s=Object.defineProperty;var t=(o,r)=>s(o,"name",{value:r,configurable:!0});import{StdioServerTransport as e}from"@modelcontextprotocol/sdk/server/stdio.js";import{s as i}from"../server-DrYJkNtU.js";import{i as a}from"../index-Bz3fxPpO.js";import"node:fs";import"node:url";import"@modelcontextprotocol/sdk/server/mcp.js";import"node:module";import"remark-parse";import"unified";import"zod";async function n(){await a(i);const o=new e;await i.connect(o);const r=t(async()=>{await o.close(),process.exit(0)},"cleanup");process.on("SIGTERM",r),process.on("SIGINT",r)}t(n,"main"),n().catch(o=>{console.error("\u274C Failed to start server:",o),process.exit(1)});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volue/wave-mcp",
|
|
3
|
-
"version": "0.1.0-next.
|
|
3
|
+
"version": "0.1.0-next.5",
|
|
4
4
|
"description": "An MCP server that connects AI tools to the Wave Design System",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"format": "yarn run _prettier --write",
|
|
44
44
|
"test": "run-p lint:types lint \"_prettier --check\"",
|
|
45
45
|
"clean": "premove dist",
|
|
46
|
-
"build": "yarn run clean && pkgroll",
|
|
46
|
+
"build": "yarn run clean && pkgroll --minify",
|
|
47
47
|
"start": "yarn run build && node dist/transports/http.js",
|
|
48
48
|
"dev": "tsx watch --clear-screen=false src/transports/http.ts",
|
|
49
49
|
"prepack": "yarn run build",
|
|
@@ -51,12 +51,12 @@
|
|
|
51
51
|
"inspect:http": "yarn dlx -q @modelcontextprotocol/inspector --transport http --server-url http://localhost:3000/mcp"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@modelcontextprotocol/sdk": "1.
|
|
54
|
+
"@modelcontextprotocol/sdk": "1.19.1",
|
|
55
55
|
"@types/cors": "2.8.19",
|
|
56
56
|
"@types/express": "5.0.3",
|
|
57
57
|
"@types/express-serve-static-core": "5.0.7",
|
|
58
|
-
"@volue/design-colors": "^3.0.10-next.
|
|
59
|
-
"@volue/design-icons": "^1.9.4-next.
|
|
58
|
+
"@volue/design-colors": "^3.0.10-next.3",
|
|
59
|
+
"@volue/design-icons": "^1.9.4-next.3",
|
|
60
60
|
"cors": "2.8.5",
|
|
61
61
|
"express": "5.1.0",
|
|
62
62
|
"remark-parse": "8.0.3",
|
|
@@ -65,15 +65,15 @@
|
|
|
65
65
|
"zod": "3.25.76"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@types/mdast": "3.0.
|
|
69
|
-
"@types/node": "22.18.
|
|
68
|
+
"@types/mdast": "3.0.15",
|
|
69
|
+
"@types/node": "22.18.8",
|
|
70
70
|
"@volue/eslint-config": "1.3.11",
|
|
71
|
-
"eslint": "9.
|
|
71
|
+
"eslint": "9.36.0",
|
|
72
72
|
"npm-run-all2": "8.0.4",
|
|
73
|
-
"pkgroll": "2.
|
|
73
|
+
"pkgroll": "2.17.0",
|
|
74
74
|
"premove": "4.0.0",
|
|
75
75
|
"prettier": "3.6.2",
|
|
76
|
-
"tsx": "4.20.
|
|
77
|
-
"typescript": "5.9.
|
|
76
|
+
"tsx": "4.20.6",
|
|
77
|
+
"typescript": "5.9.3"
|
|
78
78
|
}
|
|
79
79
|
}
|
package/dist/index-EJr2aPNi.js
DELETED
|
@@ -1,619 +0,0 @@
|
|
|
1
|
-
import { a as isNodeType, f as fetchMarkdown, u as uniq, g as getErrorMessage } from './server-DtR5gJns.js';
|
|
2
|
-
import { createRequire } from 'node:module';
|
|
3
|
-
import remarkParse from 'remark-parse';
|
|
4
|
-
import unified from 'unified';
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
|
|
7
|
-
const markdownParser = unified().use(remarkParse);
|
|
8
|
-
const SECTION_TYPE_MAP = {
|
|
9
|
-
Components: "component",
|
|
10
|
-
"UX patterns": "pattern"
|
|
11
|
-
};
|
|
12
|
-
function parseLlmsIndex(indexContent, baseUrl) {
|
|
13
|
-
const ast = markdownParser.parse(indexContent);
|
|
14
|
-
let currentSection = null;
|
|
15
|
-
return ast.children.reduce((resources, node) => {
|
|
16
|
-
if (isNodeType(node, "heading") && node.depth === 2) {
|
|
17
|
-
currentSection = extractSectionContext(node);
|
|
18
|
-
return resources;
|
|
19
|
-
}
|
|
20
|
-
if (!currentSection || !isNodeType(node, "list")) {
|
|
21
|
-
return resources;
|
|
22
|
-
}
|
|
23
|
-
const section = currentSection;
|
|
24
|
-
const sectionResources = node.children.map((item) => listItemToResource(item, section, baseUrl)).filter((resource) => resource !== null);
|
|
25
|
-
return resources.concat(sectionResources);
|
|
26
|
-
}, []);
|
|
27
|
-
}
|
|
28
|
-
function extractSectionContext(node) {
|
|
29
|
-
const [headingChild] = node.children;
|
|
30
|
-
if (!headingChild || !isNodeType(headingChild, "text")) {
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
const title = headingChild.value.trim();
|
|
34
|
-
if (!(title in SECTION_TYPE_MAP)) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
const type = SECTION_TYPE_MAP[title];
|
|
38
|
-
return {
|
|
39
|
-
title,
|
|
40
|
-
type
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
function listItemToResource(item, section, baseUrl) {
|
|
44
|
-
const [paragraph] = item.children;
|
|
45
|
-
if (!paragraph || !isNodeType(paragraph, "paragraph") || paragraph.children.length !== 2) {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
const [linkNode, textNode] = paragraph.children;
|
|
49
|
-
if (!isNodeType(linkNode, "link") || !isNodeType(textNode, "text") || linkNode.children.length !== 1) {
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
const linkChild = linkNode.children[0];
|
|
53
|
-
if (!isNodeType(linkChild, "text")) {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
const name = linkChild.value.trim();
|
|
57
|
-
const normalizedPath = linkNode.url.replace(/^\//, "");
|
|
58
|
-
const url = new URL(normalizedPath, `${baseUrl}/`).toString();
|
|
59
|
-
const slug = deriveSlug(normalizedPath);
|
|
60
|
-
const category = deriveCategory(section.title, normalizedPath, section.type);
|
|
61
|
-
const description = normalizeDescription(textNode.value);
|
|
62
|
-
return {
|
|
63
|
-
name,
|
|
64
|
-
slug,
|
|
65
|
-
category,
|
|
66
|
-
section: section.title,
|
|
67
|
-
type: section.type,
|
|
68
|
-
url,
|
|
69
|
-
description: description ?? void 0
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
function deriveSlug(path) {
|
|
73
|
-
return path.replace(/\.md$/, "").split("/").pop() ?? "";
|
|
74
|
-
}
|
|
75
|
-
function deriveCategory(sectionTitle, path, type) {
|
|
76
|
-
const segments = path.replace(/\.md$/, "").split("/");
|
|
77
|
-
if (type === "component" || type === "pattern") {
|
|
78
|
-
const subcategory = segments.length >= 3 ? segments[segments.length - 2] : null;
|
|
79
|
-
return subcategory ? `${sectionTitle} / ${formatSegment(subcategory)}` : sectionTitle;
|
|
80
|
-
}
|
|
81
|
-
return sectionTitle;
|
|
82
|
-
}
|
|
83
|
-
function formatSegment(segment) {
|
|
84
|
-
return segment.split("-").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
85
|
-
}
|
|
86
|
-
function normalizeDescription(rawDescription) {
|
|
87
|
-
if (!rawDescription) {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
const withoutLeadingColon = rawDescription.replace(/^\s*:\s*/, "").trim();
|
|
91
|
-
if (withoutLeadingColon.toLowerCase() === "no description available") {
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
return withoutLeadingColon;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const require = createRequire(import.meta.url);
|
|
98
|
-
const colorTokensData = require("@volue/design-colors/generic");
|
|
99
|
-
const iconTokensData = require("@volue/design-icons");
|
|
100
|
-
const WAVE_BASE_URL = "https://wave-design-system.vercel.app";
|
|
101
|
-
const LLMS_INDEX_URL = `${WAVE_BASE_URL}/llms.txt`;
|
|
102
|
-
const DEFAULT_RESOURCES_CACHE_TIME = 1e3 * 60 * 15;
|
|
103
|
-
const RESOURCES_CACHE_TIME = process.env.WAVE_RESOURCES_CACHE_TIME ? Number.parseInt(process.env.WAVE_RESOURCES_CACHE_TIME, 10) : DEFAULT_RESOURCES_CACHE_TIME;
|
|
104
|
-
function createUrl(path) {
|
|
105
|
-
const withSlash = path.startsWith("/") ? path : `/${path}`;
|
|
106
|
-
return `${WAVE_BASE_URL}${withSlash}.md`;
|
|
107
|
-
}
|
|
108
|
-
let cachedResources = null;
|
|
109
|
-
let cacheExpiresAt = 0;
|
|
110
|
-
async function listCachedResources() {
|
|
111
|
-
if (cachedResources !== null && Date.now() < cacheExpiresAt) {
|
|
112
|
-
return cachedResources;
|
|
113
|
-
}
|
|
114
|
-
const resources = await idempotentLoadResourcesFromLlms();
|
|
115
|
-
cachedResources = resources;
|
|
116
|
-
cacheExpiresAt = Date.now() + RESOURCES_CACHE_TIME;
|
|
117
|
-
return resources;
|
|
118
|
-
}
|
|
119
|
-
let inflightResources = null;
|
|
120
|
-
async function idempotentLoadResourcesFromLlms() {
|
|
121
|
-
if (!inflightResources) {
|
|
122
|
-
inflightResources = loadResourcesFromLlms().finally(() => {
|
|
123
|
-
inflightResources = null;
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
return inflightResources;
|
|
127
|
-
}
|
|
128
|
-
async function loadResourcesFromLlms() {
|
|
129
|
-
const body = await fetchMarkdown(LLMS_INDEX_URL, void 0, "fetch llms.txt");
|
|
130
|
-
const parsedBody = parseLlmsIndex(body, WAVE_BASE_URL);
|
|
131
|
-
return parsedBody;
|
|
132
|
-
}
|
|
133
|
-
async function fetchComponent(component) {
|
|
134
|
-
return fetchMarkdown(
|
|
135
|
-
component.url,
|
|
136
|
-
void 0,
|
|
137
|
-
`fetch ${component.name} documentation`
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
function fetchPattern(pattern) {
|
|
141
|
-
return fetchMarkdown(pattern.url, void 0, `fetch ${pattern.name} pattern`);
|
|
142
|
-
}
|
|
143
|
-
async function fetchDevelopingGuide() {
|
|
144
|
-
return fetchMarkdown(
|
|
145
|
-
createUrl("get-started/developing"),
|
|
146
|
-
void 0,
|
|
147
|
-
"fetch developing guide"
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
async function fetchColorUsageGuidelines() {
|
|
151
|
-
return fetchMarkdown(
|
|
152
|
-
createUrl("tokens/colors"),
|
|
153
|
-
void 0,
|
|
154
|
-
"fetch color usage guidelines"
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
function listIcons() {
|
|
158
|
-
return Object.entries(iconTokensData).map(([name, svg]) => ({ name, svg }));
|
|
159
|
-
}
|
|
160
|
-
function getIconNames() {
|
|
161
|
-
const icons = listIcons();
|
|
162
|
-
return uniq(icons.map((icon) => icon.name));
|
|
163
|
-
}
|
|
164
|
-
function findIconByName(icons, name) {
|
|
165
|
-
const normalized = name.trim().toLowerCase();
|
|
166
|
-
return icons.find((icon) => icon.name.toLowerCase() === normalized);
|
|
167
|
-
}
|
|
168
|
-
async function listResourcesByType(type) {
|
|
169
|
-
const resources = await listCachedResources();
|
|
170
|
-
return resources.filter(
|
|
171
|
-
(resource) => resource.type === type
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
function findResourceByName(resources, name) {
|
|
175
|
-
const normalized = name.trim().toLowerCase();
|
|
176
|
-
return resources.find((resource) => resource.name.toLowerCase() === normalized);
|
|
177
|
-
}
|
|
178
|
-
function listComponents() {
|
|
179
|
-
return listResourcesByType("component");
|
|
180
|
-
}
|
|
181
|
-
async function getComponentNames() {
|
|
182
|
-
const components = await listComponents();
|
|
183
|
-
return uniq(components.map((component) => component.name));
|
|
184
|
-
}
|
|
185
|
-
function listPatterns() {
|
|
186
|
-
return listResourcesByType("pattern");
|
|
187
|
-
}
|
|
188
|
-
async function getPatternNames() {
|
|
189
|
-
const patterns = await listPatterns();
|
|
190
|
-
return uniq(patterns.map((pattern) => pattern.name));
|
|
191
|
-
}
|
|
192
|
-
function groupResourcesByCategory(resources) {
|
|
193
|
-
return resources.reduce((acc, resource) => {
|
|
194
|
-
if (!acc[resource.category]) {
|
|
195
|
-
acc[resource.category] = [];
|
|
196
|
-
}
|
|
197
|
-
acc[resource.category].push(resource);
|
|
198
|
-
return acc;
|
|
199
|
-
}, {});
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const getColorUsageTool = {
|
|
203
|
-
name: "get_color_usage",
|
|
204
|
-
description: "Retrieve the Wave Design System color tokens and guidelines for applying color in user interfaces.",
|
|
205
|
-
exec(server, { name, description }) {
|
|
206
|
-
server.tool(name, description, async () => {
|
|
207
|
-
try {
|
|
208
|
-
const markdown = await fetchColorUsageGuidelines();
|
|
209
|
-
return {
|
|
210
|
-
content: [
|
|
211
|
-
{
|
|
212
|
-
type: "text",
|
|
213
|
-
text: JSON.stringify(colorTokensData)
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
type: "text",
|
|
217
|
-
text: `Here are the Wave Design System color usage guidelines:
|
|
218
|
-
|
|
219
|
-
${markdown}
|
|
220
|
-
|
|
221
|
-
---
|
|
222
|
-
Source: ${createUrl("tokens/colors")}`
|
|
223
|
-
}
|
|
224
|
-
]
|
|
225
|
-
};
|
|
226
|
-
} catch (error) {
|
|
227
|
-
return {
|
|
228
|
-
isError: true,
|
|
229
|
-
content: [
|
|
230
|
-
{
|
|
231
|
-
type: "text",
|
|
232
|
-
text: `Failed to retrieve color usage guidelines: ${getErrorMessage(error)}`
|
|
233
|
-
}
|
|
234
|
-
]
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
const getComponentTool = {
|
|
242
|
-
name: "get_component",
|
|
243
|
-
description: "Retrieve comprehensive documentation for a specific Wave Design System component by name. This tool retrieves the import instructions, basic usage and advanced configuration examples, code snippets for common scenarios, available props/properties table and some general guidelines for a given Wave Design System component.",
|
|
244
|
-
async ctx() {
|
|
245
|
-
try {
|
|
246
|
-
const componentNames = await getComponentNames();
|
|
247
|
-
return { componentNames };
|
|
248
|
-
} catch (error) {
|
|
249
|
-
throw new Error(
|
|
250
|
-
`Failed to initialize component tool: ${getErrorMessage(error)}`
|
|
251
|
-
);
|
|
252
|
-
}
|
|
253
|
-
},
|
|
254
|
-
exec(server, { ctx, name, description }) {
|
|
255
|
-
server.tool(
|
|
256
|
-
name,
|
|
257
|
-
description,
|
|
258
|
-
{
|
|
259
|
-
name: z.enum(ctx.componentNames).describe(
|
|
260
|
-
"The name of the component to retrieve documentation for. This can be derived from calling the `get_components` tool."
|
|
261
|
-
)
|
|
262
|
-
},
|
|
263
|
-
async ({ name: name2 }) => {
|
|
264
|
-
try {
|
|
265
|
-
const components = await listComponents();
|
|
266
|
-
const component = findResourceByName(components, name2);
|
|
267
|
-
if (!component) {
|
|
268
|
-
throw new Error(`Component not found`);
|
|
269
|
-
}
|
|
270
|
-
const markdown = await fetchComponent(component);
|
|
271
|
-
if (!markdown) {
|
|
272
|
-
return {
|
|
273
|
-
content: [
|
|
274
|
-
{
|
|
275
|
-
type: "text",
|
|
276
|
-
text: `No documentation content found for the \`${component.name}\` component.`
|
|
277
|
-
}
|
|
278
|
-
]
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
return {
|
|
282
|
-
content: [
|
|
283
|
-
{
|
|
284
|
-
type: "text",
|
|
285
|
-
text: `Here is the documentation for the \`${component.name}\` component from the Wave Design System:
|
|
286
|
-
|
|
287
|
-
${markdown}
|
|
288
|
-
|
|
289
|
-
---
|
|
290
|
-
Source: ${component.url}`
|
|
291
|
-
}
|
|
292
|
-
]
|
|
293
|
-
};
|
|
294
|
-
} catch (error) {
|
|
295
|
-
return {
|
|
296
|
-
isError: true,
|
|
297
|
-
content: [
|
|
298
|
-
{
|
|
299
|
-
type: "text",
|
|
300
|
-
text: `Failed to retrieve documentation for \`${name2}\`: ${getErrorMessage(error)}`
|
|
301
|
-
}
|
|
302
|
-
]
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
);
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
const getIconTool = {
|
|
311
|
-
name: "get_icon",
|
|
312
|
-
description: "Retrieve a specific icon information by name from Wave Design System.",
|
|
313
|
-
exec(server, { name, description }) {
|
|
314
|
-
server.tool(
|
|
315
|
-
name,
|
|
316
|
-
description,
|
|
317
|
-
{
|
|
318
|
-
name: z.enum(getIconNames()).describe(
|
|
319
|
-
"The icon name to retrieve. This can be derived from calling the `list_icon` tool."
|
|
320
|
-
)
|
|
321
|
-
},
|
|
322
|
-
async ({ name: name2 }) => {
|
|
323
|
-
try {
|
|
324
|
-
const icons = listIcons();
|
|
325
|
-
const icon = findIconByName(icons, name2);
|
|
326
|
-
if (!icon) {
|
|
327
|
-
throw new Error("Icon not found.");
|
|
328
|
-
}
|
|
329
|
-
const reactComponentSnippet = `<SvgIcon iconName="${icon.name}" />`;
|
|
330
|
-
const htmlMarkup = `<div class="svgIcon svgIcon--stroked">
|
|
331
|
-
<svg focusable="false" aria-hidden="true">
|
|
332
|
-
<use xlink:href="#svg--${icon.name}"></use>
|
|
333
|
-
</svg>
|
|
334
|
-
</div>`;
|
|
335
|
-
return {
|
|
336
|
-
content: [
|
|
337
|
-
{
|
|
338
|
-
type: "text",
|
|
339
|
-
text: `# ${icon.name} icon
|
|
340
|
-
|
|
341
|
-
## React component:
|
|
342
|
-
|
|
343
|
-
\`\`\`tsx
|
|
344
|
-
${reactComponentSnippet}
|
|
345
|
-
\`\`\`
|
|
346
|
-
|
|
347
|
-
## HTML markup:
|
|
348
|
-
|
|
349
|
-
\`\`\`html
|
|
350
|
-
${htmlMarkup}
|
|
351
|
-
\`\`\`
|
|
352
|
-
|
|
353
|
-
## Raw SVG code:
|
|
354
|
-
|
|
355
|
-
\`\`\`svg
|
|
356
|
-
${icon.svg}
|
|
357
|
-
\`\`\``
|
|
358
|
-
}
|
|
359
|
-
]
|
|
360
|
-
};
|
|
361
|
-
} catch (error) {
|
|
362
|
-
return {
|
|
363
|
-
isError: true,
|
|
364
|
-
content: [
|
|
365
|
-
{
|
|
366
|
-
type: "text",
|
|
367
|
-
text: `Failed to retrieve icon \`${name2}\`: ${getErrorMessage(error)}`
|
|
368
|
-
}
|
|
369
|
-
]
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
);
|
|
374
|
-
}
|
|
375
|
-
};
|
|
376
|
-
|
|
377
|
-
const getPatternTool = {
|
|
378
|
-
name: "get_pattern",
|
|
379
|
-
description: "Retrieve documentation for a specific Wave Design System UX pattern by name. This tool provides guidance, examples and components that implement the selected pattern.",
|
|
380
|
-
async ctx() {
|
|
381
|
-
try {
|
|
382
|
-
const patternNames = await getPatternNames();
|
|
383
|
-
return { patternNames };
|
|
384
|
-
} catch (error) {
|
|
385
|
-
throw new Error(
|
|
386
|
-
`Failed to initialize pattern tool: ${getErrorMessage(error)}`
|
|
387
|
-
);
|
|
388
|
-
}
|
|
389
|
-
},
|
|
390
|
-
exec(server, { ctx, name, description }) {
|
|
391
|
-
server.tool(
|
|
392
|
-
name,
|
|
393
|
-
description,
|
|
394
|
-
{
|
|
395
|
-
name: z.enum(ctx.patternNames).describe(
|
|
396
|
-
"The name of the UX pattern to retrieve documentation for. This can be derived from calling the `list_patterns` tool."
|
|
397
|
-
)
|
|
398
|
-
},
|
|
399
|
-
async ({ name: name2 }) => {
|
|
400
|
-
try {
|
|
401
|
-
const patterns = await listPatterns();
|
|
402
|
-
const pattern = findResourceByName(patterns, name2);
|
|
403
|
-
if (!pattern) {
|
|
404
|
-
throw new Error("Pattern not found");
|
|
405
|
-
}
|
|
406
|
-
const markdown = await fetchPattern(pattern);
|
|
407
|
-
return {
|
|
408
|
-
content: [
|
|
409
|
-
{
|
|
410
|
-
type: "text",
|
|
411
|
-
text: `Here are the guidelines for the \`${pattern.name}\` UX pattern from the Wave Design System:
|
|
412
|
-
|
|
413
|
-
${markdown}
|
|
414
|
-
|
|
415
|
-
---
|
|
416
|
-
Source: ${pattern.url}`
|
|
417
|
-
}
|
|
418
|
-
]
|
|
419
|
-
};
|
|
420
|
-
} catch (error) {
|
|
421
|
-
return {
|
|
422
|
-
isError: true,
|
|
423
|
-
content: [
|
|
424
|
-
{
|
|
425
|
-
type: "text",
|
|
426
|
-
text: `Failed to retrieve documentation for \`${name2}\`: ${getErrorMessage(error)}`
|
|
427
|
-
}
|
|
428
|
-
]
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
);
|
|
433
|
-
}
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
const initTool = {
|
|
437
|
-
name: "init",
|
|
438
|
-
description: "Get guide on how to setup or create a project that includes Wave Design System.",
|
|
439
|
-
exec(server, { name, description }) {
|
|
440
|
-
server.tool(name, description, async () => {
|
|
441
|
-
try {
|
|
442
|
-
const markdown = await fetchDevelopingGuide();
|
|
443
|
-
return {
|
|
444
|
-
content: [
|
|
445
|
-
{
|
|
446
|
-
type: "text",
|
|
447
|
-
text: `The getting started documentation for developing with Wave Design System is included below.
|
|
448
|
-
|
|
449
|
-
It's **important** that if the project is using a tool like Vite or Next.js, one of the pre-configured starter templates should be used instead of manual configuration:
|
|
450
|
-
|
|
451
|
-
- https://github.com/Volue/wave-vite-example (Vite template)
|
|
452
|
-
- https://github.com/Volue/wave-nextjs-app-router-example (Next.js App Router template)
|
|
453
|
-
|
|
454
|
-
---
|
|
455
|
-
|
|
456
|
-
${markdown}
|
|
457
|
-
`
|
|
458
|
-
}
|
|
459
|
-
]
|
|
460
|
-
};
|
|
461
|
-
} catch (error) {
|
|
462
|
-
return {
|
|
463
|
-
isError: true,
|
|
464
|
-
content: [
|
|
465
|
-
{
|
|
466
|
-
type: "text",
|
|
467
|
-
text: `Failed to retrieve getting started documentation: ${getErrorMessage(error)}`
|
|
468
|
-
}
|
|
469
|
-
]
|
|
470
|
-
};
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
};
|
|
475
|
-
|
|
476
|
-
const listComponentsTool = {
|
|
477
|
-
name: "list_components",
|
|
478
|
-
description: "List all components available from the Wave Design System. This tool retrieves the names of all available Wave Design System components grouped by category.",
|
|
479
|
-
exec(server, { name, description }) {
|
|
480
|
-
server.tool(name, description, async () => {
|
|
481
|
-
try {
|
|
482
|
-
const components = await listComponents();
|
|
483
|
-
const componentsByCategory = groupResourcesByCategory(components);
|
|
484
|
-
const formattedList = Object.entries(componentsByCategory).map(
|
|
485
|
-
([category, items]) => `## ${category}
|
|
486
|
-
${items.map((item) => `- ${item.name}`).join("\n")}`
|
|
487
|
-
).join("\n\n");
|
|
488
|
-
return {
|
|
489
|
-
content: [
|
|
490
|
-
{
|
|
491
|
-
type: "text",
|
|
492
|
-
text: `The following components are available in the Wave Design System:
|
|
493
|
-
|
|
494
|
-
${formattedList}
|
|
495
|
-
|
|
496
|
-
Use the \`get_component\` tool to get more information about a specific component.
|
|
497
|
-
|
|
498
|
-
Use these components from the @volue/wave-react package.`
|
|
499
|
-
}
|
|
500
|
-
]
|
|
501
|
-
};
|
|
502
|
-
} catch (error) {
|
|
503
|
-
return {
|
|
504
|
-
isError: true,
|
|
505
|
-
content: [
|
|
506
|
-
{
|
|
507
|
-
type: "text",
|
|
508
|
-
text: `Failed to retrieve the component index: ${getErrorMessage(error)}`
|
|
509
|
-
}
|
|
510
|
-
]
|
|
511
|
-
};
|
|
512
|
-
}
|
|
513
|
-
});
|
|
514
|
-
}
|
|
515
|
-
};
|
|
516
|
-
|
|
517
|
-
const listIconsTool = {
|
|
518
|
-
name: "list_icons",
|
|
519
|
-
description: "List all icons available from the Wave Design System. Icons are sourced directly from the `@volue/design-icons` package.",
|
|
520
|
-
exec(server, { name, description }) {
|
|
521
|
-
server.tool(name, description, async () => {
|
|
522
|
-
try {
|
|
523
|
-
const icons = listIcons();
|
|
524
|
-
const formattedList = icons.map((icon) => `- ${icon.name}`).join("\n");
|
|
525
|
-
return {
|
|
526
|
-
content: [
|
|
527
|
-
{
|
|
528
|
-
type: "text",
|
|
529
|
-
text: `The following icons are available in the Wave Design System:
|
|
530
|
-
|
|
531
|
-
${formattedList}
|
|
532
|
-
|
|
533
|
-
Use the \`get_icon\` tool to retrieve the SVG markup and usage guidelines for a specific icon.`
|
|
534
|
-
}
|
|
535
|
-
]
|
|
536
|
-
};
|
|
537
|
-
} catch (error) {
|
|
538
|
-
return {
|
|
539
|
-
isError: true,
|
|
540
|
-
content: [
|
|
541
|
-
{
|
|
542
|
-
type: "text",
|
|
543
|
-
text: `Failed to retrieve the icon data: ${getErrorMessage(error)}`
|
|
544
|
-
}
|
|
545
|
-
]
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
};
|
|
551
|
-
|
|
552
|
-
const listPatternsTool = {
|
|
553
|
-
name: "list_patterns",
|
|
554
|
-
description: "List all UX patterns available from the Wave Design System. This tool retrieves the names of all available Wave Design System patterns grouped by category.",
|
|
555
|
-
exec(server, { name, description }) {
|
|
556
|
-
server.tool(name, description, async () => {
|
|
557
|
-
try {
|
|
558
|
-
const patterns = await listPatterns();
|
|
559
|
-
const patternsByCategory = groupResourcesByCategory(patterns);
|
|
560
|
-
const formattedList = Object.entries(patternsByCategory).map(
|
|
561
|
-
([category, items]) => `## ${category}
|
|
562
|
-
${items.map((item) => `- ${item.name}`).join("\n")}`
|
|
563
|
-
).join("\n\n");
|
|
564
|
-
return {
|
|
565
|
-
content: [
|
|
566
|
-
{
|
|
567
|
-
type: "text",
|
|
568
|
-
text: `The following UX patterns are available in the Wave Design System:
|
|
569
|
-
|
|
570
|
-
${formattedList}
|
|
571
|
-
|
|
572
|
-
Use the \`get_pattern\` tool to access detailed guidance for a specific pattern.`
|
|
573
|
-
}
|
|
574
|
-
]
|
|
575
|
-
};
|
|
576
|
-
} catch (error) {
|
|
577
|
-
return {
|
|
578
|
-
isError: true,
|
|
579
|
-
content: [
|
|
580
|
-
{
|
|
581
|
-
type: "text",
|
|
582
|
-
text: `Failed to retrieve the pattern index: ${getErrorMessage(error)}`
|
|
583
|
-
}
|
|
584
|
-
]
|
|
585
|
-
};
|
|
586
|
-
}
|
|
587
|
-
});
|
|
588
|
-
}
|
|
589
|
-
};
|
|
590
|
-
|
|
591
|
-
const tools = [
|
|
592
|
-
initTool,
|
|
593
|
-
listComponentsTool,
|
|
594
|
-
getComponentTool,
|
|
595
|
-
listPatternsTool,
|
|
596
|
-
getPatternTool,
|
|
597
|
-
getColorUsageTool,
|
|
598
|
-
listIconsTool,
|
|
599
|
-
getIconTool
|
|
600
|
-
];
|
|
601
|
-
const registeredToolNames = /* @__PURE__ */ new Set();
|
|
602
|
-
async function initializeTools(server) {
|
|
603
|
-
await Promise.all(
|
|
604
|
-
tools.map(async (tool) => {
|
|
605
|
-
if (registeredToolNames.has(tool.name)) {
|
|
606
|
-
return;
|
|
607
|
-
}
|
|
608
|
-
const toolCtx = await tool.ctx?.();
|
|
609
|
-
registeredToolNames.add(tool.name);
|
|
610
|
-
tool.exec(server, {
|
|
611
|
-
name: tool.name,
|
|
612
|
-
description: tool.description,
|
|
613
|
-
ctx: toolCtx
|
|
614
|
-
});
|
|
615
|
-
})
|
|
616
|
-
);
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
export { initializeTools as i };
|
package/dist/server-DtR5gJns.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { realpathSync } from 'node:fs';
|
|
2
|
-
import { pathToFileURL } from 'node:url';
|
|
3
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
-
|
|
5
|
-
async function fetchMarkdown(url, options, errorContext) {
|
|
6
|
-
const response = await fetch(url, options);
|
|
7
|
-
if (!response.ok) {
|
|
8
|
-
const context = errorContext ?? `fetch ${url}`;
|
|
9
|
-
throw new Error(
|
|
10
|
-
`Failed to ${context}: ${response.status} ${response.statusText}`
|
|
11
|
-
);
|
|
12
|
-
}
|
|
13
|
-
return response.text();
|
|
14
|
-
}
|
|
15
|
-
function isNodeType(node, type) {
|
|
16
|
-
return node.type === type;
|
|
17
|
-
}
|
|
18
|
-
function uniq(array) {
|
|
19
|
-
return Array.from(new Set(array));
|
|
20
|
-
}
|
|
21
|
-
function getErrorMessage(error) {
|
|
22
|
-
return error instanceof Error ? error.message : "Unknown error";
|
|
23
|
-
}
|
|
24
|
-
function isEntryFile(url) {
|
|
25
|
-
if (!process.argv[1]) return false;
|
|
26
|
-
const realPath = realpathSync(process.argv[1]);
|
|
27
|
-
const realPathURL = pathToFileURL(realPath);
|
|
28
|
-
return url === realPathURL.href;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
var version = "0.1.0-next.3";
|
|
32
|
-
var packageJson = {
|
|
33
|
-
version: version};
|
|
34
|
-
|
|
35
|
-
const server = new McpServer({
|
|
36
|
-
name: "Wave Design System",
|
|
37
|
-
version: packageJson.version,
|
|
38
|
-
capabilities: {
|
|
39
|
-
prompts: {},
|
|
40
|
-
resources: {},
|
|
41
|
-
tools: {}
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
export { isNodeType as a, fetchMarkdown as f, getErrorMessage as g, isEntryFile as i, packageJson as p, server as s, uniq as u };
|