Complete guide to APM package dependency management - share and reuse context collections across projects for consistent, scalable AI-native development.
APM dependencies are git repositories containing .apm/ directories with context collections (instructions, chatmodes, contexts) and agent workflows (prompts). They enable teams to:
Share proven workflows across projects and team members
Standardize compliance and design patterns organization-wide
Build on tested context instead of starting from scratch
Maintain consistency across multiple repositories and teams
APM supports any git-accessible host — GitHub, GitLab, Bitbucket, self-hosted instances, and more.
Claude Skills are packages with a SKILL.md file that describe capabilities for AI agents. APM can install them and transform them for your target platform:
path: instructions/security# virtual sub-path inside the repo
ref: v2.0# pin to a tag, branch, or commit
- git: git@bitbucket.org:team/rules.git
path: prompts/review.prompt.md
alias: review
Fields: git (required), path, ref, alias (all optional). The git value is any HTTPS or SSH clone URL.
Nested groups (GitLab, Gitea, etc.): APM treats all path segments after the host as the repo path, so gitlab.com/group/subgroup/repo resolves to a repo at group/subgroup/repo. Virtual paths on simple 2-segment repos work with shorthand (gitlab.com/owner/repo/file.prompt.md). But for nested-group repos + virtual paths, use the object format — the shorthand is ambiguous:
gitlab.com/group/subgroup/repo/file.prompt.md
# DON'T — ambiguous: APM can't tell where the repo path ends
# → parsed as repo=group/subgroup, virtual=repo/file.prompt.md (wrong!)
APM normalizes every dependency entry on write — no matter how you specify a package, the stored form in apm.yml is always a clean, canonical string. This works like Docker’s default registry convention:
GitHub is the default registry. The github.com host is stripped, leaving just owner/repo.
Non-default hosts (GitLab, Bitbucket, self-hosted) keep their FQDN: gitlab.com/owner/repo.
For private or corporate MCP servers not published to any registry:
mcp:
- name: internal-knowledge-base
registry: false
transport: http
url: "https://mcp.internal.example.com"
env:
API_TOKEN: "${API_TOKEN}"
headers:
Authorization: "Bearer ${API_TOKEN}"
Stdio example:
mcp:
- name: local-db-tool
registry: false
transport: stdio
command: my-mcp-server
args:
- "--port"
- "8080"
Required fields when registry: false:
transport — always required
url — required for http, sse, streamable-http transports
command — required for stdio transport
⚠️ Transitive trust rule: Self-defined servers from direct dependencies (depth=1 in the lockfile) are auto-trusted. Self-defined servers from transitive dependencies (depth > 1) are skipped with a warning by default. You can either re-declare them in your own apm.yml, or use --trust-transitive-mcp to trust all self-defined servers from upstream packages:
Run apm install --dry-run to preview MCP dependency configuration without writing any files. Self-defined deps are validated for required fields and transport values; overlay deps are loaded as-is and unknown fields are ignored.
This example shows how APM dependencies enable powerful layered functionality by combining multiple specialized packages. The company website project uses microsoft/apm-sample-package as a full APM package and individual prompts from github/awesome-copilot to supercharge development workflows:
company-website/apm.yml
name: company-website
version: 1.0.0
description: Corporate website with design standards and code review
APM automatically retries failed HTTP requests with exponential backoff and jitter. Rate-limited responses (HTTP 429/503) are handled transparently, respecting Retry-After headers when provided. This ensures reliable installs even under heavy API usage or transient network issues.
APM downloads packages in parallel using a thread pool, significantly reducing wall-clock time for large dependency trees. The concurrency level defaults to 4 and is configurable via --parallel-downloads (set to 0 to disable). For subdirectory packages in monorepos, APM attempts git sparse-checkout (git 2.25+) to download only the needed directory, falling back to a shallow clone if sparse-checkout is unavailable.
APM uses instruction-level merging rather than file-level precedence. When local and dependency files contribute instructions with overlapping applyTo patterns:
my-project/
├── .apm/
│ └── instructions/
│ └── security.instructions.md # Local instructions (applyTo: "**/*.py")
│ │ └── apm-sample-package/ # From microsoft/apm-sample-package
│ │ ├── .apm/
│ │ │ ├── instructions/
│ │ │ │ └── design-standards.instructions.md
│ │ │ ├── prompts/
│ │ │ │ ├── design-review.prompt.md
│ │ │ │ └── accessibility-audit.prompt.md
│ │ │ ├── agents/
│ │ │ │ └── design-reviewer.agent.md
│ │ │ └── skills/
│ │ │ └── style-checker/SKILL.md
│ │ └── apm.yml
│ └── github/
│ └── awesome-copilot/ # Virtual subdirectory from github/awesome-copilot
│ └── skills/
│ └── review-and-refactor/
│ ├── SKILL.md
│ └── apm.yml
├── .apm/ # Local context (highest priority)
├── apm.yml # Project configuration
└── .gitignore # Manually add apm_modules/ to ignore
Note: Full APM packages store primitives under .apm/ subdirectories. Virtual file packages extract individual files from monorepos like github/awesome-copilot.
The deployed_files field tracks exactly which files APM placed in your project. This enables safe cleanup on apm uninstall and apm prune — only tracked files are removed.
The mcp_servers field records the MCP dependency references (e.g. io.github.github/github-mcp-server) for servers currently managed by APM. It is used to detect and clean up stale servers when dependencies change.
First install: APM resolves dependencies, downloads packages, and writes apm.lock
Subsequent installs: APM reads apm.lock and uses locked commits for exact reproducibility. If the local checkout already matches the locked commit SHA, the download is skipped entirely.
Updating: Use --update to re-resolve dependencies and generate a fresh lockfile
APM fully resolves transitive dependencies. If package A depends on B, and B depends on C:
apm install contoso/package-a
Result:
Downloads A, B, and C
Records all three in apm.lock with depth information
depth: 1 = direct dependency
depth: 2+ = transitive dependency
Uninstalling a package also removes its orphaned transitive dependencies (npm-style pruning).
You can use any input form — APM resolves it to the canonical identity stored in apm.yml:
Terminal window
apmuninstallacme/package-a
apmuninstallhttps://github.com/acme/package-a.git# same effect
apmuninstallgit@github.com:acme/package-a.git# same effect
# Also removes B and C if no other package depends on them