Skip to content

Manifest Schema

Version
0.1 (Working Draft)
Date
2026-03-06
Editors
Daniel Meppiel (Microsoft)
Repository
https://github.com/microsoft/apm
Format
YAML 1.2

This is a Working Draft. It may be updated, replaced, or made obsolete at any time. It is inappropriate to cite this document as other than work in progress.

This specification defines the manifest format (apm.yml) used by the Agent Package Manager (APM). Feedback is welcome via GitHub Issues.


The apm.yml manifest declares the full closure of agent primitive dependencies, MCP servers, scripts, and compilation settings for a project. It is the contract between package authors, runtimes, and integrators — any conforming resolver can consume this format to install, compile, and run agentic workflows.


The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

A conforming manifest is a YAML 1.2 document that satisfies all MUST-level requirements in this specification. A conforming resolver is a program that correctly parses conforming manifests and performs dependency resolution as described herein.


A conforming manifest MUST be a YAML mapping at the top level with the following shape:

apm.yml
name: <string> # REQUIRED
version: <string> # REQUIRED
description: <string>
author: <string>
license: <string>
target: <enum>
type: <enum>
scripts: <map<string, string>>
includes: <enum | list<string>>
dependencies:
apm: <list<ApmDependency>>
mcp: <list<McpDependency>>
devDependencies:
apm: <list<ApmDependency>>
mcp: <list<McpDependency>>
compilation: <CompilationConfig>
policy: <PolicyConfig>
marketplace: <MarketplaceConfig> # OPTIONAL; marketplace authoring

marketplace: is the source for apm pack’s marketplace output and is OPTIONAL. Repositories that do not publish a marketplace omit it entirely. The block, its schema, and the build flow are documented in the Authoring a marketplace guide. Within marketplace:, the inheritable fields name, description, and version default to the top-level values above and SHOULD be omitted unless an override is required.


Typestring
RequiredMUST be present
DescriptionPackage identifier. Free-form string (no pattern enforced at parse time). Convention: alphanumeric, dots, hyphens, underscores.
Typestring
RequiredMUST be present
Pattern^\d+\.\d+\.\d+ (semver; pre-release/build suffixes allowed)
DescriptionSemantic version. A value that does not match the pattern SHOULD produce a validation warning (non-blocking).
Typestring
RequiredOPTIONAL
DescriptionBrief human-readable description.
Typestring
RequiredOPTIONAL
DescriptionPackage author or organization.
Typestring
RequiredOPTIONAL
DescriptionSPDX license identifier (e.g. MIT, Apache-2.0).
Typestring | list<string>
RequiredOPTIONAL
DefaultAuto-detect: vscode if .github/ exists, claude if .claude/ exists, codex if .codex/ exists, windsurf if .windsurf/ exists, all if multiple target folders exist, minimal if none
Allowed valuesvscode · agents · copilot · claude · cursor · opencode · codex · gemini · windsurf · all

Controls which output targets are generated during compilation and installation. Accepts a single string or a list of strings. When unset, a conforming resolver SHOULD auto-detect based on folder presence. Unknown values MUST raise a parse error pointing at the offending token. Auto-detection applies only when target: is unset.

# Single target
target: copilot
# Multiple targets -- flow-list form
target: [claude, copilot]
# Multiple targets -- block-list form (equivalent)
target:
- claude
- copilot

When a list is specified, only those targets are compiled, installed, and packed — no output is generated for unlisted targets. all cannot be combined with other values.

ValueEffect
vscodeEmits AGENTS.md at the project root (and per-directory files in distributed mode)
agentsAlias for vscode
copilotAlias for vscode
claudeEmits CLAUDE.md at the project root
cursorEmits to .cursor/rules/, .cursor/agents/, .cursor/skills/
opencodeEmits to .opencode/agents/, .opencode/commands/, .opencode/skills/
codexEmits AGENTS.md and deploys skills to .agents/skills/, agents to .codex/agents/
geminiEmits GEMINI.md and deploys to .gemini/commands/, .gemini/skills/, .gemini/settings.json
windsurfEmits AGENTS.md and deploys to .windsurf/rules/, .windsurf/skills/, .windsurf/workflows/, .windsurf/hooks.json
allAll targets. Cannot be combined with other values in a list.
minimalAGENTS.md only at project root. Auto-detected only — this value MUST NOT be set explicitly in manifests; it is an internal fallback when no target folder is detected.
Typeenum<string>
RequiredOPTIONAL
DefaultNone (behaviour driven by package content; synthesized plugin manifests use hybrid)
Allowed valuesinstructions · skill · hybrid · prompts

Declares how the package’s content is processed during install and compile. Currently behaviour is driven by package content (presence of SKILL.md, component directories, etc.); this field is reserved for future explicit overrides.

ValueBehaviour
instructionsCompiled into AGENTS.md only. No skill directory created.
skillInstalled as a native skill only. No AGENTS.md output.
hybridBoth AGENTS.md compilation and skill installation.
promptsCommands/prompts only. No instructions or skills.
Typemap<string, string>
RequiredOPTIONAL
Key patternScript name (free-form string)
ValueShell command string
DescriptionNamed commands executed via apm run <name>. MUST support --param key=value substitution.
Typestring (literal auto) | list<string>
RequiredOPTIONAL
DefaultUndeclared (legacy implicit auto-publish; flagged by apm audit)
Allowed valuesauto or a list of paths relative to the project root

Declares which local .apm/ content the project consents to publish when packing or deploying. Three forms are supported:

  1. Undeclared — field omitted. Legacy behaviour: all local .apm/ content is published as if auto were set. apm audit emits an includes-consent advisory (the check itself passes; the message recommends declaring includes: auto) whenever local content is deployed under this form.
  2. includes: auto — explicit consent to publish all local .apm/ content via the file scanner. No path enumeration required. Default for newly initialised projects.
  3. includes: [<path>, ...] — explicit allow-list of paths the project consents to publish. Strongest governance form; changes are reviewable in PR diffs.
# Form 1: undeclared (legacy; audit advisory)
# includes: <omitted>
# Form 2: explicit auto-publish (default for new projects)
includes: auto
# Form 3: explicit path list (strongest governance)
# includes:
# - .apm/instructions/
# - .apm/skills/my-skill/

includes: is allow-list only. There is no exclude: form. The field controls which .apm/ content the project consents to publish; it cannot be used to fence off subdirectories of .apm/ from the scanner. To keep maintainer-only primitives out of shipped artifacts, author them OUTSIDE .apm/ and reference them via a local-path devDependency — see Dev-only Primitives.

When policy.manifest.require_explicit_includes is true (see Governance guide), only form 3 passes the policy check; auto and undeclared are rejected at install/audit time by the explicit-includes policy check (not at YAML parse time).

Typemap<string, string>
RequiredOPTIONAL
DescriptionConsumer-side controls for org policy discovery and verification. All fields are optional; defaults preserve current fail-open install behaviour.
policy:
fetch_failure_default: warn # warn | block, default warn (#829)
hash: "sha256:<hex>" # optional consumer-side pin on the org policy bytes
hash_algorithm: sha256 # sha256 (default) | sha384 | sha512
Sub-keyTypeDefaultAllowed valuesSemantic
fetch_failure_defaultstringwarnwarn, blockPosture when no enforceable policy is available — covers fetch failures (malformed, cache_miss_fetch_fail, garbage_response) AND no-policy outcomes (no_git_remote, absent, empty). warn keeps installs unblocked when GitHub is unreachable or no org policy is published; block opts into fail-closed semantics for both apm install and apm audit --ci. See Network failure semantics.
hashstringunset<algo>:<hex-digest> (e.g. sha256:6a8c...e2f1)Pin on the raw bytes of the fetched leaf org policy. Verified before YAML parsing; mismatch is always fail-closed regardless of fetch_failure_default. See Hash pin: policy.hash.
hash_algorithmstringsha256sha256, sha384, sha512Digest algorithm for policy.hash. Inferred from the <algo>: prefix when present; this field is the explicit override. MD5 and SHA-1 are rejected at parse time.

Typeobject
RequiredOPTIONAL
Known keysapm, mcp

Contains two OPTIONAL lists: apm for agent primitive packages and mcp for MCP servers. Each list entry is either a string shorthand or a typed object. Additional keys MAY be present for future dependency types; conforming resolvers MUST ignore unknown keys for resolution but MUST preserve them when reading and rewriting manifests, to allow forward compatibility.


4.1. dependencies.apmlist<ApmDependency>

Section titled “4.1. dependencies.apm — list<ApmDependency>”

Each element MUST be one of two forms: string or object.

Grammar (ABNF-style):

dependency = url_form / shorthand_form / local_path_form
url_form = ("https://" / "http://" / "ssh://git@" / "git@") clone-url
shorthand_form = [host "/"] owner "/" repo ["/" virtual_path] ["#" ref]
local_path_form = ("./" / "../" / "/" / "~/" / ".\\" / "..\\" / "~\\") path

clone-url MAY include a :port segment on https://, http://, and ssh://git@ forms (e.g. ssh://git@host:7999/owner/repo.git). The SCP shorthand git@host:path cannot carry a port — : is the path separator in that form. When a port is present, APM preserves it across all clone attempts: the SSH attempt uses ssh://host:PORT/... and the HTTPS fallback uses https://host:PORT/... (same port on both protocols).

SegmentRequiredPatternDescription
hostOPTIONALFQDN (e.g. gitlab.com)Git host. Defaults to github.com.
portOPTIONAL165535Non-default port on ssh://, https://, http:// clone URLs. Not expressible in SCP shorthand.
owner/repoREQUIRED2+ path segments of [a-zA-Z0-9._-]+Repository path. GitHub uses exactly 2 segments (owner/repo). Non-GitHub hosts MAY use nested groups (e.g. gitlab.com/group/sub/repo).
virtual_pathOPTIONALPath segments after repoSubdirectory or file within the repo. See §4.1.3.
refOPTIONALBranch, tag, or commit SHAGit reference. Commit SHAs matched by ^[a-f0-9]{7,40}$. Semver tags matched by ^v?\d+\.\d+\.\d+.

Examples:

dependencies:
apm:
# GitHub shorthand (default host) — each line shows a syntax variant
- microsoft/apm-sample-package # latest (lockfile pins commit SHA)
- microsoft/apm-sample-package#v1.0.0 # pinned to tag (immutable)
- microsoft/apm-sample-package#main # branch ref (may change over time)
# Non-GitHub hosts (FQDN preserved)
- gitlab.com/acme/coding-standards
- bitbucket.org/team/repo#main
# Full URLs
- https://github.com/microsoft/apm-sample-package.git
- http://github.com/microsoft/apm-sample-package.git
- git@github.com:microsoft/apm-sample-package.git
- ssh://git@github.com/microsoft/apm-sample-package.git
# Custom ports (e.g. Bitbucket Datacenter, self-hosted GitLab)
- ssh://git@bitbucket.example.com:7999/project/repo.git
- https://git.internal:8443/team/repo.git
# Virtual packages
- ComposioHQ/awesome-claude-skills/brand-guidelines # subdirectory
- contoso/prompts/review.prompt.md # single file
# Azure DevOps
- dev.azure.com/org/project/_git/repo
# Local path (development only)
- ./packages/my-shared-skills # relative to project root
- ../sibling-repo/my-package # parent directory

REQUIRED when the shorthand is ambiguous (e.g. nested-group repos with virtual paths).

FieldTypeRequiredPattern / ConstraintDescription
gitstringREQUIRED (remote)HTTPS URL, SSH URL, or FQDN shorthandClone URL of the repository. Required for remote dependencies.
pathstringOPTIONAL / REQUIRED (local)Relative path within the repo, or local filesystem pathWhen git is present: subdirectory or file (virtual package). When git is absent: local filesystem path (must start with ./, ../, /, or ~/).
refstringOPTIONALBranch, tag, or commit SHAGit reference to checkout.
aliasstringOPTIONAL^[a-zA-Z0-9._-]+$Local alias.

Remote dependency (git URL + sub-path):

- git: https://gitlab.com/acme/repo.git
path: instructions/security
ref: v2.0
alias: acme-sec

Local path dependency (development only):

- path: ./packages/my-shared-skills

Monorepo sibling reference (git: parent):

# In agents/pkg-a/apm.yml inside org/monorepo
- git: parent
path: skills/shared

The literal sentinel git: parent is valid only inside a transitively resolved package whose clone coordinates are known to the resolver. APM expands parent to the consumer’s host, repo_url, and resolved ref, with virtual_path set from path. The lockfile records the expanded coordinates — parent MUST NOT appear as durable identity (repo_url / source). path is REQUIRED for git: parent and is normalised to a single relative path; absolute paths and .. traversal are refused. ref and alias overrides are accepted; when ref is omitted the parent’s resolved ref is inherited.

A dependency MAY target a subdirectory or a file within a repository rather than the whole repo. Conforming resolvers MUST classify virtual packages using the following rules, evaluated in order:

KindDetection ruleExample
Filevirtual_path ends in .prompt.md, .instructions.md, .agent.md, or .chatmode.mdowner/repo/prompts/review.prompt.md
Subdirectoryvirtual_path does not match any file extension aboveowner/repo/skills/security

Classification is by extension only — never by path segment. A path like owner/repo/collections/security (no extension) is Subdirectory: the actual on-disk shape (APM package with apm.yml, skill bundle, or plugin) is resolved at fetch time by probing for apm.yml first.

Removed (#1094): the legacy .collection.yml / .collection.yaml virtual-package form is no longer supported. Convert any such reference to an apm.yml with a dependencies: section, then reference the resulting subdirectory as a regular subdirectory virtual package.

Conforming writers MUST normalise entries to canonical form on write. github.com is the default host and MUST be stripped; all other hosts MUST be preserved as FQDN.

InputCanonical form
https://github.com/microsoft/apm-sample-package.gitmicrosoft/apm-sample-package
git@github.com:microsoft/apm-sample-package.gitmicrosoft/apm-sample-package
gitlab.com/acme/repogitlab.com/acme/repo

4.2. dependencies.mcplist<McpDependency>

Section titled “4.2. dependencies.mcp — list<McpDependency>”

Each element MUST be one of two forms: string or object.

A plain registry reference: io.github.github/github-mcp-server

FieldTypeRequiredConstraintDescription
namestringREQUIREDNon-emptyServer identifier (registry name or custom name).
transportenum<string>Conditionalstdio · sse · http · streamable-httpTransport protocol. REQUIRED when registry: false. Values are MCP transport names, not URL schemes: remote variants connect over HTTPS.
envmap<string, string>OPTIONALEnvironment variable overrides. Values may contain ${VAR}, ${env:VAR}, or ${input:<id>} references — see §4.2.4.
argsdict or listOPTIONALDict for overlay variable overrides (registry), list for positional args (self-defined).
versionstringOPTIONALPin to a specific server version.
registrybool or stringOPTIONALDefault: true (public registry)false = self-defined (private) server. String = custom registry URL.
packageenum<string>OPTIONALnpm · pypi · ociPackage manager type hint.
headersmap<string, string>OPTIONALCustom HTTP headers for remote endpoints. Values may contain ${VAR}, ${env:VAR}, or ${input:<id>} references — see §4.2.4.
toolslist<string>OPTIONALDefault: ["*"]Restrict which tools are exposed.
urlstringConditionalEndpoint URL. REQUIRED when registry: false and transport is http, sse, or streamable-http.
commandstringConditionalSingle binary path; no embedded whitespace unless args is also presentBinary path. REQUIRED when registry: false and transport is stdio.

4.2.3. Validation Rules for Self-Defined Servers

Section titled “4.2.3. Validation Rules for Self-Defined Servers”

When registry is false, the following constraints apply:

  1. transport MUST be present.
  2. If transport is stdio, command MUST be present.
  3. If transport is http, sse, or streamable-http, url MUST be present.
  4. If transport is stdio, command MUST be a single binary path with no embedded whitespace. APM does not split command on whitespace; use args for additional arguments. A path that legitimately contains spaces (e.g. /opt/My App/server) is allowed when args is also provided (including an explicit empty list args: []), signaling the author has taken responsibility for the shape.
dependencies:
mcp:
# Registry reference (string)
- io.github.github/github-mcp-server
# Registry with overlays (object)
- name: io.github.github/github-mcp-server
tools: ["repos", "issues"]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Self-defined server (object, registry: false)
- name: my-private-server
registry: false
transport: stdio
command: ./bin/my-server
args: ["--port", "3000"]
env:
API_KEY: ${{ secrets.KEY }}

4.2.4. Variable References in headers and env

Section titled “4.2.4. Variable References in headers and env”

Values in headers and env may contain three placeholder syntaxes. APM resolves them per-target so secrets stay out of generated config files where possible.

SyntaxSourceVS CodeCopilot CLI / Codex
${VAR}host environmentTranslated to ${env:VAR} (resolved at server-start by VS Code)Resolved at install time from env (or interactive prompt)
${env:VAR}host environmentNative — passed through verbatimResolved at install time from env (or interactive prompt)
${input:<id>}user promptNative — VS Code prompts at runtimeNot supported — use ${VAR} or ${env:VAR} instead
<VAR> (legacy)host environmentNot recognizedResolved at install time (kept for back-compat)
  • VS Code has native ${env:VAR} and ${input:VAR} interpolation, so APM emits placeholders rather than baking secrets into mcp.json. Bare ${VAR} is normalized to ${env:VAR} for you.
  • Copilot CLI has no runtime interpolation, so APM resolves ${VAR}, ${env:VAR}, and the legacy <VAR> at install time using os.environ (or an interactive prompt when missing). Resolved values are not re-scanned, so a value containing literal ${...} text is preserved.
  • Codex currently resolves only the legacy <VAR> placeholder at install time; ${VAR} / ${env:VAR} are passed through verbatim in the Codex adapter today.
  • Recommended: Use ${VAR} or ${env:VAR} in all new manifests — they work on every target that supports remote MCP servers. <VAR> is legacy and only resolved by Copilot CLI and Codex; in VS Code it would silently render as literal text in the generated config.
  • Registry-backed servers — APM auto-generates input prompts from registry metadata for ${input:...}.
  • Self-defined servers — APM detects ${input:...} patterns in apm.yml and generates matching input definitions automatically.

GitHub Actions templates (${{ ... }}) are intentionally left untouched.

dependencies:
mcp:
- name: my-server
registry: false
transport: http
url: https://my-server.example.com/mcp/
headers:
Authorization: "Bearer ${MY_SECRET_TOKEN}" # bare env-var
X-Tenant: "${env:TENANT_ID}" # env-prefixed
X-Project: "${input:my-server-project}" # VS Code input prompt

Typeobject
RequiredOPTIONAL
Known keysapm, mcp

Development-only dependencies installed locally but excluded from plugin bundles (apm pack, plugin format is the default). Uses the same structure as dependencies.

devDependencies:
apm:
- owner/test-helpers
- owner/lint-rules#v2.0.0

Created automatically by apm init --plugin. Use apm install --dev to add packages:

Terminal window
apm install --dev owner/test-helpers

Plain apm install (no flag) deploys both dependencies and devDependencies. There is currently no --omit=dev flag — the dev/prod separation kicks in at apm pack (plugin format, the default). The local-content scanner that builds plugin bundles also operates on .apm/ only and does not consult the devDep marker. To keep maintainer-only primitives out of shipped artifacts, author them outside .apm/ and reference them via a local-path devDependency. See Dev-only Primitives.

Local-path devDependency example:

devDependencies:
apm:
- path: ./dev/skills/release-checklist

The compilation key is OPTIONAL. It controls apm compile behaviour. All fields have sensible defaults; omitting the entire section is valid.

FieldTypeDefaultConstraintDescription
targetenum<string>allvscode · agents · claude · codex · gemini · windsurf · allOutput target (same values as §3.6). Defaults to all when set explicitly in compilation config.
strategyenum<string>distributeddistributed · single-filedistributed generates per-directory AGENTS.md files. single-file generates one monolithic file.
single_fileboolfalseLegacy alias. When true, overrides strategy to single-file.
outputstringAGENTS.mdFile pathCustom output path for the compiled file.
chatmodestringChatmode filter for compilation.
resolve_linksbooltrueResolve relative Markdown links in primitives.
source_attributionbooltrueInclude source-file origin comments in compiled output.
excludelist<string> or string[]Glob patternsDirectories to skip during compilation (e.g. apm_modules/**).
placementobjectPlacement tuning. See §6.1.
FieldTypeDefaultDescription
min_instructions_per_fileint1Minimum instruction count to warrant a separate AGENTS.md file.
compilation:
target: all
strategy: distributed
source_attribution: true
exclude:
- "apm_modules/**"
- "tmp/**"
placement:
min_instructions_per_file: 1

After successful dependency resolution, a conforming resolver MUST write a lockfile capturing the exact resolved state. The lockfile MUST be a YAML file named apm.lock.yaml at the project root. It SHOULD be committed to version control.

lockfile_version: "1"
generated_at: <ISO 8601 timestamp>
apm_version: <string>
dependencies: # YAML list (not a map)
- repo_url: <string> # Resolved clone URL
host: <string> # Git host (OPTIONAL, e.g. "gitlab.com")
port: <int> # Non-default git port (OPTIONAL, 1-65535; omitted when default)
resolved_commit: <string> # Full commit SHA
resolved_ref: <string> # Branch/tag that was resolved
version: <string> # Package version from its apm.yml
virtual_path: <string> # Virtual package path (if applicable)
is_virtual: <bool> # True for virtual (file/subdirectory) packages
depth: <int> # 1 = direct, 2+ = transitive
resolved_by: <string> # Parent dependency (transitive only)
package_type: <string> # Package type (e.g. "apm_package", "marketplace_plugin", "meta_package")
content_hash: <string> # SHA-256 of package file tree (e.g. "sha256:a1b2c3...")
is_dev: <bool> # True for devDependencies
deployed_files: <list<string>> # Workspace-relative paths of installed files
mcp_servers: <list<string>> # MCP dependency references managed by APM (OPTIONAL, e.g. "io.github.github/github-mcp-server")
  1. First install — Resolve all dependencies, write apm.lock.yaml.
  2. Subsequent installs — Read apm.lock.yaml, use locked commit SHAs. A resolver SHOULD skip download if local checkout already matches.
  3. --update flag — Re-resolve from apm.yml, overwrite lockfile.

Any runtime adopting this format (e.g. GitHub Agentic Workflows, CI systems, IDEs) MUST implement these steps:

  1. Parse — Read apm.yml as YAML. Validate the two REQUIRED fields (name, version) and the dependencies object shape.
  2. Resolve dependencies.apm — For each entry, clone/fetch the git repo (respecting ref), locate the .apm/ directory (or virtual path), and extract primitives.
  3. Resolve dependencies.mcp — For each entry, resolve from the MCP registry or validate self-defined transport config per §4.2.3.
  4. Transitive resolution — Resolved packages MAY contain their own apm.yml with further dependencies, forming a dependency tree. Resolvers MUST resolve transitively. Conflicts are merged at instruction level (by applyTo pattern), not file level.
  5. Write lockfile — Record exact commit SHAs and deployed file paths in apm.lock.yaml per §7.

name: my-project
version: 1.0.0
description: AI-native web application
author: Contoso
license: MIT
target: all
type: hybrid # instructions | skill | hybrid | prompts
scripts:
review: "copilot -p 'code-review.prompt.md'"
impl: "copilot -p 'implement-feature.prompt.md'"
dependencies:
apm:
- microsoft/apm-sample-package#v1.0.0
- gitlab.com/acme/coding-standards#main
- git: https://gitlab.com/acme/repo.git
path: instructions/security
ref: v2.0
mcp:
- io.github.github/github-mcp-server
- name: my-private-server
registry: false
transport: stdio
command: ./bin/my-server
env:
API_KEY: ${{ secrets.KEY }}
devDependencies:
apm:
- owner/test-helpers
compilation:
target: all
strategy: distributed
exclude:
- "apm_modules/**"
placement:
min_instructions_per_file: 1

VersionDateChanges
0.12026-03-06Initial Working Draft.