diff --git a/.cursor/flows/ds-refactoring-flow/01-find-violations.mdc b/.cursor/flows/ds-refactoring-flow/01-find-violations.mdc
index 9cee81b..255899c 100644
--- a/.cursor/flows/ds-refactoring-flow/01-find-violations.mdc
+++ b/.cursor/flows/ds-refactoring-flow/01-find-violations.mdc
@@ -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:
+ ⚠️ *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.
+ Then stop execution.
+ - If the function call returns any other error, respond with:
🚨 *Tool execution failed* – [error message]
Then stop execution.
- If scanResult.totalViolations is 0, respond with:
@@ -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:
+ ⚠️ *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.
+ Then stop execution.
+ - If the function call returns any other error, respond with:
🚨 *Tool execution failed* – [error message]
Then stop execution.
- If fileScan.rows.length is 0, respond with:
diff --git a/.cursor/flows/ds-refactoring-flow/01b-find-all-violations.mdc b/.cursor/flows/ds-refactoring-flow/01b-find-all-violations.mdc
index 714da1b..0ee4864 100644
--- a/.cursor/flows/ds-refactoring-flow/01b-find-all-violations.mdc
+++ b/.cursor/flows/ds-refactoring-flow/01b-find-all-violations.mdc
@@ -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:
+ ⚠️ *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.
+ Then stop execution.
+ - If the function call returns any other error, respond with:
🚨 *Tool execution failed* – [error message]
Then stop execution.
- If no violations are found, respond with:
@@ -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:
+ ⚠️ *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.
+ Then stop execution.
+ - If the function call returns any other error, respond with:
🚨 *Tool execution failed* – [error message]
Then stop execution.
- If fileScan.rows.length is 0, respond with:
diff --git a/.cursor/mcp.json b/.cursor/mcp.json
deleted file mode 100644
index dc83135..0000000
--- a/.cursor/mcp.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "mcpServers": {
- "nx-mcp": {
- "url": "http://localhost:9665/sse"
- },
- "angular-toolkit-mcp": {
- "command": "node",
- "args": [
- "./packages/angular-mcp/dist/main.js",
- "--workspaceRoot=/home/spoltorak/projects/x-mcp",
- "--ds.storybookDocsRoot=packages/minimal-repo/packages/design-system/storybook-host-app/src/components",
- "--ds.deprecatedCssClassesPath=packages/minimal-repo/packages/design-system/component-options.js",
- "--ds.uiRoot=packages/minimal-repo/packages/design-system/ui"
- ]
- },
- "ESLint": {
- "type": "stdio",
- "command": "npx",
- "args": ["@eslint/mcp@latest"]
- }
- }
-}
diff --git a/README.md b/README.md
index 11e3a4b..8e943fc 100644
--- a/README.md
+++ b/README.md
@@ -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
@@ -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:
@@ -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
diff --git a/packages/angular-mcp-server/src/lib/angular-mcp-server.ts b/packages/angular-mcp-server/src/lib/angular-mcp-server.ts
index 8b7857f..11146f8 100644
--- a/packages/angular-mcp-server/src/lib/angular-mcp-server.ts
+++ b/packages/angular-mcp-server/src/lib/angular-mcp-server.ts
@@ -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;
/**
@@ -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);
}
@@ -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,
diff --git a/packages/angular-mcp-server/src/lib/tools/ds/component/get-deprecated-css-classes.tool.ts b/packages/angular-mcp-server/src/lib/tools/ds/component/get-deprecated-css-classes.tool.ts
index 1fb33a1..ceacbf4 100644
--- a/packages/angular-mcp-server/src/lib/tools/ds/component/get-deprecated-css-classes.tool.ts
+++ b/packages/angular-mcp-server/src/lib/tools/ds/component/get-deprecated-css-classes.tool.ts
@@ -28,6 +28,11 @@ export const getDeprecatedCssClassesHandler = createHandler<
>(
getDeprecatedCssClassesSchema.name,
async ({ componentName }, { cwd, deprecatedCssClassesPath }) => {
+ if (!deprecatedCssClassesPath) {
+ throw new Error(
+ 'Missing ds.deprecatedCssClassesPath. Provide --ds.deprecatedCssClassesPath in mcp.json file.',
+ );
+ }
return getDeprecatedCssClasses(
componentName,
deprecatedCssClassesPath,
diff --git a/packages/angular-mcp-server/src/lib/tools/ds/component/get-ds-component-data.tool.ts b/packages/angular-mcp-server/src/lib/tools/ds/component/get-ds-component-data.tool.ts
index 43e3049..4ae1c44 100644
--- a/packages/angular-mcp-server/src/lib/tools/ds/component/get-ds-component-data.tool.ts
+++ b/packages/angular-mcp-server/src/lib/tools/ds/component/get-ds-component-data.tool.ts
@@ -137,31 +137,34 @@ export const getDsComponentDataHandler = createHandler<
}
const documentationFiles: string[] = [];
- if (includeDocumentation) {
+
+ let storiesFilePaths: string[] = [];
+ if (storybookDocsRoot) {
const docsBasePath = resolveCrossPlatformPath(cwd, storybookDocsRoot);
- const docPaths = getComponentDocPathsForName(
- docsBasePath,
- componentName,
- );
- if (fs.existsSync(docPaths.paths.api)) {
- documentationFiles.push(`file://${docPaths.paths.api}`);
+ if (includeDocumentation) {
+ const docPaths = getComponentDocPathsForName(
+ docsBasePath,
+ componentName,
+ );
+
+ if (fs.existsSync(docPaths.paths.api)) {
+ documentationFiles.push(`file://${docPaths.paths.api}`);
+ }
+ if (fs.existsSync(docPaths.paths.overview)) {
+ documentationFiles.push(`file://${docPaths.paths.overview}`);
+ }
}
- if (fs.existsSync(docPaths.paths.overview)) {
- documentationFiles.push(`file://${docPaths.paths.overview}`);
- }
- }
- let storiesFilePaths: string[] = [];
- if (includeStories) {
- const docsBasePath = resolveCrossPlatformPath(cwd, storybookDocsRoot);
- const componentFolderName = componentNameToKebabCase(componentName);
- const storiesComponentFolderPath = path.join(
- docsBasePath,
- componentFolderName,
- );
- const storiesFiles = findStoriesFiles(storiesComponentFolderPath);
- storiesFilePaths = storiesFiles.map((file) => `file://${file}`);
+ if (includeStories) {
+ const componentFolderName = componentNameToKebabCase(componentName);
+ const storiesComponentFolderPath = path.join(
+ docsBasePath,
+ componentFolderName,
+ );
+ const storiesFiles = findStoriesFiles(storiesComponentFolderPath);
+ storiesFilePaths = storiesFiles.map((file) => `file://${file}`);
+ }
}
return {
diff --git a/packages/angular-mcp-server/src/lib/tools/ds/component/list-ds-components.tool.ts b/packages/angular-mcp-server/src/lib/tools/ds/component/list-ds-components.tool.ts
index d657a35..0169511 100644
--- a/packages/angular-mcp-server/src/lib/tools/ds/component/list-ds-components.tool.ts
+++ b/packages/angular-mcp-server/src/lib/tools/ds/component/list-ds-components.tool.ts
@@ -150,7 +150,6 @@ export const listDsComponentsHandler = createHandler<
});
const components: DsComponentInfo[] = [];
- const docsBasePath = resolveCrossPlatformPath(cwd, storybookDocsRoot);
const includeAll = sections.includes('all');
const includeImplementation =
@@ -172,28 +171,36 @@ export const listDsComponentsHandler = createHandler<
}
const documentationFiles: string[] = [];
- if (includeDocumentation) {
- const docPaths = getComponentDocPathsForName(
- docsBasePath,
- componentName,
+
+ let storiesFilePaths: string[] = [];
+ if (storybookDocsRoot) {
+ const docsBasePath = resolveCrossPlatformPath(
+ cwd,
+ storybookDocsRoot,
);
- if (fs.existsSync(docPaths.paths.api)) {
- documentationFiles.push(`file://${docPaths.paths.api}`);
+ if (includeDocumentation) {
+ const docPaths = getComponentDocPathsForName(
+ docsBasePath,
+ componentName,
+ );
+
+ if (fs.existsSync(docPaths.paths.api)) {
+ documentationFiles.push(`file://${docPaths.paths.api}`);
+ }
+ if (fs.existsSync(docPaths.paths.overview)) {
+ documentationFiles.push(`file://${docPaths.paths.overview}`);
+ }
}
- if (fs.existsSync(docPaths.paths.overview)) {
- documentationFiles.push(`file://${docPaths.paths.overview}`);
- }
- }
- let storiesFilePaths: string[] = [];
- if (includeStories) {
- const storiesComponentFolderPath = path.join(
- docsBasePath,
- folderName,
- );
- const storiesFiles = findStoriesFiles(storiesComponentFolderPath);
- storiesFilePaths = storiesFiles.map((file) => `file://${file}`);
+ if (includeStories) {
+ const storiesComponentFolderPath = path.join(
+ docsBasePath,
+ folderName,
+ );
+ const storiesFiles = findStoriesFiles(storiesComponentFolderPath);
+ storiesFilePaths = storiesFiles.map((file) => `file://${file}`);
+ }
}
components.push({
diff --git a/packages/angular-mcp-server/src/lib/tools/ds/project/report-deprecated-css.tool.ts b/packages/angular-mcp-server/src/lib/tools/ds/project/report-deprecated-css.tool.ts
index 5c2e4d1..5af3b82 100644
--- a/packages/angular-mcp-server/src/lib/tools/ds/project/report-deprecated-css.tool.ts
+++ b/packages/angular-mcp-server/src/lib/tools/ds/project/report-deprecated-css.tool.ts
@@ -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 --ds.deprecatedCssClassesPath in mcp.json file.',
+ );
+ }
+
const deprecated = getDeprecatedCssClasses(
componentName,
deprecatedCssClassesPath,
diff --git a/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-all-violations.tool.ts b/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-all-violations.tool.ts
index a624d56..b57a622 100644
--- a/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-all-violations.tool.ts
+++ b/packages/angular-mcp-server/src/lib/tools/ds/report-violations/report-all-violations.tool.ts
@@ -47,6 +47,11 @@ export const reportAllViolationsHandler = createHandler<
>(
reportAllViolationsSchema.name,
async (params, { cwd, deprecatedCssClassesPath }) => {
+ if (!deprecatedCssClassesPath) {
+ throw new Error(
+ 'Missing ds.deprecatedCssClassesPath. Provide --ds.deprecatedCssClassesPath in mcp.json file.',
+ );
+ }
const groupBy = params.groupBy || 'file';
const dsComponents = await loadAndValidateDsComponentsFile(
cwd,
diff --git a/packages/angular-mcp-server/src/lib/tools/ds/shared/utils/handler-helpers.ts b/packages/angular-mcp-server/src/lib/tools/ds/shared/utils/handler-helpers.ts
index 76a6e44..a446a17 100644
--- a/packages/angular-mcp-server/src/lib/tools/ds/shared/utils/handler-helpers.ts
+++ b/packages/angular-mcp-server/src/lib/tools/ds/shared/utils/handler-helpers.ts
@@ -26,8 +26,8 @@ export interface BaseHandlerOptions {
export interface HandlerContext {
cwd: string;
workspaceRoot: string;
- storybookDocsRoot: string;
- deprecatedCssClassesPath: string;
+ storybookDocsRoot?: string;
+ deprecatedCssClassesPath?: string;
uiRoot: string;
}
@@ -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 || '',
};
}
diff --git a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/base-analyzer.ts b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/base-analyzer.ts
index 302021b..11df2fa 100644
--- a/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/base-analyzer.ts
+++ b/packages/angular-mcp-server/src/lib/tools/ds/shared/violation-analysis/base-analyzer.ts
@@ -29,6 +29,12 @@ export async function analyzeViolationsBase(
process.chdir(cwd);
+ if (!deprecatedCssClassesPath) {
+ throw new Error(
+ 'Missing ds.deprecatedCssClassesPath. Provide --ds.deprecatedCssClassesPath in mcp.json file.',
+ );
+ }
+
const deprecatedCssClasses = getDeprecatedCssClasses(
componentName,
deprecatedCssClassesPath || '',
diff --git a/packages/angular-mcp-server/src/lib/validation/angular-mcp-server-options.schema.ts b/packages/angular-mcp-server/src/lib/validation/angular-mcp-server-options.schema.ts
index 4237a03..acbfd04 100644
--- a/packages/angular-mcp-server/src/lib/validation/angular-mcp-server-options.schema.ts
+++ b/packages/angular-mcp-server/src/lib/validation/angular-mcp-server-options.schema.ts
@@ -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)',
diff --git a/packages/angular-mcp-server/src/lib/validation/ds-components-file.validation.ts b/packages/angular-mcp-server/src/lib/validation/ds-components-file.validation.ts
index c8012e3..45bd857 100644
--- a/packages/angular-mcp-server/src/lib/validation/ds-components-file.validation.ts
+++ b/packages/angular-mcp-server/src/lib/validation/ds-components-file.validation.ts
@@ -6,9 +6,14 @@ import { DsComponentsArraySchema } from './ds-components.schema';
export async function validateDeprecatedCssClassesFile(
config: AngularMcpServerOptions,
): Promise {
+ 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;
diff --git a/packages/angular-mcp-server/src/lib/validation/file-existence.ts b/packages/angular-mcp-server/src/lib/validation/file-existence.ts
index f2ecdf6..6ad8253 100644
--- a/packages/angular-mcp-server/src/lib/validation/file-existence.ts
+++ b/packages/angular-mcp-server/src/lib/validation/file-existence.ts
@@ -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);
diff --git a/packages/angular-mcp/src/main.ts b/packages/angular-mcp/src/main.ts
index aeef3f1..07a9065 100644
--- a/packages/angular-mcp/src/main.ts
+++ b/packages/angular-mcp/src/main.ts
@@ -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;
};
};