MCP Server
The Content Island MCP server (@content-island/mcp) is a Model Context Protocol server that exposes your Content Island project to any MCP-compatible AI client — VS Code, Cursor, Windsurf, Claude Desktop, Claude Code, and others.
Once configured, the agent can read your project schema, list and filter contents, create new entries, update field values, publish drafts, upload media, and even manage the project’s schema — creating, editing and deleting models (Entities) and Enums — all without leaving the editor. Under the hood every tool wraps the same operations already documented in the REST API and the JavaScript client library.
Installation
The server is published to npm and runs via npx — no global install is needed. Your MCP client will spawn it on demand using the config snippet below.
npx @content-island/mcp@latestRequirements: Node.js 18 or newer, plus any MCP client (VS Code, Cursor, Windsurf, Claude Desktop, Claude Code, …).
Configure your MCP client
All major MCP clients accept the same JSON configuration shape. Add the snippet below to your client’s MCP servers config — the file location varies per client (e.g. claude_desktop_config.json for Claude Desktop, MCP settings UI for Cursor / Windsurf / VS Code).
{"mcpServers": { "contentIsland": { "command": "npx", "args": ["@content-island/mcp@latest"], "env": { "CONTENT_ISLAND_ACCESS_TOKEN": "<YOUR_CONTENT_ISLAND_ACCESS_TOKEN>" } }}}Replace <YOUR_CONTENT_ISLAND_ACCESS_TOKEN> with your project’s token from the General tab.
Environment variables
| Name | Required | Default | Description |
|---|---|---|---|
CONTENT_ISLAND_ACCESS_TOKEN | yes | — | Project access token. Use a Write Token if you want the agent to perform write operations. |
CONTENT_ISLAND_DOMAIN | no | api.contentisland.net | API domain. Override this when running Content Island on premise. |
CONTENT_ISLAND_SECURE_PROTOCOL | no | true | Whether to use HTTPS. Set to false to fall back to HTTP. |
CONTENT_ISLAND_API_VERSION | no | 1.0 | Version of the REST API to talk to. |
Tools reference
The server registers twelve tools. Two are read-only; the other ten are gated by the write token. The first six manage content; the last six manage the project’s schema (models & enums — see Managing your project schema).
| Tool | Type | Wraps | Client equivalent |
|---|---|---|---|
get-content-island-project | read | GET /project | client.getProject |
list-content-island-contents | read | GET /contents | client.getContentList |
create-content-island-content | write | POST /content | client.createContent |
update-content-island-field-value | write | PUT /content/:id | client.updateContentFieldValue |
publish-content-island-content | write | POST /content/:id/publish | client.publishContent |
upload-content-island-media | write | POST /resource/upload | client.uploadMedia |
create-content-island-model | write | POST /model/entity | client.createModel |
update-content-island-model | write | PUT /model/entity/:id | client.updateModel |
delete-content-island-model | write | DELETE /model/entity/:id | client.deleteModel |
create-content-island-enum | write | POST /model/enum | client.createEnum |
update-content-island-enum | write | PUT /model/enum/:id | client.updateEnum |
delete-content-island-enum | write | DELETE /model/enum/:id | client.deleteEnum |
get-content-island-project
Returns the project’s schema: languages, content types, and the fields of each content type. The agent should normally call this first to know which contentType names, field names and languages are valid.
No input parameters.
list-content-island-contents
Lists content entries with optional filters and pagination. Returns { items, skip, take, hasMore } so the agent can page through large projects.
| Input | Type | Description |
|---|---|---|
contentType | string | Filter by content type name. |
id | string | string[] | A single content id, or an array of ids. |
language | string | Restrict which field values are included (does not remove contents that lack a translation). |
status | 'draft' | 'changed' | 'published' | array thereof | Filter by publication state. Pass ['draft', 'changed'] to find contents with unpublished data. |
includeRelatedContent | boolean | Expand related content references inline. |
sort | { contentType?: 'asc' | 'desc'; lastUpdate?: 'asc' | 'desc' } | Sort order. |
take | number (1–100) | Page size. Default 25. |
skip | number | Offset. Use together with take for pagination. |
create-content-island-content
Creates a new content entry. The agent is expected to call get-content-island-project first so it knows the valid contentType, field names, and language codes.
| Input | Type | Description |
|---|---|---|
contentType | string (required) | The content type name (must match an existing model). |
name | string (required) | Display name for the new entry. |
content | Array<{ language?: string; fields: { name: string; value: any }[] }> (optional) | One entry per language. If omitted, the entry is created without any field values — they can be filled in later with the next tool. |
update-content-island-field-value
Updates (or upserts, when the value doesn’t exist yet) a single field on an existing content entry.
| Input | Type | Description |
|---|---|---|
contentId | string (required) | ID of the content entry. |
fieldName | string (required) | Field name as defined in the model. |
language | string (required) | Language code of the value being updated. |
value | any (required) | The new value. Type depends on the field’s definition. |
publish-content-island-content
Promotes the current draft of a content entry to the live state.
| Input | Type | Description |
|---|---|---|
contentId | string (required) | ID of the content entry. |
upload-content-island-media
Uploads a file from a local path or a public URL to the project’s storage. Returns { name, url }.
| Input | Type | Description |
|---|---|---|
source | string (required) | Local file path (e.g. ./assets/hero.png) or HTTP(S) URL (e.g. https://example.com/photo.jpg). |
fileName | string (optional) | Override the file name registered in Content Island. Defaults to the file name derived from source. |
Managing your project schema (models & enums)
The last six tools let an agent evolve the project’s schema — not just its content. With them the agent can create an Entity (a content type) with a structured field list, create an Enum (a closed list of values), and edit or delete either. All six require a Write Token.
The agent should call get-content-island-project first: it returns every model with its id, name, type (entity / enum) and field/value ids, so the agent can reference things by name in your prompt and resolve the ids itself.
Field types
A field’s type is one of eight primitives — short-text, long-text, number, date, date-time, media, boolean, color — or one of two relational types:
relation— points at another entity. Pass the target entity’s id asrelatedModelId.enum— points at an enum. Pass the enum’s id asrelatedModelId.
Any field can set isArray: true to store a list of that type. You never build the stored "<id>|<Name>" composite — the server assembles it from relatedModelId.
Validations
Each field may carry validations, stored at model creation and enforced when content is saved or published:
| Validation | Args |
|---|---|
required | — |
unique | — |
min-length | { length: <number> } |
max-length | { length: <number> } |
media-type | { allowedExtensions: ["png", "jpg"] } |
Name rules
Model, enum, field and value names must match /^[a-zA-Z0-9_ -]+$/, be at most 128 characters, not be a reserved field-type word (the eight primitives above), and be unique in their scope (models/enums unique in the project; field names and enum values unique within their model, case-insensitive). A violation comes back as 400 VALIDATION_ERROR.
Declarative edits
The update-* tools are declarative: the tool reads the current model and applies your operations on top, so untouched fields/values (and their ids) are preserved automatically — you never re-send the whole list.
update-content-island-model:rename,addFields,updateFields(by id),removeFieldIds.update-content-island-enum:rename,addValues,renameValues(by id),removeValueIds.
Destructive 2-step gate
Removing a field/value or deleting a model/enum is destructive (it cascade-deletes content values). These tools enforce a two-step gate: without confirm: true they return a dry-run describing the impact and mutate nothing. The agent is instructed to never pass confirm: true on the first call — it must show you the dry-run and only re-invoke with confirm: true after you explicitly approve.
// delete-content-island-model, first call (no confirm) → dry-run, nothing deleted{ "dryRun": true, "wouldDeleteContentCount": 12, "summary": "Deleting \"Author\" would also delete 12 content(s) of this type. Re-invoke with confirm: true to apply. …"}Reference protection
Deleting a model or enum that is referenced by a field on another model is rejected with 409 CONFLICT — there is no force override. Remove or repoint the referencing field first, then delete.
create-content-island-model
Creates an Entity with a structured field list.
| Input | Type | Description |
|---|---|---|
name | string (required) | Model name. Non-empty, matches the name rules, not a reserved word, unique in the project. |
fieldList | array (required) | The fields, in order (≥1). Each: { name, type, relatedModelId?, isArray?, validations? }. |
update-content-island-model
Declarative edit of an Entity.
| Input | Type | Description |
|---|---|---|
modelId | string (required) | The id of the entity to update. |
rename | string (optional) | New model name. |
addFields | array (optional) | Fields to append. |
updateFields | array (optional) | Existing fields to modify, keyed by id. |
removeFieldIds | string[] (opt.) | Ids of fields to remove. Destructive — gated by confirm. |
confirm | boolean (opt.) | Apply a destructive change. Never set on the first call (see above). |
delete-content-island-model
Deletes an Entity and all of its content (dry-run unless confirm: true; 409 if referenced).
| Input | Type | Description |
|---|---|---|
modelId | string (required) | The id of the entity to delete. |
confirm | boolean (opt.) | Perform the deletion. Never set on the first call — confirm a dry-run. |
create-content-island-enum
Creates an Enum (closed list of string values).
| Input | Type | Description |
|---|---|---|
name | string (required) | Enum name (same rules as a model name). |
values | string[] (req.) | The values, in order (≥1). Plain strings; the server assigns ids. |
update-content-island-enum
Declarative edit of an Enum.
| Input | Type | Description |
|---|---|---|
enumId | string (required) | The id of the enum to update. |
rename | string (optional) | New enum name. |
addValues | string[] (opt.) | Values to append. |
renameValues | array (optional) | Existing values to rename, as { id, value }. |
removeValueIds | string[] (opt.) | Ids of values to remove. Destructive — gated by confirm. |
confirm | boolean (opt.) | Apply a destructive change. Never set on the first call. |
delete-content-island-enum
Deletes an Enum (dry-run unless confirm: true; 409 if referenced by an enum field).
| Input | Type | Description |
|---|---|---|
enumId | string (required) | The id of the enum to delete. |
confirm | boolean (opt.) | Perform the deletion. Never set on the first call — confirm a dry-run. |
Worked example
A typical end-to-end flow, driven entirely by natural-language prompts (the agent resolves names → ids via get-content-island-project):
- Create an enum — “Create an enum called Size with the values S, M and L.” →
create-content-island-enum. - Create an entity with a relation + enum field — “Create an Author entity with a short-text fullName, then an Article entity with a required title (max 120 chars), a body long-text, a tags list of short-text, a writtenBy relation to Author, and a size field pointing at the Size enum.” → two
create-content-island-modelcalls. - Edit declaratively — “Rename Article to BlogArticle, add a readingMinutes number, and raise title’s max length to 200.” →
update-content-island-model(untouched fields preserved). - Delete safely — “Delete the Author entity.” →
delete-content-island-modelreturns a dry-run (it’s still referenced byArticle.writtenBy); after you remove that field, confirming the deletion succeeds. Trying to confirm while the reference exists returns409 CONFLICT.
Prompt: create-content-island-project
The server also registers a prompt that asks the agent to scaffold a complete frontend project (Astro, Next.js, Nuxt, SvelteKit, Vite, …) wired up to your Content Island project. After the agent fills in the five inputs below, the prompt outputs a detailed step-by-step instruction set the agent must follow — including running the framework CLI, calling get-content-island-project to discover the schema, generating TypeScript interfaces, installing @content-island/api-client, and so on.
| Input | Description |
|---|---|
framework | Framework of choice (Next.js, Astro, Nuxt, SvelteKit, Vite, …). |
pages | Pages to scaffold (e.g. “Homepage, Blog listing, Blog detail, Contact”). |
location | Project location (root directory or a subfolder name). |
styling | Styling preference (Tailwind CSS, CSS Modules, Styled Components, …). |
design | Design assets (wireframes, mockups, or “use best practices”). |