DEV Community

Cover image for Microsoft's APM is npm for agent context: one apm.yml, 9 harnesses, byte-exact lockfiles
Piekwerk
Piekwerk

Posted on

Microsoft's APM is npm for agent context: one apm.yml, 9 harnesses, byte-exact lockfiles

Microsoft put a dependency manager for AI agents online this week. The Agent Package Manager, APM, describes itself as "like npm for agent context": you declare the skills, prompts, instruction files, plugins and MCP servers a project needs in one apm.yml, and anyone who clones the repo runs apm install to deploy each piece into every harness that supports it. The docs and the manifest spec live at microsoft.github.io/apm, with a targets matrix covering GitHub Copilot, Claude Code, Grok Build, Cursor, OpenCode, Codex, Gemini, Windsurf and Kiro.

If your repo already carries a CLAUDE.md, an AGENTS.md, a .cursor/rules/ directory and a handful of skill folders today, this is squarely your problem domain. I read the manifest spec and the targets documentation end to end. Here is the mapping that matters, the security defaults worth knowing, and the part a lockfile does not fix.

What actually shipped

APM is a CLI you install with brew install apm, curl -sSL https://aka.ms/apm-unix | sh, or irm https://aka.ms/apm-windows | iex on Windows. A minimal package is two manifest fields:

# apm.yml
name: my-pkg
version: 1.0.0
Enter fullscreen mode Exit fullscreen mode

apm init writes exactly that one file and nothing else. The .apm/ source tree next to it is yours to author: instructions/ for always-on rules attached to file globs, skills/ for multi-file capabilities, prompts/ for reusable templates, agents/ for named agent definitions, context/ for shared fragments, hooks/ for lifecycle hooks. From there apm install resolves dependencies, generates a lockfile, and compiles your sources into each target harness's native layout.

That last verb is the important one. APM is a compiler plus a resolver, the same two jobs npm does for code dependencies, aimed at the files we have been hand-copying between harnesses for two years.

The manifest is the contract

The interesting half of the spec is dependency declaration. Adapted from the docs' own example:

# apm.yml, ships with your repo like package.json
name: my-project
version: 1.0.0
dependencies:
  apm:
    - anthropics/skills/skills/frontend-design
    - microsoft/apm-sample-package
    - github/awesome-copilot/plugins/context-engineering#v2.1
    - git: https://gitlab.com/acme/coding-standards.git
      path: instructions/security
      ref: v2.0
  mcp:
    - io.github.github/github-mcp-server
    - io.github.microsoft/playwright-mcp
Enter fullscreen mode Exit fullscreen mode

Three things stand out. GitHub dependencies are plain slugs with optional #ref pinning, and the git form accepts any server, GitLab, Azure DevOps, Bitbucket or Gitea, with a git/path/ref triple where path can name a subdirectory or a single file (the spec calls a file dependency a virtual package). MCP servers live in the same manifest rather than in per-tool JSON blobs, which is what makes install-time policy checks on them possible at all. And every dependency can carry its own targets: list; the spec defines the effective reach as the intersection of your install targets and that list, so a Claude-only skill will not leak into .cursor/.

A skills: field on a dependency selects individual skills instead of taking everything it ships, which is the right granularity for big multi-skill repos.

Where the files actually land

The compile step maps primitives to each harness's native format, and the differences are where hand-maintenance used to hurt. From the targets documentation:

  • Copilot gets AGENTS.md at the project root plus agents under .github/agents/ and instruction files under .github/instructions/
  • Claude Code gets CLAUDE.md and .claude/, with one real quirk: hooks merge into .claude/settings.json instead of living as separate files
  • Cursor gets rules under .cursor/rules/ with the .mdc extension, and instructions cannot be deployed at user scope at all, Cursor only exposes them through its Settings UI
  • Codex gets agents and hooks in TOML under .codex/
  • Gemini gets GEMINI.md with TOML commands, and has no native instructions or agents primitives, both arrive as compiled context files
  • Skills mostly land in .agents/skills/, which Copilot, Cursor, OpenCode, Codex, Gemini and Windsurf all read, while Claude Code, Grok Build and Kiro keep their own .claude/skills/, .grok/skills/ and .kiro/skills/

The discipline rule is the one npm taught us for node_modules, now applied to instruction files: everything compiled is build output, and the docs say it plainly, edit the source under .apm/ and re-run apm install, never edit the deployed copy.

One operational trap: if you omit targets:, APM auto-detects harnesses from filesystem signals like an existing .github/ or .claude/ directory. Great locally, fragile in CI, where a stray directory silently changes what gets deployed. Pin the list.

The lockfile pins bytes, not quality

apm install generates apm.lock.yaml with exact versions and content hashes, so a fresh clone resolves the same package content byte-for-byte. For teams this kills a real failure mode: two machines, two different drafts of a shared skill, and an agent that behaves differently depending on who spawned it.

But a lockfile guarantees determinism, not correctness. A pinned skill that contradicts your actual build tooling is now consistently wrong on every machine, which is quieter and harder to notice than intermittent wrongness. Hashing an instruction set does not review it. This is the same gap AGENTS.md generators have: a generator can emit 1,280 plausible rules and never check one of them against the repo. APM gives that output a version and a hash, which is genuine progress, and it is not validation. Whatever review process you run on hand-written rules still has to run on locked ones.

The security defaults are the interesting part

Three defaults ship on with no opt-in, per the homepage: every install scans package content for hidden Unicode (the confusable-character tricks that hide payloads in text that looks clean), content hashes pin what you audited to what you get, and transitive MCP servers are blocked unless explicitly declared or trusted.

That third one is the supply-chain control. An MCP dependency that quietly pulls in its own servers is the same shape as an npm package reaching for unexpected postinstall scripts, and every ecosystem learned about that the hard way. There is also an apm-policy.yml enforced at install time, with tighten-only inheritance flowing enterprise to org to repo: a lower level can restrict, never relax. If you want a reminder of why MCP-adjacent trust boundaries deserve install-time checks, the MCP Inspector OAuth CVE from last week is a good one.

The docs draw the boundary honestly: "Runtime behavior is your harness's domain." APM gates what touches disk. What an MCP server or a skill does when the agent actually runs it is still governed by your harness's permission config, which is where the live risk sits. Narrowed attack surface, not a closed one.

What it deliberately does not do

Keep three limits in view before you reorganize a repo around it.

First, it is an install-time tool. No sandboxing, no runtime enforcement, no evaluation of anything.

Second, the manifest spec is version 0.3, a Working Draft that carries the standard warning, it may be updated, replaced, or made obsolete at any time. The docs themselves note that older published schemas already reject the newer Git-plus-path dependency form, so schema drift is not hypothetical. Pinning your CI to a v0.3 format is fine; keep an upgrade path.

Third, the target catalog is still moving. Thirteen slugs exist, and four of them (agent-skills, antigravity, hermes, grok-bot) are explicit-only: not auto-detected, not included in --target all. The all value itself is already deprecated and will become a hard parse error in a future release.

If you maintain a multi-harness repo today

The migration shape that follows from the docs:

  1. Run apm init, then move always-on rules into .apm/instructions/, capabilities into .apm/skills/, and agent definitions into .apm/agents/ (one .agent.md per agent: model choice, system prompt, tool whitelist)
  2. Pin targets: explicitly instead of relying on filesystem auto-detect
  3. Commit apm.yml and apm.lock.yaml; apm_modules/ is generated and the installer adds it to .gitignore for you
  4. Treat every compiled directory as build output. Review diffs on .apm/ sources, not on the generated CLAUDE.md
  5. Declare the MCP servers you actually use under dependencies.mcp, so the transitive-server block works in your favor instead of surprising a teammate mid-install. Version drift across MCP servers is its own documented failure mode (MCPhantom measured agents picking deprecated servers in 98% of tracked sessions), and a manifest with pinned refs is the first real countermeasure I have seen shipped by a platform vendor

Honest limits

The docs went live this week, so nobody has production mileage yet, including me. I read the spec and targets documentation closely for this piece and did not migrate a live repository. Adoption is the whole bet for any package manager: it is only as useful as the packages published for it, and today the manifest examples point at a handful of official repos. The direction, though, is the correct one. Agent config stopped being "a file in your repo" and became a dependency graph the moment teams started sharing skills and MCP servers across nine harnesses, and dependency graphs get lockfiles, hashes and policy gates. That is what APM is. Early, draft-numbered, and aimed at exactly the right problem.

Top comments (0)