Your client. Your Strapi.
The recommended setup is to paste the master prompt into Codex from your repository. It configures the local stdio package against your existing project. No hosted endpoint, MCP_API_KEY, or STRAPI_MCP_TOKEN is required.
1. Let Codex configure it
Open Codex in the repository that contains your Strapi project and use the master prompt. It inspects existing environment variables, configures a project-scoped local stdio server, and verifies it read-only. For manual setup, install Node.js 22.5 or newer, then install the latest npm release.
npm install --global safe-strapi-mcp@0.3.0 safe-strapi-mcp --help
View on npm or review the source on GitHub. Releases are built from the public repository with npm provenance.
2. Create a Strapi API token
In Strapi Admin, open Settings → Global settings → API Tokens → Create new API Token. Choose Custom. Grant find and findOne for the page collection, every populated relation, and Upload/media. Grant create and update only for collection types whose drafts Safe Strapi may create. Delete and publish permissions are not required.
Copy the token when Strapi displays it; the full value is shown only once. If page listing works but inspection returns HTTP 403, the token normally lacks read access to a populated relation or media collection.
3. Connect your Strapi project
Create a dedicated private environment file. Use the Strapi origin without /admin or /api, then paste the token after the equals sign.
STRAPI_URL=https://cms.example.com STRAPI_API_TOKEN=paste-token-here
Never commit this file or paste the token into chat. Add both the credential file and Safe Strapi state to the project’s gitignore.
.env.safe-strapi .safe-strapi/
Point the tool at the Strapi source directory containing src/api and src/components. npm 0.3.0 discovers every collection and single type, including those without dynamic zones, UID fields or Draft & Publish. Types appear by name in list_projects; existing page-zone aliases remain available. Upgrade older installations to 0.3.0 and restart your MCP client.
STRAPI_PROJECT_ROOT=/absolute/path/to/strapi STRAPI_CONTENT_TYPE=page STRAPI_BLOCKS_FIELD=blocks STRAPI_SLUG_FIELD=slug STRAPI_ROUTE_FIELD=url STRAPI_TITLE_FIELD=title STRAPI_LOCALE=en
Set STRAPI_ROUTE_FIELD only when the frontend routes by a separate top-level field that must track cloned slugs. Safe Strapi validates it against the content-type schema and shows the change in preview. For an existing token variable such as MY_CMS_TOKEN, set STRAPI_TOKEN_ENV=MY_CMS_TOKEN.
4. Add it to your MCP client
Use absolute paths. This Codex command runs the published npm package and keeps the secret in the env file:
codex mcp add safe-strapi -- \ npm exec --yes --package=safe-strapi-mcp@0.3.0 -- safe-strapi-mcp \ --env-file /absolute/path/to/strapi/.env.safe-strapi \ --project-root /absolute/path/to/strapi codex mcp get safe-strapi
For another MCP client, use the equivalent stdio configuration:
{
"mcpServers": {
"safe-strapi": {
"command": "npm",
"args": [
"exec",
"--yes",
"--package=safe-strapi-mcp@0.3.0",
"--",
"safe-strapi-mcp",
"--env-file",
"/absolute/path/to/strapi/.env.safe-strapi",
"--project-root",
"/absolute/path/to/strapi"
]
}
}
}The MCP process loads the file itself, so the secret does not appear in client configuration or chat. Restart the client after adding the server.
5. Test before writing
“Use Safe Strapi to list my projects and component schemas. Do not make changes.”
“Find the homepage in my project. Inspect its components and return its content hash. Do not write.”
“Preview a draft copy with a new hero title. Show the proposed changes before creating it.”
“Create the draft from that exact preview, validate it, and compare it with the source. Confirm the source hash stayed the same and nothing was published.”
Do not proceed to preview until both project listing and full page inspection succeed. Preview is read-only. The first write happens only when you explicitly approve draft creation.
Your AI client uses the environment file selected at setup. For another Strapi installation, add another named MCP server with its own file and source directory.
Continue with the prompt cookbook for copy-ready examples covering JSON input, component insertion, reordering, long pages, and production team workflows. Agents can fetch the same guide as raw Markdown from /prompts.md.
Schema-first content entry
News, Blogs, models, forms and settings use their actual Content API schemas. Start with list_content_types and inspect_content_type. The inspection returns required fields, defaults, constraints, nested components, relation/media targets, sample entries and precise REST methods/endpoints. It records an inspectionId and schema hash; the preview refuses uninspected or changed schemas.
Research the type’s routes, controllers, services and lifecycle hooks, and read the frontend route and language selector before choosing slugs. Resolve real media IDs using list_media and related documentIds through find_entries/get_entry. Never invent identifiers or assume a Markdown string is a Blocks array.
Call preview_entry_write with the inspectionId, exact type UID, data and create, update or localize. Review the persisted contractVersion 2 diff, exact API request, affected/shared locale fields, hashes and publication state. Apply the approved plan through apply_entry_write with previewId, expectedOperationHash and a stable idempotencyKey. The writer rechecks schema/target/revisions, sends only the approved fields and verifies changed and unchanged content plus published versions.
npm exec --yes --package=safe-strapi-mcp@0.3.0 -- safe-strapi-mcp --env-file /absolute/path/to/project/.env.safe-strapi --project-root /absolute/path/to/strapi --allow-entry-editing
Existing entry updates need allowEntryEditing: true, STRAPI_ALLOW_ENTRY_EDITING=true or the flag above. This is separate from protected page edits. Generic previews can explicitly change schema-approved fields, references and arrays; private/password fields, polymorphic relations and unknown custom storage contracts are refused.
Collections create with POST; translations use PUT on the same documentId; single types use PUT on their singular endpoint. The default localeSlugPolicy: shared copies the source slug and rejects locale suffixes. Configure localized only when the frontend resolves translated slugs. Shared fields cannot change during localization.
Draft & Publish writes always pass status=draft. A type without draft state requires allowImmediateWrite=true and approval of immediate visibility. There is no publish/delete tool.
For new media, use preview_media_upload, approve the exact local file/destination/hash/metadata, then upload_media. Multipart uploads validate signatures, size and hashes and verify the saved Media Library ID/metadata. Assets become immediately visible. Unknown create/upload outcomes are never automatically retried.
All content writes use the REST Content API. Report Strapi JSON errors separately from upstream WAF/authentication HTML errors. Do not bypass security challenges or switch to Computer Use, browser/admin UI, another MCP or shell writes after an API failure.
Available tools
General content: list_content_types, inspect_content_type, find_entries, get_entry, preview_entry_write, apply_entry_write, list_media, preview_media_upload and upload_media.
Legacy pages: list_projects, find_pages, inspect_page, list_components, get_component_schema, preview_clone_and_modify, clone_page_and_modify, inspect_owned_draft, modify_owned_draft, duplicate_component, replace_component, validate_draft, and compare_pages. The release also includes preview_modify_page, modify_page, list_page_operations, preview_rollback_page, and rollback_page.
Operations support insertion, patching, moving, duplication, replacement, and removal on draft copies. API indexes are zero-based and operations run in order. Inspect the current order before selecting a block. Publish reviewed content yourself in Strapi.
Edit an existing draft without cloning
Use the published package with --allow-in-place-editing. Building from source is optional; keep any source checkout separate from your Strapi repository.
git clone https://github.com/metehankasapp/safe-strapi-mcp.git cd safe-strapi-mcp npm ci npm run build node dist/src/index.js --help
node /absolute/path/to/safe-strapi-mcp/dist/src/index.js --env-file /absolute/path/to/strapi/.env.safe-strapi --project-root /absolute/path/to/strapi --allow-in-place-editing
For automatic discovery, the flag or STRAPI_ALLOW_IN_PLACE_EDITING=true enables direct editing. With a custom projects config, set allowInPlaceEditing: true on each selected project; the CLI flag does not override that config. Confirm the setting with list_projects. Existing drafts need update and read permissions; create permission is needed only for the clone workflow.
Inspect the existing document and component schemas. Call preview_modify_page with project, documentId, locale and patch/insert operations. Review the differences, then call modify_page with exactly those operations, expectedPageHash from the returned pageHash, expectedOperationHash from operationHash, and a stable idempotency key. The preview must exist in the same installation’s audit database.
Other blocks, their order and component IDs are preserved. Existing draft ownership is not required. Re-inspect the same document after writing; owned-draft validation tools cannot validate an unowned page. There is no cloning, publishing or top-level slug, route or title change in this workflow.
Only patch and insert are supported. Removing, replacing or moving blocks, clearing nonempty fields, replacing arrays and changing existing media/relations are rejected. Supply all schema attributes for inserted components, including explicit null/empty values for unused optional fields; omit component IDs. Full population and complete schemas are required. Private/custom fields are unsupported.
Draft visibility depends on your frontend configuration. A frontend that reads only published content will not show these changes until a separate publishing step. Static sites may also need a rebuild and deployment.
REST checks cannot prevent every race with external writers. Use schema-first entry tools for general content and the dedicated tools for protected zone edits. Any independent API writer can race the final REST check; no atomic server-side compare-and-swap is claimed.
Consistent previews and explicit rollback
Page previews return contractVersion: "1" for clone, direct-edit and rollback previews. All use the same fields: target, summary, changes, fieldChanges, safety, revision and nextAction. Field changes include old/new values and existence flags. Reports are capped at 500 differences; check fieldChangesTruncated. Ask your AI client to consistently show the target, changes, protection rules, publication state and rollback eligibility before requesting approval.
Direct-edit results return an operationId. Use list_page_operations to find recorded operations for the same project, document and locale. Call preview_rollback_page with that target and operation ID. After approving the preview, call rollback_page with both returned hashes and a new idempotency key. Re-inspect the same draft afterward.
Rollback restores only completed, verified patch-only operations with the exact post-operation revision. Later edits, uncertain outcomes and operations containing insert are rejected. Component count, order, IDs and existing media/relations are preserved. Previously empty scalar values may be restored to empty/null; normal patching still forbids clearing content. No components are removed and nothing is published. Approval is requested by the AI client; the MCP does not independently authenticate a human approval. REST concurrency limits also apply to rollback.
The private audit database contains content snapshots. Preserve it between sessions and keep it out of source control; deleting it removes rollback history.
Remote acceptance workflow
The current source was tested against a remote CMS on one newly created unpublished draft: creation, middle/nested edits, rollback, insertion, optional copy clearing, component removal, and stale rollback rejection. Full hashes of original draft and published pages remained unchanged. A separate saved Codex conversation also verified tool selection, consistent preview labels, follow-up approval turns and actual MCP writes; it used no shell bypass or file changes. See the step-by-step prompts and results.
Clearing and removing test content used modify_owned_draft, restricted to drafts registered by this installation. That tool uses create normalization and can regenerate retained component IDs. It has no dedicated persisted preview tool; inspect first, review an AI-side plan and supply the exact draft hash. Direct patch/insert/rollback preserve existing IDs and reject content removal. Page-record deletion is unavailable. Public frontend visibility was not verified because the HTTP request returned 403.
What is supported?
The package supports Strapi 5 collection types and single types, ordinary scalar fields, Markdown/Blocks rich text, JSON, nested/repeatable components, dynamic zones, media, relations and linked locales. Localhost and remote CMS URLs work when reachable from the MCP. Unknown schemas, private/password fields and polymorphic references are not writable. A verified custom-field storage contract can be configured with customFieldTypes. Draft publishing and record deletion remain unavailable.
If you only have a remote URL and token, request the schema files from the project owner, or supply a --config /absolute/path/projects.json file with embedded component and content-type schemas. A normal content API token does not provide the complete schema catalog.
The ownership and retry database is stored at .safe-strapi/audit.sqlite inside the selected project by default. Keep it private, preserve it between sessions, and add .safe-strapi/ to your gitignore. Deleting it removes the ownership records required to modify existing drafts.
Troubleshooting
CONFIG_ERROR: check the source directory and supported content types. TOKEN_MISSING: check the selected env file and token variable name. STRAPI_ERROR: check the CMS URL, permissions, and population rules. AMBIGUOUS_SELECTOR: specify an index or occurrence. SOURCE_CHANGED, DRAFT_CHANGED or PAGE_CHANGED: inspect and preview again. DRAFT_NOT_OWNED: only drafts registered in this installation’s audit database can be modified. OPERATION_BUSY: another MCP process is writing; retry later. WRITE_OUTCOME_UNKNOWN: a write may have succeeded without a verified response. An in-place retry with the same inputs/key only reconciles by re-reading; it does not send another update. Inspect an unresolved result before starting a new operation.
IN_PLACE_DISABLED: explicitly opt in to direct editing. PREVIEW_REQUIRED or PREVIEW_MISMATCH: generate a new matching preview in this installation. CONTENT_REMOVAL_BLOCKED: the operation could discard existing content. INCOMPLETE_CONTENT: check full population and field visibility. OPERATION_NOT_FOUND: check the operation ID, project, document, locale and local audit database. ROLLBACK_UNSUPPORTED: only completed patch-only operations with verified snapshots are eligible; inserted components are never removed.
Writes are serialized when clients share the same audit database. Strapi REST has no atomic revision precondition for external writers, so avoiding every possible GET/PUT race requires a Strapi-side conditional-update endpoint.
Safe Strapi runs locally over stdio. If a client asks for an MCP access key, remove its hosted URL configuration and use the master prompt again.