Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions src/schemas/0.4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,23 @@ export const McpbManifestMcpConfigSchema = McpServerConfigSchema.extend({
.optional(),
});

export const McpbManifestServerSchema = z.strictObject({
type: z.enum(["python", "node", "binary", "uv"]),
entry_point: z.string(),
mcp_config: McpbManifestMcpConfigSchema,
});
export const McpbManifestServerSchema = z
.strictObject({
type: z.enum(["python", "node", "binary", "uv"]),
entry_point: z.string(),
mcp_config: McpbManifestMcpConfigSchema.optional(),
})
.superRefine((server, ctx) => {
// mcp_config is optional only for the "uv" server type (the host manages
// execution — see MANIFEST.md). All other server types require it.
if (server.type !== "uv" && server.mcp_config === undefined) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["mcp_config"],
message: "Required",
});
}
});

export const McpbManifestCompatibilitySchema = z.strictObject({
claude_desktop: z.string().optional(),
Expand Down
37 changes: 36 additions & 1 deletion test/schemas.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { readFileSync } from "fs";
import { join } from "path";

import { v0_2, v0_3 } from "../src/schemas/index.js";
import { v0_2, v0_3, v0_4 } from "../src/schemas/index.js";

describe("McpbManifestSchema", () => {
it("should validate a valid manifest", () => {
Expand Down Expand Up @@ -335,4 +335,39 @@ describe("McpbManifestSchema", () => {
expect(result.success).toBe(true);
});
});

describe("0.4 uv server mcp_config", () => {
const base = {
manifest_version: "0.4",
name: "uv-extension",
version: "1.0.0",
description: "UV extension",
author: { name: "Test Author" },
compatibility: { claude_desktop: "0.1.0" },
};

it("accepts a uv server with no mcp_config (host manages execution)", () => {
const manifest = {
...base,
server: { type: "uv", entry_point: "main.py" },
};
const result = v0_4.McpbManifestSchema.safeParse(manifest);
expect(result.success).toBe(true);
});

it("still requires mcp_config for non-uv server types", () => {
const manifest = {
...base,
server: { type: "node", entry_point: "server.js" },
};
const result = v0_4.McpbManifestSchema.safeParse(manifest);
expect(result.success).toBe(false);
if (!result.success) {
const errors = result.error.issues.map((issue) =>
issue.path.join("."),
);
expect(errors).toContain("server.mcp_config");
}
});
});
});