Skip to content

Commit 7aa2521

Browse files
itosa-kazuitosa-kazucursoragent
authored
fix(mcp-server): point types metadata to emitted declarations (#146)
* fix mcp package declaration metadata * fix(mcp-server): guard execNpm npm_execpath with existsSync Fall back to plain npm when npm_execpath is set but the file is missing, matching spawnNpx behavior. --------- Co-authored-by: itosa-kazu <wangewnyi1000@gmail.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent 01eef56 commit 7aa2521

4 files changed

Lines changed: 71 additions & 11 deletions

File tree

mcp-server/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
"description": "Currents MCP server",
1919
"main": "./dist/api.cjs",
2020
"module": "./dist/api.mjs",
21-
"types": "./dist/api.d.ts",
21+
"types": "./dist/api.d.mts",
2222
"exports": {
2323
".": {
24-
"types": "./dist/api.d.ts",
24+
"types": "./dist/api.d.mts",
2525
"import": "./dist/api.mjs",
2626
"require": "./dist/api.cjs"
2727
}

mcp-server/src/cli-bin.integration.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,19 @@
1212
* (same artifact shape as registry install, minus release-only publish.cjs
1313
* mutations).
1414
*/
15-
import { execFileSync, spawnSync } from "node:child_process";
1615
import { existsSync, mkdtempSync, readdirSync } from "node:fs";
1716
import { tmpdir } from "node:os";
1817
import path from "node:path";
1918
import { fileURLToPath } from "node:url";
2019
import { describe, expect, it } from "vitest";
20+
import { execNpm, spawnNpx } from "../test/npm-exec.js";
2121

2222
const root = fileURLToPath(new URL("..", import.meta.url));
2323
const buildIndex = path.join(root, "dist", "index.mjs");
2424

2525
function packTarball(packDest: string): string {
2626
// Respect `files` and standard pack rules; do not mutate package.json (unlike release `publish.cjs`).
27-
execFileSync("npm", ["pack", "--ignore-scripts", "--pack-destination", packDest], {
27+
execNpm(["pack", "--ignore-scripts", "--pack-destination", packDest], {
2828
cwd: root,
2929
stdio: ["ignore", "pipe", "pipe"],
3030
});
@@ -50,7 +50,7 @@ describe.skipIf(!existsSync(buildIndex))(
5050
it("starts via npx --package tarball mcp", () => {
5151
const packDir = mkdtempSync(path.join(tmpdir(), "mcp-pack-"));
5252
const tarball = packTarball(packDir);
53-
const r = spawnSync("npx", ["-y", "--package", tarball, "mcp"], {
53+
const r = spawnNpx(["-y", "--package", tarball, "mcp"], {
5454
cwd: packDir,
5555
timeout: 45_000,
5656
encoding: "utf-8",
@@ -80,11 +80,11 @@ describe.skipIf(!existsSync(buildIndex))(
8080
const packDir = mkdtempSync(path.join(tmpdir(), "mcp-pack-"));
8181
const installDir = mkdtempSync(path.join(tmpdir(), "mcp-install-"));
8282
const tarball = packTarball(packDir);
83-
execFileSync("npm", ["init", "-y"], {
83+
execNpm(["init", "-y"], {
8484
cwd: installDir,
8585
stdio: "ignore",
8686
});
87-
execFileSync("npm", ["install", tarball], {
87+
execNpm(["install", tarball], {
8888
cwd: installDir,
8989
stdio: "ignore",
9090
});

mcp-server/src/package-published-esm.integration.test.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,26 @@
2929
* layout.
3030
* */
3131
import { execFileSync } from "node:child_process";
32-
import { copyFileSync, existsSync, mkdtempSync, readdirSync } from "node:fs";
32+
import {
33+
copyFileSync,
34+
existsSync,
35+
mkdtempSync,
36+
readFileSync,
37+
readdirSync,
38+
} from "node:fs";
3339
import { tmpdir } from "node:os";
3440
import path from "node:path";
3541
import { fileURLToPath } from "node:url";
3642
import { describe, expect, it } from "vitest";
43+
import { execNpm } from "../test/npm-exec.js";
3744

3845
const root = fileURLToPath(new URL("..", import.meta.url));
3946
const buildIndex = path.join(root, "dist", "index.mjs");
4047

4148
/** Run `npm pack` from the package root and return the path to the single `.tgz` in `packDest`. */
4249
function packTarball(packDest: string): string {
4350
// Respect `files` and standard pack rules; do not mutate package.json (unlike release `publish.cjs`).
44-
execFileSync("npm", ["pack", "--ignore-scripts", "--pack-destination", packDest], {
51+
execNpm(["pack", "--ignore-scripts", "--pack-destination", packDest], {
4552
cwd: root,
4653
stdio: ["ignore", "pipe", "pipe"],
4754
});
@@ -62,11 +69,11 @@ describe.skipIf(!existsSync(buildIndex))(
6269
path.join(tmpdir(), "mcp-install-published-")
6370
);
6471
const tarball = packTarball(packDir);
65-
execFileSync("npm", ["init", "-y"], {
72+
execNpm(["init", "-y"], {
6673
cwd: installDir,
6774
stdio: "ignore",
6875
});
69-
execFileSync("npm", ["install", tarball], {
76+
execNpm(["install", tarball], {
7077
cwd: installDir,
7178
stdio: "ignore",
7279
});
@@ -84,5 +91,32 @@ describe.skipIf(!existsSync(buildIndex))(
8491
});
8592
expect(out.trim()).toBe("published-esm-ok");
8693
});
94+
95+
it("ships package metadata that points at existing declaration files", () => {
96+
const packDir = mkdtempSync(path.join(tmpdir(), "mcp-pack-types-"));
97+
const installDir = mkdtempSync(path.join(tmpdir(), "mcp-install-types-"));
98+
const tarball = packTarball(packDir);
99+
execNpm(["init", "-y"], {
100+
cwd: installDir,
101+
stdio: "ignore",
102+
});
103+
execNpm(["install", tarball], {
104+
cwd: installDir,
105+
stdio: "ignore",
106+
});
107+
const installedRoot = path.join(
108+
installDir,
109+
"node_modules",
110+
"@currents",
111+
"mcp"
112+
);
113+
const pkg = JSON.parse(
114+
readFileSync(path.join(installedRoot, "package.json"), "utf-8")
115+
);
116+
expect(existsSync(path.join(installedRoot, pkg.types))).toBe(true);
117+
expect(existsSync(path.join(installedRoot, pkg.exports["."].types))).toBe(
118+
true
119+
);
120+
});
87121
}
88122
);

mcp-server/test/npm-exec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { execFileSync, spawnSync } from "node:child_process";
2+
import { existsSync } from "node:fs";
3+
import path from "node:path";
4+
5+
const npmCli = process.env.npm_execpath;
6+
const npxCli = npmCli ? path.join(path.dirname(npmCli), "npx-cli.js") : undefined;
7+
8+
export function execNpm(
9+
args: string[],
10+
options: Parameters<typeof execFileSync>[2]
11+
) {
12+
if (npmCli && existsSync(npmCli)) {
13+
return execFileSync(process.execPath, [npmCli, ...args], options);
14+
}
15+
return execFileSync("npm", args, options);
16+
}
17+
18+
export function spawnNpx(
19+
args: string[],
20+
options: Parameters<typeof spawnSync>[2]
21+
) {
22+
if (npxCli && existsSync(npxCli)) {
23+
return spawnSync(process.execPath, [npxCli, ...args], options);
24+
}
25+
return spawnSync("npx", args, options);
26+
}

0 commit comments

Comments
 (0)