@1money/component-ui 0.0.22 → 0.0.24
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/es/components/Input/Input/Input.css +1 -1
- package/es/index.css +1 -1
- package/es/stories/docs/ComponentDocsPage.js +234 -0
- package/es/stories/docs/componentDocMeta.js +97 -0
- package/es/stories/docs/storybook-docs.css +79 -0
- package/lib/components/Input/Input/Input.css +1 -1
- package/lib/index.css +1 -1
- package/lib/stories/docs/ComponentDocsPage.js +244 -0
- package/lib/stories/docs/componentDocMeta.js +104 -0
- package/lib/stories/docs/storybook-docs.css +79 -0
- package/package.json +23 -8
- package/scripts/mcp-server/README.md +267 -0
- package/scripts/mcp-server/bin.mjs +2 -0
- package/scripts/mcp-server/drift.json +5 -0
- package/scripts/mcp-server/examples.generated.json +2651 -0
- package/scripts/mcp-server/index.generated.json +18098 -0
- package/scripts/mcp-server/index.mjs +308 -26
- package/scripts/mcp-server/tools/get-examples.mjs +125 -0
- package/scripts/mcp-server/tools/get-library-info.mjs +25 -0
- package/scripts/mcp-server/tools/get-symbol.mjs +232 -0
- package/scripts/mcp-server/tools/get-token.mjs +60 -0
- package/scripts/mcp-server/tools/list-icons.mjs +38 -0
- package/scripts/mcp-server/tools/list-symbols.mjs +46 -0
- package/scripts/mcp-server/tools/resolve-import.mjs +125 -0
- package/scripts/mcp-server/tools/search-symbols.mjs +79 -0
- package/.agents/skills/1money-component-dev/SKILL.md +0 -224
- package/.agents/skills/1money-component-dev/checklist.md +0 -159
- package/.agents/skills/1money-component-dev/references/ComponentPatterns.md +0 -478
- package/.agents/skills/1money-component-dev/references/FigmaExtractionChecklist.md +0 -144
- package/.agents/skills/1money-component-dev/references/HooksGuide.md +0 -360
- package/.agents/skills/1money-component-dev/references/SemanticColors.md +0 -215
- package/.agents/skills/1money-component-dev/references/StyleSystemAPI.md +0 -389
- package/.claude/settings.local.json +0 -111
- package/.claude/skills/1money-component-dev/SKILL.md +0 -229
- package/.claude/skills/1money-component-dev/checklist.md +0 -159
- package/.claude/skills/1money-component-dev/references/ComponentPatterns.md +0 -478
- package/.claude/skills/1money-component-dev/references/FigmaExtractionChecklist.md +0 -144
- package/.claude/skills/1money-component-dev/references/HooksGuide.md +0 -360
- package/.claude/skills/1money-component-dev/references/SemanticColors.md +0 -215
- package/.claude/skills/1money-component-dev/references/StyleSystemAPI.md +0 -389
- package/.claude/skills/1money-component-review/SKILL.md +0 -316
- package/.claude/skills/component-pipeline/SKILL.md +0 -116
- package/.claude/skills/component-pipeline/checklist.md +0 -125
- package/.hintrc +0 -13
- package/@types/global.d.ts +0 -28
- package/AGENTS.md +0 -546
- package/CLAUDE.md +0 -1
- package/jest.setup.d.ts +0 -1
- package/jest.setup.ts +0 -1
- package/patches/primereact.patch +0 -323
- package/patches/react-pro-sidebar.patch +0 -6421
- package/public/favicon.ico +0 -0
- package/public/fonts/Aeonik/Aeonik-Air.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-AirItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Black.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-BlackItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Bold.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-BoldItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Light.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-LightItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Medium.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-MediumItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Regular.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-RegularItalic.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-Thin.ttf +0 -0
- package/public/fonts/Aeonik/Aeonik-ThinItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Black.ttf +0 -0
- package/public/fonts/Inter/Inter-BlackItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Bold.ttf +0 -0
- package/public/fonts/Inter/Inter-BoldItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-ExtraBold.ttf +0 -0
- package/public/fonts/Inter/Inter-ExtraBoldItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-ExtraLight.ttf +0 -0
- package/public/fonts/Inter/Inter-ExtraLightItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Italic.ttf +0 -0
- package/public/fonts/Inter/Inter-Light.ttf +0 -0
- package/public/fonts/Inter/Inter-LightItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Medium.ttf +0 -0
- package/public/fonts/Inter/Inter-MediumItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Regular.ttf +0 -0
- package/public/fonts/Inter/Inter-SemiBold.ttf +0 -0
- package/public/fonts/Inter/Inter-SemiBoldItalic.ttf +0 -0
- package/public/fonts/Inter/Inter-Thin.ttf +0 -0
- package/public/fonts/Inter/Inter-ThinItalic.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Black.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Bold.ttf +0 -0
- package/public/fonts/Outfit/Outfit-ExtraBold.ttf +0 -0
- package/public/fonts/Outfit/Outfit-ExtraLight.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Light.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Medium.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Regular.ttf +0 -0
- package/public/fonts/Outfit/Outfit-SemiBold.ttf +0 -0
- package/public/fonts/Outfit/Outfit-Thin.ttf +0 -0
- package/public/github-mark.svg +0 -3
- package/public/tokens/GYEN.svg +0 -9
- package/public/tokens/PYUSD.svg +0 -9
- package/public/tokens/USDT.svg +0 -6
- package/scripts/mcp-server/resources.d.mts +0 -1
- package/scripts/mcp-server/resources.mjs +0 -102
- package/test/jsdom-global-register.d.ts +0 -1
- package/test/jsdom-global-register.js +0 -1
|
@@ -1,45 +1,327 @@
|
|
|
1
1
|
/* eslint-disable no-undef */
|
|
2
2
|
/* eslint-env node */
|
|
3
|
+
/**
|
|
4
|
+
* 1money-react-ui MCP stdio server.
|
|
5
|
+
*
|
|
6
|
+
* Loads the committed `index.generated.json`, `drift.json`, and `package.json`
|
|
7
|
+
* at startup (never on per-request basis) and serves 8 MCP tools over a
|
|
8
|
+
* stdio transport.
|
|
9
|
+
*
|
|
10
|
+
* Use `node scripts/mcp-server/index.mjs --selftest` to run each handler
|
|
11
|
+
* with a canned input — useful for smoke-testing without a real MCP client.
|
|
12
|
+
*/
|
|
13
|
+
|
|
3
14
|
import process from 'node:process';
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import fs from 'node:fs/promises';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
19
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
20
|
+
import {
|
|
21
|
+
ListToolsRequestSchema,
|
|
22
|
+
CallToolRequestSchema
|
|
23
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
8
24
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
25
|
+
import listSymbols from './tools/list-symbols.mjs';
|
|
26
|
+
import searchSymbols from './tools/search-symbols.mjs';
|
|
27
|
+
import getSymbol from './tools/get-symbol.mjs';
|
|
28
|
+
import getExamples from './tools/get-examples.mjs';
|
|
29
|
+
import listIcons from './tools/list-icons.mjs';
|
|
30
|
+
import getToken from './tools/get-token.mjs';
|
|
31
|
+
import resolveImport from './tools/resolve-import.mjs';
|
|
32
|
+
import getLibraryInfo from './tools/get-library-info.mjs';
|
|
12
33
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
34
|
+
const EXPECTED_SCHEMA_VERSION = '1';
|
|
35
|
+
const SERVER_NAME = '1money-react-ui';
|
|
36
|
+
const SERVER_VERSION = '0.1.0';
|
|
37
|
+
|
|
38
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
39
|
+
const __dirname = path.dirname(__filename);
|
|
40
|
+
const repoRoot = path.resolve(__dirname, '..', '..');
|
|
41
|
+
const indexPath = path.join(__dirname, 'index.generated.json');
|
|
42
|
+
const driftPath = path.join(__dirname, 'drift.json');
|
|
43
|
+
const examplesPath = path.join(__dirname, 'examples.generated.json');
|
|
44
|
+
const pkgPath = path.join(repoRoot, 'package.json');
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Tool catalog — exposed to clients via tools/list. Input schemas are plain
|
|
48
|
+
* JSON Schema (not Zod) so we don't take a hard dep on zod here; the server
|
|
49
|
+
* routes to plain JS handlers below that do their own manual validation.
|
|
50
|
+
*/
|
|
51
|
+
const TOOL_CATALOG = [
|
|
52
|
+
{
|
|
53
|
+
name: 'list_symbols',
|
|
54
|
+
description:
|
|
55
|
+
'List top-level exported symbols (components, modules, hooks, types). Namespace roots (ProForm, Icons, Input) surface as kind=module.',
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: {
|
|
59
|
+
kind: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
enum: ['component', 'module', 'hook', 'function', 'type', 'all'],
|
|
62
|
+
default: 'all',
|
|
63
|
+
description: 'Filter by symbol kind. Use "all" to include every kind.'
|
|
64
|
+
},
|
|
65
|
+
category: {
|
|
66
|
+
type: 'string',
|
|
67
|
+
description: 'Optional category filter (e.g., "input", "display").'
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
additionalProperties: false
|
|
71
|
+
},
|
|
72
|
+
handler: listSymbols
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'search_symbols',
|
|
76
|
+
description:
|
|
77
|
+
'Fuzzy search over the symbol index. Searches name, aliases (searchTags), summary, and prop names.',
|
|
78
|
+
inputSchema: {
|
|
79
|
+
type: 'object',
|
|
80
|
+
properties: {
|
|
81
|
+
query: { type: 'string', description: 'Search term.' },
|
|
82
|
+
limit: { type: 'integer', minimum: 1, default: 5 }
|
|
83
|
+
},
|
|
84
|
+
required: ['query'],
|
|
85
|
+
additionalProperties: false
|
|
86
|
+
},
|
|
87
|
+
handler: searchSymbols
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'get_symbol',
|
|
91
|
+
description:
|
|
92
|
+
'Fetch a detailed symbol record. Accepts both flat names (e.g., "ProFormItem") and dotted paths (e.g., "ProForm.Item"). Returns { kind: "not_exported", hint } when the name cannot be resolved.',
|
|
93
|
+
inputSchema: {
|
|
94
|
+
type: 'object',
|
|
95
|
+
properties: {
|
|
96
|
+
name: { type: 'string', description: 'Flat or dotted symbol name.' },
|
|
97
|
+
include: {
|
|
98
|
+
type: 'array',
|
|
99
|
+
items: { enum: ['props', 'examples', 'members', 'notes', 'related'] },
|
|
100
|
+
default: ['props', 'members']
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
required: ['name'],
|
|
104
|
+
additionalProperties: false
|
|
105
|
+
},
|
|
106
|
+
handler: getSymbol
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'get_examples',
|
|
110
|
+
description:
|
|
111
|
+
'Fetch curated usage examples for a symbol. Sources: README fenced blocks, Storybook `render` bodies, and canonical snippets. Each entry carries a compilable flag.',
|
|
112
|
+
inputSchema: {
|
|
113
|
+
type: 'object',
|
|
114
|
+
properties: {
|
|
115
|
+
name: { type: 'string' },
|
|
116
|
+
limit: { type: 'integer', minimum: 1, default: 3 },
|
|
117
|
+
source: {
|
|
118
|
+
type: 'string',
|
|
119
|
+
enum: ['all', 'readme', 'stories', 'canonical'],
|
|
120
|
+
default: 'all'
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
required: ['name'],
|
|
124
|
+
additionalProperties: false
|
|
125
|
+
},
|
|
126
|
+
handler: getExamples
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: 'list_icons',
|
|
130
|
+
description:
|
|
131
|
+
'List icon names for the <Icons /> component. Optionally filter by case-insensitive substring.',
|
|
132
|
+
inputSchema: {
|
|
133
|
+
type: 'object',
|
|
134
|
+
properties: {
|
|
135
|
+
query: { type: 'string' },
|
|
136
|
+
limit: { type: 'integer', minimum: 1 }
|
|
137
|
+
},
|
|
138
|
+
additionalProperties: false
|
|
139
|
+
},
|
|
140
|
+
handler: listIcons
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: 'get_token',
|
|
144
|
+
description:
|
|
145
|
+
'Query design tokens by name, kind, or name prefix. Filters combine with AND semantics.',
|
|
146
|
+
inputSchema: {
|
|
147
|
+
type: 'object',
|
|
148
|
+
properties: {
|
|
149
|
+
name: { type: 'string' },
|
|
150
|
+
kind: {
|
|
151
|
+
type: 'string',
|
|
152
|
+
enum: [
|
|
153
|
+
'color',
|
|
154
|
+
'spacing',
|
|
155
|
+
'radius',
|
|
156
|
+
'shadow',
|
|
157
|
+
'sizing',
|
|
158
|
+
'opacity',
|
|
159
|
+
'typography',
|
|
160
|
+
'breakpoint'
|
|
161
|
+
]
|
|
162
|
+
},
|
|
163
|
+
prefix: { type: 'string' }
|
|
164
|
+
},
|
|
165
|
+
additionalProperties: false
|
|
166
|
+
},
|
|
167
|
+
handler: getToken
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'resolve_import',
|
|
171
|
+
description:
|
|
172
|
+
'Resolve how to import a symbol. Supports flat names and namespace-member paths (e.g., "ProForm.Item"). Rejects Icons.<Name> with a clear hint.',
|
|
173
|
+
inputSchema: {
|
|
174
|
+
type: 'object',
|
|
175
|
+
properties: {
|
|
176
|
+
symbol: { type: 'string' }
|
|
177
|
+
},
|
|
178
|
+
required: ['symbol'],
|
|
179
|
+
additionalProperties: false
|
|
180
|
+
},
|
|
181
|
+
handler: resolveImport
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: 'get_library_info',
|
|
185
|
+
description:
|
|
186
|
+
'Return package metadata: name, version, peer deps, available subpath exports, CSS entry, and schema version.',
|
|
187
|
+
inputSchema: {
|
|
188
|
+
type: 'object',
|
|
189
|
+
properties: {},
|
|
190
|
+
additionalProperties: false
|
|
191
|
+
},
|
|
192
|
+
handler: getLibraryInfo
|
|
193
|
+
}
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
async function loadContext() {
|
|
197
|
+
const [indexRaw, driftRaw, examplesRaw, pkgRaw] = await Promise.all([
|
|
198
|
+
fs.readFile(indexPath, 'utf8'),
|
|
199
|
+
fs.readFile(driftPath, 'utf8'),
|
|
200
|
+
fs.readFile(examplesPath, 'utf8'),
|
|
201
|
+
fs.readFile(pkgPath, 'utf8')
|
|
202
|
+
]);
|
|
203
|
+
|
|
204
|
+
const index = JSON.parse(indexRaw);
|
|
205
|
+
const drift = JSON.parse(driftRaw);
|
|
206
|
+
const examplesIndex = JSON.parse(examplesRaw);
|
|
207
|
+
const pkg = JSON.parse(pkgRaw);
|
|
208
|
+
|
|
209
|
+
if (index.schemaVersion !== EXPECTED_SCHEMA_VERSION) {
|
|
210
|
+
console.error(
|
|
211
|
+
`[mcp] schemaVersion mismatch: expected '${EXPECTED_SCHEMA_VERSION}', got '${index.schemaVersion}'. ` +
|
|
212
|
+
'Regenerate index.generated.json with `pnpm build:mcp-index` and ensure the indexer produces the expected schemaVersion.'
|
|
213
|
+
);
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (examplesIndex.schemaVersion !== EXPECTED_SCHEMA_VERSION) {
|
|
218
|
+
console.error(
|
|
219
|
+
`[mcp] schemaVersion mismatch in examples.generated.json: expected '${EXPECTED_SCHEMA_VERSION}', got '${examplesIndex.schemaVersion}'. ` +
|
|
220
|
+
'Regenerate with `pnpm build:mcp-index`.'
|
|
221
|
+
);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return { index, drift, examplesIndex, pkg };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function toToolResult(value) {
|
|
229
|
+
return {
|
|
230
|
+
content: [{ type: 'text', text: JSON.stringify(value, null, 2) }],
|
|
231
|
+
structuredContent: value
|
|
232
|
+
};
|
|
233
|
+
}
|
|
17
234
|
|
|
18
|
-
|
|
235
|
+
function toToolError(message) {
|
|
236
|
+
return {
|
|
237
|
+
content: [{ type: 'text', text: message }],
|
|
238
|
+
isError: true
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function findTool(name) {
|
|
243
|
+
return TOOL_CATALOG.find(t => t.name === name);
|
|
244
|
+
}
|
|
19
245
|
|
|
20
|
-
|
|
21
|
-
|
|
246
|
+
async function runTool(name, rawInput, ctx) {
|
|
247
|
+
const tool = findTool(name);
|
|
248
|
+
if (!tool) {
|
|
249
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
250
|
+
}
|
|
251
|
+
return Promise.resolve(tool.handler(rawInput ?? {}, ctx));
|
|
252
|
+
}
|
|
22
253
|
|
|
23
|
-
|
|
24
|
-
|
|
254
|
+
async function runSelfTest(ctx) {
|
|
255
|
+
const cannedInputs = {
|
|
256
|
+
list_symbols: { kind: 'component' },
|
|
257
|
+
search_symbols: { query: 'button', limit: 3 },
|
|
258
|
+
get_symbol: { name: 'ProForm.Item' },
|
|
259
|
+
get_examples: { name: 'Button' },
|
|
260
|
+
list_icons: { query: 'chevron', limit: 5 },
|
|
261
|
+
get_token: { kind: 'breakpoint' },
|
|
262
|
+
resolve_import: { symbol: 'ProForm.Item' },
|
|
263
|
+
get_library_info: {}
|
|
264
|
+
};
|
|
25
265
|
|
|
26
|
-
const
|
|
266
|
+
const results = [];
|
|
267
|
+
for (const tool of TOOL_CATALOG) {
|
|
268
|
+
const input = cannedInputs[tool.name] ?? {};
|
|
27
269
|
try {
|
|
28
|
-
await
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
270
|
+
const output = await runTool(tool.name, input, ctx);
|
|
271
|
+
results.push({ name: tool.name, ok: true, output });
|
|
272
|
+
} catch (err) {
|
|
273
|
+
results.push({ name: tool.name, ok: false, error: err.message });
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
process.stdout.write(JSON.stringify({ selftest: true, toolCount: results.length, results }, null, 2) + '\n');
|
|
278
|
+
const anyFailed = results.some(r => !r.ok);
|
|
279
|
+
process.exit(anyFailed ? 1 : 0);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function startStdioServer(ctx) {
|
|
283
|
+
const server = new Server(
|
|
284
|
+
{ name: SERVER_NAME, version: SERVER_VERSION },
|
|
285
|
+
{ capabilities: { tools: {} } }
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
server.setRequestHandler(ListToolsRequestSchema, () => ({
|
|
289
|
+
tools: TOOL_CATALOG.map(({ name, description, inputSchema }) => ({
|
|
290
|
+
name,
|
|
291
|
+
description,
|
|
292
|
+
inputSchema
|
|
293
|
+
}))
|
|
294
|
+
}));
|
|
295
|
+
|
|
296
|
+
server.setRequestHandler(CallToolRequestSchema, async request => {
|
|
297
|
+
const { name, arguments: args } = request.params;
|
|
298
|
+
try {
|
|
299
|
+
const output = await runTool(name, args ?? {}, ctx);
|
|
300
|
+
return toToolResult(output);
|
|
301
|
+
} catch (err) {
|
|
302
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
303
|
+
return toToolError(message);
|
|
33
304
|
}
|
|
34
305
|
});
|
|
35
306
|
|
|
307
|
+
const transport = new StdioServerTransport();
|
|
36
308
|
await server.connect(transport);
|
|
309
|
+
console.error(
|
|
310
|
+
`[mcp] ${SERVER_NAME} v${SERVER_VERSION} listening on stdio (tools: ${TOOL_CATALOG.length})`
|
|
311
|
+
);
|
|
312
|
+
}
|
|
37
313
|
|
|
38
|
-
|
|
39
|
-
|
|
314
|
+
async function main() {
|
|
315
|
+
const argv = process.argv.slice(2);
|
|
316
|
+
const ctx = await loadContext();
|
|
317
|
+
if (argv.includes('--selftest')) {
|
|
318
|
+
await runSelfTest(ctx);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
await startStdioServer(ctx);
|
|
40
322
|
}
|
|
41
323
|
|
|
42
|
-
main().catch(
|
|
43
|
-
console.error('[mcp] Fatal error:',
|
|
324
|
+
main().catch(err => {
|
|
325
|
+
console.error('[mcp] Fatal error:', err);
|
|
44
326
|
process.exit(1);
|
|
45
327
|
});
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/* eslint-disable no-undef */
|
|
2
|
+
/* eslint-env node */
|
|
3
|
+
/**
|
|
4
|
+
* Tool: get_examples
|
|
5
|
+
*
|
|
6
|
+
* Returns curated usage examples for a symbol.
|
|
7
|
+
*
|
|
8
|
+
* Input: { name, limit = 3, source = 'all' }
|
|
9
|
+
* Output: { examples: [{ title, code, source, compilable }] }
|
|
10
|
+
*
|
|
11
|
+
* Name resolution mirrors `get_symbol`:
|
|
12
|
+
* - dotted paths (`ProForm.Item`) resolve through a module's memberMap
|
|
13
|
+
* - flat names (`Button`) resolve directly
|
|
14
|
+
* - unknown names return `{ examples: [], note, hint }`
|
|
15
|
+
*
|
|
16
|
+
* The example bodies live in `examples.generated.json` (content-hashed). The
|
|
17
|
+
* per-symbol references (in `symbol.examples`) are hydrated against that
|
|
18
|
+
* payload at request time.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const VALID_SOURCES = ['all', 'readme', 'stories', 'canonical'];
|
|
22
|
+
const DEFAULT_LIMIT = 3;
|
|
23
|
+
const LOCAL_PREFIX = '$local:';
|
|
24
|
+
|
|
25
|
+
function validateInput(input) {
|
|
26
|
+
const args = input && typeof input === 'object' ? input : {};
|
|
27
|
+
if (typeof args.name !== 'string' || args.name.trim().length === 0) {
|
|
28
|
+
throw new Error('get_examples: `name` must be a non-empty string');
|
|
29
|
+
}
|
|
30
|
+
const source = args.source ?? 'all';
|
|
31
|
+
if (!VALID_SOURCES.includes(source)) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`get_examples: invalid source '${source}', expected one of ${VALID_SOURCES.join(', ')}`,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
const limit =
|
|
37
|
+
typeof args.limit === 'number' && Number.isFinite(args.limit) && args.limit > 0
|
|
38
|
+
? Math.floor(args.limit)
|
|
39
|
+
: DEFAULT_LIMIT;
|
|
40
|
+
return { name: args.name.trim(), source, limit };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function buildByName(index) {
|
|
44
|
+
const byName = new Map();
|
|
45
|
+
for (const sym of index.symbols) {
|
|
46
|
+
byName.set(sym.name, sym);
|
|
47
|
+
}
|
|
48
|
+
return byName;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Resolve a dotted-path lookup against a module's memberMap. */
|
|
52
|
+
function resolveDotted(inputName, byName) {
|
|
53
|
+
const dotIdx = inputName.indexOf('.');
|
|
54
|
+
const ownerName = inputName.slice(0, dotIdx);
|
|
55
|
+
const memberKey = inputName.slice(dotIdx + 1);
|
|
56
|
+
const owner = byName.get(ownerName);
|
|
57
|
+
if (!owner || owner.kind !== 'module' || !owner.memberMap) return null;
|
|
58
|
+
const target = owner.memberMap[memberKey];
|
|
59
|
+
if (typeof target !== 'string') return null;
|
|
60
|
+
if (target.startsWith(LOCAL_PREFIX)) {
|
|
61
|
+
return { internal: true, ownerName };
|
|
62
|
+
}
|
|
63
|
+
return byName.get(target) ?? null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function hintForMissing(name) {
|
|
67
|
+
if (/^Icons\.[A-Z]/.test(name)) {
|
|
68
|
+
return 'Icon names are string keys, not exported symbols. Use `<Icons name="chevronDown" />` (camelCase) and call `list_icons` for names.';
|
|
69
|
+
}
|
|
70
|
+
if (/^use[A-Z]/.test(name)) {
|
|
71
|
+
return 'This hook may live in `@1money/hooks`, not `@1money/component-ui`. Check that package.';
|
|
72
|
+
}
|
|
73
|
+
return `No exported symbol named '${name}'. Use search_symbols to discover candidates.`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default function getExamples(input, ctx) {
|
|
77
|
+
const { name, source, limit } = validateInput(input);
|
|
78
|
+
const { index, examplesIndex } = ctx ?? {};
|
|
79
|
+
if (!index || !Array.isArray(index.symbols)) {
|
|
80
|
+
throw new Error('get_examples: context missing `index`');
|
|
81
|
+
}
|
|
82
|
+
if (!examplesIndex || typeof examplesIndex.examples !== 'object') {
|
|
83
|
+
throw new Error('get_examples: context missing `examplesIndex`');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const byName = buildByName(index);
|
|
87
|
+
|
|
88
|
+
let sym = null;
|
|
89
|
+
if (name.includes('.')) {
|
|
90
|
+
const resolved = resolveDotted(name, byName);
|
|
91
|
+
if (resolved && resolved.internal) {
|
|
92
|
+
return {
|
|
93
|
+
examples: [],
|
|
94
|
+
note: `'${name}' is an internal member of '${resolved.ownerName}'; not separately exported.`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
sym = resolved ?? null;
|
|
98
|
+
} else {
|
|
99
|
+
sym = byName.get(name) ?? null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!sym) {
|
|
103
|
+
return { examples: [], note: hintForMissing(name) };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const refs = Array.isArray(sym.examples) ? sym.examples : [];
|
|
107
|
+
const filtered =
|
|
108
|
+
source === 'all' ? refs : refs.filter(r => r && r.source === source);
|
|
109
|
+
|
|
110
|
+
const hydrated = [];
|
|
111
|
+
for (const ref of filtered) {
|
|
112
|
+
if (!ref || typeof ref.hash !== 'string') continue;
|
|
113
|
+
const body = examplesIndex.examples[ref.hash];
|
|
114
|
+
if (!body) continue;
|
|
115
|
+
hydrated.push({
|
|
116
|
+
title: body.title,
|
|
117
|
+
code: body.code,
|
|
118
|
+
source: body.source,
|
|
119
|
+
compilable: body.compilable,
|
|
120
|
+
});
|
|
121
|
+
if (hydrated.length >= limit) break;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return { examples: hydrated };
|
|
125
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* eslint-disable no-undef */
|
|
2
|
+
/* eslint-env node */
|
|
3
|
+
/**
|
|
4
|
+
* Tool: get_library_info
|
|
5
|
+
*
|
|
6
|
+
* Returns package metadata loaded at server startup (never per-request).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export default function getLibraryInfo(_input, ctx) {
|
|
10
|
+
const { index, pkg } = ctx;
|
|
11
|
+
|
|
12
|
+
const exportsMap = (pkg && typeof pkg.exports === 'object' && pkg.exports) || {};
|
|
13
|
+
const subpathExports = Object.keys(exportsMap)
|
|
14
|
+
.filter(key => key !== '.' && key !== './index.css')
|
|
15
|
+
.sort();
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
name: pkg?.name ?? null,
|
|
19
|
+
version: pkg?.version ?? null,
|
|
20
|
+
peerDependencies: pkg?.peerDependencies ?? {},
|
|
21
|
+
subpathExports,
|
|
22
|
+
cssEntry: '@1money/component-ui/index.css',
|
|
23
|
+
schemaVersion: index?.schemaVersion ?? null
|
|
24
|
+
};
|
|
25
|
+
}
|