@svgrid/mcp 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/dist/index.js ADDED
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SvGrid MCP server (stdio).
4
+ *
5
+ * Exposes the SvGrid example sources, docs, and curated API reference as
6
+ * Model Context Protocol tools. Point an MCP-capable client (Claude
7
+ * Desktop, Claude Code, etc.) at this server to give the model accurate,
8
+ * version-pinned answers about SvGrid - no hallucinated APIs, no stale
9
+ * blog-post output.
10
+ *
11
+ * Run with:
12
+ * npx sv-grid-mcp
13
+ */
14
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
15
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
16
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
17
+ import { apiReference, docs, examples } from './data.js';
18
+ const server = new Server({
19
+ name: 'sv-grid-mcp',
20
+ version: '0.1.0',
21
+ }, {
22
+ capabilities: {
23
+ tools: {},
24
+ },
25
+ });
26
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
27
+ return {
28
+ tools: [
29
+ {
30
+ name: 'list_examples',
31
+ description: 'List every SvGrid example demo with id, title, and short blurb. Use to discover what is available before fetching source.',
32
+ inputSchema: { type: 'object', properties: {} },
33
+ },
34
+ {
35
+ name: 'get_example_source',
36
+ description: 'Return the full .svelte source of a specific demo by id (e.g. "11-stock-market"). The source is what a user would copy into their project as-is.',
37
+ inputSchema: {
38
+ type: 'object',
39
+ properties: { id: { type: 'string', description: 'Demo id, e.g. "11-stock-market"' } },
40
+ required: ['id'],
41
+ },
42
+ },
43
+ {
44
+ name: 'list_docs',
45
+ description: 'List every documentation page with slug and title. Slugs use forward slashes, e.g. "help/columns/column-definitions".',
46
+ inputSchema: { type: 'object', properties: {} },
47
+ },
48
+ {
49
+ name: 'get_doc',
50
+ description: 'Return the markdown content of a specific documentation page by slug.',
51
+ inputSchema: {
52
+ type: 'object',
53
+ properties: { slug: { type: 'string', description: 'Doc slug, e.g. "getting-started" or "help/columns/column-definitions"' } },
54
+ required: ['slug'],
55
+ },
56
+ },
57
+ {
58
+ name: 'search_docs',
59
+ description: 'Case-insensitive substring search across all SvGrid docs. Returns matching slugs with a one-line excerpt around the first hit.',
60
+ inputSchema: {
61
+ type: 'object',
62
+ properties: {
63
+ query: { type: 'string', description: 'Free-text query, e.g. "row virtualization"' },
64
+ limit: { type: 'number', description: 'Max results, default 10', default: 10 },
65
+ },
66
+ required: ['query'],
67
+ },
68
+ },
69
+ {
70
+ name: 'get_api_reference',
71
+ description: 'Return the curated SvGrid public-API surface, grouped by category (components, headless, row models, features, virtualization, accessibility, utilities).',
72
+ inputSchema: { type: 'object', properties: {} },
73
+ },
74
+ ],
75
+ };
76
+ });
77
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
78
+ const { name, arguments: args } = req.params;
79
+ switch (name) {
80
+ case 'list_examples': {
81
+ const items = examples.map((e) => ({ id: e.id, title: e.title, blurb: e.blurb, path: e.path }));
82
+ return { content: [{ type: 'text', text: JSON.stringify(items, null, 2) }] };
83
+ }
84
+ case 'get_example_source': {
85
+ const id = String(args?.id ?? '');
86
+ const match = examples.find((e) => e.id === id);
87
+ if (!match) {
88
+ return {
89
+ isError: true,
90
+ content: [{ type: 'text', text: `No example with id "${id}". Call list_examples for available ids.` }],
91
+ };
92
+ }
93
+ return {
94
+ content: [
95
+ { type: 'text', text: `// ${match.path}\n// ${match.title} - ${match.blurb}\n\n${match.source}` },
96
+ ],
97
+ };
98
+ }
99
+ case 'list_docs': {
100
+ const items = docs.map((d) => ({ slug: d.slug, title: d.title, path: d.path }));
101
+ return { content: [{ type: 'text', text: JSON.stringify(items, null, 2) }] };
102
+ }
103
+ case 'get_doc': {
104
+ const slug = String(args?.slug ?? '');
105
+ const match = docs.find((d) => d.slug === slug);
106
+ if (!match) {
107
+ return {
108
+ isError: true,
109
+ content: [{ type: 'text', text: `No doc with slug "${slug}". Call list_docs for available slugs.` }],
110
+ };
111
+ }
112
+ return { content: [{ type: 'text', text: match.markdown }] };
113
+ }
114
+ case 'search_docs': {
115
+ const a = (args ?? {});
116
+ const query = String(a.query ?? '').trim();
117
+ const limit = Math.max(1, Math.min(50, Number(a.limit ?? 10)));
118
+ if (!query) {
119
+ return { isError: true, content: [{ type: 'text', text: 'query is required' }] };
120
+ }
121
+ const q = query.toLowerCase();
122
+ const hits = [];
123
+ for (const d of docs) {
124
+ const lower = d.markdown.toLowerCase();
125
+ const idx = lower.indexOf(q);
126
+ if (idx >= 0) {
127
+ const start = Math.max(0, idx - 60);
128
+ const end = Math.min(d.markdown.length, idx + q.length + 120);
129
+ const excerpt = d.markdown.slice(start, end).replace(/\s+/g, ' ').trim();
130
+ hits.push({ slug: d.slug, title: d.title, excerpt });
131
+ if (hits.length >= limit)
132
+ break;
133
+ }
134
+ }
135
+ return {
136
+ content: [{ type: 'text', text: JSON.stringify({ query, total: hits.length, hits }, null, 2) }],
137
+ };
138
+ }
139
+ case 'get_api_reference': {
140
+ return { content: [{ type: 'text', text: JSON.stringify(apiReference, null, 2) }] };
141
+ }
142
+ default:
143
+ return {
144
+ isError: true,
145
+ content: [{ type: 'text', text: `Unknown tool: ${name}` }],
146
+ };
147
+ }
148
+ });
149
+ async function main() {
150
+ const transport = new StdioServerTransport();
151
+ await server.connect(transport);
152
+ // The MCP SDK keeps the process alive on the stdio transport, so we just
153
+ // log a startup banner to stderr (stdout is reserved for the JSON-RPC
154
+ // protocol) and let the SDK take over.
155
+ process.stderr.write('sv-grid-mcp started on stdio\n');
156
+ }
157
+ main().catch((err) => {
158
+ process.stderr.write(`sv-grid-mcp fatal: ${err?.stack ?? err}\n`);
159
+ process.exit(1);
160
+ });
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@svgrid/mcp",
3
+ "version": "1.0.0",
4
+ "description": "Model Context Protocol (MCP) server for SvGrid. Exposes example sources, docs, and API reference to AI assistants.",
5
+ "license": "SEE LICENSE IN LICENSE",
6
+ "author": "jQWidgets <sales@jqwidgets.com>",
7
+ "homepage": "https://sv-grid.github.io/sv-grid/#/mcp",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/sv-grid/sv-grid.git",
11
+ "directory": "packages/mcp"
12
+ },
13
+ "type": "module",
14
+ "main": "dist/index.js",
15
+ "bin": {
16
+ "@svgrid/mcp": "dist/index.js"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md",
21
+ "LICENSE"
22
+ ],
23
+ "scripts": {
24
+ "build:manifests": "node ./scripts/build-manifests.mjs",
25
+ "build:ts": "tsc -p tsconfig.json",
26
+ "build": "pnpm build:manifests && pnpm build:ts",
27
+ "start": "node dist/index.js",
28
+ "test:types": "tsc -p tsconfig.json --noEmit"
29
+ },
30
+ "dependencies": {
31
+ "@modelcontextprotocol/sdk": "^1.0.4",
32
+ "zod": "^3.23.8"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^22.10.7",
36
+ "typescript": "6.0.3"
37
+ },
38
+ "engines": {
39
+ "node": ">=18"
40
+ }
41
+ }