Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .cursor/flows/ds-refactoring-flow/01-find-violations.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ Step 1: Find violations
Store the result in a variable called scanResult.

2. Perform first-level error handling:
- If the function call returns an error, respond with:
- If the function call returns an error or result containing "Missing ds.deprecatedCssClassesPath", respond with:
<commentary>⚠️ *Cannot proceed: Missing required configuration parameter* – The `ds.deprecatedCssClassesPath` parameter must be provided when starting the MCP server to use violation detection tools. Please restart the server with this parameter configured.</commentary>
Then stop execution.
- If the function call returns any other error, respond with:
<commentary>🚨 *Tool execution failed* – [error message]</commentary>
Then stop execution.
- If scanResult.totalViolations is 0, respond with:
Expand Down Expand Up @@ -57,7 +60,10 @@ Once the user provides a subfolder choice, proceed as follows:
- Store the result in a variable called fileScan.

3. Perform error handling and validation:
- If the function call returns an error, respond with:
- If the function call returns an error containing "Missing ds.deprecatedCssClassesPath", respond with:
<commentary>⚠️ *Cannot proceed: Missing required configuration parameter* – The `ds.deprecatedCssClassesPath` parameter must be provided when starting the MCP server to use violation detection tools. Please restart the server with this parameter configured.</commentary>
Then stop execution.
- If the function call returns any other error, respond with:
<commentary>🚨 *Tool execution failed* – [error message]</commentary>
Then stop execution.
- If fileScan.rows.length is 0, respond with:
Expand Down
10 changes: 8 additions & 2 deletions .cursor/flows/ds-refactoring-flow/01b-find-all-violations.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ Step 1: Find violations
Store the result in a variable called scanResult.

2. Perform first-level error handling:
- If the function call returns an error, respond with:
- If the function call returns an error or result containing "Missing ds.deprecatedCssClassesPath", respond with:
<commentary>⚠️ *Cannot proceed: Missing required configuration parameter* – The `ds.deprecatedCssClassesPath` parameter must be provided when starting the MCP server to use violation detection tools. Please restart the server with this parameter configured.</commentary>
Then stop execution.
- If the function call returns any other error, respond with:
<commentary>🚨 *Tool execution failed* – [error message]</commentary>
Then stop execution.
- If no violations are found, respond with:
Expand Down Expand Up @@ -54,7 +57,10 @@ Once the user provides a subfolder choice, proceed as follows:
- Store the result in a variable called fileScan.

3. Perform error handling and validation:
- If the function call returns an error, respond with:
- If the function call returns an error containing "Missing ds.deprecatedCssClassesPath", respond with:
<commentary>⚠️ *Cannot proceed: Missing required configuration parameter* – The `ds.deprecatedCssClassesPath` parameter must be provided when starting the MCP server to use violation detection tools. Please restart the server with this parameter configured.</commentary>
Then stop execution.
- If the function call returns any other error, respond with:
<commentary>🚨 *Tool execution failed* – [error message]</commentary>
Then stop execution.
- If fileScan.rows.length is 0, respond with:
Expand Down
22 changes: 0 additions & 22 deletions .cursor/mcp.json

This file was deleted.

39 changes: 37 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ Copy `.cursor/mcp.json.example` to the project you're working on. Copied file sh
}
```

> Note: `ds.storybookDocsRoot` and `ds.deprecatedCssClassesPath` are optional. The server will start without them. Tools that require these paths will return a clear error prompting you to provide the missing parameter.

> **Note**: The example file contains configuration for `ESLint` official MCP which is required for the toolkit to work properly.

### Configuration Parameters
Expand All @@ -76,10 +78,20 @@ Copy `.cursor/mcp.json.example` to the project you're working on. Copied file sh
| Parameter | Type | Description | Example |
|-----------|------|-------------|---------|
| `workspaceRoot` | Absolute path | Root directory of your Angular workspace | `/Users/dev/my-angular-app` |
| `ds.storybookDocsRoot` | Relative path | Root directory containing Storybook documentation | `storybook/docs` |
| `ds.deprecatedCssClassesPath` | Relative path | JavaScript file mapping deprecated CSS classes | `design-system/component-options.js` |
| `ds.uiRoot` | Relative path | Directory containing UI components | `packages/ui` |

#### Optional Parameters

| Parameter | Type | Description | Example |
|-----------|------|-------------|---------|
| `ds.storybookDocsRoot` | Relative path | Root directory containing Storybook documentation used by documentation-related tools | `storybook/docs` |
| `ds.deprecatedCssClassesPath` | Relative path | JavaScript file mapping deprecated CSS classes used by violation and deprecated CSS tools | `design-system/component-options.js` |

When optional parameters are omitted:

- `ds.storybookDocsRoot`: Tools will skip Storybook documentation lookups (e.g., `get-ds-component-data` will still return implementation/import data but may have no docs files).
- `ds.deprecatedCssClassesPath`: Tools that require the mapping will fail fast with a clear error. Affected tools include: `get-deprecated-css-classes`, `report-deprecated-css`, `report-all-violations`, and `report-violations`.

#### Deprecated CSS Classes File Format

The `component-options.js` file should export an array of component configurations:
Expand Down Expand Up @@ -141,6 +153,29 @@ my-angular-workspace/

- **`build-component-usage-graph`**: Maps where given Angular components are imported (modules, specs, templates, styles) so refactors touch every file

### Tool behavior with optional parameters

The following tools work without optional params:

- `get-project-dependencies`
- `build-component-usage-graph`
- `get-ds-component-data` (documentation section is empty if `ds.storybookDocsRoot` is not set)
- Component contract tools:
- `build_component_contract`
- `diff_component_contract`
- `list_component_contracts`

The following tools require optional params to work:

- Requires `ds.deprecatedCssClassesPath`:
- `get-deprecated-css-classes`
- `report-deprecated-css`
- `report-all-violations`
- `report-violations`

- Requires `ds.storybookDocsRoot` for docs lookup (skipped otherwise):
- `get-ds-component-data` (docs files discovery only)

### Component Contracts

- **`build_component_contract`**: Generate a static surface contract for a component's template and SCSS
Expand Down
18 changes: 13 additions & 5 deletions packages/angular-mcp-server/src/lib/angular-mcp-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import { validateDeprecatedCssClassesFile } from './validation/ds-components-fil
export class AngularMcpServerWrapper {
private readonly mcpServer: McpServer;
private readonly workspaceRoot: string;
private readonly storybookDocsRoot: string;
private readonly deprecatedCssClassesPath: string;
private readonly storybookDocsRoot?: string;
private readonly deprecatedCssClassesPath?: string;
private readonly uiRoot: string;

/**
Expand Down Expand Up @@ -71,11 +71,13 @@ export class AngularMcpServerWrapper {
// Validate config using the Zod schema - only once here
const validatedConfig = AngularMcpServerOptionsSchema.parse(config);

// Validate file existence
// Validate file existence (optional keys are checked only when provided)
validateAngularMcpServerFilesExist(validatedConfig);

// Load and validate deprecatedCssClassesPath content
await validateDeprecatedCssClassesFile(validatedConfig);
// Load and validate deprecatedCssClassesPath content only if provided
if (validatedConfig.ds.deprecatedCssClassesPath) {
await validateDeprecatedCssClassesFile(validatedConfig);
}

return new AngularMcpServerWrapper(validatedConfig);
}
Expand Down Expand Up @@ -140,6 +142,12 @@ export class AngularMcpServerWrapper {

// Scan available design system components to add them as discoverable resources
try {
if (!this.storybookDocsRoot) {
return {
resources,
};
}

const dsUiPath = path.resolve(
process.cwd(),
this.storybookDocsRoot,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export const getDeprecatedCssClassesHandler = createHandler<
>(
getDeprecatedCssClassesSchema.name,
async ({ componentName }, { cwd, deprecatedCssClassesPath }) => {
if (!deprecatedCssClassesPath) {
throw new Error(
'Missing ds.deprecatedCssClassesPath. Provide it when starting the server to use this tool.',
);
}
return getDeprecatedCssClasses(
componentName,
deprecatedCssClassesPath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export const getDsComponentDataHandler = createHandler<
}

const documentationFiles: string[] = [];
if (includeDocumentation) {
if (includeDocumentation && storybookDocsRoot) {
const docsBasePath = resolveCrossPlatformPath(cwd, storybookDocsRoot);
const docPaths = getComponentDocPathsForName(
docsBasePath,
Expand All @@ -153,7 +153,7 @@ export const getDsComponentDataHandler = createHandler<
}

let storiesFilePaths: string[] = [];
if (includeStories) {
if (includeStories && storybookDocsRoot) {
const docsBasePath = resolveCrossPlatformPath(cwd, storybookDocsRoot);
const componentFolderName = componentNameToKebabCase(componentName);
const storiesComponentFolderPath = path.join(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ export const listDsComponentsHandler = createHandler<
});

const components: DsComponentInfo[] = [];
const docsBasePath = resolveCrossPlatformPath(cwd, storybookDocsRoot);

const includeAll = sections.includes('all');
const includeImplementation =
Expand All @@ -172,7 +171,11 @@ export const listDsComponentsHandler = createHandler<
}

const documentationFiles: string[] = [];
if (includeDocumentation) {
if (includeDocumentation && storybookDocsRoot) {
const docsBasePath = resolveCrossPlatformPath(
cwd,
storybookDocsRoot,
);
const docPaths = getComponentDocPathsForName(
docsBasePath,
componentName,
Expand All @@ -187,7 +190,11 @@ export const listDsComponentsHandler = createHandler<
}

let storiesFilePaths: string[] = [];
if (includeStories) {
if (includeStories && storybookDocsRoot) {
const docsBasePath = resolveCrossPlatformPath(
cwd,
storybookDocsRoot,
);
const storiesComponentFolderPath = path.join(
docsBasePath,
folderName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ export const reportDeprecatedCssHandler = createHandler<
async (params, { cwd, deprecatedCssClassesPath }) => {
const { directory, componentName } = params;

if (!deprecatedCssClassesPath) {
throw new Error(
'Missing ds.deprecatedCssClassesPath. Provide it when starting the server to use this tool.',
);
}

const deprecated = getDeprecatedCssClasses(
componentName,
deprecatedCssClassesPath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ export const reportAllViolationsHandler = createHandler<
>(
reportAllViolationsSchema.name,
async (params, { cwd, deprecatedCssClassesPath }) => {
if (!deprecatedCssClassesPath) {
throw new Error(
'Missing ds.deprecatedCssClassesPath. Provide it when starting the server to use this tool.',
);
}
const groupBy = params.groupBy || 'file';
const dsComponents = await loadAndValidateDsComponentsFile(
cwd,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ export interface BaseHandlerOptions {
export interface HandlerContext {
cwd: string;
workspaceRoot: string;
storybookDocsRoot: string;
deprecatedCssClassesPath: string;
storybookDocsRoot?: string;
deprecatedCssClassesPath?: string;
uiRoot: string;
}

Expand Down Expand Up @@ -60,8 +60,8 @@ export function setupHandlerEnvironment(
return {
cwd,
workspaceRoot: params.workspaceRoot || cwd,
storybookDocsRoot: params.storybookDocsRoot || '',
deprecatedCssClassesPath: params.deprecatedCssClassesPath || '',
storybookDocsRoot: params.storybookDocsRoot,
deprecatedCssClassesPath: params.deprecatedCssClassesPath,
uiRoot: params.uiRoot || '',
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ export async function analyzeViolationsBase<T extends BaseViolationResult>(

process.chdir(cwd);

if (!deprecatedCssClassesPath) {
throw new Error(
'Missing ds.deprecatedCssClassesPath. Provide it when starting the server to use this tool.',
);
}

const deprecatedCssClasses = getDeprecatedCssClasses(
componentName,
deprecatedCssClassesPath || '',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@ export const AngularMcpServerOptionsSchema = z.object({
'workspaceRoot must be an absolute path to the repository root where MCP server is working (e.g., /path/to/workspace-root)',
}),
ds: z.object({
storybookDocsRoot: z.string().refine(isRelativePath, {
message:
'ds.storybookDocsRoot must be a relative path from workspace root to the storybook project root (e.g., path/to/storybook/components)',
}),
deprecatedCssClassesPath: z.string().refine(isRelativePath, {
message:
'ds.deprecatedCssClassesPath must be a relative path from workspace root to the file component to deprecated css classes mapping (e.g., path/to/components-config.js)',
}),
storybookDocsRoot: z
.string()
.optional()
.refine((val) => val === undefined || isRelativePath(val), {
message:
'ds.storybookDocsRoot must be a relative path from workspace root to the storybook project root (e.g., path/to/storybook/components)',
}),
deprecatedCssClassesPath: z
.string()
.optional()
.refine((val) => val === undefined || isRelativePath(val), {
message:
'ds.deprecatedCssClassesPath must be a relative path from workspace root to the file component to deprecated css classes mapping (e.g., path/to/components-config.js)',
}),
uiRoot: z.string().refine(isRelativePath, {
message:
'ds.uiRoot must be a relative path from workspace root to the components folder (e.g., path/to/components)',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ import { DsComponentsArraySchema } from './ds-components.schema';
export async function validateDeprecatedCssClassesFile(
config: AngularMcpServerOptions,
): Promise<void> {
const relPath = config.ds.deprecatedCssClassesPath;
if (!relPath) {
// Optional parameter not provided; nothing to validate
return;
}
const deprecatedCssClassesAbsPath = path.resolve(
config.workspaceRoot,
config.ds.deprecatedCssClassesPath,
relPath,
);

let dsComponents;
Expand Down
17 changes: 11 additions & 6 deletions packages/angular-mcp-server/src/lib/validation/file-existence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,19 @@ export function validateAngularMcpServerFilesExist(

const missingFiles: string[] = [];

// Always require uiRoot, optional: storybookDocsRoot, deprecatedCssClassesPath
const dsPaths = [
{ label: 'ds.storybookDocsRoot', relPath: config.ds.storybookDocsRoot },
{
label: 'ds.deprecatedCssClassesPath',
relPath: config.ds.deprecatedCssClassesPath,
},
config.ds.storybookDocsRoot
? { label: 'ds.storybookDocsRoot', relPath: config.ds.storybookDocsRoot }
: null,
config.ds.deprecatedCssClassesPath
? {
label: 'ds.deprecatedCssClassesPath',
relPath: config.ds.deprecatedCssClassesPath,
}
: null,
{ label: 'ds.uiRoot', relPath: config.ds.uiRoot },
];
].filter(Boolean) as { label: string; relPath: string }[];

for (const { label, relPath } of dsPaths) {
const absPath = path.resolve(root, relPath);
Expand Down
4 changes: 2 additions & 2 deletions packages/angular-mcp/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ const argv = yargs(hideBin(process.argv))
const { workspaceRoot, ds } = argv as unknown as {
workspaceRoot: string;
ds: {
storybookDocsRoot: string;
deprecatedCssClassesPath: string;
storybookDocsRoot?: string;
deprecatedCssClassesPath?: string;
uiRoot: string;
};
};
Expand Down