A new engineer learns the unwritten rules in their first month. They find out which template their team builds in, which tools security has signed off on, and which setup quirks will cost them an afternoon if nobody warns them. An AI coding agent starts every session without any of that.
Most teams patch the gap one developer at a time, with a personal CLAUDE.md here and pasted coding standards there, and the knowledge drifts as fast as it spreads. Coder's AI Operating Layer whitepaper argues for the opposite approach: set the guidance once, at the environment level, so every engineer and agent that arrives after inherits it.
Coder Agents runs the agent loop in your Coder control plane rather than inside developer workspaces. That architectural choice puts "institutional knowledge" somewhere you can actually govern it, instead of leaving it to each developer's local setup. This guide covers the four layers Coder Agents gives you for that, then shows how to manage the deployment-wide system prompt as code.
Fair warning on shelf life: the specifics below are a snapshot. They were checked against Coder v2.37.3 and coder/coderd v0.0.28, current as of September 2026, and Coder Agents is moving quickly enough that API paths, limits, and flag names will drift before the ideas do. v2.37 is mainline; stable is v2.36.x, and version-specific notes are called out inline. Check the details against your own deployment, and take the four-layer structure as the durable part.
By the end of this guide, you'll have:
- A deployment-wide system prompt stored in git and deployed through CI
- Domain skills committed alongside the code they describe
- Internal knowledge systems connected through governed MCP servers
- A clear rule for which layer each piece of knowledge belongs in
Four layers of institutional knowledge in Coder Agents
Think of these as concentric rings, from deployment-wide down to per-repository.
- Layer 1 (system prompt): company identity, standards, approved-SaaS list, template routing hints, escalation paths.
- Layer 2 (
.agents/skills/): commit domain playbooks alongside code, under <repo>/.agents/skills/<name>/SKILL.md. Lazy loading means scaling to many skills is cheap.
- Layer 3 (admin MCP servers, organization-scoped): connect the knowledge systems you already run. Use
default_on for broadly useful org-wide knowledge, force_on for security/compliance tooling, default_off for niche integrations.
- Layer 4 (workspace
.mcp.json): repo- or template-specific tools that don't make sense deployment-wide.
- Personal skills: individual engineer preferences, without polluting shared repositories.
Layer 1: Set a deployment-wide system prompt
The deployment-wide system prompt is an admin-set prompt that Coder appends to its built-in prompt for every Coder Agents chat. Put things here that apply to every engineer on every task:
- Company identity ("You are helping engineers at Acme Corp.")
- Coding standards, commit-message conventions, branch-naming rules
- Approved-vendor / approved-SaaS policy and escalation paths
- Template routing hints ("For payments work, prefer the
payments-python template.")
- Environment-specific gotchas your engineers hit repeatedly
You can configure it two ways:
- Dashboard: Admin settings → AI → Coder Agents → Instructions. Admin-only.
- API:
PUT /api/v2/chats/config/system-prompt. This route was promoted out of /api/experimental in Coder v2.37.0. On v2.36 and earlier it is only available at PUT /api/experimental/chats/config/system-prompt; v2.37 serves both paths as a compatibility window, and the source records an intention to unmount the /api/experimental routes once that window closes. Use /api/v2 on v2.37 and later, and expect the compatibility path to disappear.
The PUT payload accepts two fields:
system_prompt: your prompt text.
include_default_system_prompt: when true, your text is appended to Coder's built-in prompt instead of replacing it. Leave it true.
GET on the same path returns the current system_prompt, the include_default_system_prompt flag, and the platform-provided default_system_prompt (for reference). Omitting include_default_system_prompt on a PUT preserves whatever is currently stored, so an older client that sends only system_prompt will not silently flip the flag.
Coder sanitizes the prompt before storing it (LF line endings, invisible Unicode stripped, runs of blank lines collapsed, surrounding whitespace trimmed) and caps the sanitized text at 128 KiB (131,072 bytes). Exceeding the cap returns HTTP 400. The handler separately caps the raw request body, so a payload padded with characters that sanitize away is rejected before it is decoded.
Layer 2: Package domain knowledge as agent skills
Skills are structured, reusable instruction sets that live in the workspace filesystem and are auto-discovered by the workspace agent. Use them for domain playbooks: deep-review, release-checklist, payments-domain-conventions, and so on.
Where Coder Agents discovers skills
The workspace agent runs a resolver that scans a set of scan roots and looks for skills only in a fixed set of container subdirectories under each root.
The agent evaluates these built-in scan roots on every resolve:
- The workspace agent's working directory (whatever it resolves to for that workspace, e.g.
~/myrepo if your repo is cloned there and the agent runs from it).
~/.coder
~/.coder/skills
~/.claude/plugins/cache
Under each scan root, it checks these fixed skill container subdirectories:
skills/
.agents/skills/
.claude/skills/
.codex/skills/
Discovery is shallow: a skill is an immediate subdirectory of one of those containers that holds a SKILL.md. The resolver does not walk the tree.
Concretely, if your repo is cloned to ~/myrepo and the agent's working directory is ~/myrepo, the natural home for a skill is:
Because that path sits inside the repository, you can commit the skill alongside your code, and the agent finds it without any extra setup such as symlinks. Alternatively, ~/.coder/skills/<skill-name>/SKILL.md also works out of the box. That is a deliberate exception to the container rule: when a scan root's own basename is skills, the resolver treats the root itself as a skill container, so skills sit directly beneath it rather than under a subdirectory. Note that ~/.agents/skills (under the home directory) is not a built-in scan root; symlinking to that path will not make skills discoverable.
To register an extra scan root (for example, a second repo cloned elsewhere in the developer's home directory), run this from inside the workspace:
The agent then treats that path as an additional scan root, applying the same discovery rules for instruction files (AGENTS.md, CLAUDE.md, .cursorrules) and skills (.agents/skills/<name>/SKILL.md), and picks up changes live. A .mcp.json inside a registered source is inventoried but not connected. MCP servers load only from the workspace working directory. Related commands include coder exp chat context list, show <path>, remove <path>, and refresh. Paths are not arbitrary: runtime additions are validated against an allow-list that defaults to the home directory, ~/.coder, ~/.claude, and the workspace working directory, so a repo at something like /srv/code is rejected. These commands live under coder exp, which is an experimental surface subject to breaking changes.
Skill directory structure
How skills load into a chat
The workspace agent owns discovery. It watches the filesystem, and when you add or edit a skill it re-scans after a short delay and pushes a new context snapshot to Coder. Chats never scan the workspace themselves; they read the snapshot the agent pushed, and a workspace-attached chat pins one snapshot. The snapshot's skill index holds only each skill's name and description, not the full instructions. That lightweight index is what gets added to a chat.
Here is the sequence when you start a chat:
- Workspace starts. The agent publishes nothing until startup scripts finish, so a chat opened during workspace startup can legitimately show no skills and no MCP tools until the first real push lands.
- The agent resolver scans the scan roots and pushes a snapshot.
- You attach a chat to the workspace, and the chat pins that snapshot.
- On the first turn of that chat, an
<available-skills> block listing each pinned skill's name and description is included in the system prompt. Only frontmatter appears there.
- On demand, when the model decides a skill is relevant, it calls the
read_skill tool to load the full SKILL.md body from the pinned snapshot. Supporting files are fetched via read_skill_file. That is the "lazy" part: full skill contents don't live in the system prompt.
A push never rewrites a chat's pinned snapshot. Chats that have not pinned one yet take the new snapshot immediately; chats that already pinned an older one are marked out of date instead of being switched over.
Two tools are registered when skills are present:
read_skill returns the SKILL.md body from the chat's pinned snapshot, the absolute skill directory (for workspace skills), and a list of supporting files. Because the body comes from the pin, it keeps working when the workspace is unreachable.
read_skill_file returns the content of a supporting file, with path-safe resolution. Supporting files are read over the workspace connection, so this tool and the supporting-file list both need a reachable workspace.
For workspace skills, read_skill also returns dir, the absolute path to the skill directory in the workspace. The agent's read_file and execute tools operate on that same workspace filesystem, so you can join dir with a supporting file's relative path to read or run that file directly, for example to execute a bundled scripts/ helper. read_skill_file remains available as a path-safe convenience for reading supporting files.
Skill naming, size, and path-safety limits
- Names must be kebab-case (
^[a-z0-9]+(-[a-z0-9]+)*$) and match the directory name exactly.
SKILL.md has a maximum size of 64 KB.
- Supporting files have a maximum of 512 KB; files exceeding the limit are silently truncated.
- Path safety:
read_skill_file rejects absolute paths, paths containing .., and references to hidden files. All paths are resolved relative to the skill directory.
- Validity: a skill whose frontmatter is missing, unparsable, not kebab-case, or mismatched with its directory name is reported as invalid and contributes no instructions.
Per-skill limits are not the only ceiling. The whole context snapshot is capped so a large workspace cannot flood a chat's prompt: 64 KiB per file resource, 2 MiB of file content across the snapshot, and 500 resources per snapshot. Resources past a cap are reported as oversize or excluded with no content. MCP server entries count toward the 500-resource cap, but their tool definitions are not measured by the byte caps. Excluded and oversize resources still appear in the chat's context list with their status and error, so a dropped skill is visible rather than silently missing.
Using skills across multiple workspaces
A chat is attached to one workspace at a time. That workspace's agent supplies the skill index, and registering a path never reaches into another workspace's filesystem. If you want a chat to see skills that live elsewhere, bring them into the attached workspace: clone the repository there, or register a local path as an extra scan root with coder exp chat context add.
Avoid symlinks. The container scan skips symlinked skill directories outright, and a symlinked SKILL.md is resolved and rejected when its target escapes the contributing scan root. Copy or clone instead.
Refreshing skills mid-chat
If you attach a workspace, add a new skill, or change scan roots during an ongoing chat, the chat's system prompt does not retroactively update. The agent pushes a new snapshot and marks the chat out of date, but the chat keeps its pinned copy. coder exp chat context refresh has two forms. Run with no argument from inside the workspace, it forces the agent to re-resolve its sources, catching freshly cloned repos and startup-script writes the watcher has not seen, then refreshes every drifted chat on that agent. It authenticates with the agent token, so it does not require coder login. Run with a <chat> argument, it refreshes that one chat through the user-facing API from anywhere, and it re-pins whether or not the chat carries a drift marker. Either way the chat's next turn uses the refreshed context. The dashboard exposes Refresh context only on chats that are marked out of date.
Personal skills for individual preferences
Personal Skills are the user-scoped counterpart, managed under Agents → Settings → Personal Skills. They use the same SKILL.md format and are portable to and from workspace skills, subject to a few extra limits:
- Supporting files are not supported (single
SKILL.md file only).
- Each
SKILL.md is up to 64 KB.
- Each user can create up to 100 personal skills.
Use these for individual preferences that shouldn't live in a shared repository.
Layer 3: Connect internal knowledge systems with MCP servers
Admin-registered MCP servers are how you wire existing knowledge systems into agents: wikis, service catalogs, ticket systems, internal RAG endpoints, vector databases, code search. If it has an API, an MCP server can front it.
Unlike the deployment-wide system prompt, MCP servers are organization-scoped: each organization has its own set, and a chat is only offered servers from its own organization. In a multi-organization deployment you register them once per organization. Organization admins do this at Admin settings → AI → Coder Agents → MCP servers (/ai/settings/mcp-servers). The options that matter:
- Transports.
streamable_http or sse.
- Availability policies:
force_on: always injected into every chat whose owner has access to the server; users cannot opt out. Appropriate for security/compliance tooling. It guarantees the tools are available to the model, not that the model will call them, so treat it as availability control rather than enforcement.
default_on: pre-selected in new chats; users can opt out. A good default for broadly useful org-wide knowledge.
default_off: available in the server list but users must opt in. Good for niche tools.
- Authentication modes: None, OAuth2 (with optional RFC 9728 / RFC 8414 / RFC 7591 auto-discovery and RFC 7009 token revocation), API key, custom headers, or User OIDC Identity (forwards the calling user's OIDC access token as
Authorization: Bearer <token>; only works for users who authenticated to Coder via OIDC). Note that only deployment administrators can add or update a server using this auth mode; organization-admin rights are not enough.
- Tool governance: per-server
tool_allow_list and tool_deny_list. If tool_allow_list is non-empty, only the listed tool names are exposed; tool_deny_list blocks named tools even if they appear in the allow list. You do not have to expose every tool a server ships.
- Identity forwarding (opt-in): setting
forward_coder_headers = true sends Coder identity headers on every outgoing request so a first-party MCP server can correlate a tool call back to the originating chat:
X-Coder-Owner-Id: the Coder user who owns the chat that issued the tool call.
X-Coder-Chat-Id: the top-level (parent) chat ID. For root chats this is the chat's own ID.
X-Coder-Subchat-Id: the subchat ID, absent for root chats.
X-Coder-Workspace-Id: the workspace associated with the chat, absent when the chat has no workspace.
Coder sends the same identity headers to LLM providers. The headers carry no signature, so a server cannot prove they came from Coder. Enable forwarding only for first-party or trusted internal servers reached over a trusted network path.
Only enabled servers are visible to non-admins, and sensitive fields such as API keys and client secrets are never returned in API responses (Coder returns boolean flags indicating whether a value is set).
For MCP servers that are specific to a repository or template (for example, a schema explorer for a particular service, or a repo-local internal API docs server), drop a .mcp.json at the workspace working directory root. Unlike instruction files and skills, .mcp.json is read from the working directory only. A .mcp.json inside a scan root you registered with coder exp chat context add appears in the context inventory, but the agent will not connect to the servers it lists. A template can override the path list with the experimental CODER_AGENT_EXP_MCP_CONFIG_FILES env var.
- Stdio transport: set
command, and optionally args and env. The agent spawns the process in the workspace.
- HTTP transport: set
url, and optionally headers. The agent connects to the HTTP endpoint from the workspace.
- Discovery: the workspace agent connects to the servers in
.mcp.json once startup scripts finish, then reports their tools in the context snapshot it pushes to Coder. Chats read that snapshot rather than re-reading the file each turn. Connecting is bounded at 30 seconds per server; servers that fail to connect are skipped, and partial success is acceptable. Editing .mcp.json does not require a workspace restart, since the agent notices the edit and reloads. But an MCP-only change does not mark existing chats out of date, so an in-flight chat keeps its current tool set and the dashboard shows no Refresh context button. You do not need to fake an unrelated edit to get around that. Run coder exp chat context refresh <chat-id> and it re-pins the chat regardless of its drift marker, picking up the new tool definitions.
- Execution: the snapshot carries tool definitions only. Every workspace MCP tool call is proxied back through the workspace agent, so a chat can list workspace MCP tools while the workspace is unreachable, but calling one requires a running workspace with the server connected.
- Tool naming: tool names are prefixed with the server name as
serverName__toolName to avoid collisions between servers and with built-in tools.
- Tool call timeout: 60 seconds per invocation.
AGENTS.md and template routing
Add repository context with AGENTS.md
Create an AGENTS.md file in the workspace agent's working directory (or ~/.coder/AGENTS.md). It is automatically read and included in the system prompt for every conversation with a Coder Agent that uses that workspace. It's a good place for repository-specific build and test instructions, architectural constraints, and links to runbooks. The resolver also recognizes CLAUDE.md and .cursorrules at the top of any scan root.
Improve template routing with clear descriptions
When the agent needs a workspace, it selects a template by reading the template's name, display name, short description (limited to under 128 characters), and a bounded README excerpt (roughly the first 1,000 characters in the listing view, up to roughly 8,000 characters in the detail view). The list_templates tool orders candidates by query relevance tier first, then by an affinity score that weights the developer's own recent usage of a template far more heavily than organization-wide popularity, which enters only as a logarithmic term. The excerpt is reduced to plain text: frontmatter is stripped, link text is kept while URLs are dropped, images and badges are dropped entirely, and code blocks and tables are preserved as text.
Because the agent never reads the Terraform behind a template, clear and specific descriptions are the strongest routing signal you can give it.
Admins can also restrict which templates the agent may use at Admin settings → AI → Coder Agents → Templates, or with the equivalent toggle on each template's own settings page, without affecting manual workspace creation. When a template disallows agents, the list_templates, read_template, and create_workspace tools all exclude it. If you're still building those templates, our guide to using Coder Agents to author templates walks through turning a Template Builder starter into a tested, reviewable template.
Bring your own knowledge base
Coder Agents ships without a built-in knowledge base or RAG. It acts as the integration and governance layer: you bring the knowledge system you already run (a wiki, code search, a RAG endpoint, or a vector database), and Coder exposes it via MCP with auth, tool governance, and identity forwarding. The same pattern applies to skills. The skills Coder publishes in the registry cover authoring Coder templates and modules rather than your domain, so the approach this guide recommends is to author your own for the conventions that are specific to your organization.
Once every agent in the organization reads the same system prompt, that prompt is production configuration, and production configuration needs an owner. The AI Operating Layer whitepaper makes the same point about infrastructure more broadly, noting that "every layer in a stack depends on someone owning it and a discipline that keeps it current."
Editing the system prompt directly in the Coder dashboard (Admin settings → AI → Coder Agents → Instructions) is fine for iteration, but at enterprise scale the deployment-wide system prompt benefits from the same discipline you apply to any other production configuration: version control, peer review, CI-driven deploys, and drift detection.
Why version-control the system prompt
- Auditability. Every change is a git commit with an author, a reviewer, and a rationale.
- Rollback.
git revert and redeploy.
- Drift detection. With
coderd_agents_system_prompt, a change someone makes in the dashboard shows up as a diff on the next PR plan. The API fallback below has no equivalent, because the stored value is sanitized and a naive text comparison reports differences that are not real.
- Environment parity. The same prompt promotes cleanly across staging and production.
Repository layout for Coder configuration
Here is a minimal repository layout:
Use the coder/coderd Terraform provider for AI providers and models. It authenticates against a Coder deployment via url and token (or the CODER_URL and CODER_SESSION_TOKEN environment variables). The provider as a whole requires Coder v2.10.1 or later, but every Agents-family resource (coderd_agents_system_prompt, coderd_agents_model, coderd_agents_default_model, coderd_agents_mcp_server) requires Coder v2.37.0 or later.
Start at provider v0.0.25 rather than v0.0.24. v0.0.24 introduced the Agents resources but shipped a state migration that failed at plan time for everyone; v0.0.25 is that release plus the fix. The ~> constraint below is a floor rather than an exact pin, so it resolves to the newest 0.0.x, currently v0.0.28. Check the provider's release notes when upgrading.
Here is an example AI provider resource. It uses write-only credential arguments so the live API key value is never stored in Terraform state. The key stays in place each time you run terraform apply, and Terraform only sends a new one when you bump api_key_wo_version to rotate it:
Note: coderd_ai_provider is currently marked experimental by the provider. See its resource documentation for the latest schema and stability guidance before adopting it in production. Write-only arguments require Terraform 1.11 or later.
Write the system prompt file
system-prompt.md is plain markdown, but Coder does not store it verbatim. On write it normalizes line endings to LF, strips invisible Unicode codepoints commonly used for prompt-injection steganography (zero-width space, zero-width joiner, soft hyphen, bidi marks; ZWNJ is deliberately preserved for Persian, Urdu, and Kurdish), collapses runs of three or more newlines to one blank line, and trims surrounding whitespace. Add the file to .prettierignore anyway, so formatter churn never shows up in review diffs. Structure it however maps best to your organization. Here is a generic starting shape:
Nothing in that template is filler. Each block either routes the agent to a template, points at a real internal system, warns about a known footgun, or states a convention. None of it is enforcement, though. Enforcement comes from template restrictions, MCP tool allow-lists, workspace permissions, and network controls. Swap your own systems into the placeholders and keep the shape, because the shape is what makes it useful.
Deploy with a GitHub Actions CI workflow
On Coder v2.37.0 and later, use the coderd_agents_system_prompt resource described below and let terraform plan handle drift detection. It is shorter and more reliable, and you can skip the API step entirely. The workflow below is for cases where the resource is not an option yet: v2.36 or earlier, where it does not exist, or v2.37 before you adopt it.
This is a skeleton. It leaves out authentication to your Terraform state backend, and it does not implement the OIDC roles described at the end of this section. It runs two jobs: a preview on pull requests and a deploy on merge to main.
There are two things to note about this workflow:
- API path.
/api/v2/chats/config/system-prompt is correct for Coder v2.37.0 and later. On v2.36 and earlier, set SYSTEM_PROMPT_PATH to /api/experimental/chats/config/system-prompt instead. v2.37 answers on both, and the compatibility routes are scheduled for removal.
Native Terraform resource for the system prompt. Provider v0.0.24 added coderd_agents_system_prompt (use v0.0.25 or later, per the version note above), an experimental resource that manages the deployment-wide prompt directly and removes the need for the workflow-driven API PUT shown above. It requires Coder v2.37.0 or later and an owner-scoped token. It is a deployment-wide singleton, so declare it once; duplicate resources silently overwrite each other. Coder sanitizes the stored prompt (strips invisible Unicode, normalizes line endings, collapses blank-line runs, trims surrounding whitespace) and the resource compares the same way, so a trailing newline from file(...) does not cause drift. If a prompt was already set in the dashboard, terraform import coderd_agents_system_prompt.this agents_system_prompt before your first apply, or Terraform will overwrite the live value. Note that terraform destroy resets the prompt to empty with include_default_system_prompt = true, because the API has no delete operation for this setting.
resource "coderd_agents_system_prompt" "this" {
system_prompt = trimspace(file("${path.module}/system-prompt.md"))
include_default_system_prompt = true
}
With this resource in place, terraform plan reports dashboard edits as a normal diff, and the Deploy system prompt via API step and the SYSTEM_PROMPT_PATH variable can both be deleted. The provider also ships coderd_agents_mcp_server, coderd_agents_model, and coderd_agents_default_model, so Layer 3 can be version-controlled the same way. All three are experimental, require Coder v2.37.0 or later, and are organization-scoped rather than deployment-wide. You declare them per organization, and their import IDs are <organization-name>/<slug>, <organization-name>/<id>, and <organization-name> respectively.
Scope CI roles with least privilege
Two least-privilege OIDC roles works well for the state backend. You have to wire the OIDC login step yourself, since the skeleton above does not include it.
plan role: read-only against the state backend. Used on PRs.
apply role: read/write against the state backend. Used on merge to main.
Add the github.ref == 'refs/heads/main' condition to the apply job so it only runs on main. Then put it in a concurrency group, which makes deploys run one at a time. Without it, two merges close together could try to update the same Terraform state at once and conflict.
Splitting the state-backend role does not split your privilege against Coder. This is the part worth getting right. Managing AI providers or the deployment-wide system prompt requires a site-level owner token, so a naive setup hands that same token to a job that runs on every pull request. Scope it down: give the PR job a read-only Coder token, or drop its Coder calls entirely and let it plan against state only. If the repository accepts contributions from forks, confirm the owner token is never exposed to a fork-triggered run. Agents models, default-model selection, and MCP servers are organization-scoped and need only organization-admin rights, so keeping the system prompt in a separate, apply-only module lets everything else run below owner.
Getting started with institutional knowledge in Coder Agents
Start with three things: a system prompt carrying your template routing hints, one skill for whatever domain your team asks about most, and one knowledge system your engineers already use. Each layer you add moves more institutional knowledge out of people's heads and into something you can version and review. The getting started guide covers setup, and coder.com/solutions/agents shows how Coder Agents fits alongside the agents your developers already use.
Coder Agents documentation and resources