# AGENTS.md and CLAUDE.md template for website projects

> An AGENTS.md for a website should tell your AI coding agent what it cannot discover from the code: where content comes from, the exact CMS API contract, which strings may be hardcoded, and what it must never do. Codex, Cursor and GitHub Copilot read AGENTS.md as is; Claude Code reads it only while the project has no CLAUDE.md, so any CLAUDE.md should import it with @AGENTS.md, and Gemini CLI needs one setting. The template below fits in about 150 lines, so every agent gets the same rules from one file.

Source: https://diggama.com/guides/agents-md-template-for-websites/
Last updated: 2026-10-07
Tested with: Claude Code 2.1.293

`AGENTS.md` is where a website project tells your AI coding agent the things it cannot work out from the code. For a site whose content lives in a headless CMS, that list is longer than usual: the agent cannot see which strings an editor will want to change, how the CMS API paginates, or which field keys exist. This page gives a complete template for that case, the small files that make Claude Code, Gemini CLI, Cursor and Copilot pick it up, framework variants, a safe MCP config for each agent, and a prompt pack for each stage of the project.

The examples use **Northwind Studio**, the demo company site from the [Astro](/guides/build-a-website-with-ai/astro/) and [Next.js](/guides/build-a-website-with-ai/nextjs/) tutorials, with content in Diggama. Replace the names, and the structure carries over to any headless CMS.

## Which instructions file does each AI coding agent read?

Most agents now read `AGENTS.md`, an open format that [agents.md](https://agents.md/) describes as "a README for agents" and that the Agentic AI Foundation under the Linux Foundation stewards. Write the rules once in `AGENTS.md` and add a pointer only for the two agents that need one.

| Agent | Reads `AGENTS.md` | Its own file | What to add |
|---|---|---|---|
| OpenAI Codex (CLI, IDE, app, cloud) | [Yes](https://learn.chatgpt.com/docs/agent-configuration/agents-md), from the Git root down | `AGENTS.override.md` | Nothing |
| Cursor (editor and CLI) | [Yes](https://cursor.com/docs/context/rules), root and nested | `.cursor/rules/*.mdc` | Nothing |
| GitHub Copilot in VS Code | [Yes](https://code.visualstudio.com/docs/copilot/customization/custom-instructions), `chat.useAgentsMdFile` is on by default | `.github/copilot-instructions.md` | Nothing |
| Claude Code | Since 2.1.277, but [only when there is no `CLAUDE.md`](https://code.claude.com/docs/en/memory#agents-md) | `CLAUDE.md` | A `CLAUDE.md` that starts with `@AGENTS.md`, if you want one |
| Gemini CLI | [Not by default](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/gemini-md.md) | `GEMINI.md` | `context.fileName` in settings, or a `GEMINI.md` that imports it |

```mermaid
flowchart LR
  A["AGENTS.md"] --> X["Codex"]
  A --> CU["Cursor"]
  A --> CP["Copilot"]
  A -->|"imported by"| C["CLAUDE.md"]
  C --> CC["Claude Code"]
  A -->|"context.fileName"| G["Gemini CLI"]
```

All of these files are context, not enforced configuration. Anthropic's [memory documentation](https://code.claude.com/docs/en/memory) says so plainly about `CLAUDE.md`, and it holds for every agent: a rule that must never be broken also needs a permission or approval setting, covered further down.

Two size limits are worth knowing. Anthropic recommends under 200 lines per file: "Longer files consume more context and reduce adherence." Codex concatenates every `AGENTS.md` from the Git root to the current directory and stops at `project_doc_max_bytes`, 32 KiB by default. The template below is about 150 lines and well under that.

## What should an AGENTS.md for a CMS-backed website contain?

It should contain the rules the agent cannot infer from the repository: where content comes from, what may be hardcoded, the exact API contract, the content model, and the actions that are off limits. Build commands and folder layout matter too, but an agent finds most of those on its own.

A CMS-backed site adds three needs: the agent must treat a component full of copy as a bug, know the API's habits (a wrong guess about page size fails silently), and know that the content model belongs to the people who manage the CMS, so new fields are requested, not invented.

## The complete AGENTS.md template

Copy this file to the root of your repository and edit the names, blueprints and tokens. It is framework-neutral; the Astro and Next.js sections below show the changes for each.

```markdown title="AGENTS.md"
<!-- Template: diggama.com/guides/agents-md-template-for-websites/
     One file for every AI coding agent. Keep it under 200 lines. -->

# Northwind Studio website

Company website for Northwind Studio: home, blog, team, contact.
The code lives in this repository. All content lives in Diggama (headless CMS)
and is edited there by the marketing team, never in this repo.

## Stack
- Framework: see "Framework rules" at the end of this file
- TypeScript, strict mode. Node.js 22.
- Content: Diggama REST API v2, read on the server only
- Hosting: Cloudflare Workers, built by Workers Builds on every push to `main`

## Project map
- `src/lib/diggama.ts`: the only module that calls the Diggama API
- `src/components/RichText.*`: the only component that renders CMS HTML
- `src/layouts/`: HTML shell, meta tags, navigation, footer
- `src/pages/`: one file per route

## Commands
- `npm run dev`: local dev server
- `npm run build`: production build, fetches all content from Diggama
- `npm run check`: type check
- After every change, run `npm run check` and `npm run build` and fix until
  both pass. A build that fails because content is missing is working as intended.
- Never run `npm run deploy` or `npx wrangler deploy`. Deploys go through Git.

## Content rules (non-negotiable)
- Never hardcode copy that an editor should change. Every text, image and link
  a marketer might edit comes from Diggama.
- Allowed hardcoded strings: navigation labels, form labels and validation
  messages, the legal footer, the 404 page.
- No fallback copy. Never write `field ?? 'Some default'`. An empty required
  field fails the build with blueprint, record id and field key.
  An empty optional field removes its element from the page.
- If a design needs content that no field holds, stop and tell me which field
  to add (blueprint, name, type). Do not invent it.
- Pages never call fetch() on Diggama. They import from `src/lib/diggama.ts`.
- Never send a Diggama token to the browser: not in client code, not in a
  public env var, not in build output.

## Diggama API contract
- Base URL: `DIGGAMA_API_URL`, default `https://api.diggama.com/v2`
- Headers: `Authorization: Bearer <token>`, `Accept: application/json`
- List: `GET /resources/{blueprint}?page=N&per_page=100`. Default page size 25,
  maximum 100. Loop until `meta.current_page >= meta.last_page`.
- List response: `{ data: [...], meta: { current_page, last_page, per_page, total }, links }`
- Record: `{ id, type, published_at, created_at, updated_at, attributes }`.
  `id` is an opaque string: never build or parse one.
- Singular blueprint: same list endpoint, no published filter, read `data[0]`.
  It always holds exactly one record: never create or delete one (422).
- Drafts: a `view` token returns published records only (singular and
  read-only blueprints have no drafts and always show). The preview token
  also returns drafts and scheduled records. `filter[published]=true` limits
  any token to records whose `published_at` is now or earlier, to the second.
- One record by slug: `filter[slug][eq]=<slug>`.
- Sort: `sort=-published_at,title`. Unknown sort keys are ignored without an
  error, so check every key. An unknown filter key is a 422 `INVALID_FILTER`.
- Values: text, slug, email, link are strings. Image and file are absolute URL
  strings, with no alt text and no dimensions. Rich text is sanitized HTML.
  Date is `YYYY-MM-DD`. A relation is a record id (or an array of ids),
  never the related record: fetch it separately.
- Errors: `{ error: { code, message, details, request_id } }`. Throw with the
  HTTP status, `code`, URL and `request_id`.
- Rate limit: 1,000 requests a minute per project, shared by every token.
  On 429, wait `error.details.retry_after` seconds.

## Blueprints
| Blueprint | Kind | Fields (key: type) |
|---|---|---|
| `homepage` | singular | `hero-title`: text, `hero-subtitle`: text, `hero-image`: image, `intro`: rich text |
| `posts` | resource | `title`: text, `slug`: slug, `excerpt`: text, `cover`: image, `content`: rich text |
| `team-members` | resource | `name`: text, `role`: text, `photo`: image, `bio`: rich text |
| `contact-submissions` | read-only | `name`: text, `email`: email, `message`: text |

- Keys use hyphens: `attributes['hero-title']`. Before writing code against a
  blueprint, confirm its keys with the `diggama` MCP tool `describe_blueprint`.
- Posts have no date field. Use the system field `published_at`.

## Tokens and environment variables (server only)
- `DIGGAMA_TOKEN`: `view` on homepage, posts, team-members. Build time.
- `DIGGAMA_PREVIEW_TOKEN`: `preview` on the same three. Preview builds only.
- `DIGGAMA_FORM_TOKEN`: `create` on contact-submissions only. Runtime secret.
- `DIGGAMA_MCP_TOKEN`: each developer's agent MCP connection, from their shell.
- Never read, print or commit `.env`, `.env.*` or `.dev.vars`.

## Adding a new content type
1. Propose the blueprint in chat: slug, kind, and for each field its name,
   type and whether it is required. Write no code yet.
2. Wait. A human creates it in Diggama (Configuration › Blueprints), adds it
   to the build token and to the MCP connection, then reconnects your MCP client.
3. Call `describe_blueprint` and use the exact keys it returns.
4. Add the attribute type and fetch function to `src/lib/diggama.ts`, then
   the pages. Run check and build.
5. Remind me to add the rebuild Workflow for the new blueprint.

## Using the Diggama MCP server
- Read freely: list_blueprints, describe_blueprint, list_resources, get_resource.
- MCP returns records flat (`record['hero-title']`). The REST API nests them
  under `attributes`. Code always uses the REST shape.
- Write only when I ask, only drafts, and show a `dry_run` first.
- `homepage` has no draft state: an update to it goes live at the next build.
- Never publish, unpublish or delete records. Editors do that.
- Every write fires Diggama Workflows and can start a site build. Writes are
  capped at 60 a minute per project.
- MCP cannot upload files. An image field takes the URL of a file already
  uploaded to Diggama: ask me for it.

## SEO
- One `<h1>` per page. Heading levels never skip.
- Posts: `<title>` from `title`, meta description from `excerpt`.
- Absolute canonical URL on every page, with a trailing slash.
- Open Graph image: the post `cover`, otherwise the homepage `hero-image`.
- Posts get JSON-LD `BlogPosting`: `datePublished` from `published_at`,
  `dateModified` from `updated_at`, `image` from `cover`.
- The sitemap lists every published post. `/contact/thanks/` is excluded
  and has `noindex`.

## Accessibility (WCAG 2.2 AA)
- Landmarks: header, nav, main, footer, plus a skip link to main.
- CMS images have no alt text field: use the adjacent text (post title, member
  name), or `alt=""` when decorative. If an image carries meaning no field
  describes, ask for an alt text field.
- Text contrast at least 4.5:1, large text 3:1. Visible focus on every control.
- Every form field has a label. Errors are linked with `aria-describedby`.
- Targets at least 24 by 24 CSS pixels. Respect `prefers-reduced-motion`.

## Performance budget
- Core Web Vitals at the 75th percentile: LCP 2.5 s or less, INP 200 ms or
  less, CLS 0.1 or less.
- Content is in the HTML. No client-side fetch of CMS content.
- Every `<img>` has width and height. The hero image loads eagerly with
  `fetchpriority="high"`, every other image with `loading="lazy"`.
- At most two self-hosted font files, with `font-display: swap`.
- Ask before adding any npm dependency.

## Deployment
- `DIGGAMA_TOKEN` is a Workers Builds build variable. `DIGGAMA_FORM_TOKEN`
  is a Worker runtime secret.
- One Diggama Workflow per content blueprint POSTs to the Cloudflare deploy
  hook on create, update and delete. The body is `{"event": "..."}`, no data.
  No Workflow on contact-submissions: a form entry must not rebuild the site.

## Framework rules
- Pages render at build time. Only the contact form endpoint runs at request
  time. Ask before adding another server route.

## Never
- Hardcode marketing copy, prices, testimonials, team data or posts.
- Add placeholder copy ("Lorem ipsum", "Coming soon").
- Guess a field key, a blueprint slug or an API parameter.
- Publish, unpublish or delete content in Diggama.
- Deploy, change DNS or edit Cloudflare settings.
- Commit `.env*`, `.dev.vars` or any token.
```

## Why does each rule in the template exist?

Most rules in the template prevent a failure that builds without an error. Those are the expensive ones, because nobody notices until an editor or a visitor does.

- **No fallback copy.** `title ?? 'Design that works'` renders fine on day one. The day the field is empty, invented copy ships and no editor can find where it comes from.
- **Pagination.** Diggama lists return 25 records by default and at most 100 per request ([pagination docs](https://docs.diggama.com/pagination)). Generated code that makes one request shows the first 25 posts and nothing else.
- **Drafts and the singular homepage.** A `view` token never returns drafts or scheduled posts, and a singular resource is always served because it has no publication cycle ([publishing docs](https://docs.diggama.com/publishing)). Adding `filter[published]=true` to the homepage request would exclude it, since it is never published, and the build would fail on an empty list. Creating or deleting a singular record through the API returns a 422 ([blueprints docs](https://docs.diggama.com/blueprints)).
- **Unknown sort keys.** The API ignores them without an error ([sorting docs](https://docs.diggama.com/sorting)). `sort=-date,title` on a blueprint with no `date` field sorts by title only, and if every key is unknown the order is unspecified. Filters, by contrast, fail loudly ([filtering docs](https://docs.diggama.com/filtering-and-search)).
- **Two record shapes.** Over REST, field values sit under `attributes`. Over MCP, the same record comes back flat. The agent reads both in one session and will mix them up unless told which one the code uses.
- **Images and relations.** An image field is a bare URL and a relation is an id ([field types](https://docs.diggama.com/blueprints)). Agents tend to assume objects with `alt` and nested records; they should not.
- **Performance and accessibility numbers.** The budget uses Google's [Core Web Vitals thresholds](https://web.dev/articles/vitals), measured at the 75th percentile. The contrast ratios and the 24 by 24 pixel target size come from WCAG 2.2 success criteria [1.4.3](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html) and [2.5.8](https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html), both level AA. Numbers give the agent something to check; "make it fast" does not.
- **New content types.** Diggama tokens are scoped per blueprint ([authentication docs](https://docs.diggama.com/authentication)). A new blueprint is invisible to the build token until someone adds it, and the build fails with a 403. Neither the REST API nor the MCP server creates blueprints, which is why the template hands that step to a human.
- **One Workflow per blueprint.** A Diggama webhook carries only the event name, such as `{"event": "resource_updated"}`, so the deploy hook cannot tell what changed and rebuilds everything. Scoping each Workflow to a content blueprint keeps form submissions from triggering builds. Scheduled posts need no extra setup: Diggama fires a Resource Updated event within about five minutes of a scheduled record going live ([Workflows docs](https://docs.diggama.com/workflows)). Cloudflare [deploy hooks](https://developers.cloudflare.com/workers/ci-cd/builds/deploy-hooks/) skip redundant builds when a hook fires again before the first build starts, and allow 10 builds per minute per Worker, which matters when an agent writes twenty drafts over MCP.

## How does each agent pick up AGENTS.md?

Codex, Cursor and Copilot need nothing more. Gemini CLI needs one setting or file, and Claude Code needs one as soon as the project has a `CLAUDE.md`.

### Claude Code: a CLAUDE.md that imports AGENTS.md

Claude Code reads `AGENTS.md` only while the project has no `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md`. The moment someone adds one, `AGENTS.md` stops loading. The [memory documentation](https://code.claude.com/docs/en/memory#share-one-file-with-other-coding-tools) recommends an import on the first line, which it never reads twice:

```markdown title="CLAUDE.md"
@AGENTS.md

## Claude Code
- Personal notes go in CLAUDE.local.md, which is in .gitignore.
- Check MCP servers with /mcp before a task that reads Diggama.
```

Keep this file short. Copilot in VS Code also reads `CLAUDE.md` by default (`chat.useClaudeMdFile`), so anything you put here reaches Copilot too.

Claude Code also offers `/init` to draft a file from the codebase, `/memory` to open the loaded files, and `/context` to list which ones loaded. It re-reads the root instruction file after `/compact`; instructions given only in chat are lost.

### Gemini CLI: one setting or a GEMINI.md

Gemini CLI reads `GEMINI.md` and needs to be told about `AGENTS.md`. The [documented key](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/gemini-md.md) is `context.fileName`, not the older top-level `contextFileName`:

```json title=".gemini/settings.json"
{
  "context": {
    "fileName": ["AGENTS.md", "GEMINI.md"]
  }
}
```

The alternative is a `GEMINI.md` whose first line is `@AGENTS.md`, since Gemini CLI imports files with the same `@file.md` syntax. Run `/memory show` to confirm what loaded. If you enable Folder Trust, an untrusted folder ignores workspace settings, this one included.

### Cursor and Copilot: optional scoped rules

Both read `AGENTS.md` at the project root. Cursor also applies nested `AGENTS.md` files to their directory; Copilot in VS Code ignores nested ones unless you turn on `chat.useNestedAgentsMdFiles`. Use their own formats only for what does not belong in the shared file. In Cursor, a project rule in `.cursor/rules/` takes `description`, `globs` and `alwaysApply` frontmatter, so a rule can apply to one file:

```markdown title=".cursor/rules/diggama-client.mdc"
---
description: Rules for the Diggama API client
globs: src/lib/diggama.ts
alwaysApply: false
---
Follow the "Diggama API contract" section of AGENTS.md exactly.
Every exported function pages through the full list and throws on an empty
required field. Do not add caching here: the framework handles it.
```

Copilot's equivalents are `.github/copilot-instructions.md` for the whole repository and `*.instructions.md` files under `.github/instructions/` with an `applyTo` glob for parts of it ([custom instructions documentation](https://code.visualstudio.com/docs/copilot/customization/custom-instructions)).

## How do you adapt the template for Astro?

For Astro, change the stack, the preview token and the framework rules. `create-astro` 5.2.5 writes an `AGENTS.md` and makes `CLAUDE.md` a symbolic link to it (we checked the package source; `--no-ai` skips both), so append the template below the generated part of `AGENTS.md`.

Consider replacing the link with the two-line `CLAUDE.md` shown above. Copilot reads both `AGENTS.md` and `CLAUDE.md` by default, so the link makes it load the same text twice, and the [memory documentation](https://code.claude.com/docs/en/memory) warns that Git checks a committed symlink out as a plain text file on Windows unless `core.symlinks` is enabled.

```diff title="AGENTS.md (Astro changes)"
 ## Stack
-- Framework: see "Framework rules" at the end of this file
+- Framework: Astro 7, `output: 'static'`, `@astrojs/cloudflare` adapter

 ## Tokens and environment variables (server only)
-- `DIGGAMA_PREVIEW_TOKEN`: `preview` on the same three. Preview builds only.
+- `DIGGAMA_DRAFTS`: `true` on the preview build only, where `DIGGAMA_TOKEN`
+  holds a `preview` token. Production never sets it.

 ## Framework rules
-- Pages render at build time. Only the contact form endpoint runs at request
-  time. Ask before adding another server route.
+- Pages are prerendered. Only `src/pages/api/contact.ts` has
+  `export const prerender = false`. Ask before adding another.
+- Env vars are declared in `astro.config.mjs` (`env.schema`) and read from
+  `astro:env/server`. Never use `import.meta.env` for tokens.
+- Prerendered pages read content through `cms` from `src/lib/cms.ts`.
+- Rich text renders only through `src/components/RichText.astro`.
+- No client-side JavaScript on content pages. Ask before adding an island.
+- Check Astro APIs with the `astro-docs` MCP server, not from memory.
```

The last rule needs Astro's documentation server at `https://mcp.docs.astro.build/mcp`, which the [Astro tutorial](/guides/build-a-website-with-ai/astro/) adds next to Diggama's. It needs no token, so register it in your agent the same way as the Diggama server below, without the header. In Claude Code:

```bash
claude mcp add --transport http astro-docs https://mcp.docs.astro.build/mcp
```

## How do you adapt the template for Next.js?

For Next.js, keep the generated block of `AGENTS.md`, append the template below it, fix the paths, and replace the deploy hook with tag revalidation. `create-next-app` 16.4 writes an `AGENTS.md` that sends agents to the docs bundled in `node_modules/next/dist/docs/`, and no `CLAUDE.md`. Claude Code therefore reads `AGENTS.md` until someone adds a `CLAUDE.md`; if you do, start it with `@AGENTS.md`.

```diff title="AGENTS.md (Next.js changes)"
 ## Stack
-- Framework: see "Framework rules" at the end of this file
+- Framework: Next.js 16 App Router, deployed with @opennextjs/cloudflare
 ## Project map
-- `src/lib/diggama.ts`: the only module that calls the Diggama API
-- `src/components/RichText.*`: the only component that renders CMS HTML
+- `lib/diggama.ts`: the only module that calls the Diggama API
+- `components/RichText.tsx`: the only component that renders CMS HTML
+- `app/`: routes, layouts, route handlers
 ## Commands
-- `npm run check`: type check
+- `npx tsc --noEmit`: type check
+- `npm run preview`: build for Cloudflare and run it in the Workers runtime
 ## Tokens and environment variables (server only)
-- `DIGGAMA_TOKEN`: `view` on homepage, posts, team-members. Build time.
-- `DIGGAMA_PREVIEW_TOKEN`: `preview` on the same three. Preview builds only.
+- `DIGGAMA_TOKEN`: `view` on homepage, posts, team-members. Build and runtime.
+- `DIGGAMA_PREVIEW_TOKEN`: `preview` on the same three. Draft Mode only.
+- `REVALIDATE_SECRET`, `DRAFT_SECRET`: for /api/revalidate and /api/draft.
 ## Deployment
-- `DIGGAMA_TOKEN` is a Workers Builds build variable. `DIGGAMA_FORM_TOKEN`
-  is a Worker runtime secret.
-- One Diggama Workflow per content blueprint POSTs to the Cloudflare deploy
-  hook on create, update and delete. The body is `{"event": "..."}`, no data.
+- `DIGGAMA_TOKEN` is both a Workers Builds build variable and a Worker
+  runtime secret. Every other token and secret is a runtime secret only.
+- One Diggama Workflow per content blueprint POSTs to
+  `/api/revalidate?secret=...&blueprint=<slug>`. No rebuild is needed.
 ## Framework rules
-- Pages render at build time. Only the contact form endpoint runs at request
-  time. Ask before adding another server route.
+- Diggama reads happen in Server Components only. `lib/diggama.ts` imports
+  'server-only'. Never expose a token through a NEXT_PUBLIC_ variable.
+- Every cached fetch is tagged `diggama` and `diggama:<blueprint>`.
+  `/api/revalidate` expires them when a Diggama Workflow calls it.
+- Pages pass `{ draft }` from `draftMode()` to every lib/diggama.ts call.
+- Do not upgrade `next` before `npm run preview` passes on the new version.
```

The `next` rule is there for a reason: in our [Next.js tutorial](/guides/build-a-website-with-ai/nextjs/), Next.js 16.4.0 made every page fail with a 500 error under `@opennextjs/cloudflare` 1.20.9, so the tutorial pins 16.3.8. The tutorial also shows the revalidation route and the Draft Mode routes these rules refer to.

## How do you commit the MCP config without leaking the Diggama token?

Commit a project-level MCP config that names an environment variable, never the token itself. Each agent has its own file and syntax, but all of them can keep the secret out of the repository:

| Agent | Project file | Token reference |
|---|---|---|
| Claude Code | `.mcp.json` | `${DIGGAMA_MCP_TOKEN}` |
| OpenAI Codex | `.codex/config.toml` (trusted projects only) | `bearer_token_env_var = "DIGGAMA_MCP_TOKEN"` |
| Cursor | `.cursor/mcp.json` | `${env:DIGGAMA_MCP_TOKEN}` |
| Gemini CLI | `.gemini/settings.json` | `${DIGGAMA_MCP_TOKEN}`, with `httpUrl` |
| Copilot in VS Code | `.vscode/mcp.json` | `${input:diggama-token}`, prompted once |

First create the connection in Diggama under **Configuration › Connect to AI** and copy the token, which is shown once. The Read & write preset gives `view`, `preview`, `create` and `update`. A preset covers only the blueprints that exist when you create it, so create the blueprints first. It also applies to every blueprint, `contact-submissions` included, which would let the agent read visitors' names and emails. Click **Customise per content type** instead and tick only `create` on `contact-submissions`: the agent can still describe that blueprint, but cannot list the messages. The connection is an ordinary project token, so it also appears on the **Configuration › API Tokens** page.

Then export the token in your shell before you start the agent:

```bash
export DIGGAMA_MCP_TOKEN="your-connection-token"
```

### Claude Code

Claude Code expands `${VAR}` and `${VAR:-default}` in the `url` and `headers` of a server in `.mcp.json` ([MCP documentation](https://code.claude.com/docs/en/mcp#environment-variable-expansion-in-mcp-json)).

```json title=".mcp.json"
{
  "mcpServers": {
    "diggama": {
      "type": "http",
      "url": "https://api.diggama.com/mcp",
      "headers": {
        "Authorization": "Bearer ${DIGGAMA_MCP_TOKEN}"
      }
    }
  }
}
```

You can also generate the file with the CLI. Single quotes keep your shell from expanding the variable, so the placeholder is written as is:

```bash
claude mcp add --transport http diggama https://api.diggama.com/mcp \
  --scope project \
  --header 'Authorization: Bearer ${DIGGAMA_MCP_TOKEN}'
```

What we observed with Claude Code 2.1.293: with the variable unset, `claude mcp list` prints `Missing environment variables: DIGGAMA_MCP_TOKEN`, the literal `${DIGGAMA_MCP_TOKEN}` is sent, and Diggama answers `401 Unauthenticated`. A project server shows `Pending approval` until the developer approves it in an interactive session; `claude mcp reset-project-choices` resets those choices. In `claude -p` runs and cloud sessions there is no prompt, and the documentation says project servers load without asking, so set the variable in those environments too.

### OpenAI Codex

Codex reads the token from the variable you name at runtime ([MCP documentation](https://learn.chatgpt.com/docs/extend/mcp)). The project file is used only in trusted projects; otherwise put the same table in `~/.codex/config.toml`, or run `codex mcp add diggama --url https://api.diggama.com/mcp --bearer-token-env-var DIGGAMA_MCP_TOKEN`. The ChatGPT desktop app, the Codex CLI and the IDE extension share this configuration.

```toml title=".codex/config.toml"
[mcp_servers.diggama]
url = "https://api.diggama.com/mcp"
bearer_token_env_var = "DIGGAMA_MCP_TOKEN"
default_tools_approval_mode = "writes"
```

`writes` makes Codex ask before any tool that is not marked read-only. Codex cloud tasks have no documented MCP support, so this applies to the local clients only.

### Cursor

Cursor interpolates `${env:NAME}` in `url` and `headers` ([MCP documentation](https://cursor.com/docs/context/mcp)), and its `agent` CLI uses the same file.

```json title=".cursor/mcp.json"
{
  "mcpServers": {
    "diggama": {
      "url": "https://api.diggama.com/mcp",
      "headers": {
        "Authorization": "Bearer ${env:DIGGAMA_MCP_TOKEN}"
      }
    }
  }
}
```

### Gemini CLI

Gemini CLI uses `httpUrl` for streamable HTTP (`url` means SSE) and expands `$VAR` or `${VAR}` in any string of `settings.json` ([MCP documentation](https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md)). This is the complete project file, with the context setting from earlier:

```json title=".gemini/settings.json"
{
  "context": {
    "fileName": ["AGENTS.md", "GEMINI.md"]
  },
  "mcpServers": {
    "diggama": {
      "httpUrl": "https://api.diggama.com/mcp",
      "headers": {
        "Authorization": "Bearer ${DIGGAMA_MCP_TOKEN}"
      },
      "timeout": 30000
    }
  }
}
```

### GitHub Copilot in VS Code

VS Code can prompt for the token once and store it securely ([MCP configuration reference](https://github.com/microsoft/vscode-docs/blob/main/docs/agents/reference/mcp-configuration.md)):

```json title=".vscode/mcp.json"
{
  "inputs": [
    {
      "type": "promptString",
      "id": "diggama-token",
      "description": "Diggama MCP token",
      "password": true
    }
  ],
  "servers": {
    "diggama": {
      "type": "http",
      "url": "https://api.diggama.com/mcp",
      "headers": {
        "Authorization": "Bearer ${input:diggama-token}"
      }
    }
  }
}
```

VS Code now prefers a portable `.mcp.json` for new servers and keeps `.vscode/mcp.json` for compatibility. We kept the latter because its docs do not yet say which variable syntax the portable format expands. Servers that use `${input:...}` are not forwarded to the Agent Host; for the Agent Host and Copilot CLI, VS Code's documentation points to the portable files instead.

### Two naming notes

Do not reuse `DIGGAMA_TOKEN` for the agent, even though the `.mcp.json` snippet on Diggama's Connect to AI page uses it: in this project that name holds the build's `view` token, and over MCP a token without `preview` never sees drafts or scheduled posts (asking for them returns `FORBIDDEN`). And avoid the credential names that Claude Code always reads as empty in a remote server's `url` and `headers`, such as `ANTHROPIC_API_KEY` or `NPM_TOKEN` ([MCP documentation](https://code.claude.com/docs/en/mcp#credential-variables-that-read-as-empty)). The [MCP guide](/guides/headless-cms-mcp-server/) covers every Diggama tool, the write safeguards, and the hosted apps (ChatGPT, claude.ai) that cannot send a Bearer header.

## How do you enforce what AGENTS.md can only ask for?

Use your agent's permission and approval settings for anything that must never happen. The instructions file shapes what the agent chooses to do; a permission rule blocks the action whatever it decides. In Claude Code, a `deny` rule in `.claude/settings.json` is evaluated first ([permissions documentation](https://code.claude.com/docs/en/permissions)), and a trailing ` *` also matches the bare command:

```json title=".claude/settings.json"
{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "deny": [
      "Bash(npm run deploy *)",
      "Bash(npx wrangler deploy *)",
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./.dev.vars)"
    ]
  }
}
```

The documentation is candid about the limits: a Bash deny rule does not match the same program called by path or inside `sh -c`, and a `Read` rule does not stop a script that opens the file itself.

For Diggama writes, keep the agent asking before each MCP tool call, which is the default almost everywhere:

| Agent | Default for MCP tools | Setting to leave alone |
|---|---|---|
| Claude Code | Asks in the default mode, unless allowed in `permissions.allow` | Do not allow `mcp__diggama` or `mcp__diggama__*` as a whole |
| Codex | Set per server with `default_tools_approval_mode` | Keep `"writes"` as above |
| Cursor | "asks for approval before using MCP tools" | The auto-run allowlist |
| Gemini CLI | Asks for every call | `"trust": true` on the server |
| Copilot in VS Code | Asks; `chat.tools.global.autoApprove` is off | `/autoApprove` in a session |

Treat all of this as a seatbelt: keep the Diggama tokens scoped to what each job needs, and review diffs.

## Which prompts work best for building a CMS-backed website with an AI agent?

The prompts that work name the files, the blueprints and the check that ends the task, and they say what not to do yet. Each prompt below works in Claude Code, Codex, Cursor, Gemini CLI or Copilot, assuming the template is in place and the `diggama` MCP server is connected. They follow the order of a real project.

### Setup and content model

```prompt
Read AGENTS.md and compare it with this repository. List every command, file path, environment variable and blueprint it mentions that does not exist or is named differently, with the line in AGENTS.md. Do not edit anything.
```

```prompt
Read the screenshots in design/. Propose a Diggama content model for this site: for each blueprint give its slug, kind (resource, singular resource or read-only resource) and fields with name, type (text, rich text, slug, email, link, image, file, choice, multi-choice, boolean, number, date, date and time, relation, or another Diggama field type) and whether it is required. Mark which component uses each field. Do not write code; I will create the blueprints in Diggama.
```

```prompt
Using the diggama MCP server, call list_blueprints, then describe_blueprint on each blueprint. Compare the result with the Blueprints table in AGENTS.md and report every missing blueprint, missing field, extra field or type mismatch in a table. Do not change any file.
```

### Data layer

```prompt
Create src/lib/diggama.ts following the Diggama API contract in AGENTS.md. Use describe_blueprint for the exact field keys. Export one attribute type per blueprint and homepage(), posts(), post(slug), teamMembers(). Page through every list with per_page=100 until meta.current_page reaches meta.last_page, read homepage as data[0] of its list without any published filter, and throw errors that include status, error.code, URL and request_id. Read field values from attributes, not from the flat shape MCP returns. Run npm run check. No page yet.
```

### Pages and components

```prompt
Build the homepage from the homepage blueprint through src/lib/diggama.ts: hero-title as the only h1, hero-subtitle, hero-image with width, height and fetchpriority="high", then intro through the RichText component. Follow the content, SEO and accessibility rules in AGENTS.md. Run check and build until both pass.
```

```prompt
I want a client logo strip under the hero. Call describe_blueprint on homepage first. If no field can hold the logos, stop and tell me exactly which blueprint and fields to create in Diggama, with types. Do not write the component until the fields exist.
```

```prompt
The Pricing component in src/components/ contains hardcoded plans, prices and features. List every string and image in it, propose the Diggama blueprint that would hold them, and wait for me to create it. Then replace the hardcoded values with data from src/lib/diggama.ts, keeping the markup and classes unchanged.
```

```prompt
I created the blueprint case-studies in Diggama and added it to the build token and to your MCP connection. Follow "Adding a new content type" in AGENTS.md: describe_blueprint, types and fetch functions in src/lib/diggama.ts, a /case-studies/ index and one page per record by slug, sitemap entries. Then tell me which Workflow to add.
```

### SEO, accessibility and performance

```prompt
Search every component, layout and page for user-facing strings that are not in the allowed list in AGENTS.md (navigation, form labels and messages, legal footer, 404). List each with file, line and the Diggama field it should come from. Do not fix anything yet.
```

```prompt
Run npm run build, then check every HTML file in the build output: one h1, a title under 60 characters, a meta description, an absolute canonical URL with a trailing slash, og:image, and BlogPosting JSON-LD on posts. Report failures per URL in a table, then fix the templates, not the content.
```

```prompt
Audit the built pages against the Accessibility rules in AGENTS.md: landmarks and skip link, alt text on every CMS image, heading order, form labels and aria-describedby on errors, focus styles, and color contrast of the tokens in the CSS. List each issue with file and line, then fix them one commit at a time.
```

```prompt
Check the build output against the Performance budget in AGENTS.md: images without width and height, the hero image loading attributes, lazy loading on the others, font files and font-display, client-side JavaScript per route with its size, and any fetch to api.diggama.com from the browser. Report, then propose fixes.
```

### Content operations through MCP

```prompt
Using the diggama MCP server, draft three posts for Northwind Studio about our design process, 300 to 400 words each, with title, slug, excerpt and content. Show a dry_run of the first one and wait for my approval. Create them as drafts and do not publish.
```

```prompt
Using the diggama MCP server, list posts with an empty excerpt or an empty cover, and team-members with an empty photo or bio. Use detail "full" so empty fields are visible, and page through every result. Give me a table with the record title, the record id and the missing fields. Read only.
```

```prompt
In posts, the company name is written "Northwind Studios" in some records. Find them with list_resources and search, then for each one show me an update_resource dry_run diff of the affected fields. Apply the changes one record at a time only after I approve each diff. Remember that each write triggers a rebuild.
```

### Shipping and maintenance

```prompt
Before I merge: run npm run check and npm run build, then search the build output and every client-side bundle for "Bearer", for api.diggama.com, and for any DIGGAMA_ variable name. Confirm that .env, .env.*, .dev.vars and CLAUDE.local.md are in .gitignore, and that no committed MCP config file contains a literal token. Report what you find.
```

```prompt
The Cloudflare build failed with the log below. Find the Diggama error code, HTTP status and request_id in it, explain the cause using the API contract in AGENTS.md (401 token, 403 missing ability on a blueprint, 404 unknown blueprint slug or record id, 422 INVALID_FILTER or VALIDATION_ERROR, 429 rate limit), and tell me whether the fix belongs in the code or in Diggama.

<paste the build log here>
```

## How do you reuse a prompt across agents?

Save it as a Markdown file in the repository, for example `prompts/audit-copy.md`, and ask any agent to "follow prompts/audit-copy.md for src/components". That works in every tool. The hardcoded-string audit is the best candidate:

```markdown title="prompts/audit-copy.md"
Search the given folder for user-facing strings that are not in the allowed
list in AGENTS.md (navigation labels, form labels and messages, legal footer,
404 page). Include alt attributes, aria-label values, button text and image
URLs. List each one with file, line and the Diggama blueprint and field it
should come from. If no field exists for it, say which field to add
(blueprint, name, type). Do not change any file.
```

To make it a slash command in Claude Code, save it as a project skill in `.claude/skills/<name>/SKILL.md`: the directory name becomes the command. The [skills documentation](https://code.claude.com/docs/en/skills) adds two useful details: `disable-model-invocation: true` stops Claude from running it on its own, and `$ARGUMENTS` receives what you type after the command.

```markdown title=".claude/skills/audit-copy/SKILL.md"
---
description: List user-facing strings that should come from Diggama
disable-model-invocation: true
---
Follow prompts/audit-copy.md for $ARGUMENTS.
```

Run the audit after every feature. That habit, more than any single rule, keeps the site editable by the people who own the content.

## How do you keep AGENTS.md accurate over time?

Treat `AGENTS.md` like code: change it in the same pull request as the thing it describes, and add a rule only when an agent makes the same mistake twice. A new blueprint updates the Blueprints table; a new env var updates the token list. The first prompt in the pack checks the file against the repository and works with any agent.

Anthropic's memory documentation gives the same test (a repeated mistake, a review that catches something the agent should have known, a correction you type twice) and warns that with contradictory instructions Claude "may pick one arbitrarily". That risk grows with every per-tool file, which is why the shared rules live in `AGENTS.md` only. Claude Code users can also run `/doctor prompt-audit` (since 2.1.283), which checks instruction files for references to files or commands that no longer exist and for conflicts, and proposes edits without applying them.

For the rest of the setup (the content model, tokens, deploy hooks and handover to editors), start from the [pillar guide](/guides/build-a-website-with-ai/). Rebuilding an existing Webflow site? The [migration guide](/guides/migrate-from-webflow/) uses this same `AGENTS.md` to keep the rebuilt pages reading from the CMS. Diggama's plans all include MCP and Workflows, start at 29.90 EUR a month, and come with a three-month free trial without a credit card ([pricing](/pricing/)).

## FAQ

### What is an AGENTS.md file?

AGENTS.md is a Markdown file of project instructions for AI coding agents, described on agents.md as a README for agents. It holds what the agent would otherwise have to be told again in every session: commands, conventions, architecture and rules. Agents treat it as context, not as enforced configuration, so a rule that must never be broken also needs a permission or approval setting.

### Which AI coding agents read AGENTS.md?

OpenAI Codex, Cursor and GitHub Copilot in VS Code read AGENTS.md natively. Claude Code reads it since version 2.1.277, but by default only when the project has no CLAUDE.md. Gemini CLI reads GEMINI.md by default and reads AGENTS.md once you list it in the context.fileName setting.

### How do I make Claude Code read AGENTS.md and CLAUDE.md?

Start CLAUDE.md with the line @AGENTS.md, which imports the shared file, then add any Claude-specific notes below it. Without the import, Claude Code reads only CLAUDE.md when both files exist. A user-level setting can also make it read both, but the import works for everyone who clones the repository.

### How long should AGENTS.md be?

Short enough to be followed. Anthropic recommends under 200 lines per CLAUDE.md, because longer files consume more context and reduce adherence, and Codex stops reading project instructions at 32 KiB by default. Keep rules the agent cannot infer from the code and move topic-specific rules to scoped files.

### Can AGENTS.md stop an agent from hardcoding website copy?

It makes hardcoding much less likely, but it cannot guarantee it, because the file is guidance rather than enforcement. Combine it with a clear list of allowed hardcoded strings, a ban on fallback copy, and a regular audit prompt that lists every user-facing string in the components. Review the diff before merging.

### Should I commit my agent's MCP config to the repository?

Yes, as long as it contains no secret. Claude Code, Cursor and Gemini CLI expand an environment variable inside the Authorization header, Codex reads the token from a named variable, and VS Code can prompt for it and store it securely. Each developer then keeps the token in their own environment.
