Skip to content

Commit 4e876be

Browse files
authored
fix(mcp): copy resource data files to build output directory (#2777)
# Fix: MCP server resource files missing from VSIX ## The Problem The MCP server has 4 data files that tools need at runtime: - `agents.md` — best practices guidelines used by `ansible_content_best_practices` - `ee-rules.md` — execution environment rules used by `define_and_build_execution_env` - `execution-environment-schema.json` — JSON schema for EE validation - `execution-environment-sample.yml` — sample EE config These files live at `packages/ansible-mcp-server/src/resources/data/`. **How the code finds them:** When a tool like `define_and_build_execution_env` runs, it calls `resolveResourcePath("ee-rules.md", import.meta.url)` in `src/utils/resourcePath.ts`. This function figures out which directory the running code lives in (using `__dirname` for CJS or `import.meta.url` for ESM), then appends `/data/ee-rules.md` to that directory. **What happens in the packaged extension:** The build tool (tsup) bundles all TypeScript into `dist/cli.cjs`. When this bundled file runs, `__dirname` resolves to `dist/`. So the code looks for `dist/data/ee-rules.md`. **But `dist/data/` didn't exist.** tsup only compiles `.ts` files — it has no built-in mechanism to copy non-code assets like `.md` or `.json` files. The data files stayed in `src/resources/data/` and were never copied to the build output. The `.vscodeignore` had a line `!packages/ansible-mcp-server/src/resources/data/**/*` which did ship the source data files inside the VSIX — but it didn't matter because the runtime code never looks in `src/resources/data/`. It only looks relative to where the compiled code runs from (`dist/`). ## The Fix **1. `packages/ansible-mcp-server/tsup.config.ts`** — Added an `onSuccess` callback that runs after each tsup build and copies the data files to the right location: - Production build (`dist/`): copies to `dist/data/` — because the bundled `cli.cjs` has `__dirname = dist/` - Dev build (`lib/`): copies to `lib/resources/data/` — because non-bundled files like `lib/resources/eeSchema.js` have `__dirname = lib/resources/` The `onSuccess` hook runs after tsup finishes (and after `clean: true` wipes the output dir), so the copied files survive in the final output. **2. `.vscodeignore`** — Removed the `!packages/ansible-mcp-server/src/resources/data/**/*` line. Since data files are now properly in `dist/data/`, the existing `!packages/ansible-mcp-server/dist/**/*` line already includes them. This avoids shipping duplicate copies of the data files in the VSIX. **3. `packages/ansible-mcp-server/package.json`** — Removed `"./src/resources/data/**/*"` from the `files` array. Same reasoning — `"dist"` and `"lib"` already cover the data files for npm publishing now. --------- Signed-off-by: shatakshiiii <shatakshimishra01@gmail.com>
1 parent c3b9b8f commit 4e876be

4 files changed

Lines changed: 59 additions & 16 deletions

File tree

.vscodeignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
!packages/ansible-language-server/dist/cli.cjs
2626
!packages/ansible-mcp-server/dist/**/*
2727
!packages/ansible-mcp-server/package.json
28-
!packages/ansible-mcp-server/src/resources/data/**/*
2928
!package.json
3029
!package.nls.json
3130
!syntaxes/**/*

packages/ansible-mcp-server/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@
2323
},
2424
"files": [
2525
"lib",
26-
"dist",
27-
"./src/resources/data/**/*"
26+
"dist"
2827
],
2928
"keywords": [
3029
"mcp",

packages/ansible-mcp-server/tsup.config.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import type { Options } from "tsup";
2+
import { cpSync } from "node:fs";
23
import { builtinModules } from "node:module";
34

45
const env = process.env.NODE_ENV;
6+
const outDir = env === "production" ? "dist" : "lib";
57

68
export const tsup: Options = {
79
clean: true,
@@ -15,10 +17,15 @@ export const tsup: Options = {
1517
bundle: env === "production",
1618
entry: ["src/**/*.ts"],
1719
format: ["esm", "cjs"],
18-
outDir: env === "production" ? "dist" : "lib",
20+
outDir,
1921
splitting: false,
2022
watch: env === "development",
2123
skipNodeModulesBundle: false,
2224
noExternal: [/./],
2325
external: [...builtinModules],
26+
async onSuccess() {
27+
const dataTarget =
28+
env === "production" ? `${outDir}/data` : `${outDir}/resources/data`;
29+
cpSync("src/resources/data", dataTarget, { recursive: true });
30+
},
2431
};

test/unit/mcp/mcpPackaging.test.ts

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ import * as fs from "node:fs";
33
import * as path from "node:path";
44

55
const projectRoot = path.resolve(__dirname, "..", "..", "..");
6+
const mcpPackageDir = path.join(projectRoot, "packages", "ansible-mcp-server");
7+
8+
const resourceDataFiles = [
9+
"agents.md",
10+
"ee-rules.md",
11+
"execution-environment-schema.json",
12+
"execution-environment-sample.yml",
13+
];
614

715
describe("MCP server packaging", function () {
816
describe(".vscodeignore includes MCP server files", function () {
@@ -20,21 +28,9 @@ describe("MCP server packaging", function () {
2028
"!packages/ansible-mcp-server/package.json",
2129
);
2230
});
23-
24-
it("should whitelist MCP server resource data files", function () {
25-
expect(vscodeignore).toContain(
26-
"!packages/ansible-mcp-server/src/resources/data/**/*",
27-
);
28-
});
2931
});
3032

3133
describe("MCP server package structure", function () {
32-
const mcpPackageDir = path.join(
33-
projectRoot,
34-
"packages",
35-
"ansible-mcp-server",
36-
);
37-
3834
it("should have package.json with correct main entry", function () {
3935
const pkg = JSON.parse(
4036
fs.readFileSync(path.join(mcpPackageDir, "package.json"), "utf8"),
@@ -54,4 +50,46 @@ describe("MCP server packaging", function () {
5450
expect(fs.statSync(cliPath).isFile()).toBe(true);
5551
});
5652
});
53+
54+
describe("resource data files are present in build output", function () {
55+
const distDataDir = path.join(mcpPackageDir, "dist", "data");
56+
const sourceDataDir = path.join(mcpPackageDir, "src", "resources", "data");
57+
58+
it("should have source resource data files", function () {
59+
for (const file of resourceDataFiles) {
60+
expect(
61+
fs.existsSync(path.join(sourceDataDir, file)),
62+
`source file missing: src/resources/data/${file}`,
63+
).toBe(true);
64+
}
65+
});
66+
67+
it("should have resource data files copied to dist/data/ after build", function () {
68+
if (!fs.existsSync(path.join(mcpPackageDir, "dist", "cli.js"))) {
69+
console.warn(
70+
"MCP server not built yet (dist/cli.js missing) - run 'pnpm build' first",
71+
);
72+
return;
73+
}
74+
for (const file of resourceDataFiles) {
75+
expect(
76+
fs.existsSync(path.join(distDataDir, file)),
77+
`dist/data/${file} missing — tsup onSuccess copy may have failed`,
78+
).toBe(true);
79+
}
80+
});
81+
82+
it("should have non-empty resource data files in dist/data/", function () {
83+
if (!fs.existsSync(distDataDir)) {
84+
console.warn("dist/data/ not found - run 'pnpm build' first");
85+
return;
86+
}
87+
for (const file of resourceDataFiles) {
88+
const filePath = path.join(distDataDir, file);
89+
if (!fs.existsSync(filePath)) continue;
90+
const stat = fs.statSync(filePath);
91+
expect(stat.size, `dist/data/${file} is empty`).toBeGreaterThan(0);
92+
}
93+
});
94+
});
5795
});

0 commit comments

Comments
 (0)