Skip to content

Commit 8b20022

Browse files
authored
fix(mcp-server): fix missing data files in published package (#8462)
1 parent f451fe6 commit 8b20022

7 files changed

Lines changed: 72 additions & 19 deletions

File tree

.github/workflows/release.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ jobs:
129129
- name: build
130130
run: yarn build
131131

132+
- name: build mcp-server data
133+
run: yarn build:mcp
134+
132135
- name: publish to npm
133136
run: |
134137
${GITHUB_WORKSPACE}/node_modules/.bin/lerna publish from-git ${{ (github.event.inputs.snapshot == 'true' && '--pre-dist-tag dev') || '' }}

.github/workflows/test.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,27 @@ on:
99
required: false
1010

1111
jobs:
12+
mcp-server:
13+
name: MCP Server
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
18+
19+
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
20+
with:
21+
node-version-file: '.nvmrc'
22+
cache: 'yarn'
23+
24+
- name: Install
25+
run: yarn install --immutable
26+
27+
- name: Generate MCP data
28+
run: yarn build:mcp
29+
30+
- name: Run MCP server tests
31+
run: yarn test:mcp:ci
32+
1233
playwright:
1334
name: Playwright
1435
runs-on: ubuntu-latest

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
"sb:prepare-cem": "node packages/cli/dist/bin/index.js resolve-cem --packageName @ui5/webcomponents --out ./.storybook/custom-element-manifests/main.json && node packages/cli/dist/bin/index.js resolve-cem --packageName @ui5/webcomponents-fiori --out ./.storybook/custom-element-manifests/fiori.json && node packages/cli/dist/bin/index.js resolve-cem --packageName @ui5/webcomponents-ai --out ./.storybook/custom-element-manifests/ai.json",
3838
"create-theming-parameters": "node scripts/generate-theming-parameters.js",
3939
"create-exports": "node --experimental-strip-types scripts/create-export-paths.ts",
40-
"build:mcp": "yarn workspace @ui5/webcomponents-react-mcp update"
40+
"build:mcp": "yarn workspace @ui5/webcomponents-react-mcp update",
41+
"test:mcp:ci": "yarn workspace @ui5/webcomponents-react-mcp test:ci"
4142
},
4243
"dependencies": {
4344
"@stackblitz/sdk": "1.11.0",

packages/mcp-server/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ This MCP server gives AI assistants direct access to UI5 Web Components for Reac
4747

4848
## Setup
4949

50-
This server is available in the [MCP Registry](https://registry.modelcontextprotocol.io/servers/io.github.UI5/webcomponents-react-mcp-server), which allows compatible clients to install it directly.
50+
This server is available in the [MCP Registry](https://registry.modelcontextprotocol.io), which allows compatible clients to install it directly.
5151

5252
### Claude Code
5353

@@ -97,14 +97,14 @@ Once configured, your AI assistant will have access to the tools. You can ask qu
9797
### Scripts
9898

9999
```bash
100-
npm run build # Build TypeScript + copy JSON assets
101-
npm run dev # Build and run locally
100+
npm run compile # Build TypeScript + copy JSON assets (fast rebuild)
102101
npm run inspector # Debug with MCP Inspector (opens web UI)
103102
npm run test # Run tests in watch mode (AVA + tsx)
103+
npm run test:ci # Run tests once (CI)
104104
npm run extract:descriptions # Regenerate component metadata from monorepo sources
105105
npm run bundle:docs # Copy documentation files from monorepo into docs/
106106
npm run fetch:skills # Fetch upstream documentation (e.g. accessibility skill)
107-
npm run update # Full pipeline: fetch:skills + extract:descriptions + bundle:docs + build
107+
npm run update # Full pipeline: fetch:skills + extract:descriptions + bundle:docs + compile
108108
npm run clean # Remove dist/, build cache, and all generated files
109109
```
110110

@@ -181,7 +181,7 @@ The MCP server is a **stdio-based** Node.js process that communicates with AI cl
181181
1. **`fetch:skills`** — Downloads upstream skill documents (e.g. accessibility) from GitHub, adapts HTML examples to React JSX, writes to `docs/`
182182
2. **`extract:descriptions`** — Uses `react-docgen-typescript` to parse component sources and Custom Elements Manifests (CEM). Outputs `descriptions.json` and `component-apis.json`. Also attaches `subTypeDocs` (markdown for complex prop types) and `docUrl` (upstream doc links) from `component-config.ts`
183183
3. **`bundle:docs`** — Copies MDX/MD documentation files from the monorepo into `docs/`. For JSON data sources (e.g. project templates), generates LLM-friendly markdown. Updates `localPath` fields in `documentation_sections.json`
184-
4. **`build`** — Compiles TypeScript, then `post-build.ts` copies JSON files from `src/` to `dist/` and makes the entry point executable
184+
4. **`compile`** — Compiles TypeScript, then `post-build.ts` copies JSON files from `src/` to `dist/` and makes the entry point executable
185185

186186
### Updating Component Data
187187

@@ -210,4 +210,4 @@ Then add the server to any project using the absolute path to the built entry po
210210
claude mcp add --scope project ui5-wcr -- node /path/to/ui5-webcomponents-react/packages/mcp-server/dist/index.js
211211
```
212212

213-
After code changes, `npm run build` is enough.
213+
After code changes, `npm run compile` is enough.

packages/mcp-server/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@
2626
"scripts": {
2727
"extract:descriptions": "tsx scripts/extract-component-descriptions.ts",
2828
"bundle:docs": "tsx scripts/bundle-docs.ts",
29-
"build": "tsc --build tsconfig.build.json && tsx scripts/post-build.ts",
3029
"fetch:skills": "tsx scripts/fetch-upstream-skills.ts",
31-
"update": "npm run fetch:skills && npm run extract:descriptions && npm run bundle:docs && npm run build",
30+
"compile": "tsc --build tsconfig.build.json && tsx scripts/post-build.ts",
31+
"update": "npm run fetch:skills && npm run extract:descriptions && npm run bundle:docs && npm run compile",
3232
"clean": "rimraf dist tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo src/tools/get_component_api/component-apis.json src/tools/list_components/descriptions.json docs/",
33-
"dev": "npm run build && node dist/index.js",
3433
"inspector": "npx @modelcontextprotocol/inspector node ./dist/index.js",
3534
"test": "ava --watch",
35+
"test:ci": "ava",
3636
"version": "tsx scripts/sync-server-version.ts && git add server.json"
3737
},
3838
"publishConfig": {

packages/mcp-server/src/tools/get_component_api/get_component_api.test.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import test from 'ava';
1+
import test, { type ExecutionContext } from 'ava';
2+
import { z } from 'zod';
23
import { getComponentApiTool } from './get_component_api.js';
34

45
const handler = getComponentApiTool.handler;
6+
const outputSchema = z.object(getComponentApiTool.outputSchema);
57

68
// eslint-disable-next-line @typescript-eslint/no-explicit-any
79
function getStructured(response: ReturnType<typeof handler>): any {
@@ -11,6 +13,15 @@ function getStructured(response: ReturnType<typeof handler>): any {
1113
return response.structuredContent;
1214
}
1315

16+
function assertMatchesOutputSchema(t: ExecutionContext, data: unknown) {
17+
const result = outputSchema.safeParse(data);
18+
if (!result.success) {
19+
t.fail(
20+
`Output schema validation failed:\n${result.error.issues.map((i) => ` ${i.path.join('.')}: ${i.message}`).join('\n')}`,
21+
);
22+
}
23+
}
24+
1425
test('handler: finds component case-insensitively from generated component-apis.json', (t) => {
1526
// component-apis.json is gitignored (generated by extract:descriptions)
1627
// Also verifies findComponent() toLowerCase logic
@@ -31,3 +42,15 @@ test('handler: not-found returns error with available components list', (t) => {
3142
t.is(result.errorType, 'not_found');
3243
t.true(result.availableComponents.length > 0);
3344
});
45+
46+
test('handler: AnalyticalTable response passes outputSchema validation', (t) => {
47+
const result = getStructured(handler({ componentName: 'AnalyticalTable' }));
48+
assertMatchesOutputSchema(t, result);
49+
t.pass();
50+
});
51+
52+
test('handler: not-found response passes outputSchema validation', (t) => {
53+
const result = getStructured(handler({ componentName: 'FakeWidget' }));
54+
assertMatchesOutputSchema(t, result);
55+
t.pass();
56+
});

packages/mcp-server/src/tools/get_component_api/get_component_api.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,13 @@ EXAMPLE INPUT: { "componentName": "Dialog" }
126126
),
127127
},
128128
outputSchema: {
129-
package: z.string().describe('NPM package name'),
130-
description: z.string().describe('Full component description'),
131-
props: z.record(z.string(), z.any()).describe('Component props with types, descriptions, and eventDetail'),
132-
methods: z.array(z.any()).describe('Imperative methods accessible via ref'),
129+
package: z.string().optional().describe('NPM package name'),
130+
description: z.string().optional().describe('Full component description'),
131+
props: z
132+
.record(z.string(), z.any())
133+
.optional()
134+
.describe('Component props with types, descriptions, and eventDetail'),
135+
methods: z.array(z.any()).optional().describe('Imperative methods accessible via ref'),
133136
cssParts: z
134137
.array(z.object({ name: z.string(), description: z.string() }))
135138
.optional()
@@ -142,10 +145,12 @@ EXAMPLE INPUT: { "componentName": "Dialog" }
142145
.string()
143146
.optional()
144147
.describe('Link to upstream documentation for complex behavioral concepts (e.g. layout algorithms)'),
145-
_meta: z.object({
146-
apiVersion: z.string(),
147-
extractedAt: z.string().optional(),
148-
}),
148+
_meta: z
149+
.object({
150+
apiVersion: z.string(),
151+
extractedAt: z.string().optional(),
152+
})
153+
.optional(),
149154
error: z.string().optional().describe('Error message if component not found'),
150155
errorType: z
151156
.enum(['not_found', 'invalid_input'])

0 commit comments

Comments
 (0)