diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cf9d8e8..7b19f2b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,6 +46,7 @@ To set up commit signing, see [GitHub's documentation on commit signature verifi ### Bug Reports When filing a bug report, please include: + - Clear description of the issue - Steps to reproduce - Expected vs actual behavior @@ -55,6 +56,7 @@ When filing a bug report, please include: ### Feature Requests We welcome feature suggestions! Please: + - Clearly describe the feature and use case - Explain why this would be valuable to users - Consider if it aligns with the project's goals @@ -62,6 +64,7 @@ We welcome feature suggestions! Please: ### Code Contributions We especially welcome: + - Bug fixes - Documentation improvements - Test coverage improvements diff --git a/MANIFEST.md b/MANIFEST.md index 837cee8..bedc65e 100644 --- a/MANIFEST.md +++ b/MANIFEST.md @@ -17,7 +17,7 @@ A basic `manifest.json` with just the required fields looks like this: "description": "A simple MCP extension", // Brief description of what the extension does "author": { // Author information (required) - "name": "Extension Author" // Author's name (required field) + "name": "Extension Author", // Author's name (required field) }, "server": { // Server configuration (required) @@ -28,10 +28,10 @@ A basic `manifest.json` with just the required fields looks like this: "command": "node", // Command to run the server "args": [ // Arguments passed to the command - "${__dirname}/server/index.js" // ${__dirname} is replaced with the extension's directory - ] - } - } + "${__dirname}/server/index.js", // ${__dirname} is replaced with the extension's directory + ], + }, + }, } ``` @@ -212,12 +212,12 @@ A full `manifest.json` with most of the optional fields looks like this: "type": "string" } } - }, + } } } ] } - }, + } } } } @@ -254,7 +254,7 @@ A full `manifest.json` with most of the optional fields looks like this: - **privacy_policies**: Array of URLs to privacy policies for external services that handle user data. Required when the extension connects to external services (first- or third-party) that process user data. Each URL should link to the respective service's privacy policy document. - **compatibility**: Compatibility requirements (client app version, platforms, and runtime versions). - **user_config**: User-configurable options for the extension (see User Configuration section). -- **_meta**: Platform-specific client integration metadata (e.g., Windows `package_family_name`, macOS bundle identifiers) enabling tighter OS/app store integration. The keys in the `_meta` object are reverse-DNS namespaced, and the values are a dictionary of platform-specific metadata. +- **\_meta**: Platform-specific client integration metadata (e.g., Windows `package_family_name`, macOS bundle identifiers) enabling tighter OS/app store integration. The keys in the `_meta` object are reverse-DNS namespaced, and the values are a dictionary of platform-specific metadata. - **localization**: Location of translated strings for user-facing fields (`resources` path containing a `${locale}` placeholder and `default_locale`). ### Localization @@ -268,15 +268,16 @@ Provide localized strings without bloating the manifest by pointing to external } ``` -- `resources` must include a `${locale}` placeholder. Clients resolve it relative to the server install directory. +- `resources` must include a `${locale}` placeholder. Clients resolve it relative to the server install directory. - This property is optional, and its default value is **`mcpb-resources/${locale}.json`**. - `default_locale` must be a valid [BCP 47](https://www.rfc-editor.org/rfc/bcp/bcp47.txt) identifier such as `en-US` or `zh-Hans`. - This property is optional, and its default value is `en-US`. - Values for the default locale stay in the main manifest; localized files only need to contain overrides. -For tools and prompts, the descriptions are also localizable. +For tools and prompts, the descriptions are also localizable. #### Client guidelines + - if a client wants to show tool or prompt descriptions in their UI, the client should look for the locale-specific description override in the corresponding per-locale file. - clients should only look for tools/prompts present in the manifest.json, i.e. prompts and tools that only exist in the per-locale file should be ignored. - Clients should apply locale fallbacks if the client/user locale is not represented by the server. For example, if the user is in the `es-UY` locale but the server does not include that per-locale file, the client should look for an approrpiate fallback, e.g. `es-MX` or `es-ES`, or fall back to the values in the manifest. @@ -712,4 +713,3 @@ The `_generated` fields: - **prompts_generated**: Server generates additional prompts beyond those listed (default: false) This helps implementing apps understand that querying the server at runtime will reveal more capabilities than what's declared in the manifest. - diff --git a/package.json b/package.json index 3a01cce..cec3546 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@anthropic-ai/mcpb", "description": "Tools for building MCP Bundles", - "version": "1.1.2", + "version": "1.1.3", "type": "module", "main": "dist/index.js", "module": "dist/index.js", @@ -27,7 +27,25 @@ "import": "./dist/cli.js", "require": "./dist/cli.js" }, - "./mcpb-manifest.schema.json": "./dist/mcpb-manifest.schema.json" + "./mcpb-manifest-v0.1.schema.json": "./dist/mcpb-manifest-v0.1.schema.json", + "./mcpb-manifest-v0.2.schema.json": "./dist/mcpb-manifest-v0.2.schema.json", + "./mcpb-manifest-v0.3.schema.json": "./dist/mcpb-manifest-v0.3.schema.json", + "./mcpb-manifest-latest.schema.json": "./dist/mcpb-manifest-latest.schema.json", + "./schemas": { + "types": "./dist/schemas/index.d.ts", + "import": "./dist/schemas/index.js", + "require": "./dist/schemas/index.js" + }, + "./schemas/*": { + "types": "./dist/schemas/*.d.ts", + "import": "./dist/schemas/*.js", + "require": "./dist/schemas/*.js" + }, + "./schemas-loose": { + "types": "./dist/schemas-loose.d.ts", + "import": "./dist/schemas-loose.js", + "require": "./dist/schemas-loose.js" + } }, "bin": "dist/cli/cli.js", "files": [ @@ -81,4 +99,4 @@ "@babel/parser": "7.27.3" }, "packageManager": "yarn@4.10.3+sha512.c38cafb5c7bb273f3926d04e55e1d8c9dfa7d9c3ea1f36a4868fa028b9e5f72298f0b7f401ad5eb921749eb012eb1c3bb74bf7503df3ee43fd600d14a018266f" -} \ No newline at end of file +} diff --git a/schemas/mcpb-manifest-latest.schema.json b/schemas/mcpb-manifest-latest.schema.json new file mode 100644 index 0000000..a2dc082 --- /dev/null +++ b/schemas/mcpb-manifest-latest.schema.json @@ -0,0 +1,376 @@ +{ + "type": "object", + "properties": { + "$schema": { + "type": "string" + }, + "dxt_version": { + "type": "string", + "const": "0.3", + "description": "@deprecated Use manifest_version instead" + }, + "manifest_version": { + "type": "string", + "const": "0.3" + }, + "name": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "long_description": { + "type": "string" + }, + "author": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + }, + "repository": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "type", + "url" + ], + "additionalProperties": false + }, + "homepage": { + "type": "string", + "format": "uri" + }, + "documentation": { + "type": "string", + "format": "uri" + }, + "support": { + "type": "string", + "format": "uri" + }, + "icon": { + "type": "string" + }, + "icons": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "type": "string" + }, + "sizes": { + "type": "string", + "pattern": "^\\d+x\\d+$" + }, + "theme": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "src", + "sizes" + ], + "additionalProperties": false + } + }, + "screenshots": { + "type": "array", + "items": { + "type": "string" + } + }, + "localization": { + "type": "object", + "properties": { + "resources": { + "type": "string", + "pattern": "\\$\\{locale\\}" + }, + "default_locale": { + "type": "string", + "pattern": "^[A-Za-z0-9]{2,8}(?:-[A-Za-z0-9]{1,8})*$" + } + }, + "required": [ + "resources", + "default_locale" + ], + "additionalProperties": false + }, + "server": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "python", + "node", + "binary" + ] + }, + "entry_point": { + "type": "string" + }, + "mcp_config": { + "type": "object", + "properties": { + "command": { + "type": "string" + }, + "args": { + "type": "array", + "items": { + "type": "string" + } + }, + "env": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "platform_overrides": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "command": { + "$ref": "#/properties/server/properties/mcp_config/properties/command" + }, + "args": { + "$ref": "#/properties/server/properties/mcp_config/properties/args" + }, + "env": { + "$ref": "#/properties/server/properties/mcp_config/properties/env" + } + }, + "additionalProperties": false + } + } + }, + "required": [ + "command" + ], + "additionalProperties": false + } + }, + "required": [ + "type", + "entry_point", + "mcp_config" + ], + "additionalProperties": false + }, + "tools": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "tools_generated": { + "type": "boolean" + }, + "prompts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "arguments": { + "type": "array", + "items": { + "type": "string" + } + }, + "text": { + "type": "string" + } + }, + "required": [ + "name", + "text" + ], + "additionalProperties": false + } + }, + "prompts_generated": { + "type": "boolean" + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + } + }, + "license": { + "type": "string" + }, + "privacy_policies": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + } + }, + "compatibility": { + "type": "object", + "properties": { + "claude_desktop": { + "type": "string" + }, + "platforms": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "darwin", + "win32", + "linux" + ] + } + }, + "runtimes": { + "type": "object", + "properties": { + "python": { + "type": "string" + }, + "node": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "user_config": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "directory", + "file" + ] + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "default": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "multiple": { + "type": "boolean" + }, + "sensitive": { + "type": "boolean" + }, + "min": { + "type": "number" + }, + "max": { + "type": "number" + } + }, + "required": [ + "type", + "title", + "description" + ], + "additionalProperties": false + } + }, + "_meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": {} + } + } + }, + "required": [ + "name", + "version", + "description", + "author", + "server" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/schemas/mcpb-manifest-v0.1.schema.json b/schemas/mcpb-manifest-v0.1.schema.json new file mode 100644 index 0000000..17e6fd8 --- /dev/null +++ b/schemas/mcpb-manifest-v0.1.schema.json @@ -0,0 +1,327 @@ +{ + "type": "object", + "properties": { + "$schema": { + "type": "string" + }, + "dxt_version": { + "type": "string", + "const": "0.1", + "description": "@deprecated Use manifest_version instead" + }, + "manifest_version": { + "type": "string", + "const": "0.1" + }, + "name": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "long_description": { + "type": "string" + }, + "author": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + }, + "repository": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "type", + "url" + ], + "additionalProperties": false + }, + "homepage": { + "type": "string", + "format": "uri" + }, + "documentation": { + "type": "string", + "format": "uri" + }, + "support": { + "type": "string", + "format": "uri" + }, + "icon": { + "type": "string" + }, + "screenshots": { + "type": "array", + "items": { + "type": "string" + } + }, + "server": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "python", + "node", + "binary" + ] + }, + "entry_point": { + "type": "string" + }, + "mcp_config": { + "type": "object", + "properties": { + "command": { + "type": "string" + }, + "args": { + "type": "array", + "items": { + "type": "string" + } + }, + "env": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "platform_overrides": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "command": { + "$ref": "#/properties/server/properties/mcp_config/properties/command" + }, + "args": { + "$ref": "#/properties/server/properties/mcp_config/properties/args" + }, + "env": { + "$ref": "#/properties/server/properties/mcp_config/properties/env" + } + }, + "additionalProperties": false + } + } + }, + "required": [ + "command" + ], + "additionalProperties": false + } + }, + "required": [ + "type", + "entry_point", + "mcp_config" + ], + "additionalProperties": false + }, + "tools": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "tools_generated": { + "type": "boolean" + }, + "prompts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "arguments": { + "type": "array", + "items": { + "type": "string" + } + }, + "text": { + "type": "string" + } + }, + "required": [ + "name", + "text" + ], + "additionalProperties": false + } + }, + "prompts_generated": { + "type": "boolean" + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + } + }, + "license": { + "type": "string" + }, + "compatibility": { + "type": "object", + "properties": { + "claude_desktop": { + "type": "string" + }, + "platforms": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "darwin", + "win32", + "linux" + ] + } + }, + "runtimes": { + "type": "object", + "properties": { + "python": { + "type": "string" + }, + "node": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "user_config": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "directory", + "file" + ] + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "default": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "multiple": { + "type": "boolean" + }, + "sensitive": { + "type": "boolean" + }, + "min": { + "type": "number" + }, + "max": { + "type": "number" + } + }, + "required": [ + "type", + "title", + "description" + ], + "additionalProperties": false + } + }, + "_meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": {} + } + } + }, + "required": [ + "name", + "version", + "description", + "author", + "server" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/schemas/mcpb-manifest.schema.json b/schemas/mcpb-manifest-v0.2.schema.json similarity index 100% rename from schemas/mcpb-manifest.schema.json rename to schemas/mcpb-manifest-v0.2.schema.json diff --git a/schemas/mcpb-manifest-v0.3.schema.json b/schemas/mcpb-manifest-v0.3.schema.json new file mode 100644 index 0000000..a2dc082 --- /dev/null +++ b/schemas/mcpb-manifest-v0.3.schema.json @@ -0,0 +1,376 @@ +{ + "type": "object", + "properties": { + "$schema": { + "type": "string" + }, + "dxt_version": { + "type": "string", + "const": "0.3", + "description": "@deprecated Use manifest_version instead" + }, + "manifest_version": { + "type": "string", + "const": "0.3" + }, + "name": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "long_description": { + "type": "string" + }, + "author": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + }, + "repository": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "type", + "url" + ], + "additionalProperties": false + }, + "homepage": { + "type": "string", + "format": "uri" + }, + "documentation": { + "type": "string", + "format": "uri" + }, + "support": { + "type": "string", + "format": "uri" + }, + "icon": { + "type": "string" + }, + "icons": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "type": "string" + }, + "sizes": { + "type": "string", + "pattern": "^\\d+x\\d+$" + }, + "theme": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "src", + "sizes" + ], + "additionalProperties": false + } + }, + "screenshots": { + "type": "array", + "items": { + "type": "string" + } + }, + "localization": { + "type": "object", + "properties": { + "resources": { + "type": "string", + "pattern": "\\$\\{locale\\}" + }, + "default_locale": { + "type": "string", + "pattern": "^[A-Za-z0-9]{2,8}(?:-[A-Za-z0-9]{1,8})*$" + } + }, + "required": [ + "resources", + "default_locale" + ], + "additionalProperties": false + }, + "server": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "python", + "node", + "binary" + ] + }, + "entry_point": { + "type": "string" + }, + "mcp_config": { + "type": "object", + "properties": { + "command": { + "type": "string" + }, + "args": { + "type": "array", + "items": { + "type": "string" + } + }, + "env": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "platform_overrides": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "command": { + "$ref": "#/properties/server/properties/mcp_config/properties/command" + }, + "args": { + "$ref": "#/properties/server/properties/mcp_config/properties/args" + }, + "env": { + "$ref": "#/properties/server/properties/mcp_config/properties/env" + } + }, + "additionalProperties": false + } + } + }, + "required": [ + "command" + ], + "additionalProperties": false + } + }, + "required": [ + "type", + "entry_point", + "mcp_config" + ], + "additionalProperties": false + }, + "tools": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "tools_generated": { + "type": "boolean" + }, + "prompts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "arguments": { + "type": "array", + "items": { + "type": "string" + } + }, + "text": { + "type": "string" + } + }, + "required": [ + "name", + "text" + ], + "additionalProperties": false + } + }, + "prompts_generated": { + "type": "boolean" + }, + "keywords": { + "type": "array", + "items": { + "type": "string" + } + }, + "license": { + "type": "string" + }, + "privacy_policies": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + } + }, + "compatibility": { + "type": "object", + "properties": { + "claude_desktop": { + "type": "string" + }, + "platforms": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "darwin", + "win32", + "linux" + ] + } + }, + "runtimes": { + "type": "object", + "properties": { + "python": { + "type": "string" + }, + "node": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "user_config": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "directory", + "file" + ] + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "default": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "multiple": { + "type": "boolean" + }, + "sensitive": { + "type": "boolean" + }, + "min": { + "type": "number" + }, + "max": { + "type": "number" + } + }, + "required": [ + "type", + "title", + "description" + ], + "additionalProperties": false + } + }, + "_meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": {} + } + } + }, + "required": [ + "name", + "version", + "description", + "author", + "server" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/scripts/build-mcpb-schema.js b/scripts/build-mcpb-schema.js index a359274..a62fcdb 100644 --- a/scripts/build-mcpb-schema.js +++ b/scripts/build-mcpb-schema.js @@ -1,33 +1,48 @@ import { - McpbManifestSchema, + McpbManifestSchema as McpbManifestSchemaLatest, McpbSignatureInfoSchema, } from "../dist/schemas/latest.js"; +import { McpbManifestSchema as McpbManifestSchema_v0_1 } from "../dist/schemas/0.1.js"; +import { McpbManifestSchema as McpbManifestSchema_v0_2 } from "../dist/schemas/0.2.js"; +import { McpbManifestSchema as McpbManifestSchema_v0_3 } from "../dist/schemas/0.3.js"; import { zodToJsonSchema } from "zod-to-json-schema"; import fs from "node:fs/promises"; import path from "node:path"; -const schemasToWrite = { - "mcpb-manifest": McpbManifestSchema, +const distDir = path.join(import.meta.dirname, "../dist"); +const schemasDir = path.join(import.meta.dirname, "../schemas"); + +// Versioned manifest schemas +const versionedManifestSchemas = { + "mcpb-manifest-v0.1": McpbManifestSchema_v0_1, + "mcpb-manifest-v0.2": McpbManifestSchema_v0_2, + "mcpb-manifest-v0.3": McpbManifestSchema_v0_3, + "mcpb-manifest-latest": McpbManifestSchemaLatest, +}; + +// Other schemas +const otherSchemas = { "mcpb-signature-info": McpbSignatureInfoSchema, }; -const distDir = path.join(import.meta.dirname, "../dist"); -const schemasDir = path.join(import.meta.dirname, "../schemas"); +const allSchemas = { ...versionedManifestSchemas, ...otherSchemas }; // Generate all schemas to dist/ await fs.mkdir(distDir, { recursive: true }); -for (const key in schemasToWrite) { - const schema = zodToJsonSchema(schemasToWrite[key]); +for (const [key, schema] of Object.entries(allSchemas)) { + const jsonSchema = zodToJsonSchema(schema); const filePath = path.join(distDir, `${key}.schema.json`); - await fs.writeFile(filePath, JSON.stringify(schema, null, 2), { + await fs.writeFile(filePath, JSON.stringify(jsonSchema, null, 2), { encoding: "utf8", }); } -// Generate only manifest schema to schemas/ +// Generate all versioned manifest schemas to schemas/ await fs.mkdir(schemasDir, { recursive: true }); -const manifestSchema = zodToJsonSchema(McpbManifestSchema); -const manifestPath = path.join(schemasDir, "mcpb-manifest.schema.json"); -await fs.writeFile(manifestPath, JSON.stringify(manifestSchema, null, 2), { - encoding: "utf8", -}); +for (const [key, schema] of Object.entries(versionedManifestSchemas)) { + const jsonSchema = zodToJsonSchema(schema); + const filePath = path.join(schemasDir, `${key}.schema.json`); + await fs.writeFile(filePath, JSON.stringify(jsonSchema, null, 2), { + encoding: "utf8", + }); +} diff --git a/src/schemas/0.1.ts b/src/schemas/0.1.ts index 3ff5453..4d77ccc 100644 --- a/src/schemas/0.1.ts +++ b/src/schemas/0.1.ts @@ -98,7 +98,7 @@ export const McpbManifestSchema = z screenshots: z.array(z.string()).optional(), server: McpbManifestServerSchema, tools: z.array(McpbManifestToolSchema).optional(), - tools_generated: z.boolean().optional(), + tools_generated: z.boolean().optional(), prompts: z.array(McpbManifestPromptSchema).optional(), prompts_generated: z.boolean().optional(), keywords: z.array(z.string()).optional(), diff --git a/src/schemas/any.ts b/src/schemas/any.ts new file mode 100644 index 0000000..bd7cec8 --- /dev/null +++ b/src/schemas/any.ts @@ -0,0 +1,15 @@ +import * as z from "zod"; + +import * as v0_1 from "./0.1.js"; +import * as v0_2 from "./0.2.js"; +import * as v0_3 from "./0.3.js"; + +/** + * Union schema that accepts any supported manifest version (0.1, 0.2, 0.3). + * Use this when you need to validate manifests of any version. + */ +export const McpbManifestSchema = z.union([ + v0_1.McpbManifestSchema, + v0_2.McpbManifestSchema, + v0_3.McpbManifestSchema, +]); diff --git a/src/schemas/index.ts b/src/schemas/index.ts index 1b7c64b..3729f0e 100644 --- a/src/schemas/index.ts +++ b/src/schemas/index.ts @@ -1,6 +1,7 @@ export * as v0_1 from "./0.1.js"; export * as v0_2 from "./0.2.js"; export * as v0_3 from "./0.3.js"; +export * as any from "./any.js"; export * as latest from "./latest.js"; export { MANIFEST_VERSION as LATEST_MANIFEST_VERSION, diff --git a/test/schemas.test.ts b/test/schemas.test.ts index 325af17..d05551a 100644 --- a/test/schemas.test.ts +++ b/test/schemas.test.ts @@ -335,5 +335,4 @@ describe("McpbManifestSchema", () => { expect(result.success).toBe(true); }); }); - }); diff --git a/test/valid-manifest-0.2.json b/test/valid-manifest-0.2.json index c6f5fdb..51130df 100644 --- a/test/valid-manifest-0.2.json +++ b/test/valid-manifest-0.2.json @@ -1,21 +1,19 @@ { - "manifest_version": "0.2", - "name": "legacy-extension", - "display_name": "Legacy Extension", - "version": "1.0.0", - "description": "An extension that predates 0.3", - "author": { - "name": "Legacy Author", - "email": "legacy@example.com" - }, - "server": { - "type": "node", - "entry_point": "server/index.js", - "mcp_config": { - "command": "node", - "args": [ - "${__dirname}/server/index.js" - ] - } + "manifest_version": "0.2", + "name": "legacy-extension", + "display_name": "Legacy Extension", + "version": "1.0.0", + "description": "An extension that predates 0.3", + "author": { + "name": "Legacy Author", + "email": "legacy@example.com" + }, + "server": { + "type": "node", + "entry_point": "server/index.js", + "mcp_config": { + "command": "node", + "args": ["${__dirname}/server/index.js"] } -} \ No newline at end of file + } +} diff --git a/test/valid-manifest.json b/test/valid-manifest.json index c833deb..fcc9b9b 100644 --- a/test/valid-manifest.json +++ b/test/valid-manifest.json @@ -13,9 +13,7 @@ "entry_point": "server/index.js", "mcp_config": { "command": "node", - "args": [ - "${__dirname}/server/index.js" - ] + "args": ["${__dirname}/server/index.js"] } } -} \ No newline at end of file +}