Extensions
An extension packages capabilities that would otherwise live under
bot/, such as tools, skills, and connections. One mount file adds them
under a namespace without copying their source into your project.
Extensions and Cursor plugins run as trusted host code. A namespace prevents name collisions but doesn't sandbox the package, so 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. The __ separator is reserved, so
root contributions can't impersonate a mounted one.
// bot/extensions/ci.tsimport ci from "@acme/ci-triage";export default ci({ org: "acme", token: () => process.env.BUILDKITE_TOKEN ?? "",});Pass token as a function so it reads BUILDKITE_TOKEN when a tool
calls it.
The extension validates its settings at load, so a missing or mistyped
setting fails agent-sdk validate.
Extension instructions follow 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:
agent-sdk validate --dir .agent-sdk info --dir . --jsoninfo lists each mount and every namespaced name it adds. The same
data appears under extensions in GET /v1/info.
info also shows the egress hosts and secret names each mount adds to
a hosted deployment, and agent-sdk deploy names every extension that
adds egress.
Adjust a mounted extension
To replace or remove one contribution, mount as a directory. Put the
package mount in extension.ts and overlay the short name beneath it.
bot/extensions/ci/ extension.ts tools/ job_log.ts retry_job.ts// bot/extensions/ci/tools/job_log.tsimport { 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,});// bot/extensions/ci/tools/retry_job.tsimport { disableTool } from "@grok/agent-sdk/extensions";export default disableTool();The first file replaces ci__job_log, and 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
doesn't provide.
Mount a Cursor plugin
cursorPlugin() mounts a vendored Cursor or Claude plugin the same way. Skills, commands, agents, and MCP servers become Agent
SDK skills, subagents, and connections.
// bot/extensions/notion.tsimport { 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 the plugin's
MCP placeholders with ${NAME} templates read when the connection
opens. An unresolved placeholder fails
validation instead of mounting a partial server.
Plugin rules and hooks aren't mounted. Overlay a generated
mcp-connections/<server>.ts file to replace or disable one plugin
server. OAuth-backed plugin servers use
Host MCP OAuth.
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 the consumer's
settings.
@acme/ci-triage/ package.json extension.ts instructions.md tools/ failed_jobs.ts job_log.ts skills/ triage.md lib/ buildkite.ts// extension.tsimport { 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"], }),});Package modules import this definition and read the validated
extension.config, with defaults applied. hosting adds its hosts and secret names to the mounting
agent's deployment. Publish the built entry point with its slot
directories and prose assets beside it.
Keep extension instructions and tool descriptions namespace-neutral because the consumer chooses the prefix. Project layout covers each slot's contract.