/** * Plugin-bundle validation issues — stable codes and severities. * * Codes are part of the SDK's public contract: the backend persists them in * `pluginImports.failure` / validation summaries and the inspector renders * them in the import preview, so they must stay stable once released. Add new * codes; never repurpose existing ones. */ declare const PLUGIN_ISSUE_CODES: readonly ["BUNDLE_EMPTY", "BUNDLE_TOO_MANY_ENTRIES", "BUNDLE_TOO_LARGE", "FILE_TOO_LARGE", "FILE_SIZE_MISMATCH", "FILE_INVALID_UTF8", "FILE_UNREADABLE", "VALUE_TOO_DEEP", "PATH_EMPTY", "PATH_ABSOLUTE", "PATH_TRAVERSAL", "PATH_NUL_BYTE", "PATH_INVALID_CHARACTER", "PATH_DUPLICATE", "PATH_CASE_COLLISION", "PATH_LINK_ENTRY", "PATH_TOO_LONG", "PATH_TOO_DEEP", "PATH_ESCAPES_ROOT", "MANIFEST_MISSING", "MANIFEST_DUPLICATE", "MANIFEST_UNSUPPORTED_SCHEMA", "MANIFEST_INVALID_JSON", "MANIFEST_INVALID_NAME", "MANIFEST_INVALID_VERSION", "MANIFEST_INVALID_FIELD", "MANIFEST_INSECURE_URL", "MANIFEST_MISSING_FILE", "MANIFEST_PLACEHOLDER", "MANIFEST_UNKNOWN_FIELD", "MANIFEST_AMBIGUOUS_FIELD", "MANIFEST_SECRET_FIELD_OMITTED", "SKILL_TOO_MANY", "SKILL_FRONTMATTER_MISSING", "SKILL_FRONTMATTER_UNPARSED", "SKILL_MISSING_NAME", "SKILL_MISSING_DESCRIPTION", "SKILL_INVALID_NAME", "SKILL_DESCRIPTION_TOO_LONG", "SKILL_NAME_MISMATCH", "SKILL_DUPLICATE_NAME", "SKILL_INVALID_METADATA", "MCP_TOO_MANY_SERVERS", "MCP_INVALID_CONFIG", "MCP_DUPLICATE_WRAPPER", "MCP_INVALID_SERVER", "MCP_INVALID_SERVER_NAME", "MCP_AMBIGUOUS_TRANSPORT", "MCP_UNKNOWN_TRANSPORT", "MCP_MISSING_COMMAND", "MCP_MISSING_URL", "MCP_INSECURE_URL", "MCP_INSECURE_URL_LOCALHOST", "MCP_INVALID_ENV", "MCP_INVALID_HEADERS", "MCP_ENV_VALUE_OMITTED", "MCP_HEADER_VALUE_OMITTED", "MCP_SECRET_FIELD_OMITTED", "MCP_UNKNOWN_FIELD", "MCP_ABSOLUTE_WORKING_DIRECTORY", "MCP_INVALID_WORKING_DIRECTORY", "MCP_UNSUPPORTED_SCHEMA", "MCP_RESERVED_ENV_KEY", "MCP_PLACEHOLDER_IN_COMMAND", "MCP_CONFIG_IGNORED", "COMPONENT_SKIPPED", "APP_INVALID_CONFIG", "APP_MISSING_ID", "APP_UNKNOWN_SERVER", "APP_SECRET_FIELD_OMITTED", "ASSET_CONTENT_MISMATCH", "ASSET_UNSUPPORTED_TYPE", "UNSUPPORTED_COMPONENT"]; type PluginIssueCode = (typeof PLUGIN_ISSUE_CODES)[number]; type PluginIssueSeverity = "error" | "warning"; interface PluginValidationIssue { code: PluginIssueCode; severity: PluginIssueSeverity; message: string; /** Bundle path the issue refers to, when applicable. */ path?: string; /** Component key (`skill:`, `server:`, `app:`) when applicable. */ componentKey?: string; } /** * Thrown by `parsePluginBundle` when any error-severity issue is found. * `issues` carries every issue collected up to the failure (errors and * warnings), so import previews can render the full list from one throw. */ declare class PluginBundleError extends Error { readonly code: PluginIssueCode; readonly issues: PluginValidationIssue[]; constructor(issues: PluginValidationIssue[]); } /** * Agent Plugins 1.0 manifest (`plugin.json` at the bundle root) validation * and normalization — https://agent-plugins.org/schemas/1.0.0/plugin.schema.json. * * The manifest is a CLOSED object per the spec: `$schema` and `name` are * required; unknown top-level fields are reported and ignored (never * preserved, never executed); a non-object `extensions` is reported and * ignored. `$schema` selects the validation contract from a compiled-in * list — schemas are never retrieved at load time (spec MUST). * * MCPJam-specific presentation metadata (display name, icon, logo) lives in * the `com.mcpjam` reverse-domain extension namespace, per the spec's * client-extension model — never as top-level fields. */ declare const PLUGIN_MANIFEST_PATH = "plugin.json"; /** * Canonical schema identifiers per supported Agent Plugins version. Frozen: * this map IS the compiled-in allowlist, so a caller mutating it at runtime * would defeat the local-schema-selection contract. */ declare const PLUGIN_MANIFEST_SCHEMAS: Readonly>; /** Reverse-domain namespace MCPJam reads its own extension data from. */ declare const MCPJAM_EXTENSION_NAMESPACE = "com.mcpjam"; interface PluginManifestAuthor { name?: string; email?: string; url?: string; } interface NormalizedPluginManifest { /** * Agent Plugins version the bundle targets, resolved locally from * `$schema` (e.g. `"1.0.0"`). Never fetched. */ schemaVersion: string; /** Validated plugin name — the stable logical identity. Dots are legal. */ name: string; /** Declared version string, when present. Metadata only, format-free. */ version?: string; description?: string; /** From the `com.mcpjam` extension namespace, when present. */ displayName?: string; homepage?: string; repository?: string; license?: string; author?: PluginManifestAuthor; keywords?: string[]; /** Bundle-relative icon path from the `com.mcpjam` namespace. */ icon?: string; /** Bundle-relative logo path from the `com.mcpjam` namespace. */ logo?: string; /** * Client-extension data keyed by reverse-domain namespace, sanitized * (secret-looking keys/values dropped, depth-capped). MCPJam applies only * the `com.mcpjam` namespace and ignores the rest, per spec. */ extensions: Record; } /** * Agent Plugins 1.0 MCP configuration (`mcp.json` at the bundle root) — * https://agent-plugins.org/schemas/1.0.0/mcp.schema.json. * * The plugin path is spec-strict: the document requires `$schema` + * `mcpServers` (closed object); every entry requires an explicit `type` * (`stdio` | `streamable-http` | `sse`) — the declared transport is * authoritative, never inferred. One invalid entry is skipped (failure * isolation), never the whole document; an invalid document disables the * MCP component type, never the whole bundle. * * Secret hygiene: env/header VALUES that look like credentials are never * stored (a documented deviation from verbatim pass-through). Screened * non-secret literals ARE stored, so `{"MODE": "production"}` works. * `${PLUGIN_ROOT}` / `${PLUGIN_DATA}` are preserved verbatim for the * runtime to expand — only in args, env values, and cwd, per spec. * * The two policy-free shape primitives (`selectPluginMcpServerMap`, * `detectPluginMcpTransport`) keep their lenient behavior: the inspector's * generic MCP-JSON import shares them and must keep accepting wrapper * variants, spelling variants, and command/url inference. */ /** * Canonical MCP-config schema identifiers per Agent Plugins version. * Frozen: this map IS the compiled-in allowlist. */ declare const PLUGIN_MCP_SCHEMAS: Readonly>; declare const PLUGIN_ROOT_PLACEHOLDER = "${PLUGIN_ROOT}"; declare const PLUGIN_DATA_PLACEHOLDER = "${PLUGIN_DATA}"; /** * Placeholders substituted with the materialized bundle root at spawn. * `${PLUGIN_DATA}` is deliberately NOT in this list — it resolves to the * writable per-plugin data directory, never the bundle root. */ declare const PLUGIN_ROOT_PLACEHOLDERS: readonly ["${PLUGIN_ROOT}"]; /** Both runtime placeholders, for detection (not substitution). */ declare const PLUGIN_PLACEHOLDERS: readonly ["${PLUGIN_ROOT}", "${PLUGIN_DATA}"]; declare function containsRootPlaceholder(value: string): boolean; /** Does the value reference either runtime placeholder? */ declare function containsPluginPlaceholder(value: string): boolean; interface PluginEnvRequirement { name: string; required: boolean; /** * Preserved only when the declared value is a PURE placeholder path * template (`${PLUGIN_ROOT}/...`, `${PLUGIN_DATA}/...`) or a composite * reference template whose literal remainder passed the secret screen. * Placeholders are substituted by the runtime at process launch; the * parser never does. */ valueTemplate?: string; /** * A declared literal value that passed the secret screen (non-secret name, * non-secret-looking value). Secret-looking literals are never stored — * they become name-only setup requirements instead. */ value?: string; } interface PluginHeaderRequirement { name: string; secret: boolean; /** Screened non-secret literal header value, when declared. */ value?: string; } interface NormalizedPluginOAuthHint { timing?: "on_install" | "on_use"; scopes?: string[]; /** Sanitized non-secret extra metadata from the source config. */ metadata?: Record; } type NormalizedPluginMcpServer = { transport: "stdio"; command: string; args: string[]; envRequirements: PluginEnvRequirement[]; workingDirectory?: string; } | { transport: "http"; /** Declared wire transport — authoritative for the initial connection. */ httpVariant: "streamable-http" | "sse"; url: string; headerRequirements: PluginHeaderRequirement[]; oauth?: NormalizedPluginOAuthHint; }; interface ParsedPluginServer { /** `server:` — stable component identity within the plugin version. */ componentKey: string; /** Declared server name (the map key in the source config). */ key: string; /** Bundle path of the config file the server came from. */ sourcePath: string; config: NormalizedPluginMcpServer; /** SHA-256 of the canonical JSON of `config`; filled in by the parser. */ configHash: string; } /** One skipped component, per the spec's failure-isolation boundaries. */ interface PluginSkippedComponent { kind: "server" | "skill" | "mcp-config"; /** Server key, skill directory name, or the config path. */ key: string; reason: string; } /** * Result of {@link detectPluginMcpTransport}. `ok: false` carries the same * stable issue code the strict plugin path reports, so a caller with a * different policy can decide for itself whether to skip, warn, or fail. */ type PluginMcpTransportDetection = { ok: true; transport: "stdio" | "http"; } | { ok: false; code: PluginIssueCode; message: string; }; /** * Decide whether a single server configuration is stdio or http, from an * explicit `type`/`transport` discriminator when present and otherwise from * the presence of `command` vs `url`. * * Pure and policy-free: it reports what the shape says and never applies the * plugin path's stricter rules (explicit `type` required, HTTPS, server-key * format, secret stripping). The inspector's generic MCP-JSON import shares * this function so `type: "streamable_http"`, `sse`, and a bare * `command`/`url` are classified identically everywhere. `message` is * caller-facing text; the `code` is the stable contract. */ declare function detectPluginMcpTransport(config: unknown, serverKey?: string): PluginMcpTransportDetection; /** Which wrapper held the server map; `null` = the document IS the map. */ type PluginMcpWrapperKey = "mcp_servers" | "mcpServers" | null; interface PluginMcpServerEntry { key: string; /** * The server's configuration exactly as it appeared in the source document * — VALUES INTACT. This is the caller's own input handed back in a uniform * shape, not a normalized DTO: it may carry env values, header values, and * other credentials. Never persist it or fold it into a hash. Use * {@link normalizePluginMcpConfig} when you need the screened form. */ config: unknown; } /** * Why shape selection failed. Distinct from `code` because several of these * share one persisted issue code: `code` is the stable contract the backend * stores, `reason` is a typed discriminator a caller can branch on to render * its own guidance without matching on message text. */ type PluginMcpSelectionFailureReason = "document-not-an-object" | "duplicate-wrapper" | "bare-server-config" | "server-map-not-an-object"; type PluginMcpServerMapSelection = { ok: true; wrapperKey: PluginMcpWrapperKey; servers: PluginMcpServerEntry[]; } | { ok: false; code: PluginIssueCode; reason: PluginMcpSelectionFailureReason; message: string; }; /** * Resolve which of the three compatible document shapes an MCP-JSON config * uses — a direct server map, an `mcp_servers` wrapper, or an `mcpServers` * wrapper — and return its entries in declaration order. * * Pure and policy-free: entries come back unfiltered and unvalidated. The * inspector's generic MCP-JSON import keeps names/URLs the plugin path * rejects, while {@link normalizePluginMcpConfig} layers the strict Agent * Plugins rules on top. */ declare function selectPluginMcpServerMap(raw: unknown): PluginMcpServerMapSelection; /** * Plugin skill (`skills//SKILL.md`) parsing and validation, per the * Agent Skills specification Agent Plugins 1.0 references. * * Mirrors the Agent Skills rules the inspector already enforces (kebab-case * name, required description <= 1024 chars) without Node dependencies: the * frontmatter uses a deliberately small YAML subset (scalars, `- ` lists, * `|`/`>` blocks). Lines the subset cannot interpret are preserved raw and * reported as warnings — never guessed at. Scripts are never executed * during import. */ interface ParsedPluginSkillFile { /** Canonical bundle path. */ path: string; /** Path relative to the skill directory. */ relativePath: string; size: number; contentHash: string; } interface ParsedPluginSkill { /** `skill:` — stable component identity. */ componentKey: string; /** `skills/` canonical bundle path. */ directory: string; directoryName: string; skillFilePath: string; /** Declared frontmatter name. */ name: string; description: string; /** Model-facing namespaced reference: `/`. */ modelRef: string; /** SKILL.md body with frontmatter stripped. */ instructions: string; /** Parsed frontmatter (YAML subset). */ frontmatter: Record; /** Raw frontmatter text for lossless round-tripping. */ frontmatterRaw: string; allowImplicitInvocation?: boolean; /** MCP tool dependencies declared in frontmatter. */ mcpToolDependencies: string[]; /** Every skill-directory file except SKILL.md itself. */ supportingFiles: ParsedPluginSkillFile[]; /** SHA-256 of the raw SKILL.md bytes. */ contentHash: string; /** Aggregate hash over every skill-directory file (relative path + bytes). */ aggregateHash: string; } /** * App metadata (`*.app.json`) parsing. * * V1 executes an app mapping only when it can be associated with an MCP * server already present in the bundle (declared binding, or inferred when the * bundle ships exactly one server). Everything else is preserved as * `needs_server_binding` so the import preview can ask the user for an * explicit MCPJam server binding. */ type PluginAppBinding = "declared" | "inferred" | "unbound"; interface ParsedPluginApp { /** `app:` — stable component identity. */ componentKey: string; appId: string; /** Canonical bundle path of the `.app.json` file. */ sourcePath: string; /** Bundle MCP server key this app maps to, when bound. */ serverKey?: string; binding: PluginAppBinding; status: "bound" | "needs_server_binding"; /** SHA-256 of the raw `.app.json` bytes; filled in by the parser. */ contentHash: string; /** Source fields other than the id/server binding, preserved verbatim. */ extensions: Record; } /** * Shared plugin-bundle contract — pure DTOs and the abstract file source. * * This module is the SDK-owned wire/persistence contract for Agent Plugins * 1.0 imports (agent-plugins.org). The parser never touches the filesystem * or archive libraries: backend and inspector adapters implement * `PluginFileSource` over their own extraction paths and get byte-identical * normalization and hashing. */ interface PluginFileEntry { /** Entry path as reported by the source adapter (ZIP entry name or relative file path). */ path: string; /** Declared uncompressed size in bytes. */ size: number; /** * Entry kind. Adapters MUST surface link entries (`symlink`/`hardlink`) so * the parser can reject them; omitted means `file`. */ kind?: "file" | "directory" | "symlink" | "hardlink"; } /** * Abstract source of bundle content. Adapters must enforce `maxBytes`: when an * entry's content exceeds it, throw instead of returning truncated data. */ interface PluginFileSource { list(): Promise; /** * Optional convenience for adapter-side consumers. The parser itself never * calls it — it decodes text from `readBytes` so hashing and decoding see * the same bytes. */ readText?(path: string, maxBytes: number): Promise; readBytes(path: string, maxBytes: number): Promise; } interface PluginBundleLimits { /** Maximum archive entries (files + directories). */ maxEntries: number; /** Maximum total uncompressed content in bytes. */ maxTotalBytes: number; /** Maximum size of one ordinary file in bytes. */ maxFileBytes: number; /** Maximum path length in UTF-8 bytes. */ maxPathBytes: number; /** Maximum path nesting depth (segments). */ maxPathDepth: number; /** Maximum skills per plugin. */ maxSkills: number; /** Maximum MCP server entries per plugin. */ maxMcpServers: number; } /** Locked V1 limits from the import plan ("Archive and path limits"). */ declare const DEFAULT_PLUGIN_BUNDLE_LIMITS: PluginBundleLimits; type PluginAssetKind = "icon" | "logo" | "screenshot" | "other"; interface ParsedPluginAsset { /** Canonical bundle-relative path. */ path: string; kind: PluginAssetKind; size: number; /** SHA-256 hex of the asset's exact bytes. */ contentHash: string; /** MIME type inferred from the file extension. */ contentType: string; } /** * Setup the user must complete before a component is runnable. Derived from * requirement NAMES only — screened non-secret literals are stored on the * normalized config instead and never become requirements; secret-looking * values are dropped and DO become requirements. */ type PluginSetupRequirement = { kind: "env"; componentKey: string; serverKey: string; name: string; required: boolean; } | { kind: "header"; componentKey: string; serverKey: string; name: string; secret: boolean; } | { kind: "oauth"; componentKey: string; serverKey: string; timing?: "on_install" | "on_use"; }; interface ParsedPluginBundle { manifest: NormalizedPluginManifest; /** * Agent Plugins version both documents target (resolved locally from * `$schema`). Mirrors `manifest.schemaVersion` for consumers that never * look inside the manifest. */ schemaVersion: string; /** Deterministic content hash over every file (canonical path + bytes). */ bundleHash: string; /** SHA-256 hex of the raw `plugin.json` bytes. */ manifestHash: string; skills: ParsedPluginSkill[]; mcpServers: ParsedPluginServer[]; apps: ParsedPluginApp[]; assets: ParsedPluginAsset[]; /** * Components skipped under the spec's failure-isolation boundaries (one * bad server entry / skill / the whole mcp.json document). Import surfaces * MUST render these loudly — a silently absent server reads as a runtime * bug, not a bundle problem. */ skipped: PluginSkippedComponent[]; setupRequirements: PluginSetupRequirement[]; /** Warning-severity issues only; error-severity issues throw instead. */ warnings: PluginValidationIssue[]; } interface ParsePluginBundleOptions { /** Override individual archive/component limits (tests, paid plans). */ limits?: Partial; } export { type PluginManifestAuthor as A, type PluginMcpServerEntry as B, type PluginMcpServerMapSelection as C, DEFAULT_PLUGIN_BUNDLE_LIMITS as D, type PluginMcpTransportDetection as E, type PluginMcpWrapperKey as F, type PluginSetupRequirement as G, type PluginSkippedComponent as H, type PluginValidationIssue as I, containsPluginPlaceholder as J, containsRootPlaceholder as K, detectPluginMcpTransport as L, MCPJAM_EXTENSION_NAMESPACE as M, type NormalizedPluginManifest as N, selectPluginMcpServerMap as O, type PluginFileSource as P, type ParsePluginBundleOptions as a, type ParsedPluginBundle as b, type NormalizedPluginMcpServer as c, type NormalizedPluginOAuthHint as d, PLUGIN_DATA_PLACEHOLDER as e, PLUGIN_ISSUE_CODES as f, PLUGIN_MANIFEST_PATH as g, PLUGIN_MANIFEST_SCHEMAS as h, PLUGIN_MCP_SCHEMAS as i, PLUGIN_PLACEHOLDERS as j, PLUGIN_ROOT_PLACEHOLDER as k, PLUGIN_ROOT_PLACEHOLDERS as l, type ParsedPluginApp as m, type ParsedPluginAsset as n, type ParsedPluginServer as o, type ParsedPluginSkill as p, type ParsedPluginSkillFile as q, type PluginAppBinding as r, type PluginAssetKind as s, PluginBundleError as t, type PluginBundleLimits as u, type PluginEnvRequirement as v, type PluginFileEntry as w, type PluginHeaderRequirement as x, type PluginIssueCode as y, type PluginIssueSeverity as z };