# Extensions

An extension packages capabilities that would otherwise live under
`bot/`: tools, skills, instructions, connections, hooks, channels,
schedules, subagents, artifacts, and workspace seeds. One mount file
adds them under a namespace without copying their source into your
project.

Extensions and Cursor plugins execute as trusted host code. A namespace
prevents name collisions; it does not sandbox the package or limit its
side effects. Review the package before mounting it.

## Mount an extension

Install the package and add one file under `bot/extensions/`. The
filename becomes the namespace, so this mount contributes names such as
`ci__failed_jobs` and `ci__triage`.

```ts
// bot/extensions/ci.ts
import ci from "@acme/ci-triage";

export default ci({
  org: "acme",
  token: () => process.env.BUILDKITE_TOKEN ?? "",
});
```

Pass `token` as a function so a tool can call it. The function reads
`BUILDKITE_TOKEN` when the tool calls it.

The extension validates its configuration while the project loads.
Missing or mistyped settings fail `agent-sdk validate` before a turn
can call the contributed code.

Extension instructions are appended to the root agent's instructions.
The root agent still needs its own `bot/instructions.md`.

## Inspect a mount

Run discovery after mounting or upgrading an extension:

```text
agent-sdk validate --dir .
agent-sdk info --dir . --json
```

`info` lists each mount and its namespaced tools, skills, connections,
hooks, channels, schedules, subagents, and artifact kinds. The same
data appears under `extensions` in `GET /v1/info`.

A mount can also widen what a hosted deployment reaches. `info` shows the
egress hosts and secret names each mount adds, and `agent-sdk deploy` names
every extension that adds egress before it deploys.

Names inside the extension stay short. A tool authored as
`tools/failed_jobs.ts` appears to the mounting agent as
`<namespace>__failed_jobs`. The `__` separator is reserved, so root
contributions cannot impersonate a mounted one.

## Adjust a mounted extension

Mount as a directory when you need to replace or remove one
contribution. Keep the package mount in `extension.ts`, then overlay the
extension's short name beneath it.

```text
bot/extensions/ci/
  extension.ts
  tools/
    job_log.ts
    retry_job.ts
```

```ts
// bot/extensions/ci/tools/job_log.ts
import { defineTool } from "@grok/agent-sdk/tools";
import {
  jobLogInput,
  readRedactedLog,
} from "../../../lib/ci.js";

export default defineTool({
  description: "Read a redacted CI log.",
  inputSchema: jobLogInput,
  execute: readRedactedLog,
});
```

```ts
// bot/extensions/ci/tools/retry_job.ts
import { disableTool } from "@grok/agent-sdk/extensions";

export default disableTool();
```

The first file replaces `ci__job_log`; the second removes
`ci__retry_job`. Use the matching `disable*` helper for another slot, or
generic `disable()` when the file location already identifies it.
Validation fails when an overlay names a contribution the package does
not provide.

## Mount a Cursor plugin

`cursorPlugin()` adapts a vendored Cursor or Claude plugin into the same
namespace model. Skills, commands, agents, and MCP servers become Agent SDK
skills, subagents, and connections.

```ts
// bot/extensions/notion.ts
import { cursorPlugin } from "@grok/agent-sdk/extensions";

export default cursorPlugin({
  path: "plugins/notion-workspace",
  variables: {
    NOTION_TOKEN: "${NOTION_TOKEN}",
  },
});
```

`path` is relative to the project root. `variables` fills placeholders
declared by the plugin's MCP configuration. Pass a `${NAME}` template.
The connection reads `NAME` when it opens. An unresolved placeholder
fails validation instead of mounting a partial server.

Plugin rules and hooks are not mounted. Overlay a generated
`mcp-connections/<server>.ts` file to replace or disable one plugin
server. OAuth-backed plugin servers use
[Host MCP OAuth](/docs/guides/mcp-oauth.md); transport and tool-filter
details live in [MCP connections](/docs/reference/connections.md).

## Extension contents

| Ships under the namespace | Ignored |
| --- | --- |
| Instructions, tools, skills, MCP and host connections | `agent.ts`, storage, OpenTelemetry |
| Hooks, channels, schedules, and subagents | Playground code, custom sandbox backends |
| Artifact kinds and workspace seed files | Nested extensions |

`agent-sdk validate` reports unsupported paths.

## Build an extension

Lay out the package like the shareable part of `bot/`. The root
`extension.ts` supplies a description and validates any settings the
consumer must provide.

```text
@acme/ci-triage/
  package.json
  extension.ts
  instructions.md
  tools/
    failed_jobs.ts
    job_log.ts
  skills/
    triage.md
  hooks/
    audit.ts
  lib/
    buildkite.ts
```

```ts
// extension.ts
import { defineExtension } from "@grok/agent-sdk/extensions";
import { z } from "zod";

export default defineExtension({
  root: import.meta.dirname,
  description: "Investigate failing Buildkite jobs.",
  config: z.object({
    org: z.string(),
    token: z.function().args().returns(z.string()),
    logTailLines: z.number().int().positive().default(200),
  }),
  hosting: () => ({
    egressDomains: ["api.buildkite.com"],
    secretNames: ["BUILDKITE_TOKEN"],
  }),
});
```

Modules inside the package import this definition and read
`extension.config`, which is already validated and has defaults
applied. Publish the built entry point with its slot directories and
prose assets beside it.

`hosting` names the hosts the package calls and the secrets it reads
from the environment. It receives the validated settings, and the
mounting agent's `hosting.egressDomains` and `hosting.secretNames` gain
whatever it returns, so a hosted deploy reaches Buildkite without the
consumer listing `api.buildkite.com`. List `BUILDKITE_TOKEN` in
`secretNames`. Call `extension.config.token()` inside a tool.

The [Project layout](/docs/reference/project-layout.md) owns each contribution's
authoring contract. Keep extension instructions and tool descriptions
namespace-neutral because the consumer chooses the final prefix.

## Related

- [Cloud agents](/docs/guides/cloud-agents.md): delegate repository work
  through an extension
- [Jev](/docs/guides/jev.md) (experimental): typed answers, then gated
  writes
- [Self-improvement](/docs/guides/improve.md): propose source changes
  through pull requests
- [Project layout](/docs/reference/project-layout.md): contribution slots
- [Tools](/docs/reference/tools.md) and [Skills](/docs/reference/skills.md): capability contracts
