agpack - An agent assets manager

2 April 2026

Every AI coding tool has its own directory structure for skills, its own config format for MCP servers, its own spot for custom commands and agents. If you use more than one of these tools, or share resources across projects, you end up manually copying the same files into three or four different directories and keeping multiple MCP configs in sync. This was quite the manual effort that slowly drove me mad.

I tried different tools but none of them solved my issue completely. In March 2026 I therefore spent a weekend building agpack to fix this. The idea is very simple. You declare everything in a single agpack.yml and let the tool handle all of the copying and syncing. It fetches skills, commands, agents, and MCP server configs from various git repos and copies them to the right places for whichever tools you're targeting. A typical config looks like this:

name: my-project
version: 0.1.0

targets:
  - claude
  - opencode
  - cursor

dependencies:
  skills:
    - url: https://github.com/PhilippTh/agent-assets
      path: skills/article-review  # Pick just one skill from the repo
    - url: git@github.com:pbakaus/impeccable.git
      path: source/skills/  # A collection of 20 skills
    - url: https://github.com/PhilippTh/agent-assets
      path: skills/deep-dive
      ref: v1.2.0. # Pin a specific version of a skill

  commands:
    - url: git@gitlab.com:myorg/prompts.git  # Works with any git platform
      path: commands/review.md

  agents:
    - url: https://github.com/PhilippTh/agent-assets
      path: agents/pr-review.md

  mcp:
    - name: context7
      command: npx
      args: ["-y", "@upstash/context7-mcp@latest"]
      env:
        CONTEXT7_API_KEY: ${CONTEXT7_API_KEY}

The url field takes anything git clone understands: HTTPS, SSH, local paths. You can pin to a tag or a commit SHA with ref. The path field is the clever bit: point it at a single skill folder and you get that skill, point it at a parent directory and agpack figures out what's inside and deploys each subfolder separately.

Under the hood it's maybe 1500 lines of Python, split across seven modules. config.py parses and validates the YAML. fetcher.py handles the git operations (shallow clones with sparse checkout when possible, falling back to full clones). Fetches run in parallel using a ThreadPoolExecutor, which matters when you have a dozen dependencies. deployer.py does the actual file copying, using atomic writes (write-to-temp-then-rename) so you never get a half-written file. mcp.py merges MCP server definitions into each tool's config file, which is JSON for most tools but TOML for Codex. lockfile.py tracks what was installed so that agpack sync can clean up files from dependencies you've removed. And envsubst.py resolves ${VAR} references in config values from a .env file or your shell environment, which you need for API keys in MCP configs.

The target mapping was the part that required the most research. Each tool wants its stuff in different places:

Target Skills Commands Agents MCP Config
Claude .claude/skills/ .claude/commands/ .claude/agents/ .mcp.json
OpenCode .opencode/skills/ .opencode/commands/ .opencode/agents/ opencode.json
Codex .agents/skills/ -- -- .codex/config.toml
Cursor .cursor/skills/ -- .cursor/agents/ .cursor/mcp.json
Copilot .github/skills/ .github/prompts/ .github/agents/ .vscode/mcp.json

Unsupported combinations (like commands for Cursor) just get skipped silently. MCP configs are the trickiest because each tool uses a different key for the servers dict (mcpServers for Claude, mcp for OpenCode, servers for Copilot), and OpenCode wants a completely different schema where the command is an array and the type is "local" instead of "stdio". So there's a special _build_opencode_server_object path for that.

The whole thing went from initial commit to PyPI in about four days. First day was the core: config parsing, fetching, deploying skills, the lockfile. Second day I parallelised the fetches, added proper error handling, and wired up the Rich progress display with spinners that turn into checkmarks. Third day was MCP support and environment variable substitution. Fourth day I added support for pointing path at a parent directory that contains multiple skills or commands, CI, and type checking with mypy in strict mode. There are still a few features that I'll implement in the near future and considering how fast the whole AI space is moving I'm sure there will be more requirements coming soon.

It's the kind of tool where you set it up once per project and then forget about it. Run agpack init to get a starter config, edit it, run agpack sync and you are set. When you add or remove dependencies from agpack.yml, the next sync cleans up what's gone and fetches what's new. The agpack status command shows you what's installed vs what's configured, which is useful when you're not sure if you've synced recently.