Skip to content
Merged
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
4 changes: 2 additions & 2 deletions MANIFEST.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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",
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -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`
Expand Down Expand Up @@ -154,4 +155,4 @@ npm test

# License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
5 changes: 4 additions & 1 deletion scripts/build-mcpb-schema.js
Original file line number Diff line number Diff line change
@@ -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";
Expand Down
3 changes: 2 additions & 1 deletion src/cli/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 }
Expand Down
13 changes: 12 additions & 1 deletion src/cli/pack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/node/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
61 changes: 31 additions & 30 deletions src/schemas-loose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"]),
Expand Down
17 changes: 10 additions & 7 deletions src/schemas.ts
Original file line number Diff line number Diff line change
@@ -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(),
Expand Down Expand Up @@ -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(),
Expand All @@ -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"]),
Expand Down
8 changes: 5 additions & 3 deletions test/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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", () => {
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
5 changes: 3 additions & 2 deletions test/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => ({
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
Loading