fix(plugins): load Codex MCP server from root config
This commit is contained in:
parent
987cb3b96b
commit
45bae55787
|
|
@ -16,17 +16,7 @@
|
|||
"mcp-server"
|
||||
],
|
||||
"skills": "./skills",
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "node",
|
||||
"args": [
|
||||
"./scripts/plugin-mcp-launcher.mjs",
|
||||
"rea-agents@6.4.0"
|
||||
],
|
||||
"startup_timeout_sec": 30,
|
||||
"cwd": "."
|
||||
}
|
||||
},
|
||||
"mcpServers": "./.mcp.json",
|
||||
"interface": {
|
||||
"displayName": "REA",
|
||||
"shortDescription": "Reverse engineer anything with agents",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "node",
|
||||
"args": [
|
||||
"./scripts/plugin-mcp-launcher.mjs",
|
||||
"rea-agents@6.4.0"
|
||||
],
|
||||
"cwd": "."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -242,7 +242,9 @@ tag remains in main's ancestry. Keep the released tag unchanged.
|
|||
The synchronization must include `.release-please-manifest.json`,
|
||||
`package.json`, both root versions in `package-lock.json`, `CHANGELOG.md`,
|
||||
`server.json`, both plugin manifests (`.claude-plugin/plugin.json` and
|
||||
`.codex-plugin/plugin.json`), and the versioned documentation examples. Run
|
||||
`.codex-plugin/plugin.json`), the Codex server declaration (`.mcp.json`), both
|
||||
marketplaces (`.claude-plugin/marketplace.json` and
|
||||
`.agents/plugins/marketplace.json`), and the versioned documentation examples. Run
|
||||
`npm run docs:generate`, `npm run docs:check`, and the release configuration
|
||||
tests against the combined tree. After the merge, verify that the released
|
||||
tag is an ancestor of main:
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@
|
|||
"scripts/prepare.mjs",
|
||||
".claude-plugin/plugin.json",
|
||||
".codex-plugin/plugin.json",
|
||||
".mcp.json",
|
||||
"skills",
|
||||
"README.md",
|
||||
"DISCLAIMER.md",
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": ".codex-plugin/plugin.json",
|
||||
"path": ".mcp.json",
|
||||
"jsonpath": "$.mcpServers.rea.args[1]"
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import { spawn } from "node:child_process";
|
||||
import { mkdtemp, rm } from "node:fs/promises";
|
||||
import { access, mkdtemp, rm } from "node:fs/promises";
|
||||
import { constants, tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { packageRunnerCommand } from "./lib/packageRunnerCommand.mjs";
|
||||
|
||||
|
|
@ -26,6 +27,32 @@ if (
|
|||
}
|
||||
|
||||
async function launch(packageSpecifier) {
|
||||
// npm installs already carry the release-pinned runtime. Run it in this
|
||||
// process so the plugin keeps the caller's working directory and the
|
||||
// runtime retains ownership of provider and worker cleanup.
|
||||
const { default: bundledPackage } = await import("../package.json", {
|
||||
with: { type: "json" },
|
||||
});
|
||||
let bundledRuntimeMatchesPin =
|
||||
packageSpecifier === `${bundledPackage.name}@${bundledPackage.version}`;
|
||||
if (bundledRuntimeMatchesPin) {
|
||||
try {
|
||||
await access(new URL("../dist/mcp/main.js", import.meta.url));
|
||||
} catch (cause) {
|
||||
if (!isMissing(cause)) throw cause;
|
||||
bundledRuntimeMatchesPin = false;
|
||||
}
|
||||
}
|
||||
if (bundledRuntimeMatchesPin) {
|
||||
process.argv = [
|
||||
process.execPath,
|
||||
fileURLToPath(new URL("./rea.mjs", import.meta.url)),
|
||||
"mcp",
|
||||
];
|
||||
await import("./rea.mjs");
|
||||
return;
|
||||
}
|
||||
|
||||
const [command, ...args] = packageRunnerCommand(
|
||||
"npx",
|
||||
packageSpecifier,
|
||||
|
|
@ -93,3 +120,12 @@ async function launch(packageSpecifier) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
function isMissing(cause) {
|
||||
return (
|
||||
typeof cause === "object" &&
|
||||
cause !== null &&
|
||||
"code" in cause &&
|
||||
cause.code === "ENOENT"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,21 +71,38 @@ export async function verifyPackagePack({ root, workspace }) {
|
|||
const manifest = JSON.parse(
|
||||
(await exec("tar", ["-xOf", tarball, `package/${path}`])).stdout,
|
||||
);
|
||||
const serverConfig = path.startsWith(".codex")
|
||||
? JSON.parse(
|
||||
(
|
||||
await exec("tar", [
|
||||
"-xOf",
|
||||
tarball,
|
||||
"package/.mcp.json",
|
||||
])
|
||||
).stdout,
|
||||
)
|
||||
: manifest;
|
||||
const expectedArgs = [
|
||||
`${path.startsWith(".claude") ? "${CLAUDE_PLUGIN_ROOT}/" : "./"}scripts/plugin-mcp-launcher.mjs`,
|
||||
`${packedManifest.name}@${packedManifest.version}`,
|
||||
];
|
||||
if (
|
||||
manifest.version !== packedManifest.version ||
|
||||
manifest.skills !== "./skills" ||
|
||||
manifest.mcpServers?.rea?.command !== "node" ||
|
||||
(path.startsWith(".codex") && manifest.mcpServers?.rea?.cwd !== ".") ||
|
||||
JSON.stringify(manifest.mcpServers?.rea?.args) !==
|
||||
JSON.stringify([
|
||||
`${path.startsWith(".claude") ? "${CLAUDE_PLUGIN_ROOT}/" : "./"}scripts/plugin-mcp-launcher.mjs`,
|
||||
`${packedManifest.name}@${packedManifest.version}`,
|
||||
])
|
||||
(path.startsWith(".codex")
|
||||
? manifest.mcpServers !== "./.mcp.json" ||
|
||||
serverConfig.mcpServers?.rea?.cwd !== "."
|
||||
: false) ||
|
||||
serverConfig.mcpServers?.rea?.command !== "node" ||
|
||||
JSON.stringify(serverConfig.mcpServers?.rea?.args) !==
|
||||
JSON.stringify(expectedArgs)
|
||||
)
|
||||
throw new Error(
|
||||
`Packaged plugin ${path} does not match the exact runtime and canonical skill release`,
|
||||
);
|
||||
}
|
||||
if (!packedFiles.includes("package/.mcp.json"))
|
||||
throw new Error("Packaged Codex MCP server declaration is missing");
|
||||
for (const path of [
|
||||
"scripts/plugin-mcp-launcher.mjs",
|
||||
"scripts/lib/packageRunnerCommand.mjs",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const launcher = fileURLToPath(
|
|||
new URL("../../../scripts/plugin-mcp-launcher.mjs", import.meta.url),
|
||||
);
|
||||
|
||||
const start = (path: string, packageSpecifier = "rea-agents@6.4.0") => {
|
||||
const start = (path: string, packageSpecifier = "rea-agents@6.4.0-rc.1") => {
|
||||
const child = spawn(process.execPath, [launcher, packageSpecifier], {
|
||||
env: {
|
||||
...process.env,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ const pluginManifests = [
|
|||
".claude-plugin/plugin.json",
|
||||
".codex-plugin/plugin.json",
|
||||
] as const;
|
||||
const pluginServerConfig = ".mcp.json";
|
||||
|
||||
describe("release configuration", () => {
|
||||
it("keeps doctor and setup runtime support aligned with package engines", async () => {
|
||||
|
|
@ -54,13 +55,16 @@ describe("release configuration", () => {
|
|||
path: "server.json",
|
||||
jsonpath: "$.packages[0].version",
|
||||
},
|
||||
...pluginManifests.flatMap((path) =>
|
||||
["$.version", "$.mcpServers.rea.args[1]"].map((jsonpath) => ({
|
||||
type: "json",
|
||||
path,
|
||||
jsonpath,
|
||||
})),
|
||||
),
|
||||
...pluginManifests.map((path) => ({
|
||||
type: "json",
|
||||
path,
|
||||
jsonpath: "$.version",
|
||||
})),
|
||||
{
|
||||
type: "json",
|
||||
path: pluginServerConfig,
|
||||
jsonpath: "$.mcpServers.rea.args[1]",
|
||||
},
|
||||
...[
|
||||
".claude-plugin/marketplace.json",
|
||||
".agents/plugins/marketplace.json",
|
||||
|
|
@ -164,7 +168,11 @@ describe("plugin manifests", () => {
|
|||
files: string[];
|
||||
};
|
||||
expect(pkg.files).toEqual(
|
||||
expect.arrayContaining([...pluginManifests, "skills"]),
|
||||
expect.arrayContaining([
|
||||
...pluginManifests,
|
||||
pluginServerConfig,
|
||||
"skills",
|
||||
]),
|
||||
);
|
||||
for (const path of pluginManifests)
|
||||
expect(await readJson(path)).toMatchObject({ skills: "./skills" });
|
||||
|
|
@ -186,7 +194,7 @@ describe("plugin manifests", () => {
|
|||
});
|
||||
|
||||
it.each(pluginManifests)(
|
||||
"pins the plugin manifest %s and its server launch to the package version",
|
||||
"pins the plugin manifest %s and Codex's server config path",
|
||||
async (path) => {
|
||||
const packageJson = (await readJson("package.json")) as {
|
||||
name: string;
|
||||
|
|
@ -195,20 +203,33 @@ describe("plugin manifests", () => {
|
|||
|
||||
expect(await readJson(path)).toMatchObject({
|
||||
version: packageJson.version,
|
||||
mcpServers: {
|
||||
rea: {
|
||||
command: "node",
|
||||
args: [
|
||||
`${path.startsWith(".claude") ? "${CLAUDE_PLUGIN_ROOT}/" : "./"}scripts/plugin-mcp-launcher.mjs`,
|
||||
`${packageJson.name}@${packageJson.version}`,
|
||||
],
|
||||
...(path.startsWith(".codex") ? { cwd: "." } : {}),
|
||||
},
|
||||
},
|
||||
...(path.startsWith(".codex")
|
||||
? { mcpServers: `./${pluginServerConfig}` }
|
||||
: {}),
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it("pins the plugin server launch from the shared MCP declaration", async () => {
|
||||
const packageJson = (await readJson("package.json")) as {
|
||||
name: string;
|
||||
version: string;
|
||||
};
|
||||
|
||||
expect(await readJson(pluginServerConfig)).toMatchObject({
|
||||
mcpServers: {
|
||||
rea: {
|
||||
command: "node",
|
||||
args: [
|
||||
"./scripts/plugin-mcp-launcher.mjs",
|
||||
`${packageJson.name}@${packageJson.version}`,
|
||||
],
|
||||
cwd: ".",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it.each(pluginManifests)(
|
||||
"stores %s in the form Release Please rewrites it to",
|
||||
async (path) => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue