@corva/ui 3.62.0-5 → 3.62.0-6
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/MCP_README.md +18 -1
- package/mcp-server/server.mjs +163 -24
- package/package.json +1 -1
package/MCP_README.md
CHANGED
|
@@ -32,6 +32,8 @@ supports Claude Code, Cursor, and Codex CLI.
|
|
|
32
32
|
- `get_constants_docs` - Get constants documentation (values, namespaces, usage)
|
|
33
33
|
- `get_client_docs` - Get API client documentation (methods, endpoints)
|
|
34
34
|
- `get_diagnostics` - Get MCP server health metrics (memory, uptime, request stats, telemetry status)
|
|
35
|
+
- `submit_feedback` - Send feedback about `@corva/ui` or this MCP server to the maintainers (docs gaps, wrong/missing
|
|
36
|
+
props, search misses). The assistant can also call this on its own when it hits a gap.
|
|
35
37
|
|
|
36
38
|
### Usage
|
|
37
39
|
|
|
@@ -76,6 +78,20 @@ your client.
|
|
|
76
78
|
**Scope:** primarily consumer mode — app repos that depend on `@corva/ui` and import from its entry points. Authoring
|
|
77
79
|
mode (inside the `@corva/ui` repo itself) is auto-detected from `package.json` and adjusts output conventions.
|
|
78
80
|
|
|
81
|
+
- `feedback` — Send feedback about `@corva/ui` or this MCP server to the maintainers. The prompt collects your message
|
|
82
|
+
(and an optional sentiment / which component the feedback is about) and submits it via the `submit_feedback` tool.
|
|
83
|
+
|
|
84
|
+
**Argument** (optional; the assistant asks if missing):
|
|
85
|
+
|
|
86
|
+
- `input` — Your feedback. Optionally mention a sentiment (positive / negative / neutral) and which component, hook, or
|
|
87
|
+
tool it is about.
|
|
88
|
+
|
|
89
|
+
**Example invocation** (Claude Code):
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
/mcp__corva-ui__feedback the Button docs are missing the `variant` prop
|
|
93
|
+
```
|
|
94
|
+
|
|
79
95
|
## Setup
|
|
80
96
|
|
|
81
97
|
> **Just generated a fresh project with the latest `create-corva-app`?** MCP is already configured for Claude Code,
|
|
@@ -541,7 +557,8 @@ direct machine/user identifiers are collected.
|
|
|
541
557
|
|
|
542
558
|
**Tool arguments and results are exported in full, without redaction or truncation.** Span payloads may therefore
|
|
543
559
|
contain content from your queries (file paths, source snippets, component names, or other text you sent to the tool)
|
|
544
|
-
alongside the documentation responses.
|
|
560
|
+
alongside the documentation responses. This includes anything you send via the `feedback` prompt / `submit_feedback`
|
|
561
|
+
tool — the free-text message is recorded verbatim and is intended for the `@corva/ui` maintainers.
|
|
545
562
|
|
|
546
563
|
To check whether telemetry is currently active for your install, call the `get_diagnostics` tool — it reports the
|
|
547
564
|
resolved telemetry status. Telemetry is fully disabled when no valid endpoint is configured.
|
package/mcp-server/server.mjs
CHANGED
|
@@ -11,10 +11,10 @@ import { randomUUID } from 'crypto';
|
|
|
11
11
|
import { Resource } from '@opentelemetry/resources';
|
|
12
12
|
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
|
|
13
13
|
import { readFile } from 'fs/promises';
|
|
14
|
-
import { join as join$1,
|
|
15
|
-
import { fileURLToPath } from 'url';
|
|
14
|
+
import { join as join$1, resolve, dirname } from 'path';
|
|
16
15
|
import { z } from 'zod';
|
|
17
16
|
import { readFileSync } from 'fs';
|
|
17
|
+
import { fileURLToPath } from 'url';
|
|
18
18
|
|
|
19
19
|
const LOG_SYMBOLS = {
|
|
20
20
|
success: '✓',
|
|
@@ -198,6 +198,7 @@ const createNoopMetricsClient = () => ({
|
|
|
198
198
|
recordToolEmptyResult: () => { },
|
|
199
199
|
recordPromptInvocation: () => { },
|
|
200
200
|
recordPromptError: () => { },
|
|
201
|
+
recordFeedback: () => { },
|
|
201
202
|
recordSession: () => { },
|
|
202
203
|
recordSessionEnd: () => { },
|
|
203
204
|
});
|
|
@@ -228,9 +229,9 @@ const validateDsn = (dsn) => {
|
|
|
228
229
|
return url.href;
|
|
229
230
|
};
|
|
230
231
|
|
|
231
|
-
const MCP_SERVER_VERSION = '1.
|
|
232
|
+
const MCP_SERVER_VERSION = '1.3.0';
|
|
232
233
|
|
|
233
|
-
var version = "3.62.0-
|
|
234
|
+
var version = "3.62.0-6";
|
|
234
235
|
|
|
235
236
|
const CORVA_UI_VERSION = version;
|
|
236
237
|
|
|
@@ -250,6 +251,9 @@ const createMetricsClient = (meter) => {
|
|
|
250
251
|
const promptErrorCounter = meter.createCounter('mcp.server.prompt.errors', {
|
|
251
252
|
description: 'Number of prompt invocation errors',
|
|
252
253
|
});
|
|
254
|
+
const feedbackCounter = meter.createCounter('mcp.server.feedback', {
|
|
255
|
+
description: 'Number of feedback submissions',
|
|
256
|
+
});
|
|
253
257
|
const operationDuration = meter.createHistogram('mcp.server.operation.duration', {
|
|
254
258
|
description: 'Duration of MCP operations (tool and prompt calls) in milliseconds',
|
|
255
259
|
unit: 'ms',
|
|
@@ -287,6 +291,12 @@ const createMetricsClient = (meter) => {
|
|
|
287
291
|
recordPromptError: (promptName, errorType) => {
|
|
288
292
|
promptErrorCounter.add(1, { 'gen_ai.prompt.name': promptName, 'error.type': errorType });
|
|
289
293
|
},
|
|
294
|
+
recordFeedback: (sentiment, category) => {
|
|
295
|
+
feedbackCounter.add(1, {
|
|
296
|
+
'corva.mcp.feedback.sentiment': sentiment,
|
|
297
|
+
...(category ? { 'corva.mcp.feedback.category': category } : {}),
|
|
298
|
+
});
|
|
299
|
+
},
|
|
290
300
|
recordSession: (clientName, clientVersion, projectIdentity) => {
|
|
291
301
|
sessionCounter.add(1, {
|
|
292
302
|
'mcp.client.name': clientName || UNKNOWN_CLIENT_ATTR,
|
|
@@ -530,7 +540,7 @@ const createTracerTelemetryClient = (tracer, sessionId, metrics, providerShutdow
|
|
|
530
540
|
};
|
|
531
541
|
};
|
|
532
542
|
|
|
533
|
-
const getLocalConfigPath = () => join$1(
|
|
543
|
+
const getLocalConfigPath = () => join$1(process.cwd(), 'mcp-server', 'telemetry-config.local.json');
|
|
534
544
|
|
|
535
545
|
const validateLocalConfig = (data) => {
|
|
536
546
|
if (!data || typeof data !== 'object') {
|
|
@@ -100394,6 +100404,14 @@ const withTimeout = async (promise, ms) => {
|
|
|
100394
100404
|
}
|
|
100395
100405
|
};
|
|
100396
100406
|
|
|
100407
|
+
/**
|
|
100408
|
+
* Core @corva/ui entity kinds shared by tool schemas so the vocabulary stays in sync.
|
|
100409
|
+
* `search_corva_ui` (type filter) and `submit_feedback` (category) build their enums from this
|
|
100410
|
+
* base, each adding its own extra members. `list_corva_ui` intentionally uses a separate
|
|
100411
|
+
* plural/versioned representation (components-v1/v2, hooks, …) and is not derived from this list.
|
|
100412
|
+
*/
|
|
100413
|
+
const ENTITY_TYPES = ['component', 'hook', 'util', 'constant', 'client'];
|
|
100414
|
+
|
|
100397
100415
|
/**
|
|
100398
100416
|
* Split camelCase or PascalCase string into words
|
|
100399
100417
|
* e.g., "CustomGradient" -> ["custom", "gradient"]
|
|
@@ -100456,6 +100474,16 @@ const scoreMultiWordQuery = (entry, queryWords, weights) => {
|
|
|
100456
100474
|
return score;
|
|
100457
100475
|
};
|
|
100458
100476
|
|
|
100477
|
+
const SEARCH_TYPES = [
|
|
100478
|
+
'all',
|
|
100479
|
+
...ENTITY_TYPES,
|
|
100480
|
+
'permission',
|
|
100481
|
+
'icon',
|
|
100482
|
+
'hoc',
|
|
100483
|
+
'type',
|
|
100484
|
+
'testing',
|
|
100485
|
+
'style',
|
|
100486
|
+
];
|
|
100459
100487
|
const searchToolName = 'search_corva_ui';
|
|
100460
100488
|
const searchToolTitle = 'Search @corva/ui';
|
|
100461
100489
|
const searchToolDescription = `Search the @corva/ui component library by name, use case, or description.
|
|
@@ -100468,23 +100496,7 @@ Examples:
|
|
|
100468
100496
|
When category is 'v2' or 'v1' and the requested version has no match, the response surfaces a labeled fallback block from the other version so a single search still discovers a candidate. The agent should still prefer V2 when both versions match.`;
|
|
100469
100497
|
const searchToolSchema = {
|
|
100470
100498
|
query: z.string().describe('Search query - component name, use case, or description'),
|
|
100471
|
-
type: z
|
|
100472
|
-
.enum([
|
|
100473
|
-
'all',
|
|
100474
|
-
'component',
|
|
100475
|
-
'hook',
|
|
100476
|
-
'util',
|
|
100477
|
-
'constant',
|
|
100478
|
-
'client',
|
|
100479
|
-
'permission',
|
|
100480
|
-
'icon',
|
|
100481
|
-
'hoc',
|
|
100482
|
-
'type',
|
|
100483
|
-
'testing',
|
|
100484
|
-
'style',
|
|
100485
|
-
])
|
|
100486
|
-
.optional()
|
|
100487
|
-
.describe('Filter by type (default: all)'),
|
|
100499
|
+
type: z.enum(SEARCH_TYPES).optional().describe('Filter by type (default: all)'),
|
|
100488
100500
|
category: z
|
|
100489
100501
|
.enum(['all', 'v2', 'v1'])
|
|
100490
100502
|
.optional()
|
|
@@ -101400,6 +101412,53 @@ const handleGetDiagnostics = (stats, telemetry, identity) => {
|
|
|
101400
101412
|
};
|
|
101401
101413
|
};
|
|
101402
101414
|
|
|
101415
|
+
const FEEDBACK_CATEGORIES = [...ENTITY_TYPES, 'theme', 'tool', 'docs', 'other'];
|
|
101416
|
+
const submitFeedbackToolName = 'submit_feedback';
|
|
101417
|
+
const submitFeedbackToolTitle = 'Submit Feedback';
|
|
101418
|
+
const submitFeedbackToolDescription = `Send feedback about @corva/ui or this MCP server so the maintainers can improve docs and coverage.
|
|
101419
|
+
Use this when the user says something like "send feedback that…", or proactively when you hit a docs/coverage gap (a missing component, a wrong or incomplete prop, a hook that lacks an example, a search that should have matched but didn't).
|
|
101420
|
+
|
|
101421
|
+
Always infer sentiment and category from the feedback itself — do not leave them at the defaults. Reserve sentiment 'neutral' for genuinely factual/mixed feedback and category 'other' only when none of the specific categories fit.
|
|
101422
|
+
Set source to 'agent' when you are reporting a gap on your own initiative, or 'user' when relaying something the user said.
|
|
101423
|
+
Keep message specific and actionable; name the component/hook/util in target when the feedback is about a particular item.`;
|
|
101424
|
+
const submitFeedbackToolSchema = {
|
|
101425
|
+
message: z.string().min(1).describe('The feedback text (required). Be specific and actionable.'),
|
|
101426
|
+
sentiment: z
|
|
101427
|
+
.enum(['positive', 'negative', 'neutral'])
|
|
101428
|
+
.optional()
|
|
101429
|
+
.describe("Tone of the feedback — classify it from the message; don't default to neutral unless the feedback is genuinely neutral/factual (default: neutral)"),
|
|
101430
|
+
category: z
|
|
101431
|
+
.enum(FEEDBACK_CATEGORIES)
|
|
101432
|
+
.optional()
|
|
101433
|
+
.describe("What the feedback is about — pick the most specific category; use 'other' only when none apply"),
|
|
101434
|
+
target: z
|
|
101435
|
+
.string()
|
|
101436
|
+
.optional()
|
|
101437
|
+
.describe('The specific component/hook/util/tool the feedback is about (e.g. "Button")'),
|
|
101438
|
+
source: z
|
|
101439
|
+
.enum(['user', 'agent'])
|
|
101440
|
+
.optional()
|
|
101441
|
+
.describe("Who originated the feedback: 'user' (relayed) or 'agent' (default: user)"),
|
|
101442
|
+
};
|
|
101443
|
+
const handleSubmitFeedback = (args) => {
|
|
101444
|
+
const message = args.message?.trim() ?? '';
|
|
101445
|
+
if (!message) {
|
|
101446
|
+
throw new Error('Feedback message cannot be empty.');
|
|
101447
|
+
}
|
|
101448
|
+
const sentiment = args.sentiment ?? 'neutral';
|
|
101449
|
+
const source = args.source ?? 'user';
|
|
101450
|
+
const category = args.category?.trim() || undefined;
|
|
101451
|
+
const target = args.target?.trim() || undefined;
|
|
101452
|
+
return {
|
|
101453
|
+
response: createToolResponse(`Thanks — your feedback was recorded. (${sentiment})\nThe @corva/ui team uses this to improve the library and its docs.`),
|
|
101454
|
+
message,
|
|
101455
|
+
sentiment,
|
|
101456
|
+
category,
|
|
101457
|
+
target,
|
|
101458
|
+
source,
|
|
101459
|
+
};
|
|
101460
|
+
};
|
|
101461
|
+
|
|
101403
101462
|
// This file is auto-generated by `yarn mcp:generate-prompts`. Do not edit by hand.
|
|
101404
101463
|
// Source: ./figma-to-code.prompt.md
|
|
101405
101464
|
const figmaToCodePromptTemplate = "You are translating a Figma design into React components that consume the `@corva/ui` components library. Follow the\norchestration below strictly and prefer evidence from the target repo over assumptions.\n\n## Inputs\n\nUser request: {{input}}\n\nExtract the Figma URL from the user request by matching `figma.com/design/…`, `figma.com/make/…`, or\n`figma.com/board/…`. Treat the remainder as instructions (target path, file to extend, naming, behavior notes).\n\n## Precedence\n\nWhen sources of truth disagree on file layout, styling, or naming, follow this order (highest wins):\n\n1. Explicit instructions in the user request (target path, file to extend, naming).\n2. Figma Code Connect mappings returned by `mcp__figma__get_design_context`.\n3. `@corva/ui` MCP docs (`get_component_docs`, `get_theme_docs`, icon search, etc.). These are authoritative for what\n exists in the components library.\n4. `AGENTS.md` / `CLAUDE.md` in the target repo, looked up in this order and merged (earlier wins):\n a. the target directory,\n b. the nearest package root (walk up to the first `package.json`), which matters in monorepos,\n c. the repo root.\n5. 1–2 neighboring components in the target directory (fallback when 4 is silent on an axis).\n6. The minimal default policy in Step 8 (fallback when 4 and 5 are both silent).\n\n## Step 1 — Acknowledge (before any tool call)\n\nPrint one short line to the user confirming what you parsed: the Figma URL, the extracted `nodeId` and `fileKey`, and\nthe target path if stated. This is the only user-facing text until after Step 4 completes, so make it specific enough\nthat the user knows the command fired.\n\n## Step 2 — Verify Figma MCP availability\n\nBefore any other work, confirm the Figma Dev Mode MCP is active by checking whether `mcp__figma__get_design_context`\nappears in your available tools.\n\nIf the tool is NOT available:\n\n- Tell the user: \"The Figma Dev Mode MCP is not enabled in this Claude Code session. Enable it via Figma Desktop →\n Preferences → Enable Dev Mode MCP Server, then restart Claude Code and re-run `/figma_to_code`.\"\n- Stop immediately. Do not call any other tool, do not ask follow-up questions, and do not attempt to generate code\n without the design context.\n\nIf the tool IS available, proceed to the next step.\n\n## Step 3 — Resolve missing inputs\n\nIf no Figma URL is present in the user request, ask the user for it. If the target path (or file to extend) is not\nspecified, ask before generating. Do not invent a location.\n\n## Step 4 — Gather design context\n\nCall `mcp__figma__get_design_context` with the Figma URL. Inspect the response for:\n\n- Code Connect snippets / imports / designer instructions (use verbatim when present).\n- Design tokens exposed as CSS variables.\n- Screenshot and annotations.\n- Asset URLs (images, icons) that imply specific primitives.\n\nIf Code Connect coverage is unclear, `mcp__figma__get_code_connect_map` is a verification fallback.\nDo **not** call `mcp__figma__get_code_connect_suggestions`. That tool is for authoring new mappings, not codegen.\n\nIf the first `get_design_context` call returns a sparse \"too large\" response listing sublayer IDs, drill into only the\nheader, one representative row, and one content panel — do NOT recursively walk every child. A targeted sample is\nsufficient to infer the pattern for the remaining rows.\n\n## Step 5 — Read target-repo conventions\n\nBefore generating, read `AGENTS.md` / `CLAUDE.md` at precedence level 4 if present (target dir → nearest package root →\nrepo root). These files define the repo's conventions: file layout, directory structure, styling approach\n(SCSS / MUI `sx` / styled-components / CSS modules), size limits, naming, imports. Extract and apply what's relevant to\nthe target directory.\n\nIf no `AGENTS.md` / `CLAUDE.md` exists at any level, fall through to Step 8 (neighboring components).\n\nMatch the repo's documented import / module syntax (`@import` vs `@use`, named vs default exports, relative vs aliased\npaths). Do not refactor or \"modernize\" the convention as a side quest. If the documented form produces a build or type\nerror, substitute a working equivalent and list the substitution under \"Implementation deviations\" in the Gap Report so\nthe convention file can be updated.\n\n## Step 6 — Detect project mode\n\nRead `./package.json`:\n\n- `\"name\": \"@corva/ui\"` ⇒ **authoring mode**. You are contributing a new primitive to the library itself. If Step 5\n did not yield a concrete file-structure contract (target directory, file layout, export conventions), **stop and ask\n the user** where to scaffold before proceeding. Authoring a primitive in the wrong shape is worse than pausing.\n- `@corva/ui` listed in `dependencies` / `devDependencies` ⇒ **consumer mode** (default). Import from `@corva/ui` entry\n points; do not scaffold new primitives into the consumer repo.\n\n## Step 7 — Resolve @corva/ui primitives for each unmapped node\n\nPriority: **Reuse > Compose > Create**. Create a new primitive only in `@corva/ui` authoring mode (per Step 6) or when\nthe user explicitly asks. In consumer mode, always compose existing primitives — never scaffold new ones.\n\n**Search by capability, not by package name.** Before importing any third-party rendering or runtime package directly,\nsearch `@corva/ui` by capability terms drawn from the Figma node's visual and behavioral language — never let a vendor\nor library name be the decisive query. `@corva/ui` typically wraps third-party libraries (including but not limited to\ncharts, maps, code editors, virtualized lists, drag-and-drop, and rich text) behind a Corva shell whose name contains\nno library name, so queries like `highcharts` or `mapbox` return noise. Treat a Corva shell as the default hypothesis\nand disprove it with three capability search axes: (a) the user-facing widget shell (e.g. `chart container with zoom\nand pan`, `map viewport`); (b) its interaction control family (e.g. `axis dropdown`, `zoom button`,\n`chart type switch`); (c) any setup, provider, or config utility (e.g. `chart theme`, `map provider`). When a shell\nturns up, also pull in its companion controls instead of re-implementing toolbar UX. Each of the three axes must be\nqueried against both `category: 'v2'` and `category: 'v1'` — interaction-control families and shell wrappers (chart,\nmap, editor) often live only in V1, so a V2-only sweep can falsely conclude no Corva equivalent exists. A direct\nthird-party import is valid only if all three axes ran across both versions and returned no Corva equivalent — record\nthe import in the Gap Report's Implementation deviations and list the exact capability queries you ran for each\nversion, so the gap is auditable.\n\nBatch all unrelated primitive, icon, and theme lookups below into a single parallel tool call. Do not serialize them\nacross turns.\n\nFor every Figma subtree without a Code Connect hit:\n\n1. `mcp__corva-ui__search_corva_ui` with `{ query: '<descriptive term>', type: 'component', category: 'v2' }` to find\n candidate components. `query` is required; `type: 'component'` is required because category-only filtering does not\n exclude non-component entries. The tool surfaces a labeled V1 fallback block in the response when V2 has no match —\n when that block appears (or the response is \"No results\"), retry the same query with `category: 'v1'` and treat any\n hits there as candidates. Prefer V2 when both versions match; a capability is only a true gap once both V2 and V1\n have been queried explicitly.\n2. `mcp__corva-ui__get_component_docs` for each chosen component, passing the same `category` (`'v1'` or `'v2'`) that\n the search returned — names can collide across versions (e.g., `Autocomplete` exists in both). Use the returned\n import statement and prop schema verbatim. Variant values must come from the prop schema (e.g., the canonical\n union returned for `variant` or `size`); do not translate Figma variant labels (`\"Primary\"`, `\"Large\"`) by guessing.\n3. For icons: `mcp__corva-ui__search_corva_ui` with `{ query: '<icon description>', type: 'icon' }`. Import exactly the\n export name returned — naming is inconsistent (e.g., `DownSmallIcon`, `Pin`, `AttentionCustomIcon`). Do not guess a\n suffix. Use `mcp__corva-ui__list_corva_ui` with `{ type: 'icons' }` only to discover available icon sets (it lists\n sets and counts, not individual exports).\n4. For colors / spacing / typography / z-index: `mcp__corva-ui__get_theme_docs` — always use canonical token names, and\n never hard-code hex values. Express tokens through whichever mechanism the target repo's `AGENTS.md` or neighboring\n components already use (SCSS mixin, MUI `theme.*` helper, CSS custom property, Tailwind class, etc.). Match what's\n there rather than introducing a new style system.\n\n When a third-party config (chart libraries, mapping libs, canvas/WebGL code) requires palette / typography /\n semantic-color literals inline in JS/TS, do not paste Figma hex values. Source the values from `get_theme_docs`. If\n `@corva/ui` exposes a documented JS/TS theme module, import from it and cite the path. If no JS export exists,\n surface the role as an \"Unmapped token\" in the Gap Report — do not silently inline a hex. This rule does NOT apply\n to chart geometry (tick intervals, axis domains, heights, spacing) — those are domain values, not tokens.\n5. For hooks, constants, and clients implied by the design (subscriptions, feature flags, API calls, etc.):\n `mcp__corva-ui__get_hook_docs`, `mcp__corva-ui__get_constants_docs`, `mcp__corva-ui__get_client_docs`. For generic\n utilities (formatters, converters, helpers): `mcp__corva-ui__search_corva_ui` with `{ query, type: 'util' }`, or\n browse with `mcp__corva-ui__list_corva_ui({ type: 'utils' })`.\n\n## Step 8 — Fall back to neighboring components when conventions are silent\n\nIf `AGENTS.md` / `CLAUDE.md` didn't cover a given axis (styling, file layout, naming), read 1–2 neighboring components\nin the target directory and match what's already there. Do not introduce a new styling system on top of the repo's\nexisting one.\n\nIf neighbors are also inconclusive, use this minimal default policy:\n\n- **Tokens:** canonical names from `get_theme_docs`; never hard-code hex. In `.tsx`, prefer MUI `theme.*` helpers; in\n style files, prefer CSS custom properties.\n- **Component size:** prefer small, focused components. Split by responsibility when a single component starts handling\n multiple distinct concerns.\n- **Output files:** emit only what's needed to render the design. Do not generate documentation, example, or story\n files unless the user explicitly asked.\n\n## Step 9 — Generate\n\n- Emit the component file(s) at the path specified in the user's instructions (or at the location `AGENTS.md` /\n neighbors indicate).\n- Use Code Connect imports verbatim where provided.\n- Use the exact prop names, types, and `importPath` values returned by `get_component_docs`.\n- Do **not** invent @corva/ui primitives, props, icons, or tokens that the MCP tools did not surface.\n- Prefer semantic HTML first (`<button>`, `<nav>`, `<main>`, etc.); add ARIA only where native semantics are\n insufficient, and do not strip roles or semantics that `@corva/ui` primitives already provide.\n- When modifying an existing page/component, preserve existing data flow, state, and non-visual behavior unless the\n user explicitly asked to change them. Touch the UI layer only.\n- When a style value must vary per instance from data (per-row colors, per-segment widths, etc.), pass it via a CSS\n custom property rather than writing inline `style={{ <styling-property>: value }}`. Pattern:\n `<div style={{ '--phase-color': c, '--phase-flex': flex } as React.CSSProperties} />` paired with\n `background: var(--phase-color)` in the SCSS module. If the target repo uses a different style system, follow that\n system's typed dynamic-value mechanism instead.\n- Per-instance dynamic styling applies only to genuinely runtime values (user input, API data, layout measurement). A\n finite design palette of phase / status / category colors is NOT a runtime value — map those to `get_theme_docs`\n tokens or to a documented `@corva/ui` color export. If no token exists, surface them as Unmapped tokens in Step 10\n rather than committing raw hex anywhere (mock data, SCSS extension files, JS constants).\n\nBefore concluding, run this pre-delivery self-check:\n\n- Every `@corva/ui` import statement came from `get_component_docs` output — none guessed.\n- Every icon export name matches what `search_corva_ui` returned — no invented suffixes.\n- No hard-coded hex, rgba, or raw px spacing that should resolve through `get_theme_docs` tokens.\n- Every prop name **and value** matches the schema from `get_component_docs`, not a Figma variant label.\n- For every third-party rendering / runtime library I imported (chart, map, code editor, virtualization, etc.), I ran\n capability-based `@corva/ui` searches across all three axes (widget shell, interaction controls, setup / config\n utility) AND across both `category: 'v2'` and `category: 'v1'`, and either used the Corva shell with its companion\n control family or listed the import in Implementation deviations together with the exact capability queries I ran for\n each version.\n- The final section of this response is `## Gap Report` with the four required subsections (Step 10).\n\n## Step 10 — Gap Report (final section of your response)\n\nYour final assistant response must end with a top-level `## Gap Report` section using the exact shape below. When a\nsubsection has nothing to report, write `_None._` — do not omit subsections.\n\n ## Gap Report\n\n ### Unmapped design nodes\n - <node name / Figma id> — <why no @corva/ui primitive fit; suggested action>\n - …or `_None._`\n\n ### Unmapped tokens\n - <token role e.g. \"axis label color\"> — <Figma value> — <closest @corva/ui token, or \"no equivalent\">\n - …or `_None._`\n\n ### Implementation deviations\n - <departure from convention or design> — <reason> — <follow-up needed>\n - e.g. raw chart-config literals, custom HTML where a primitive existed, third-party rendering lib (chart, map,\n editor) imported directly after capability-based `@corva/ui` searches found no Corva shell — list the queries\n attempted across both `category: 'v2'` and `category: 'v1'`, deferred a11y, etc.\n - …or `_None._`\n\n ### Ambiguous props or interactions\n - <component / interaction> — <ambiguity> — <assumption made or question for the user>\n - …or `_None._`\n\nBegin with Step 1 now.\n";
|
|
@@ -101421,9 +101480,9 @@ const figmaToCodePromptArgsSchema = {
|
|
|
101421
101480
|
.optional()
|
|
101422
101481
|
.describe('Full user request. Include the Figma URL and any target path / extra context. If omitted, the assistant will ask the user before generating.'),
|
|
101423
101482
|
};
|
|
101424
|
-
const INPUT_FALLBACK = '(no input provided — ask the user for the Figma URL and target path/directory before generating)';
|
|
101483
|
+
const INPUT_FALLBACK$1 = '(no input provided — ask the user for the Figma URL and target path/directory before generating)';
|
|
101425
101484
|
const handleFigmaToCode = (args) => {
|
|
101426
|
-
const text = figmaToCodePromptTemplate.replace('{{input}}', args.input ?? INPUT_FALLBACK);
|
|
101485
|
+
const text = figmaToCodePromptTemplate.replace('{{input}}', args.input ?? INPUT_FALLBACK$1);
|
|
101427
101486
|
return {
|
|
101428
101487
|
description: figmaToCodePromptDescription,
|
|
101429
101488
|
messages: [
|
|
@@ -101435,6 +101494,44 @@ const handleFigmaToCode = (args) => {
|
|
|
101435
101494
|
};
|
|
101436
101495
|
};
|
|
101437
101496
|
|
|
101497
|
+
const feedbackPromptName = 'feedback';
|
|
101498
|
+
const feedbackPromptTitle = 'Send Feedback';
|
|
101499
|
+
const feedbackPromptDescription = `Send feedback about @corva/ui or this MCP server to the maintainers. Surfaces as /mcp__corva-ui__feedback.
|
|
101500
|
+
|
|
101501
|
+
Argument:
|
|
101502
|
+
- input: Your feedback. Optionally add a sentiment (positive / negative / neutral) and which component, hook, or tool it is about. If omitted, the assistant will ask before sending.
|
|
101503
|
+
|
|
101504
|
+
Why a single argument: Claude Code's MCP-prompt bridge splits positional string args by whitespace, so a multi-arg schema would swallow only the first token per arg. One freeform arg captures the whole message.`;
|
|
101505
|
+
const feedbackPromptArgsSchema = {
|
|
101506
|
+
input: z
|
|
101507
|
+
.string()
|
|
101508
|
+
.optional()
|
|
101509
|
+
.describe('Your feedback. Optionally mention the sentiment and which component/hook/tool it is about. If omitted, the assistant will ask before sending.'),
|
|
101510
|
+
};
|
|
101511
|
+
const INPUT_FALLBACK = '(no feedback provided — ask the user what feedback they would like to share before sending)';
|
|
101512
|
+
const handleFeedback = (args) => {
|
|
101513
|
+
const feedback = args.input?.trim() || INPUT_FALLBACK;
|
|
101514
|
+
const text = `The user wants to send feedback about @corva/ui (the shared component library) or this MCP server.
|
|
101515
|
+
|
|
101516
|
+
User feedback: ${feedback}
|
|
101517
|
+
|
|
101518
|
+
Do the following:
|
|
101519
|
+
1. If no feedback was provided above, ask the user what they would like to share and wait for their reply. Do not invent feedback.
|
|
101520
|
+
2. Infer a sentiment ('positive', 'negative', or 'neutral') from the feedback. If it is ambiguous, ask the user briefly or default to 'neutral'.
|
|
101521
|
+
3. If the feedback is about a specific item, set category (component, hook, util, constant, client, theme, tool, docs, or other) and target (e.g. the component or hook name).
|
|
101522
|
+
4. Call \`mcp__corva-ui__submit_feedback\` with the message and the fields above, and set \`source: "user"\` (this feedback is relayed from the user).
|
|
101523
|
+
5. Confirm to the user that the feedback was sent.`;
|
|
101524
|
+
return {
|
|
101525
|
+
description: feedbackPromptDescription,
|
|
101526
|
+
messages: [
|
|
101527
|
+
{
|
|
101528
|
+
role: 'user',
|
|
101529
|
+
content: { type: 'text', text },
|
|
101530
|
+
},
|
|
101531
|
+
],
|
|
101532
|
+
};
|
|
101533
|
+
};
|
|
101534
|
+
|
|
101438
101535
|
const readJsonField = (filePath, ...keys) => {
|
|
101439
101536
|
try {
|
|
101440
101537
|
let value = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
@@ -101864,6 +101961,40 @@ class CorvaUiMcpServer {
|
|
|
101864
101961
|
return createErrorResponse(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
101865
101962
|
}
|
|
101866
101963
|
});
|
|
101964
|
+
this.server.registerTool(submitFeedbackToolName, {
|
|
101965
|
+
title: submitFeedbackToolTitle,
|
|
101966
|
+
description: submitFeedbackToolDescription,
|
|
101967
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
101968
|
+
inputSchema: submitFeedbackToolSchema,
|
|
101969
|
+
}, (args, extra) => {
|
|
101970
|
+
const parentContext = extractContextFromMeta(extra?._meta);
|
|
101971
|
+
try {
|
|
101972
|
+
return this.executeToolWithObservability(submitFeedbackToolName, args, () => {
|
|
101973
|
+
const result = handleSubmitFeedback(args);
|
|
101974
|
+
// Supplemental low-cardinality counter, in addition to the standard
|
|
101975
|
+
// tool-call span/metrics recorded by the wrapper. No-op when disabled.
|
|
101976
|
+
if (this.telemetry.isEnabled()) {
|
|
101977
|
+
this.telemetry.metrics.recordFeedback(result.sentiment, result.category);
|
|
101978
|
+
}
|
|
101979
|
+
return { ...result, isEmpty: false };
|
|
101980
|
+
}, r => `feedback recorded (${r.sentiment})`, {
|
|
101981
|
+
parentContext,
|
|
101982
|
+
getSpanAttributes: r => ({
|
|
101983
|
+
// Indexed copy of the message so it is searchable/groupable in
|
|
101984
|
+
// Uptrace (the gen_ai.tool.call.arguments copy is unindexed).
|
|
101985
|
+
'corva.mcp.feedback.message': r.message,
|
|
101986
|
+
'corva.mcp.feedback.sentiment': r.sentiment,
|
|
101987
|
+
'corva.mcp.feedback.source': r.source,
|
|
101988
|
+
'corva.mcp.feedback.message_length': r.message.length,
|
|
101989
|
+
...(r.category ? { 'corva.mcp.feedback.category': r.category } : {}),
|
|
101990
|
+
...(r.target ? { 'corva.mcp.feedback.target': r.target } : {}),
|
|
101991
|
+
}),
|
|
101992
|
+
});
|
|
101993
|
+
}
|
|
101994
|
+
catch (error) {
|
|
101995
|
+
return createErrorResponse(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
101996
|
+
}
|
|
101997
|
+
});
|
|
101867
101998
|
}
|
|
101868
101999
|
setupPrompts() {
|
|
101869
102000
|
this.server.registerPrompt(figmaToCodePromptName, {
|
|
@@ -101874,6 +102005,14 @@ class CorvaUiMcpServer {
|
|
|
101874
102005
|
const parentContext = extractContextFromMeta(extra?._meta);
|
|
101875
102006
|
return this.executePromptWithObservability(figmaToCodePromptName, args, () => handleFigmaToCode(args), { parentContext, requestId: extra?.requestId });
|
|
101876
102007
|
});
|
|
102008
|
+
this.server.registerPrompt(feedbackPromptName, {
|
|
102009
|
+
title: feedbackPromptTitle,
|
|
102010
|
+
description: feedbackPromptDescription,
|
|
102011
|
+
argsSchema: feedbackPromptArgsSchema,
|
|
102012
|
+
}, (args, extra) => {
|
|
102013
|
+
const parentContext = extractContextFromMeta(extra?._meta);
|
|
102014
|
+
return this.executePromptWithObservability(feedbackPromptName, args, () => handleFeedback(args), { parentContext, requestId: extra?.requestId });
|
|
102015
|
+
});
|
|
101877
102016
|
}
|
|
101878
102017
|
async run() {
|
|
101879
102018
|
const connectTimer = this.mcpLogger.time('transport-connect');
|