Sitecore AI MCP Server
README.md
# Sitecore AI MCP Server
A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server that
exposes two read tools over the SitecoreAI / XM Cloud **Content Management
GraphQL API**:
| Tool | What it does |
| ----------------- | ----------------------------------------------------------------------------------------------------- |
| `get_item_detail` | Fetch one item's full detail: id, name, path, template name, display name, all fields, children count |
| `list_items` | List an item's immediate children, optionally filtered by template |
It runs over **stdio**, so any MCP client (Claude Desktop, Claude Code, etc.)
can launch it as a local subprocess. Authentication uses the **OAuth 2.0
client-credentials** grant, with an in-memory token manager that caches and
proactively refreshes the access token before it expires.
---
## 1. Prerequisites
- Node.js **18+** (uses the built-in global `fetch`).
- A Sitecore XM Cloud environment (or a self-hosted CM instance) whose Content
Management GraphQL API is enabled.
- An OAuth client (client id + secret) that is authorised to call that API.
## 2. Install & build
```bash
npm install
npm run build
```
This compiles `src/**` to `dist/**`. The executable entrypoint is
`dist/index.js`.
## 3. Configure environment variables
Copy the example file and fill in the values:
```bash
cp .env.example .env
```
| Variable | Required | Description |
| ------------------------ | -------- | ------------------------------------------------------------------------------ |
| `SITECORE_API_URL` | ✅ | Content Management GraphQL endpoint, e.g. `https://<cm-host>/sitecore/api/authoring/graphql/v1` |
| `SITECORE_TOKEN_URL` | ✅ | OAuth token endpoint (identity server), e.g. `https://auth.sitecorecloud.io/oauth/token` |
| `SITECORE_CLIENT_ID` | ✅ | OAuth client id |
| `SITECORE_CLIENT_SECRET` | ✅ | OAuth client secret |
| `SITECORE_SCOPE` | ⬜ | OAuth scope, if your identity server requires one |
| `SITECORE_AUDIENCE` | ⬜ | OAuth audience, if your identity server requires one |
> The server never logs the client secret or the access token. Errors are
> surfaced with a category (`auth`, `forbidden`, `not_found`, `invalid_template`,
> `graphql`, `network`, `config`) and a short, secret-free detail string.
### Access policy (deny-by-default)
Every tool call passes through a path-based policy **before** any field values
or children are read:
1. **Zero-network deny** — the well-known Sitecore protected roots
(`/sitecore/system`, `/sitecore/templates`, `/sitecore/layout`) have fixed,
public GUIDs, so a request for one is refused with no network call at all.
2. **Path gate** — for every other id, a minimal *path-only* lookup runs first,
the policy decides, and only then is the item's content fetched. Anything
outside the allow-list is denied by default; a denial surfaces as a
`[forbidden]` tool error.
| Variable | Default | Purpose |
| -------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------- |
| `SITECORE_ALLOW_PATHS` | `/sitecore/content,/sitecore/media library` | Readable path prefixes. Anything not matched is denied. |
| `SITECORE_PROTECTED_PATHS` | `/sitecore/system,/sitecore/templates,/sitecore/layout` | Blocked outright unless developer mode is on. |
| `SITECORE_DEVELOPER_MODE` | `false` | When `true`/`1`/`yes`/`on`, lifts the block on the protected areas. |
> Why block templates/system/layout by default: an agent that can read — and
> especially, once write tools exist, *edit* — a template can take down every
> page built on it with one plausible-looking change. Keep
> `SITECORE_DEVELOPER_MODE` off in any environment an agent reaches
> unsupervised, and turn it on only for a deliberate developer session.
>
> Because the tools are keyed by item **id** (an opaque GUID), the id → path
> mapping for non-root items requires exactly one lightweight metadata lookup;
> the gate then runs before any content, field values, or (future) mutation is
> touched.
### Where to generate the OAuth client id / secret
**XM Cloud (Sitecore Cloud):**
1. Sign in to the [XM Cloud Deploy / Cloud Portal](https://portal.sitecorecloud.io).
2. Open **Credentials** (Organization settings → *Automation client credentials*,
or the environment's **Developer settings**).
3. Create a new client with the scope/role needed to read content via the
Authoring/Content GraphQL API.
4. Copy the generated **Client ID** and **Client Secret** into `.env`.
5. The token endpoint for Sitecore Cloud is typically
`https://auth.sitecorecloud.io/oauth/token` — set that as `SITECORE_TOKEN_URL`.
**Self-hosted XM / CM instance:**
1. Register an OAuth client in your Sitecore Identity Server configuration
(a `ClientCredentials` grant client) with a client id and secret.
2. Grant it the API resource/scope for the GraphQL endpoint.
3. Use your identity server's token endpoint (e.g.
`https://<cm-host>/sitecore/api/identity/token` or the IdentityServer
`/connect/token`) as `SITECORE_TOKEN_URL`.
> The exact GraphQL schema differs slightly between endpoints. This server
> targets the XM Cloud **Authoring & Management GraphQL API** shape
> (`item(where: { itemId, language, version })`, `fields { nodes { name value } }`,
> `children { nodes / totalCount }`). If your endpoint uses a different schema,
> adjust the queries in `src/tools/getItemDetail.ts` and `src/tools/listItems.ts`.
## 4. Register the server in an MCP client
### Claude Desktop (`claude_desktop_config.json`)
Location:
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"sitecore-ai": {
"command": "node",
"args": ["C:\\path\\to\\SItecoreAISimpleMCP\\dist\\index.js"],
"env": {
"SITECORE_API_URL": "https://<cm-host>/sitecore/api/authoring/graphql/v1",
"SITECORE_TOKEN_URL": "https://auth.sitecorecloud.io/oauth/token",
"SITECORE_CLIENT_ID": "your-client-id",
"SITECORE_CLIENT_SECRET": "your-client-secret"
}
}
}
}
```
Restart Claude Desktop; the two tools appear under the 🔌 tools menu.
### Claude Code
```bash
claude mcp add sitecore-ai \
--env SITECORE_API_URL=https://<cm-host>/sitecore/api/authoring/graphql/v1 \
--env SITECORE_TOKEN_URL=https://auth.sitecorecloud.io/oauth/token \
--env SITECORE_CLIENT_ID=your-client-id \
--env SITECORE_CLIENT_SECRET=your-client-secret \
-- node C:\\path\\to\\SItecoreAISimpleMCP\\dist\\index.js
```
## 5. Usage examples
`get_item_detail`:
```json
{ "itemId": "110D559F-DEA5-42EA-9C1C-8A5DF7E70EF9", "language": "en" }
```
`list_items` (optionally filtered by template):
```json
{
"parentId": "0DE95AE4-41AB-4D01-9EB0-67441B7C2450",
"language": "en",
"templateId": "76036F5E-CBCE-46D1-AF0A-4143F9B557AA"
}
```
GUIDs may be dashed, braced (`{...}`), or raw 32-hex — all forms are accepted.
## 6. Tests
```bash
npm test
```
Unit tests cover:
- **Token manager** — grant request shape, caching, proactive refresh before
expiry, concurrent-refresh coalescing, `invalidate()`, and 401/network/config
error handling (plus a check that the secret never leaks into errors).
- **`get_item_detail`** — field mapping, defaults, version passthrough,
not-found handling, and input validation.
- **`list_items`** — child mapping, template filtering (GUID-form-insensitive),
invalid-template detection, empty children, not-found, input validation, and
policy enforcement (protected parent by id/path, per-child filtering).
- **Access policy** — allow-list matching, protected-area blocking, deny by
default, sibling-prefix safety, developer-mode lifting the block, and
`fromEnv` parsing.
Both tool suites mock the GraphQL client; the token-manager suite mocks
`fetch`.
## 7. Project structure
```
src/
index.ts # server entrypoint, registers tools over stdio
sitecoreClient.ts # GraphQL client wrapper (adds bearer token, 401 retry)
itemPath.ts # minimal id -> path lookup used by the policy gate
policy.ts # PathPolicy: deny-by-default allow-list + protected roots
context.ts # ToolContext = { client, policy }
schemas.ts # zod input schemas
errors.ts # SitecoreError + error categories
auth/
tokenManager.ts # OAuth client-credentials fetch/cache/refresh
tools/
getItemDetail.ts # get_item_detail implementation
listItems.ts # list_items implementation
tests/
tokenManager.test.ts
policy.test.ts
getItemDetail.test.ts
listItems.test.ts
```
## License
MIT
TDQS
A3.9/5.0
Scored across 2 tools
Disambiguation5/5
The two tools have clearly distinct purposes: one fetches detailed information about a single item, while the other lists children of an item. There is no overlap or ambiguity.
Naming Consistency5/5
Both tools follow a consistent verb_noun pattern: 'get_item_detail' and 'list_items'. The naming is predictable and conventional.
Tool Count3/5
With only 2 tools, the server feels minimal but not unreasonable for a focused read-only item browsing scenario. The count is borderline per the calibration.
Completeness2/5
The tools only support reading item details and listing children. Missing write operations (create, update, delete), search, or other content management features leave significant gaps for a Sitecore server.
Maintenance
ActivityStale
ResponsivenessNo issues