# Creating packages

Write your own AI Game Dev package: the aigd-package.json manifest, commands, slash commands, MCP tools, local development with gamedev pkg dev, sharing.

_Last updated: 2026-11-04_

Packages are not only for first-party features: you can write your own to share a repeated task with your team, add a tool for the agent, or preview a custom file format. This page shows the package layout, a minimal working package, the main manifest fields, how to add an MCP tool, how to develop a package against a running app, and how to share it.

## What you need

- The AI Game Dev desktop app or the `gamedev` CLI, version 0.18.0 or newer.
- A text editor. JavaScript is optional: a package can be manifest-only.

Packages follow the model of VS Code extensions: a folder with a manifest, `aigd-package.json`, and optional code that exports `activate(ctx)` and `deactivate()`. Your code reaches the app only through `ctx.aigd`. It runs in a separate extension host process, one per open project, and starts lazily when something needs it.

## Package layout

```text
my-first-package/
  aigd-package.json     the manifest (required)
  extension.js          the code named by "main" (optional)
  media/                icons and images (optional)
  LICENSE
```

A package must be self-contained: the app never runs `npm install` for it. If your code uses libraries, bundle them into the file `main` points to.

## A minimal package

This package adds a `/my-first` slash command, a command palette entry, a composer toolbar button, a status bar item and one setting.

```json
{
  "publisher": "you",
  "name": "my-first-package",
  "version": "0.1.0",
  "displayName": "My First Package",
  "description": "A /my-first slash command, a palette command, a toolbar button and a status bar item.",
  "license": "MIT",
  "main": "extension.js",
  "engines": { "aigd": ">=0.18.0" },
  "activationEvents": ["onSlashCommand:/my-first", "onCommand:you.my-first-package.hello"],
  "contributes": {
    "commands": [
      { "command": "you.my-first-package.hello", "title": "Say hello", "category": "My First Package", "icon": "$(smile)" }
    ],
    "slashCommands": [
      { "name": "/my-first", "description": "Say hello from my first package", "argumentHint": "[name]" }
    ],
    "menus": {
      "composer/toolbar": [{ "command": "you.my-first-package.hello", "group": "navigation" }]
    },
    "statusBar": [
      { "id": "you.my-first-package.status", "text": "My package", "command": "you.my-first-package.hello", "alignment": "left" }
    ],
    "settings": {
      "name": { "type": "string", "default": "game dev", "description": "Who /my-first greets", "scope": "project" }
    }
  }
}
```

And the code, `extension.js` (CommonJS):

```text
const ID = "you.my-first-package";

exports.activate = function activate(ctx) {
  const { aigd } = ctx;
  let count = 0;

  ctx.subscriptions.push(
    aigd.commands.registerCommand(`${ID}.hello`, async () => {
      count++;
      await aigd.window.showInformationMessage(`Hello #${count} from my first package!`);
      return count;
    }),
    // Works in the desktop chat and in the CLI.
    aigd.commands.registerSlashCommand("/my-first", (inv) => {
      const who = inv.args.trim() || aigd.settings.get("name") || "game dev";
      inv.say(`Hi ${who}!`);
    }),
  );

  ctx.log.info("activated");
};

exports.deactivate = function deactivate() {};
```

The slash command, the toolbar button and the status bar item appear from the manifest before any code runs; `activate` is called the first time one of them is used.

## Ids and naming rules

- The package id is `publisher.name`, lowercase: here `you.my-first-package`. The publisher is 2 to 40 characters (letters, digits, single hyphens). Use your own publisher name; `aigd` is AI Game Dev's.
- Every id a package contributes (commands, views, status bar items, tutorials) starts with the package id: `you.my-first-package.hello`.
- `engines.aigd` is required: the oldest AI Game Dev version the package needs, for example `">=0.18.0"`. An older app refuses the package and says which version it needs.
- `version` is semver. Installs are cached by id and version, so every change you publish needs a higher version.

## Main manifest fields

| Field | Required | Purpose |
| --- | --- | --- |
| `publisher`, `name`, `version` | Yes | The id and the version |
| `engines.aigd` | Yes | Minimum app version |
| `displayName`, `description`, `icon`, `license`, `homepage`, `repository`, `keywords` | No | What the Packages window shows |
| `main` | No | The code entry; without it the package is contributions-only |
| `activationEvents` | No | Extra events that start the code, such as `onStartupFinished` |
| `hosts` | No | `desktop`, `cli` or both |
| `dependencies` | No | Other packages, `{ "<id>": "<range>" }` |
| `claudePlugins` | No | Claude plugins the package needs, `{ "<plugin>@<marketplace>": "<range>" }` |
| `contributes` | No | Everything the package adds (below) |

Contribution points in `contributes` include `commands`, `slashCommands`, `capabilities` (switches in the session's Tools row), `mcpServers`, `settings`, `panels`, `viewsContainers` and `views` (tree or webview views in the side panels), `menus` and `submenus`, `keybindings`, `customEditors`, `statusBar` and `tutorials`. Menus use fixed ids such as `commandPalette`, `composer/toolbar`, `explorer/context`, `editor/title` and `view/item/context`, each with an optional `when` clause. The CLI shows slash commands, Tools rows, messages, quick picks, input boxes and forms; menus and panels are desktop only.

## Give the agent a tool with an MCP server

The most common reason to write a package is to give the agent new tools. Declare an MCP server and a capability that switches it on per session:

```json
{
  "publisher": "you",
  "name": "level-tools",
  "version": "0.1.0",
  "engines": { "aigd": ">=0.18.0" },
  "contributes": {
    "capabilities": [
      {
        "id": "you.level-tools",
        "label": "Level tools",
        "description": "Counts collectibles in the level files.",
        "defaultEnabled": false,
        "mcpServer": "you--level-tools"
      }
    ],
    "mcpServers": {
      "you--level-tools": {
        "command": "${node}",
        "args": ["${packageDir}/server.js"]
      }
    }
  }
}
```

- The server name must be `<publisher>--<name>` (or `<publisher>--<name>-<suffix>`) of the package id, at most 96 characters, so names from different authors never collide. An unqualified name is an error.
- Variables you can use in a stdio server: `${node}`, `${packageDir}`, `${projectDir}` and `${globalStorage}`.
- If an MCP server with the same name is already configured in a session, the package's server is not added and the session shows a notice.

## Develop against the running app

Point a project at your folder instead of a published version. The folder is used in place, reloads when a file it ships changes, and is never auto-updated. The default level is **Local**, which is never committed.

```bash
cd <your project>
gamedev pkg link <path-to-your-package>      # use the folder (default --local)
gamedev pkg list                             # shows "(local dev)" and which level it overrides
gamedev pkg unlink you.my-first-package      # back to the published version
```

If your package has a build step (`src/` built into `dist/`), use `gamedev pkg dev` instead. It runs the package's `dev` script, or re-runs `build` after every source change, links the folder after the first successful build and stops every process it started when you press Ctrl+C:

```bash
gamedev pkg dev <path-to-your-package>
```

In the desktop app the same is in the Packages window: select the package and choose **Use a local folder…**, then **Stop using local folder** to go back. A folder whose manifest names another id is refused.

**Tip**

Logs from your package (`ctx.log`, `console.*`) appear in the app log tagged `pkg:<your id>`, and a failing package lists its errors in the Packages window's **Errors** tab.

## Share your package

The AI Game Dev registry carries first-party packages. To share your own, publish it on GitHub or npm; anyone can then add it by link:

```bash
gamedev pkg add github:you/my-first-package           # not pinned: receives updates
gamedev pkg add github:you/my-first-package@v0.1.0    # a tag (never auto-updated)
gamedev pkg add npm:my-first-package                  # an npm package
gamedev pkg add npm:my-first-package@^0.1.0           # with a version range
```

For a team, add it at the **Project** level so it is committed in `.ai-game-dev/packages.json` and every teammate gets it, pinned by the project lock file.

Before you publish a new version, raise `version` in `aigd-package.json`. A changed file under an unchanged version never reaches an install that already has that version.

## Good practice

- Keep tools focused. Every tool the agent sees takes context; a small, well-described tool is used correctly more often.
- Declare everything you can in the manifest, so the UI is visible before your code starts.
- Use `scope: "project"` for settings the team should share and `scope: "machine"` for paths on one computer.
- Packages run with your user's permissions and without a sandbox. Do not ship secrets in a package, and tell users what your package does.

## Learn more

- [Packages](https://ai-game.dev/docs/wiki/app/packages) for levels, updates and settings.
- [Packages catalog](https://ai-game.dev/docs/wiki/app/packages-catalog) for what the first-party packages do.
- The post [packages, Claude plugins and MCP servers](https://ai-game.dev/blog/ai-game-dev-packages-plugins-mcp) for an MCP server added to a live chat.

---

Source (HTML): https://ai-game.dev/docs/wiki/app/creating-packages
