Skip to content

Commit 5ef5e3f

Browse files
committed
Clean up
1 parent ec84685 commit 5ef5e3f

22 files changed

Lines changed: 3728 additions & 1182 deletions

packages/mcp/README.md

Lines changed: 42 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,30 @@
22

33
`@salt-ds/mcp` is a publishable MCP server package for Salt Design System runtime use cases.
44

5-
It builds a machine-readable registry from Salt source material and is presented as a single MCP server with a consumer-first default experience.
5+
It builds a machine-readable registry from Salt source material and serves it through a single MCP server with a compact, workflow-oriented public interface.
66

7-
## Default Consumer Workflows
7+
## Public Tool Surface
88

9-
These are the primary tools to surface in docs, prompts, and demos:
9+
The server now exposes six workflow tools:
1010

1111
- `discover_salt`
12-
- Route a broad Salt question to the best starting point, with clarifying questions when the intent is still ambiguous.
13-
- `recommend_component`
14-
- Choose the right Salt primitive from a user need, with opt-in starter code and shipping/a11y filters.
15-
- `get_composition_recipe`
16-
- Assemble a common Salt flow or pattern correctly, with opt-in starter code and shipping/a11y filters.
17-
- `get_component`
18-
- Look up a specific component once the name is known.
19-
- `get_examples`
20-
- Find implementation examples with a best example, nearby variants, and scenario hints.
21-
- `get_foundation`
22-
- Look up typography, spacing, density, size, and responsiveness guidance, with opt-in starter scaffolds.
23-
- `recommend_tokens`
24-
- Find the right design tokens from styling intent.
25-
- `recommend_fix_recipes`
26-
- Turn code issues into actionable Salt remediation steps.
27-
- `compare_versions`
28-
- Understand upgrade impact between versions.
29-
- `search_salt_docs`
30-
- General escape hatch when the caller is not sure where to start.
31-
32-
## Additional Tools
33-
34-
These stay available in the same server but are not the primary consumer starting points:
35-
36-
- `list_salt_catalog`
37-
- `get_package`
38-
- `get_guide`
39-
- `list_foundations`
40-
- `get_page`
41-
- `get_changes`
42-
- `get_icon`
43-
- `get_icons`
44-
- `get_country_symbol`
45-
- `get_country_symbols`
46-
- `get_pattern`
47-
- `get_token`
48-
- `search_api_surface`
49-
- `search_component_capabilities`
50-
- `get_related_entities`
51-
- `compare_options`
52-
- `suggest_migration`
53-
- `validate_salt_usage`
12+
- Use this for broad, ambiguous, or exploratory Salt requests.
13+
- It searches, clarifies intent, and routes to the next best Salt workflow.
14+
- Do not use it when the caller already wants a direct recommendation or a known-entity lookup.
15+
- `choose_salt_solution`
16+
- Use this for recommendation or side-by-side comparison.
17+
- If `names` is present, comparison mode wins. Otherwise `query` drives recommendation mode.
18+
- `get_salt_entity`
19+
- Use this for known or near-known Salt entity lookup.
20+
- It resolves specific or almost-specific components, patterns, foundations, tokens, guides, pages, packages, icons, and country symbols.
21+
- Do not use it for broad discovery or recommendation/comparison.
22+
- `get_salt_examples`
23+
- Find the best implementation example and nearby variants for a component or pattern.
24+
- Can return lightweight starter snippets when Salt already has a corresponding starter path.
25+
- `analyze_salt_code`
26+
- Analyze existing React and Salt code with AST-based validation, version-aware deprecation checks, fix guidance, and migration suggestions.
27+
- `compare_salt_versions`
28+
- Explain upgrade impact between versions, highlight breaking changes, and suggest the next actions.
5429

5530
## Usage
5631

@@ -73,13 +48,13 @@ Build the package from the repo:
7348
yarn workspace @salt-ds/mcp build
7449
```
7550

76-
Generate registry artifacts from repo sources with the repo-only maintainer script:
51+
Generate registry artifacts from repo sources with the maintainer script:
7752

7853
```sh
7954
yarn workspace @salt-ds/mcp build:package
80-
node packages/salt-mcp/scripts/buildRegistry.mjs \
55+
node packages/mcp/scripts/buildRegistry.mjs \
8156
--source-root D:/Work/salt-ds-2 \
82-
--output-dir D:/Work/salt-ds-2/packages/salt-mcp/generated
57+
--output-dir D:/Work/salt-ds-2/packages/mcp/generated
8358
```
8459

8560
Run the MCP server over stdio from a built checkout:
@@ -90,38 +65,30 @@ node dist/salt-ds-mcp/bin/salt-mcp.js
9065

9166
## Notes
9267

93-
- Salt MCP is intentionally a single-server setup. The simplification is in the recommended workflow and documentation, not in separate profiles or install modes.
94-
- The main consumer tools are compact by default and optimized for decision-making. Use `view: "full"` on tools like `discover_salt`, `get_component`, `get_examples`, `get_foundation`, `recommend_component`, `get_composition_recipe`, `compare_versions`, `recommend_tokens`, and `recommend_fix_recipes` when you want the richer evidence.
95-
- `discover_salt` can return structured `clarifying_questions` when a broad query still needs consumer decisions such as single vs multi-select or design guidance vs implementation help.
96-
- `recommend_component`, `get_composition_recipe`, and `get_foundation` can return opt-in `starter_code` scaffolds that stay lightweight enough for MCP and avoid turning the server into a workflow skill. Where registry examples exist, the starter payload also includes an example-derived snippet.
97-
- Consumer recommendation tools support filters like `production_ready`, `prefer_stable`, `a11y_required`, and `form_field_support` to bias toward ship-ready guidance.
98-
- Consumer-facing tools now bias toward a decision-first shape with `best_example`, `caveats`, `ship_check`, and `suggested_follow_ups` fields before exposing raw scoring details.
99-
- Tool responses are compact by default to reduce token usage.
68+
- Salt MCP stays intentionally single-server. The simplification is in the public tool contract, not in server splitting.
69+
- Public tool responses are compact by default and decision-first. Use `view: "full"` when you need richer evidence.
70+
- `discover_salt` absorbs broad docs search, browse-first catalog exploration, and related-entity exploration.
71+
- `choose_salt_solution` absorbs component recommendation, pattern/composition recommendation, capability-driven selection, and option comparison.
72+
- `get_salt_entity` absorbs entity lookup plus icon and country-symbol lookup/list flows.
73+
- Tool boundary shorthand:
74+
- `discover_salt`: broad, ambiguous, exploratory routing.
75+
- `choose_salt_solution`: recommendation or comparison.
76+
- `get_salt_entity`: known or near-known lookup.
77+
- `analyze_salt_code` absorbs validation, fix recipes, and migration guidance while preserving AST-based analysis and version-aware checks.
78+
- `compare_salt_versions` supports both explicit version-to-version comparison and open-ended history mode when `to_version` is omitted.
79+
- Starter scaffolds stay intentionally lightweight to keep the MCP focused on guidance instead of turning into a workflow engine.
10080
- Tool responses include a top-level `sources` array that normalizes site routes to absolute URLs while preserving local repo paths for debugging.
101-
- `get_changes` exposes changelog-derived package and component history for AI-friendly "what changed?" lookups.
102-
- `list_salt_catalog` provides a browse-first catalog view when the consumer does not know the exact Salt name yet, including country symbols alongside icons and docs-backed entities.
103-
- `get_guide` exposes structured setup and theming guidance sourced from the Salt docs.
104-
- `list_foundations` and `get_foundation` expose foundations as a first-class consumer entry point instead of requiring generic page lookup.
105-
- Icon search metadata is sourced from the same synonym dataset used by the site icon preview.
106-
- Country symbol search metadata is sourced from `@salt-ds/countries` generated country metadata and includes both circle and sharp exports.
10781
- The published package serves the bundled registry by default. `--registry-dir` is only needed for local development or testing against a custom registry build.
10882
- Registry generation is a repo-only build step. The published CLI only supports serving the bundled registry.
109-
- Use the package bin entrypoint (`bin/salt-mcp.js` or installed `salt-mcp`) when launching the CLI. `dist-cjs/cli.js` exports `runCli` but is not a direct executable.
110-
- Use `view: "full"` or `include` fields on relevant tools to request expanded payloads.
111-
- The default consumer workflow is centered on ten tools: `discover_salt`, `recommend_component`, `get_composition_recipe`, `get_component`, `get_examples`, `get_foundation`, `recommend_tokens`, `recommend_fix_recipes`, `compare_versions`, and `search_salt_docs`.
112-
- `search_api_surface` is intended for more technical questions such as “which components support validationStatus?” or “what props are deprecated?”
113-
- `recommend_tokens` and `get_related_entities` support discovery flows that start from styling intent or a nearby Salt entity instead of an exact API name.
114-
- `compare_options` supports structured side-by-side comparison for close Salt choices such as `Button` vs `Link` or one pattern versus another.
115-
- `recommend_fix_recipes` exposes consumer-facing remediation on top of the raw registry and code-analysis tools.
116-
- `validate_salt_usage` uses AST-based React analysis and supports version-aware deprecation checks via `package_version`.
83+
- The MCP handshake advertises the `@salt-ds/mcp` runtime version. Treat that separately from the Salt registry version and `generated_at` timestamp used for content metadata.
11784

11885
## Maintainer Notes
11986

120-
- Registry artifact filenames and payload keys are centralized in [`src/registry/artifacts.ts`](./src/registry/artifacts.ts). Update that file first when adding or renaming a generated artifact.
87+
- Registry artifact filenames and payload keys are centralized in [`src/registry/artifacts.ts`](./src/registry/artifacts.ts).
12188
- Runtime loading lives in [`src/registry/loadRegistry.ts`](./src/registry/loadRegistry.ts). Registry build output is assembled in [`src/build/buildRegistry.ts`](./src/build/buildRegistry.ts).
122-
- MCP tool metadata is defined in [`src/tools/toolDefinitions.ts`](./src/tools/toolDefinitions.ts). The server registration layer in [`src/server/createServer.ts`](./src/server/createServer.ts) is intentionally thin.
123-
- Reusable lookup/ambiguity behavior for `get_*` tools lives in [`src/tools/lookupResolver.ts`](./src/tools/lookupResolver.ts).
89+
- MCP tool metadata is defined in [`src/tools/toolDefinitions.ts`](./src/tools/toolDefinitions.ts). The server registration layer in [`src/server/createServer.ts`](./src/server/createServer.ts) stays intentionally thin, with runtime-vs-registry version metadata centralized in [`src/server/serverMetadata.ts`](./src/server/serverMetadata.ts).
90+
- Lookup, recommendation, search, and code-analysis helpers still live in `src/tools/` as internal building blocks. The public surface is the curated six-tool set above.
12491
- The main test entry points are:
125-
- [`src/__tests__/tools.spec.ts`](./src/__tests__/tools.spec.ts) for catalog/query tools
126-
- [`src/__tests__/codeAnalysisTools.spec.ts`](./src/__tests__/codeAnalysisTools.spec.ts) for AST-based code analysis tools
127-
- [`src/__tests__/registry.integration.spec.ts`](./src/__tests__/registry.integration.spec.ts) for generated-registry integration
92+
- [`src/__tests__/tools.spec.ts`](./src/__tests__/tools.spec.ts)
93+
- [`src/__tests__/codeAnalysisTools.spec.ts`](./src/__tests__/codeAnalysisTools.spec.ts)
94+
- [`src/__tests__/registry.integration.spec.ts`](./src/__tests__/registry.integration.spec.ts)

packages/mcp/src/__tests__/codeAnalysisTools.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { describe, expect, it } from "vitest";
22
import {
3+
analyzeSaltCode,
34
recommendFixRecipes,
45
suggestMigration,
56
validateSaltUsage,
@@ -785,3 +786,26 @@ describe("recommendFixRecipes", () => {
785786
});
786787
});
787788
});
789+
790+
describe("analyzeSaltCode", () => {
791+
it("combines validation, fixes, and migrations into one workflow result", () => {
792+
const result = analyzeSaltCode(REGISTRY, {
793+
code: `
794+
import { Button } from "@salt-ds/core";
795+
796+
export function Demo() {
797+
return <Button href="/next">Go</Button>;
798+
}
799+
`,
800+
framework: "react",
801+
package_version: "2.0.0",
802+
});
803+
804+
expect(result.decision.status).toBe("needs_attention");
805+
expect(result.summary.errors).toBeGreaterThan(0);
806+
expect(result.fixes?.[0]).toBeTruthy();
807+
expect(result.issues?.[0]).toMatchObject({
808+
id: "component-choice.navigation",
809+
});
810+
});
811+
});

packages/mcp/src/__tests__/createServer.spec.ts

Lines changed: 144 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,33 @@
11
import { describe, expect, it } from "vitest";
22
import { createSaltMcpServer } from "../server/createServer.js";
3+
import {
4+
buildSaltMcpInstructions,
5+
buildSaltMcpServerInfo,
6+
} from "../server/serverMetadata.js";
37
import { TOOL_DEFINITIONS } from "../tools/index.js";
4-
import { withRegistryDir, writeBaseArtifacts } from "./registryTestUtils.js";
8+
import {
9+
GENERATED_AT,
10+
VERSION,
11+
withRegistryDir,
12+
writeBaseArtifacts,
13+
} from "./registryTestUtils.js";
514

615
const EXPECTED_TOOL_NAMES = [
7-
"compare_options",
8-
"compare_versions",
16+
"analyze_salt_code",
17+
"choose_salt_solution",
18+
"compare_salt_versions",
919
"discover_salt",
10-
"get_changes",
11-
"get_component",
12-
"get_composition_recipe",
13-
"get_country_symbol",
14-
"get_country_symbols",
15-
"get_examples",
16-
"get_foundation",
17-
"get_guide",
18-
"get_icon",
19-
"get_icons",
20-
"get_package",
21-
"get_page",
22-
"get_pattern",
23-
"get_related_entities",
24-
"get_token",
25-
"list_foundations",
26-
"list_salt_catalog",
27-
"recommend_component",
28-
"recommend_fix_recipes",
29-
"recommend_tokens",
30-
"search_api_surface",
31-
"search_component_capabilities",
32-
"search_salt_docs",
33-
"suggest_migration",
34-
"validate_salt_usage",
20+
"get_salt_entity",
21+
"get_salt_examples",
3522
].sort();
3623

3724
const EXPECTED_DEFAULT_TOOL_ORDER = [
3825
"discover_salt",
39-
"recommend_component",
40-
"get_composition_recipe",
41-
"get_component",
42-
"get_examples",
43-
"get_foundation",
44-
"recommend_tokens",
45-
"recommend_fix_recipes",
46-
"compare_versions",
47-
"search_salt_docs",
26+
"choose_salt_solution",
27+
"get_salt_entity",
28+
"get_salt_examples",
29+
"analyze_salt_code",
30+
"compare_salt_versions",
4831
];
4932

5033
describe("createSaltMcpServer", () => {
@@ -56,6 +39,63 @@ describe("createSaltMcpServer", () => {
5639
).toEqual(EXPECTED_DEFAULT_TOOL_ORDER);
5740
});
5841

42+
it("keeps the broad router and lookup schemas intentionally small", () => {
43+
const discoverTool = TOOL_DEFINITIONS.find(
44+
(definition) => definition.name === "discover_salt",
45+
);
46+
const chooseTool = TOOL_DEFINITIONS.find(
47+
(definition) => definition.name === "choose_salt_solution",
48+
);
49+
const lookupTool = TOOL_DEFINITIONS.find(
50+
(definition) => definition.name === "get_salt_entity",
51+
);
52+
53+
expect(Object.keys(discoverTool?.inputSchema ?? {})).toEqual([
54+
"query",
55+
"area",
56+
"package",
57+
"status",
58+
"related_to",
59+
"view",
60+
]);
61+
expect(Object.keys(chooseTool?.inputSchema ?? {})).toEqual([
62+
"query",
63+
"names",
64+
"solution_type",
65+
"package",
66+
"status",
67+
"top_k",
68+
"production_ready",
69+
"prefer_stable",
70+
"a11y_required",
71+
"form_field_support",
72+
"include_starter_code",
73+
"view",
74+
]);
75+
expect(Object.keys(lookupTool?.inputSchema ?? {})).toEqual([
76+
"entity_type",
77+
"name",
78+
"query",
79+
"package",
80+
"status",
81+
"include",
82+
"include_related",
83+
"include_starter_code",
84+
"max_results",
85+
"include_deprecated",
86+
"view",
87+
]);
88+
expect(discoverTool?.description).toContain(
89+
"Do not use this for direct recommendation or known-entity lookup.",
90+
);
91+
expect(chooseTool?.description).toContain(
92+
"If names is present, comparison mode wins",
93+
);
94+
expect(lookupTool?.description).toContain(
95+
"Do not use this for broad discovery or recommendation/comparison.",
96+
);
97+
});
98+
5999
it("registers the full Salt MCP tool surface", async () => {
60100
await withRegistryDir(
61101
async (registryDir) => {
@@ -77,8 +117,73 @@ describe("createSaltMcpServer", () => {
77117
server as unknown as {
78118
_registeredTools: Record<string, { description?: string }>;
79119
}
80-
)._registeredTools.search_salt_docs?.description,
81-
).toContain("Search Salt package");
120+
)._registeredTools.discover_salt?.description,
121+
).toContain(
122+
"Use this for broad, ambiguous, or exploratory Salt requests",
123+
);
124+
},
125+
);
126+
});
127+
128+
it("advertises the MCP runtime version separately from the registry version", async () => {
129+
await withRegistryDir(
130+
async (registryDir) => {
131+
await writeBaseArtifacts(registryDir);
132+
},
133+
async (registryDir) => {
134+
const server = await createSaltMcpServer({ registryDir });
135+
const runtimeInfo = buildSaltMcpServerInfo({
136+
generated_at: GENERATED_AT,
137+
version: VERSION,
138+
build_info: null,
139+
packages: [],
140+
components: [],
141+
icons: [],
142+
country_symbols: [],
143+
pages: [],
144+
patterns: [],
145+
guides: [],
146+
tokens: [],
147+
deprecations: [],
148+
examples: [],
149+
changes: [],
150+
search_index: [],
151+
});
152+
const instructions = buildSaltMcpInstructions({
153+
generated_at: GENERATED_AT,
154+
version: VERSION,
155+
build_info: null,
156+
packages: [],
157+
components: [],
158+
icons: [],
159+
country_symbols: [],
160+
pages: [],
161+
patterns: [],
162+
guides: [],
163+
tokens: [],
164+
deprecations: [],
165+
examples: [],
166+
changes: [],
167+
search_index: [],
168+
});
169+
const internalServer = (
170+
server as unknown as {
171+
server: {
172+
_serverInfo: { name: string; version: string };
173+
_instructions?: string;
174+
};
175+
}
176+
).server;
177+
178+
expect(internalServer._serverInfo).toEqual(runtimeInfo);
179+
expect(internalServer._serverInfo.version).not.toBe(VERSION);
180+
expect(internalServer._instructions).toBe(instructions);
181+
expect(internalServer._instructions).toContain(
182+
"When asked for the MCP version, use the runtime version.",
183+
);
184+
expect(internalServer._instructions).toContain(
185+
`Serving Salt registry v${VERSION}.`,
186+
);
82187
},
83188
);
84189
});

0 commit comments

Comments
 (0)