π File detail
utils/dxt/helpers.ts
π― Use case
This file lives under βutils/β, which covers cross-cutting helpers (shell, tempfiles, settings, messages, process input, β¦). On the API surface it exposes validateManifest, parseAndValidateManifestFromText, parseAndValidateManifestFromBytes, and generateExtensionId β mainly functions, hooks, or classes. Dependencies touch @anthropic-ai. It composes internal code from errors and slowOperations (relative imports).
Generated from folder role, exports, dependency roots, and inline comments β not hand-reviewed for every path.
π§ Inline summary
import type { McpbManifest } from '@anthropic-ai/mcpb' import { errorMessage } from '../errors.js' import { jsonParse } from '../slowOperations.js' /**
π€ Exports (heuristic)
validateManifestparseAndValidateManifestFromTextparseAndValidateManifestFromBytesgenerateExtensionId
π External import roots
Package roots from from "β¦" (relative paths omitted).
@anthropic-ai
π₯οΈ Source preview
import type { McpbManifest } from '@anthropic-ai/mcpb'
import { errorMessage } from '../errors.js'
import { jsonParse } from '../slowOperations.js'
/**
* Parses and validates a DXT manifest from a JSON object.
*
* Lazy-imports @anthropic-ai/mcpb: that package uses zod v3 which eagerly
* creates 24 .bind(this) closures per schema instance (~300 instances between
* schemas.js and schemas-loose.js). Deferring the import keeps ~700KB of bound
* closures out of the startup heap for sessions that never touch .dxt/.mcpb.
*/
export async function validateManifest(
manifestJson: unknown,
): Promise<McpbManifest> {
const { McpbManifestSchema } = await import('@anthropic-ai/mcpb')
const parseResult = McpbManifestSchema.safeParse(manifestJson)
if (!parseResult.success) {
const errors = parseResult.error.flatten()
const errorMessages = [
...Object.entries(errors.fieldErrors).map(
([field, errs]) => `${field}: ${errs?.join(', ')}`,
),
...(errors.formErrors || []),
]
.filter(Boolean)
.join('; ')
throw new Error(`Invalid manifest: ${errorMessages}`)
}
return parseResult.data
}
/**
* Parses and validates a DXT manifest from raw text data.
*/
export async function parseAndValidateManifestFromText(
manifestText: string,
): Promise<McpbManifest> {
let manifestJson: unknown
try {
manifestJson = jsonParse(manifestText)
} catch (error) {
throw new Error(`Invalid JSON in manifest.json: ${errorMessage(error)}`)
}
return validateManifest(manifestJson)
}
/**
* Parses and validates a DXT manifest from raw binary data.
*/
export async function parseAndValidateManifestFromBytes(
manifestData: Uint8Array,
): Promise<McpbManifest> {
const manifestText = new TextDecoder().decode(manifestData)
return parseAndValidateManifestFromText(manifestText)
}
/**
* Generates an extension ID from author name and extension name.
* Uses the same algorithm as the directory backend for consistency.
*/
export function generateExtensionId(
manifest: McpbManifest,
prefix?: 'local.unpacked' | 'local.dxt',
): string {
const sanitize = (str: string) =>
str
.toLowerCase()
.replace(/\s+/g, '-')
.replace(/[^a-z0-9-_.]/g, '')
.replace(/-+/g, '-')
.replace(/^-+|-+$/g, '')
const authorName = manifest.author.name
const extensionName = manifest.name
const sanitizedAuthor = sanitize(authorName)
const sanitizedName = sanitize(extensionName)
return prefix
? `${prefix}.${sanitizedAuthor}.${sanitizedName}`
: `${sanitizedAuthor}.${sanitizedName}`
}