Skip to main content
Glama
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