From f7efd3a444541555d80e6cd63793ee8e4fb6abd9 Mon Sep 17 00:00:00 2001 From: Joan Xie Date: Fri, 17 Oct 2025 20:56:31 -0700 Subject: [PATCH] add WIP 0.3 --- src/schemas/0.3.ts | 126 +++++++++++++++++++++++++++++++++++++++++++ src/schemas/index.ts | 1 + test/schemas.test.ts | 26 +++++---- 3 files changed, 143 insertions(+), 10 deletions(-) create mode 100644 src/schemas/0.3.ts diff --git a/src/schemas/0.3.ts b/src/schemas/0.3.ts new file mode 100644 index 0000000..3862ba4 --- /dev/null +++ b/src/schemas/0.3.ts @@ -0,0 +1,126 @@ +// WIP: This schema is under development and not yet finalized +import * as z from "zod"; + +export const MANIFEST_VERSION = "0.3"; + +export const McpServerConfigSchema = z.strictObject({ + command: z.string(), + args: z.array(z.string()).optional(), + env: z.record(z.string(), z.string()).optional(), +}); + +export const McpbManifestAuthorSchema = z.strictObject({ + name: z.string(), + email: z.string().email().optional(), + url: z.string().url().optional(), +}); + +export const McpbManifestRepositorySchema = z.strictObject({ + type: z.string(), + url: z.string().url(), +}); + +export const McpbManifestPlatformOverrideSchema = + McpServerConfigSchema.partial(); + +export const McpbManifestMcpConfigSchema = McpServerConfigSchema.extend({ + platform_overrides: z + .record(z.string(), McpbManifestPlatformOverrideSchema) + .optional(), +}); + +export const McpbManifestServerSchema = z.strictObject({ + type: z.enum(["python", "node", "binary"]), + entry_point: z.string(), + mcp_config: McpbManifestMcpConfigSchema, +}); + +export const McpbManifestCompatibilitySchema = z.strictObject({ + claude_desktop: z.string().optional(), + platforms: z.array(z.enum(["darwin", "win32", "linux"])).optional(), + runtimes: z + .strictObject({ + python: z.string().optional(), + node: z.string().optional(), + }) + .optional(), +}); + +export const McpbManifestToolSchema = z.strictObject({ + name: z.string(), + description: z.string().optional(), +}); + +export const McpbManifestPromptSchema = z.strictObject({ + name: z.string(), + description: z.string().optional(), + arguments: z.array(z.string()).optional(), + text: z.string(), +}); + +export const McpbUserConfigurationOptionSchema = z.strictObject({ + type: z.enum(["string", "number", "boolean", "directory", "file"]), + title: z.string(), + description: z.string(), + required: z.boolean().optional(), + default: z + .union([z.string(), z.number(), z.boolean(), z.array(z.string())]) + .optional(), + multiple: z.boolean().optional(), + sensitive: z.boolean().optional(), + min: z.number().optional(), + max: z.number().optional(), +}); + +export const McpbUserConfigValuesSchema = z.record( + z.string(), + z.union([z.string(), z.number(), z.boolean(), z.array(z.string())]), +); + +export const McpbManifestSchema = z + .strictObject({ + $schema: z.string().optional(), + dxt_version: z + .literal(MANIFEST_VERSION) + .optional() + .describe("@deprecated Use manifest_version instead"), + manifest_version: z.literal(MANIFEST_VERSION).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(), + privacy_policies: z.array(z.string().url()).optional(), + compatibility: McpbManifestCompatibilitySchema.optional(), + user_config: z + .record(z.string(), McpbUserConfigurationOptionSchema) + .optional(), + _meta: z.record(z.string(), z.record(z.string(), z.any())).optional(), + }) + .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"]), + publisher: z.string().optional(), + issuer: z.string().optional(), + valid_from: z.string().optional(), + valid_to: z.string().optional(), + fingerprint: z.string().optional(), +}); diff --git a/src/schemas/index.ts b/src/schemas/index.ts index 9e4a580..1b7c64b 100644 --- a/src/schemas/index.ts +++ b/src/schemas/index.ts @@ -1,5 +1,6 @@ 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 latest from "./latest.js"; export { MANIFEST_VERSION as LATEST_MANIFEST_VERSION, diff --git a/test/schemas.test.ts b/test/schemas.test.ts index 5acbc27..f88fe47 100644 --- a/test/schemas.test.ts +++ b/test/schemas.test.ts @@ -1,7 +1,7 @@ import { readFileSync } from "fs"; import { join } from "path"; -import { McpbManifestSchema } from "../src/schemas/index.js"; +import { McpbManifestSchema, v0_3 } from "../src/schemas/index.js"; describe("McpbManifestSchema", () => { it("should validate a valid manifest", () => { @@ -137,9 +137,9 @@ describe("McpbManifestSchema", () => { }); }); - describe.skip("_meta", () => { + describe("_meta", () => { const base = { - manifest_version: "0.2", + manifest_version: "0.3", name: "client-ext-test", version: "1.0.0", description: "Test manifest", @@ -155,11 +155,14 @@ describe("McpbManifestSchema", () => { const manifest = { ...base, _meta: { - "com.microsoft.windows": { package_family_name: "Pkg_123", channel: "stable" }, + "com.microsoft.windows": { + package_family_name: "Pkg_123", + channel: "stable", + }, "com.apple.darwin": { bundle_id: "com.example.app", notarized: true }, }, }; - const result = McpbManifestSchema.safeParse(manifest); + const result = v0_3.McpbManifestSchema.safeParse(manifest); expect(result.success).toBe(true); }); @@ -167,10 +170,13 @@ describe("McpbManifestSchema", () => { const manifest = { ...base, _meta: { - "com.microsoft.windows": "raw-string" as unknown as Record, + "com.microsoft.windows": "raw-string" as unknown as Record< + string, + unknown + >, }, }; - const result = McpbManifestSchema.safeParse(manifest); + const result = v0_3.McpbManifestSchema.safeParse(manifest); expect(result.success).toBe(false); if (!result.success) { const messages = result.error.issues.map((i) => i.message).join("\n"); @@ -185,7 +191,7 @@ describe("McpbManifestSchema", () => { "com.apple.darwin": [] as unknown as Record, }, }; - const result = McpbManifestSchema.safeParse(manifest); + const result = v0_3.McpbManifestSchema.safeParse(manifest); expect(result.success).toBe(false); }); @@ -196,13 +202,13 @@ describe("McpbManifestSchema", () => { custom: null as unknown as Record, }, }; - const result = McpbManifestSchema.safeParse(manifest); + const result = v0_3.McpbManifestSchema.safeParse(manifest); expect(result.success).toBe(false); }); it("allows empty object for _meta", () => { const manifest = { ...base, _meta: {} }; - const result = McpbManifestSchema.safeParse(manifest); + const result = v0_3.McpbManifestSchema.safeParse(manifest); expect(result.success).toBe(true); }); });