From 7997e727584ffe6e369235e62e5b7e42999378ca Mon Sep 17 00:00:00 2001 From: Joan Xie Date: Wed, 27 Aug 2025 10:38:57 -0700 Subject: [PATCH] feat: replace manifest_version with $schema field - Replace manifest_version with $schema field pointing to official schema URL - Support backward compatibility with legacy dxt_version field - Update all examples and tests to use new $schema field - Mark dxt_version as deprecated but still functional - Require either $schema or dxt_version (at least one must be present) --- MANIFEST.md | 8 +-- examples/chrome-applescript/manifest.json | 3 +- examples/file-manager-python/manifest.json | 3 +- examples/file-system-node/manifest.json | 2 +- examples/hello-world-node/manifest.json | 3 +- src/cli/init.ts | 2 +- src/schemas-loose.ts | 7 +-- src/schemas.ts | 7 +-- test/cli.test.ts | 4 +- test/config.test.ts | 4 +- test/init.test.ts | 4 +- test/invalid-manifest.json | 2 +- test/schemas.test.ts | 73 +++++++++++++++++++++- test/valid-manifest.json | 2 +- 14 files changed, 94 insertions(+), 30 deletions(-) diff --git a/MANIFEST.md b/MANIFEST.md index 08d88fd..195a9d1 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 + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", // JSON Schema URL for validation "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", + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", "name": "my-extension", "version": "1.0.0", "description": "A simple MCP extension", @@ -71,7 +71,7 @@ A full `manifest.json` with most of the optional fields looks like this: ```json { - "manifest_version": "0.1", + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", "name": "My MCP Extension", "display_name": "My Awesome MCP Extension", "version": "1.0.0", @@ -162,7 +162,7 @@ A full `manifest.json` with most of the optional fields looks like this: ### Required Fields -- **manifest_version**: Specification version this extension conforms to +- **$schema**: JSON Schema URL for validation (defaults to https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json) - **name**: Machine-readable name (used for CLI, APIs) - **version**: Semantic version (semver) - **description**: Brief description diff --git a/examples/chrome-applescript/manifest.json b/examples/chrome-applescript/manifest.json index cb9261e..eb54ca4 100644 --- a/examples/chrome-applescript/manifest.json +++ b/examples/chrome-applescript/manifest.json @@ -1,6 +1,5 @@ { - "$schema": "../../dist/mcpb-manifest.schema.json", - "manifest_version": "0.1", + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", "name": "chrome-applescript", "display_name": "Control Chrome with AppleScript", "version": "0.1.0", diff --git a/examples/file-manager-python/manifest.json b/examples/file-manager-python/manifest.json index 89546f3..8a8f4d6 100644 --- a/examples/file-manager-python/manifest.json +++ b/examples/file-manager-python/manifest.json @@ -1,6 +1,5 @@ { - "$schema": "../../dist/mcpb-manifest.schema.json", - "manifest_version": "0.1", + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", "name": "file-manager-python", "display_name": "Python File Manager MCP", "version": "0.1.0", diff --git a/examples/file-system-node/manifest.json b/examples/file-system-node/manifest.json index 890ecdb..0076dc5 100644 --- a/examples/file-system-node/manifest.json +++ b/examples/file-system-node/manifest.json @@ -1,5 +1,5 @@ { - "manifest_version": "0.1", + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", "id": "anthropic.demo.filesystem", "name": "Filesystem", "display_name": "Filesystem", diff --git a/examples/hello-world-node/manifest.json b/examples/hello-world-node/manifest.json index 30837eb..ccfa440 100644 --- a/examples/hello-world-node/manifest.json +++ b/examples/hello-world-node/manifest.json @@ -1,6 +1,5 @@ { - "$schema": "../../dist/mcpb-manifest.schema.json", - "manifest_version": "0.1", + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", "name": "hello-world-node", "display_name": "Hello World MCP Server (Reference Extension)", "version": "0.1.0", diff --git a/src/cli/init.ts b/src/cli/init.ts index 4f104be..91b97d8 100644 --- a/src/cli/init.ts +++ b/src/cli/init.ts @@ -773,7 +773,7 @@ export function buildManifest( const { keywords, license, repository } = optionalFields; return { - manifest_version: "0.1", + $schema: "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", name, ...(displayName && displayName !== name ? { display_name: displayName } diff --git a/src/schemas-loose.ts b/src/schemas-loose.ts index 882b1a5..37d2c3a 100644 --- a/src/schemas-loose.ts +++ b/src/schemas-loose.ts @@ -79,8 +79,7 @@ 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"), - manifest_version: z.string().optional(), + dxt_version: z.string().optional().describe("@deprecated - Use $schema instead"), name: z.string(), display_name: z.string().optional(), version: z.string(), @@ -106,9 +105,9 @@ export const McpbManifestSchema = z .optional(), }) .refine( - (data) => !!(data.dxt_version || data.manifest_version), + (data) => !!(data.$schema || data.dxt_version), { - message: "Either 'dxt_version' (deprecated) or 'manifest_version' must be provided", + message: "Either '$schema' or 'dxt_version' (deprecated) must be provided", } ); diff --git a/src/schemas.ts b/src/schemas.ts index 2b59709..7998d8e 100644 --- a/src/schemas.ts +++ b/src/schemas.ts @@ -79,8 +79,7 @@ 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"), - manifest_version: z.string().optional(), + dxt_version: z.string().optional().describe("@deprecated - Use $schema instead"), name: z.string(), display_name: z.string().optional(), version: z.string(), @@ -106,9 +105,9 @@ export const McpbManifestSchema = z .optional(), }) .refine( - (data) => !!(data.dxt_version || data.manifest_version), + (data) => !!(data.$schema || data.dxt_version), { - message: "Either 'dxt_version' (deprecated) or 'manifest_version' must be provided", + message: "Either '$schema' or 'dxt_version' (deprecated) must be provided", } ); diff --git a/test/cli.test.ts b/test/cli.test.ts index d250b5c..c11bdfc 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -91,7 +91,7 @@ describe("DXT CLI", () => { fs.writeFileSync( join(tempDir, "manifest.json"), JSON.stringify({ - manifest_version: "1.0", + $schema: "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", name: "Test Extension", version: "1.0.0", description: "A test extension", @@ -188,7 +188,7 @@ describe("DXT CLI", () => { fs.writeFileSync( join(tempExecDir, "manifest.json"), JSON.stringify({ - manifest_version: "1.0", + $schema: "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", name: "Test Executable Extension", version: "1.0.0", description: "A test extension with executable files", diff --git a/test/config.test.ts b/test/config.test.ts index 6461c5a..0b1c382 100644 --- a/test/config.test.ts +++ b/test/config.test.ts @@ -90,7 +90,7 @@ describe("getMcpConfigForManifest", () => { }; const baseManifest: McpbManifest = { - manifest_version: "1.0.0", + $schema: "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", name: "test-extension", version: "1.0.0", description: "Test extension", @@ -305,7 +305,7 @@ describe("getMcpConfigForManifest", () => { describe("hasRequiredConfigMissing", () => { const baseManifest: McpbManifest = { - manifest_version: "1.0.0", + $schema: "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", name: "test-extension", version: "1.0.0", description: "Test extension", diff --git a/test/init.test.ts b/test/init.test.ts index 68730ce..5685580 100644 --- a/test/init.test.ts +++ b/test/init.test.ts @@ -217,7 +217,7 @@ describe("init functions", () => { ); expect(manifest).toEqual({ - manifest_version: "0.1", + $schema: "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", name: "test-extension", version: "1.0.0", description: "Test description", @@ -303,7 +303,7 @@ describe("init functions", () => { ); expect(manifest).toEqual({ - manifest_version: "0.1", + $schema: "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", name: "test-extension", display_name: "Test Extension", version: "1.0.0", diff --git a/test/invalid-manifest.json b/test/invalid-manifest.json index ccfcd3a..729746d 100644 --- a/test/invalid-manifest.json +++ b/test/invalid-manifest.json @@ -1,5 +1,5 @@ { - "manifest_version": "1.0", + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", "name": "test-extension", "version": "1.0.0", "description": "A test DXT extension", diff --git a/test/schemas.test.ts b/test/schemas.test.ts index a9297d8..3b8f237 100644 --- a/test/schemas.test.ts +++ b/test/schemas.test.ts @@ -37,7 +37,7 @@ describe("McpbManifestSchema", () => { it("should validate manifest with all optional fields", () => { const fullManifest = { - manifest_version: "1.0", + $schema: "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", name: "full-extension", display_name: "Full Featured Extension", version: "2.0.0", @@ -117,7 +117,7 @@ describe("McpbManifestSchema", () => { serverTypes.forEach((type) => { const manifest = { - manifest_version: "1.0", + $schema: "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", name: "test", version: "1.0.0", description: "Test", @@ -136,4 +136,73 @@ describe("McpbManifestSchema", () => { expect(result.success).toBe(true); }); }); + + describe("backward compatibility", () => { + it("should accept manifest with dxt_version instead of $schema", () => { + const legacyManifest = { + dxt_version: "0.1", + name: "legacy-extension", + version: "1.0.0", + description: "Legacy extension", + author: { name: "Test Author" }, + server: { + type: "node", + entry_point: "server.js", + mcp_config: { + command: "node", + args: ["server.js"], + }, + }, + }; + + const result = McpbManifestSchema.safeParse(legacyManifest); + expect(result.success).toBe(true); + }); + + it("should accept manifest with $schema", () => { + const modernManifest = { + $schema: "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", + name: "modern-extension", + version: "1.0.0", + description: "Modern extension", + author: { name: "Test Author" }, + server: { + type: "node", + entry_point: "server.js", + mcp_config: { + command: "node", + args: ["server.js"], + }, + }, + }; + + const result = McpbManifestSchema.safeParse(modernManifest); + expect(result.success).toBe(true); + }); + + it("should reject manifest without $schema or dxt_version", () => { + const invalidManifest = { + name: "invalid-extension", + version: "1.0.0", + description: "Invalid extension", + author: { name: "Test Author" }, + server: { + type: "node", + entry_point: "server.js", + mcp_config: { + command: "node", + args: ["server.js"], + }, + }, + }; + + const result = McpbManifestSchema.safeParse(invalidManifest); + expect(result.success).toBe(false); + if (!result.success) { + expect(result.error.issues[0].message).toContain( + "Either '$schema' or 'dxt_version' (deprecated) must be provided" + ); + } + }); + }); }); diff --git a/test/valid-manifest.json b/test/valid-manifest.json index a91e78f..40a1d92 100644 --- a/test/valid-manifest.json +++ b/test/valid-manifest.json @@ -1,5 +1,5 @@ { - "manifest_version": "1.0", + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-08-26/mcpb.manifest.schema.json", "name": "test-extension", "display_name": "Test Extension", "version": "1.0.0",