Project layout
A Bot Development Kit (BDK) project keeps agent files under bot/. Each
file's path determines how BDK loads it.
Existing projects rooted at agent/ remain supported.
Folder structure
For the capabilities below, identity comes from the path.
Path under bot/ | Resolves to |
|---|---|
tools/approve_pr.ts | tool approve_pr |
mcp-connections/linear.ts | MCP connection linear (model + host) |
host-connections/inventory.ts | Host MCP connection inventory (host + mcp oauth only) |
skills/pr-review.md | skill pr-review |
subagents/reviewer/ | subagent reviewer |
extensions/ci.ts | extension mount ci; its contributions become ci__<name> |
extensions/notion.ts | Cursor plugin mount notion (cursorPlugin); its skills, agents, and MCP servers become notion__<name> |
channels/drive.ts | channel drive, routes under /v1/channels/drive |
The root agent takes its name from package.json name, falling back
to the directory name. When serving multiple agents, the slug is the
directory name and must match [A-Za-z0-9][A-Za-z0-9_-]* (and not the
reserved v1, playground, or docs segments).
Project tree
Most projects start with this shape.
my-agent/├── package.json├── bot/│ ├── agent.ts # model and local harness config│ ├── instructions.md # always-on system prompt (required)│ ├── tools/│ │ └── approve_pr.ts # one typed tool per file│ ├── skills/│ │ └── pr-review.md # on-demand procedures (SKILL.md convention)│ ├── mcp-connections/│ │ └── linear.ts # tools from external MCP servers│ ├── host-connections/│ │ └── inventory.ts # host-only MCP tools│ ├── subagents/│ │ └── reviewer/│ │ ├── agent.ts│ │ └── instructions.md│ ├── channels/│ │ └── github.ts # messages and external events│ └── hooks/│ └── audit.ts # observe session events└── evals/ └── readiness.eval.ts # regression casesEvals stay in evals/ at the project root, as a sibling of bot/, never
inside it. See Evals.
Folder reference
Each path maps to a capability and a reference page.
Path under bot/ | What it is | Reference |
|---|---|---|
agent.ts | defineAgent({ model?, local? }); the model defaults to grok-4.5 with effort=high, fast=true | Agent config |
instructions.md | Always-on system prompt, required on the root agent (.ts and directory forms exist) | Instructions |
tools/<name>.ts | One typed tool; filename = tool name. execution: "server" (in-process, default) or "agent" (a script that runs where the agent runs) | Tools |
skills/* | SKILL.md-convention procedures, loaded on demand | Skills |
mcp-connections/<name>.ts | MCP servers, available to the model, to server tools (ctx.host.mcp), and to channel/schedule handlers (args.host.mcp) | MCP connections |
host-connections/<name>.ts | Privileged MCP servers for ctx.host.mcp and mcp oauth. The model never sees them. | MCP connections |
subagents/<id>/ | Child agent directory; description required | Subagents |
extensions/<ns>.ts or extensions/<ns>/ | A mounted extension or Cursor plugin; its contributions become <ns>__<name> | Extensions |
channels/*.ts | HTTP surfaces beyond the built-in session API; slack.ts and github.ts use the platform packs | Channels |
hooks/*.ts | Observe-only event subscribers, never fatal | Hooks |
artifacts.ts | defineArtifacts kinds, the tag_artifact opt-in, and retention | Artifacts |
schedules/* | Cron-driven runs (UTC, 5-field; never auto-fire under --dev) | Schedules |
reminders/*.ts | Named reminder handlers that stay armed across restarts | Schedules |
sandbox/workspace/** | Seed files copied into each local session workspace | Sessions |
lib/ | Import-only shared code, never discovered | None |
Discovery rejects a bot/result.ts file. To act on a turn's decision, have
the model record it with an authored tool, then act on it from a
turn.completed hook.
Evals use project-root paths:
| Project-root path | What it is | Reference |
|---|---|---|
evals/evals.config.ts | Shared eval settings (e.g. maxConcurrency); required when evals exist | Evals |
evals/**/*.eval.ts | Filesystem evals; case id = path under evals/ | Evals |
Use bot/lib/ for shared imports. A .ts file in one of the listed
discovery folders loads as a definition; unrecognized directories produce
a validation warning.
Inspect discovery
Run bdk validate --dir . and bdk info --dir ..
validate prints diagnostics, and serve refuses to start on
error-severity ones. Warnings print but don't block. info lists the discovered
surface, so a missing tool or channel shows up immediately. From
there, check the folder reference: the file is usually in the wrong directory
or has the wrong extension.
bdk validate --dir . # diagnostics; non-zero exit on errorsbdk info --dir . # human-readable surfacebdk info --dir . --json # machine-readable project info (same shape as GET /v1/info)Related
- Agent config: the config at the project root
- Tools: add typed actions under
tools/ - CLI: commands that discover this tree