Skip to content

Commit 51bbf8d

Browse files
committed
fix(mcp): load skills on Windows
1 parent f6e4388 commit 51bbf8d

2 files changed

Lines changed: 19 additions & 3 deletions

File tree

‎apps/mcp/src/skills.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ export function listSkills(): string[] {
1414
}
1515

1616
export function loadSkill(name: string): string {
17-
const resolved = resolve(SKILLS_DIR, `${name}.txt`);
18-
if (!resolved.startsWith(resolve(SKILLS_DIR) + "/")) {
17+
const root = resolve(SKILLS_DIR);
18+
const resolved = resolve(root, `${name}.txt`);
19+
// Compare directories rather than a "/" prefix so the check also holds
20+
// for the backslash paths that resolve() returns on Windows.
21+
if (dirname(resolved) !== root) {
1922
throw new Error(`Invalid skill name: ${name}`);
2023
}
2124
return readFileSync(resolved, "utf-8");

‎apps/mcp/tests/skills.test.ts‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, it, expect } from "vitest";
2-
import { listSkills } from "../src/skills";
2+
import { listSkills, loadSkill } from "../src/skills";
33

44
describe("listSkills", () => {
55
it("returns an array containing the bundled skills", () => {
@@ -8,3 +8,16 @@ describe("listSkills", () => {
88
expect(skills).toContain("master-agent-playbook");
99
});
1010
});
11+
12+
describe("loadSkill", () => {
13+
it("loads a bundled skill by name", () => {
14+
expect(loadSkill("master-agent-playbook").length).toBeGreaterThan(0);
15+
});
16+
17+
it.each(["../package", "nested/skill", "/etc/passwd"])(
18+
"rejects names outside the skills directory: %s",
19+
(name) => {
20+
expect(() => loadSkill(name)).toThrow("Invalid skill name");
21+
}
22+
);
23+
});

0 commit comments

Comments
 (0)