diff --git a/MANIFEST.md b/MANIFEST.md index eb8ee46..bd0a160 100644 --- a/MANIFEST.md +++ b/MANIFEST.md @@ -11,7 +11,7 @@ A basic `manifest.json` with just the required fields looks like this: ```json { - "manifest_version": "0.1", // Manifest spec version this manifest conforms to + "manifest_version": "0.2", // Manifest spec version this manifest conforms to "name": "my-extension", // Machine-readable name (used for CLI, APIs) "version": "1.0.0", // Semantic version of your extension "description": "A simple MCP extension", // Brief description of what the extension does @@ -37,7 +37,7 @@ A basic `manifest.json` with just the required fields looks like this: ```json { - "manifest_version": "0.1", + "manifest_version": "0.2", "name": "my-extension", "version": "1.0.0", "description": "A simple MCP extension", diff --git a/README.md b/README.md index 1d768f2..0675ef9 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # MCP Bundles (MCPB) > **⚠️ IMPORTANT NOTICE: This project is being renamed from DXT (Desktop Extensions) to MCPB (MCP Bundles)** -> +> > If you're looking for the DXT tools, they have been renamed to MCPB. Please update your dependencies and tooling: +> > - `dxt` CLI is now `mcpb` > - `.dxt` files are now `.mcpb` files > - `@anthropic-ai/dxt` package will be moved to `@anthropic-ai/mcpb` @@ -154,4 +155,4 @@ npm test # License -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. \ No newline at end of file +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. diff --git a/package.json b/package.json index 6864e7c..cb00b16 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@anthropic-ai/mcpb", "description": "Tools for building MCP Bundles", - "version": "1.1.0", + "version": "1.1.1", "type": "module", "main": "dist/index.js", "module": "dist/index.js", diff --git a/scripts/build-mcpb-schema.js b/scripts/build-mcpb-schema.js index b24856f..28d813c 100644 --- a/scripts/build-mcpb-schema.js +++ b/scripts/build-mcpb-schema.js @@ -1,4 +1,7 @@ -import { McpbManifestSchema, McpbSignatureInfoSchema } from "../dist/schemas.js"; +import { + McpbManifestSchema, + McpbSignatureInfoSchema, +} from "../dist/schemas.js"; import * as z from "zod/v4"; import fs from "node:fs/promises"; import path from "node:path"; diff --git a/src/cli/init.ts b/src/cli/init.ts index 4f104be..5c16885 100644 --- a/src/cli/init.ts +++ b/src/cli/init.ts @@ -2,6 +2,7 @@ import { confirm, input, select } from "@inquirer/prompts"; import { existsSync, readFileSync, writeFileSync } from "fs"; import { basename, join, resolve } from "path"; +import { CURRENT_MANIFEST_VERSION } from "../schemas.js"; import type { McpbManifest } from "../types.js"; interface PackageJson { @@ -773,7 +774,7 @@ export function buildManifest( const { keywords, license, repository } = optionalFields; return { - manifest_version: "0.1", + manifest_version: CURRENT_MANIFEST_VERSION, name, ...(displayName && displayName !== name ? { display_name: displayName } diff --git a/src/cli/pack.ts b/src/cli/pack.ts index d095e5c..4e3c670 100644 --- a/src/cli/pack.ts +++ b/src/cli/pack.ts @@ -13,7 +13,7 @@ import { basename, join, relative, resolve, sep } from "path"; import { getAllFilesWithCount, readMcpbIgnorePatterns } from "../node/files.js"; import { validateManifest } from "../node/validate.js"; -import { McpbManifestSchema } from "../schemas.js"; +import { CURRENT_MANIFEST_VERSION, McpbManifestSchema } from "../schemas.js"; import { getLogger } from "../shared/log.js"; import { initExtension } from "./init.js"; @@ -101,6 +101,17 @@ export async function packExtension({ return false; } + const manifestVersion = manifest.manifest_version || manifest.dxt_version; + if (manifestVersion !== CURRENT_MANIFEST_VERSION) { + logger.error( + `ERROR: Manifest version mismatch. Expected "${CURRENT_MANIFEST_VERSION}", found "${manifestVersion}"`, + ); + logger.error( + ` Please update the manifest_version in your manifest.json to "${CURRENT_MANIFEST_VERSION}"`, + ); + return false; + } + // Determine output path const extensionName = basename(resolvedPath); const finalOutputPath = outputPath diff --git a/src/node/validate.ts b/src/node/validate.ts index 19727ff..2f86e5e 100644 --- a/src/node/validate.ts +++ b/src/node/validate.ts @@ -25,7 +25,7 @@ export function validateManifest(inputPath: string): boolean { const result = McpbManifestSchema.safeParse(manifestData); if (result.success) { - console.log("Manifest is valid!"); + console.log("Manifest schema validation passes!"); return true; } else { console.log("ERROR: Manifest validation failed:\n"); diff --git a/src/schemas-loose.ts b/src/schemas-loose.ts index 882b1a5..e44ba2a 100644 --- a/src/schemas-loose.ts +++ b/src/schemas-loose.ts @@ -79,38 +79,39 @@ export const McpbUserConfigValuesSchema = z.record( export const McpbManifestSchema = z .object({ $schema: z.string().optional(), - dxt_version: z.string().optional().describe("@deprecated Use manifest_version instead"), + dxt_version: z + .string() + .optional() + .describe("@deprecated Use manifest_version instead"), manifest_version: z.string().optional(), - name: z.string(), - display_name: z.string().optional(), - version: z.string(), - description: z.string(), - long_description: z.string().optional(), - author: McpbManifestAuthorSchema, - repository: McpbManifestRepositorySchema.optional(), - homepage: z.string().url().optional(), - documentation: z.string().url().optional(), - support: z.string().url().optional(), - icon: z.string().optional(), - screenshots: z.array(z.string()).optional(), - server: McpbManifestServerSchema, - tools: z.array(McpbManifestToolSchema).optional(), - tools_generated: z.boolean().optional(), - prompts: z.array(McpbManifestPromptSchema).optional(), - prompts_generated: z.boolean().optional(), - keywords: z.array(z.string()).optional(), - license: z.string().optional(), - compatibility: McpbManifestCompatibilitySchema.optional(), - user_config: z - .record(z.string(), McpbUserConfigurationOptionSchema) - .optional(), + name: z.string(), + display_name: z.string().optional(), + version: z.string(), + description: z.string(), + long_description: z.string().optional(), + author: McpbManifestAuthorSchema, + repository: McpbManifestRepositorySchema.optional(), + homepage: z.string().url().optional(), + documentation: z.string().url().optional(), + support: z.string().url().optional(), + icon: z.string().optional(), + screenshots: z.array(z.string()).optional(), + server: McpbManifestServerSchema, + tools: z.array(McpbManifestToolSchema).optional(), + tools_generated: z.boolean().optional(), + prompts: z.array(McpbManifestPromptSchema).optional(), + prompts_generated: z.boolean().optional(), + keywords: z.array(z.string()).optional(), + license: z.string().optional(), + compatibility: McpbManifestCompatibilitySchema.optional(), + user_config: z + .record(z.string(), McpbUserConfigurationOptionSchema) + .optional(), }) - .refine( - (data) => !!(data.dxt_version || data.manifest_version), - { - message: "Either 'dxt_version' (deprecated) or 'manifest_version' must be provided", - } - ); + .refine((data) => !!(data.dxt_version || data.manifest_version), { + message: + "Either 'dxt_version' (deprecated) or 'manifest_version' must be provided", + }); export const McpbSignatureInfoSchema = z.object({ status: z.enum(["signed", "unsigned", "self-signed"]), diff --git a/src/schemas.ts b/src/schemas.ts index adaca4c..1f7ece2 100644 --- a/src/schemas.ts +++ b/src/schemas.ts @@ -1,5 +1,7 @@ import * as z from "zod"; +export const CURRENT_MANIFEST_VERSION = "0.2"; + export const McpServerConfigSchema = z.strictObject({ command: z.string(), args: z.array(z.string()).optional(), @@ -79,7 +81,10 @@ export const McpbUserConfigValuesSchema = z.record( export const McpbManifestSchema = z .strictObject({ $schema: z.string().optional(), - dxt_version: z.string().optional().describe("@deprecated Use manifest_version instead"), + dxt_version: z + .string() + .optional() + .describe("@deprecated Use manifest_version instead"), manifest_version: z.string().optional(), name: z.string(), display_name: z.string().optional(), @@ -106,12 +111,10 @@ export const McpbManifestSchema = z .record(z.string(), McpbUserConfigurationOptionSchema) .optional(), }) - .refine( - (data) => !!(data.dxt_version || data.manifest_version), - { - message: "Either 'dxt_version' (deprecated) or 'manifest_version' must be provided", - } - ); + .refine((data) => !!(data.dxt_version || data.manifest_version), { + message: + "Either 'dxt_version' (deprecated) or 'manifest_version' must be provided", + }); export const McpbSignatureInfoSchema = z.strictObject({ status: z.enum(["signed", "unsigned", "self-signed"]), diff --git a/test/cli.test.ts b/test/cli.test.ts index d250b5c..dd18635 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -2,6 +2,8 @@ import { execSync } from "node:child_process"; import fs from "node:fs"; import { join } from "node:path"; +import { CURRENT_MANIFEST_VERSION } from "../src/schemas.js"; + interface ExecSyncError extends Error { stdout: Buffer; stderr: Buffer; @@ -23,7 +25,7 @@ describe("DXT CLI", () => { const result = execSync(`node ${cliPath} validate ${validManifestPath}`, { encoding: "utf-8", }); - expect(result).toContain("Manifest is valid!"); + expect(result).toContain("Manifest schema validation passes!"); }); it("should reject an invalid manifest", () => { @@ -91,7 +93,7 @@ describe("DXT CLI", () => { fs.writeFileSync( join(tempDir, "manifest.json"), JSON.stringify({ - manifest_version: "1.0", + manifest_version: CURRENT_MANIFEST_VERSION, name: "Test Extension", version: "1.0.0", description: "A test extension", @@ -188,7 +190,7 @@ describe("DXT CLI", () => { fs.writeFileSync( join(tempExecDir, "manifest.json"), JSON.stringify({ - manifest_version: "1.0", + manifest_version: CURRENT_MANIFEST_VERSION, name: "Test Executable Extension", version: "1.0.0", description: "A test extension with executable files", diff --git a/test/init.test.ts b/test/init.test.ts index 68730ce..f79191e 100644 --- a/test/init.test.ts +++ b/test/init.test.ts @@ -10,6 +10,7 @@ import { getDefaultRepositoryUrl, readPackageJson, } from "../src/cli/init.js"; +import { CURRENT_MANIFEST_VERSION } from "../src/schemas.js"; // Mock the fs module jest.mock("fs", () => ({ @@ -217,7 +218,7 @@ describe("init functions", () => { ); expect(manifest).toEqual({ - manifest_version: "0.1", + manifest_version: CURRENT_MANIFEST_VERSION, name: "test-extension", version: "1.0.0", description: "Test description", @@ -303,7 +304,7 @@ describe("init functions", () => { ); expect(manifest).toEqual({ - manifest_version: "0.1", + manifest_version: CURRENT_MANIFEST_VERSION, name: "test-extension", display_name: "Test Extension", version: "1.0.0",