Merge remote-tracking branch 'origin/main' into feat/browser-export-versioned-schemas

This commit is contained in:
Joan Xie 2025-11-01 20:28:49 -07:00
commit 2d6dd39b3e
No known key found for this signature in database
8 changed files with 74 additions and 33 deletions

View file

@ -84,7 +84,7 @@ A full `manifest.json` with most of the optional fields looks like this:
},
"repository": {
"type": "git",
"url": "https://github.com/your-username/my-mcp-extension"
"url": "https://github.com/your-username/my-mcp-extension.git"
},
"homepage": "https://example.com/my-extension",
"documentation": "https://docs.example.com/my-extension",

View file

@ -2,8 +2,8 @@ import { confirm, input, select } from "@inquirer/prompts";
import { existsSync, readFileSync, writeFileSync } from "fs";
import { basename, join, resolve } from "path";
import { LATEST_MANIFEST_VERSION } from "../shared/constants.js";
import type { McpbManifestLatest } from "../types.js";
import { DEFAULT_MANIFEST_VERSION } from "../shared/constants.js";
import type { McpbManifest } from "../types.js";
interface PackageJson {
name?: string;
@ -878,14 +878,14 @@ export function buildManifest(
resources: string;
default_locale: string;
},
): McpbManifestLatest {
): McpbManifest {
const { name, displayName, version, description, authorName } = basicInfo;
const { authorEmail, authorUrl } = authorInfo;
const { serverType, entryPoint, mcp_config } = serverConfig;
const { keywords, license, repository } = optionalFields;
return {
manifest_version: LATEST_MANIFEST_VERSION,
manifest_version: DEFAULT_MANIFEST_VERSION,
name,
...(displayName && displayName !== name
? { display_name: displayName }

View file

@ -13,11 +13,9 @@ import { basename, join, relative, resolve, sep } from "path";
import { getAllFilesWithCount, readMcpbIgnorePatterns } from "../node/files.js";
import { validateManifest } from "../node/validate.js";
import {
LATEST_MANIFEST_SCHEMA,
LATEST_MANIFEST_VERSION,
} from "../shared/constants.js";
import { MANIFEST_SCHEMAS } from "../shared/constants.js";
import { getLogger } from "../shared/log.js";
import { getManifestVersionFromRawData } from "../shared/manifestVersionResolve.js";
import { initExtension } from "./init.js";
interface PackOptions {
@ -95,7 +93,18 @@ export async function packExtension({
try {
const manifestContent = readFileSync(manifestPath, "utf-8");
const manifestData = JSON.parse(manifestContent);
manifest = LATEST_MANIFEST_SCHEMA.parse(manifestData);
const manifestVersion = getManifestVersionFromRawData(manifestData);
if (!manifestVersion) {
logger.error(
`ERROR: Manifest version mismatch. Expected "${Object.keys(MANIFEST_SCHEMAS).join(" or ")}", found "${manifestVersion}"`,
);
logger.error(
` Please update the manifest_version in your manifest.json to a supported version`,
);
return false;
}
manifest = MANIFEST_SCHEMAS[manifestVersion].parse(manifestData);
} catch (error) {
logger.error("ERROR: Failed to parse manifest.json");
if (error instanceof Error) {
@ -104,17 +113,6 @@ export async function packExtension({
return false;
}
const manifestVersion = manifest.manifest_version || manifest.dxt_version;
if (manifestVersion !== LATEST_MANIFEST_VERSION) {
logger.error(
`ERROR: Manifest version mismatch. Expected "${LATEST_MANIFEST_VERSION}", found "${manifestVersion}"`,
);
logger.error(
` Please update the manifest_version in your manifest.json to "${LATEST_MANIFEST_VERSION}"`,
);
return false;
}
// Determine output path
const extensionName = basename(resolvedPath);
const finalOutputPath = outputPath

View file

@ -7,9 +7,10 @@ import prettyBytes from "pretty-bytes";
import { unpackExtension } from "../cli/unpack.js";
import {
LATEST_MANIFEST_SCHEMA,
LATEST_MANIFEST_SCHEMA_LOOSE,
MANIFEST_SCHEMAS,
MANIFEST_SCHEMAS_LOOSE,
} from "../shared/constants.js";
import { getManifestVersionFromRawData } from "../shared/manifestVersionResolve.js";
/**
* Check if a buffer contains a valid PNG file signature
@ -119,8 +120,13 @@ export function validateManifest(inputPath: string): boolean {
const manifestContent = readFileSync(manifestPath, "utf-8");
const manifestData = JSON.parse(manifestContent);
const manifestVersion = getManifestVersionFromRawData(manifestData);
if (!manifestVersion) {
console.log("Unrecognized or unsupported manifest version");
return false;
}
const result = LATEST_MANIFEST_SCHEMA.safeParse(manifestData);
const result = MANIFEST_SCHEMAS[manifestVersion].safeParse(manifestData);
if (result.success) {
console.log("Manifest schema validation passes!");
@ -192,7 +198,12 @@ export async function cleanMcpb(inputPath: string) {
const manifestPath = resolve(unpackPath, "manifest.json");
const originalManifest = await fs.readFile(manifestPath, "utf-8");
const manifestData = JSON.parse(originalManifest);
const result = LATEST_MANIFEST_SCHEMA_LOOSE.safeParse(manifestData);
const manifestVersion = getManifestVersionFromRawData(manifestData);
if (!manifestVersion) {
throw new Error("Unrecognized or unsupported manifest version");
}
const result =
MANIFEST_SCHEMAS_LOOSE[manifestVersion].safeParse(manifestData);
if (!result.success) {
throw new Error(

View file

@ -9,9 +9,15 @@ import { McpbManifestSchema as CurrentLooseManifestSchema } from "../schemas_loo
/**
* Latest manifest version - the version that new manifests should use
* @deprecated
*/
export const LATEST_MANIFEST_VERSION = "0.3" as const;
/**
* Default manifest version for new packages
*/
export const DEFAULT_MANIFEST_VERSION = "0.2" as const;
/**
* Map of manifest versions to their strict schemas
*/
@ -32,10 +38,12 @@ export const MANIFEST_SCHEMAS_LOOSE = {
/**
* Get the latest manifest schema based on LATEST_MANIFEST_VERSION
* @deprecated
*/
export const LATEST_MANIFEST_SCHEMA = CurrentManifestSchema;
/**
* Get the latest loose manifest schema based on LATEST_MANIFEST_VERSION
* @deprecated
*/
export const LATEST_MANIFEST_SCHEMA_LOOSE = CurrentLooseManifestSchema;

View file

@ -0,0 +1,24 @@
import { MANIFEST_SCHEMAS } from "./constants.js";
export function getManifestVersionFromRawData(manifestData: unknown) {
let manifestVersion: keyof typeof MANIFEST_SCHEMAS | null = null;
if (
typeof manifestData === "object" &&
manifestData &&
"manifest_version" in manifestData &&
typeof manifestData.manifest_version === "string" &&
Object.keys(MANIFEST_SCHEMAS).includes(manifestData.manifest_version)
) {
manifestVersion =
manifestData.manifest_version as keyof typeof MANIFEST_SCHEMAS;
} else if (
typeof manifestData === "object" &&
manifestData &&
"dxt_version" in manifestData &&
typeof manifestData.dxt_version === "string" &&
Object.keys(MANIFEST_SCHEMAS).includes(manifestData.dxt_version)
) {
manifestVersion = manifestData.dxt_version as keyof typeof MANIFEST_SCHEMAS;
}
return manifestVersion;
}

View file

@ -2,7 +2,7 @@ import { execSync } from "node:child_process";
import fs from "node:fs";
import { join } from "node:path";
import { LATEST_MANIFEST_VERSION } from "../src/schemas/index.js";
import { DEFAULT_MANIFEST_VERSION } from "../src/shared/constants";
interface ExecSyncError extends Error {
stdout: Buffer;
@ -113,7 +113,7 @@ describe("DXT CLI", () => {
fs.writeFileSync(
manifestWithIcon,
JSON.stringify({
manifest_version: LATEST_MANIFEST_VERSION,
manifest_version: DEFAULT_MANIFEST_VERSION,
name: "test-with-icon",
version: "1.0.0",
description: "Test with icon",
@ -140,7 +140,7 @@ describe("DXT CLI", () => {
fs.writeFileSync(
manifestWithUrl,
JSON.stringify({
manifest_version: LATEST_MANIFEST_VERSION,
manifest_version: DEFAULT_MANIFEST_VERSION,
name: "test-with-url",
version: "1.0.0",
description: "Test with URL",
@ -177,7 +177,7 @@ describe("DXT CLI", () => {
fs.writeFileSync(
join(tempDir, "manifest.json"),
JSON.stringify({
manifest_version: LATEST_MANIFEST_VERSION,
manifest_version: DEFAULT_MANIFEST_VERSION,
name: "Test Extension",
version: "1.0.0",
description: "A test extension",
@ -274,7 +274,7 @@ describe("DXT CLI", () => {
fs.writeFileSync(
join(tempExecDir, "manifest.json"),
JSON.stringify({
manifest_version: LATEST_MANIFEST_VERSION,
manifest_version: DEFAULT_MANIFEST_VERSION,
name: "Test Executable Extension",
version: "1.0.0",
description: "A test extension with executable files",

View file

@ -10,7 +10,7 @@ import {
getDefaultRepositoryUrl,
readPackageJson,
} from "../src/cli/init.js";
import { LATEST_MANIFEST_VERSION } from "../src/schemas/index.js";
import { DEFAULT_MANIFEST_VERSION } from "../src/shared/constants.js";
// Mock the fs module
jest.mock("fs", () => ({
@ -220,7 +220,7 @@ describe("init functions", () => {
);
expect(manifest).toEqual({
manifest_version: LATEST_MANIFEST_VERSION,
manifest_version: DEFAULT_MANIFEST_VERSION,
name: "test-extension",
version: "1.0.0",
description: "Test description",
@ -322,7 +322,7 @@ describe("init functions", () => {
);
expect(manifest).toEqual({
manifest_version: LATEST_MANIFEST_VERSION,
manifest_version: DEFAULT_MANIFEST_VERSION,
name: "test-extension",
display_name: "Test Extension",
version: "1.0.0",