diff --git a/README.md b/README.md index 39c82e7..1cfa48e 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ account with trade intelligence enabled. ├── .claude-plugin/marketplace.json # Claude Code and Copilot marketplace ├── .cursor-plugin/marketplace.json # Cursor marketplace ├── openai/review.json # OpenAI directory review cases, merged into the ZIP -├── openai/listing.json # OpenAI package name, version, and category for the existing plugin +├── openai/listing.json # OpenAI package name, version, category, and MCP URL for the existing plugin ├── scripts/ │ ├── build-openai-zip.mjs # Builds the OpenAI directory ZIP │ └── validate.mjs diff --git a/openai/listing.json b/openai/listing.json index 052da8a..b5517d2 100644 --- a/openai/listing.json +++ b/openai/listing.json @@ -1,5 +1,6 @@ { "name": "app-69f5795de4a48191a35dc5c448520676", "version": "1.1.0", - "category": "Business & Operations" + "category": "Business & Operations", + "mcpUrl": "https://mcp.terminal49.com/" } diff --git a/scripts/build-openai-zip.mjs b/scripts/build-openai-zip.mjs index e778d16..cdc029c 100644 --- a/scripts/build-openai-zip.mjs +++ b/scripts/build-openai-zip.mjs @@ -33,7 +33,10 @@ await writeStaged( `${JSON.stringify({ ...manifest, name: listing.name, version: listing.version, interface: { ...manifest.interface, category: listing.category }, extensions: { "com.openai": { review, publication } } }, null, 2)}\n`, ); // The Codex-format .mcp.json in the submission guide names only the url. -const servers = Object.fromEntries(Object.entries(mcp.mcpServers).map(([name, { url }]) => [name, { url }])); +// OpenAI requires the ZIP's MCP URL to match the existing app's URL exactly. +const servers = Object.fromEntries( + Object.entries(mcp.mcpServers).map(([name, { url }]) => [name, { url: listing.mcpUrl ?? url }]), +); await writeStaged(".mcp.json", `${JSON.stringify({ mcpServers: servers }, null, 2)}\n`); await cp(join(pluginDir, "skills"), join(stage, "skills"), { recursive: true }); for (const icon of icons) await cp(join(pluginDir, icon), join(stage, icon)); @@ -69,6 +72,9 @@ for (const file of zipped) { if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(listing.name ?? "") || listing.name.length > 64) { errors.push("openai/listing.json name must be the OpenAI package name"); } +if (listing.mcpUrl !== undefined && !/^https:\/\/mcp\.terminal49\.com(\/[a-z]*)?$/.test(listing.mcpUrl)) { + errors.push("openai/listing.json mcpUrl must be a Terminal49 MCP https URL"); +} if (!listing.category) errors.push("openai/listing.json category must match the dashboard category title"); if (!/^\d+\.\d+\.\d+$/.test(listing.version ?? "")) errors.push("openai/listing.json version must be semver"); if (Object.keys(servers).length !== 1) errors.push("the ZIP must declare exactly one MCP server");