safe-strapi-mcp
Provides tools for managing content in a Strapi 5 installation, including creating draft copies, applying ordered component changes, and verifying content revisions without publishing.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@safe-strapi-mcpPreview cloning the About page and changing the hero title"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Safe Strapi MCP
Run content operations against your own Strapi 5 installation from a local MCP client. By default the service creates draft copies, applies ordered component changes, and verifies the result and source hash. An explicit project opt-in also permits previewed patch/insert operations on an existing page draft. It does not publish pages.
Install
Requires Node.js 22.5 or newer. SQLite may need native build tools on platforms without prebuilt binaries.
npm install --global safe-strapi-mcp
safe-strapi-mcp --helpYou can also run it without a global install using npx safe-strapi-mcp.
Related MCP server: MCP Strapi Server
Configure your project
1. Create a Strapi API token
In Strapi Admin, open Settings → Global settings → API Tokens → Create new API Token. Choose Custom and grant only:
findandfindOnefor every collection the page populates, including related content;findandfindOnefor Upload/media when the page contains assets;createandupdatefor the collection types whose drafts the MCP may create.
delete and publish permissions are not required. Save the token when Strapi displays it; Strapi does not show the full value again. A token that can list pages but cannot read populated relations or media may fail during inspect_page with HTTP 403.
2. Store it locally
Create a private env file outside version control:
STRAPI_URL=http://localhost:1337
STRAPI_API_TOKEN=Paste the token after STRAPI_API_TOKEN=. Use the Strapi origin for STRAPI_URL, without /admin or /api. Never commit this file or paste the token into chat. No hosted account or MCP access key is needed for stdio.
Add the local state and env file to the Strapi project's .gitignore:
.env.safe-strapi
.safe-strapi/3. Connect your MCP client
Configure your MCP client to run:
safe-strapi-mcp --env-file /absolute/path/strapi/.env --project-root /absolute/path/strapiCodex CLI can register the published package directly:
codex mcp add safe-strapi -- \
npm exec --yes --package=safe-strapi-mcp -- safe-strapi-mcp \
--env-file /absolute/path/strapi/.env.safe-strapi \
--project-root /absolute/path/strapiRestart Codex, then begin with a read-only request: Use safe-strapi. List projects, find the homepage, and inspect it. Do not create or update anything. Only continue to preview after this succeeds.
Use the absolute executable path if your desktop client does not inherit your shell PATH. The tool loads the env file directly; do not paste secrets into chat. Environment variables already present in the process take precedence over the file.
The source directory must contain src/api and src/components. Collection types with Draft & Publish, a UID field, and dynamic zones are discovered automatically. Each zone is named content-type:zone in list_projects. Single types are not supported by automatic setup.
Optional env variables: STRAPI_CONTENT_TYPE, STRAPI_BLOCKS_FIELD, STRAPI_SLUG_FIELD, STRAPI_ROUTE_FIELD, STRAPI_TITLE_FIELD, STRAPI_LOCALE (defaults to en). Set STRAPI_ROUTE_FIELD=url only when the frontend routes by a separate top-level field that must track cloned slugs. The field must exist in the content-type schema and be string-like; preview reports the change explicitly. Set STRAPI_TOKEN_ENV=MY_EXISTING_TOKEN to read a differently named token variable. Use HTTPS for remote CMS connections.
Only a remote URL and token? Obtain the schema source from the project owner, or pass --config /absolute/path/projects.json with this structure:
{
"projects": {
"my-site": {
"baseUrl": "https://cms.example.com",
"tokenEnv": "STRAPI_API_TOKEN",
"collection": "pages",
"blocksField": "blocks",
"slugField": "slug",
"routeField": "url",
"titleField": "title",
"schemaRoot": "/absolute/path/strapi",
"contentType": "page",
"populate": "*"
}
}
}For custom config, populate must include every nested component, media and relation required for the full document. Embedded componentSchemas (UID to schema object) and contentTypeSchema can replace source files. Automatic local setup generates nested populate queries from discovered schemas. Custom Strapi routes, permissions and plugins still require integration validation.
Use
For the default clone workflow, ask your client to list projects, inspect the page, preview operations, and create the reviewed draft with its source hash and a stable idempotency key. Then validate and compare it. Source pages remain read-only in this workflow. Owned-draft tools require registration in this installation's ownership database. modify_owned_draft uses create normalization and may regenerate retained component IDs; its content-preservation behavior differs from direct editing. It has no dedicated persisted preview tool: inspect, review an AI-side plan, then pass the matching draft hash.
Tools: 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, compare_pages.
Edit an existing page draft without cloning (opt-in)
This feature is available in the current source branch; it is not in the published 0.2.0 package. Build the source to use it before the next release.
For auto-discovered projects, set STRAPI_ALLOW_IN_PLACE_EDITING=true in your
private env file or pass --allow-in-place-editing. For a custom projects config,
set allowInPlaceEditing: true on each selected project. The CLI flag does not
override custom config. list_projects reports whether this is enabled.
Use preview_modify_page with a project, documentId, locale and ordered
operations. It records the plan locally and returns pageHash, operationHash
and differences. Call modify_page with the same inputs plus
expectedPageHash: pageHash, expectedOperationHash: operationHash and a stable
idempotencyKey. A matching preview must exist in this installation's audit DB.
Example operations to edit the second block and its nested single component:
[
{
"type": "patch",
"selector": { "index": 1 },
"changes": { "title": "Updated features", "content": { "description": "Updated description" } }
}
]Use field names from get_component_schema; the example's fields must exist in
your schema. Natural-language request:
Edit only the features component on the existing model page using preview_modify_page, then apply the reviewed preview with modify_page. Preserve other blocks and their order. Do not clone or publish.
Only patch and insert are accepted. Insertion supports start/end/before/after
positions and preserves existing blocks' relative order. Supply every schema
attribute of inserted components, including nested components; explicitly use
null or empty arrays for unused optional fields. Do not supply component IDs.
Existing media/relation references can be supplied as scalar IDs/document IDs.
The first version rejects remove/replace/move/duplicate operations, changes to component identity/type, clearing nonempty fields, replacing arrays (including repeatable components), replacing existing objects and changes to existing media/relation references. Nested single component and JSON object fields can be patched. Existing component IDs are retained and checked after writing.
An existing draft and full collection/component schemas are required. Missing
populated fields, private fields, unsupported field types, single types and
content types without Draft & Publish are rejected. The write changes only the
configured dynamic zone; it does not change slug, route, title or other zones.
No ownership record is needed or created for the existing page. A failed or lost
write response is reconciled by re-reading; if the result cannot be verified,
WRITE_OUTCOME_UNKNOWN blocks another write with the same key. There is no
automatic rollback.
REST revision checks cannot prevent an external editor from racing the final
GET/PUT. The result includes this limitation in concurrency. A fully atomic
guarantee requires a separate Strapi-side conditional-update endpoint, which this
version does not implement.
Consistent previews and explicit rollback (current source)
All preview tools return contractVersion: "1" and the same machine-readable
fields: target, summary, changes, fieldChanges, safety, revision and
nextAction. fieldChanges includes path, before, after, beforeExists
and afterExists; missing values are represented as null with the existence
flag set to false. Results retain the previous top-level hashes for compatibility.
The diff is limited to 500 entries; fieldChangesTruncated signals that the
report may be incomplete. AI clients should consistently present the target,
old/new values, preservation rules, publication state and rollback eligibility,
then request approval. Approval is a client workflow rule, not a server-side
human authorization mechanism.
Direct-edit results include an operationId (the caller's idempotency key).
list_page_operations lists local operations for a project, document and locale
without exposing snapshots. For an approved rollback:
Call
preview_rollback_pagewith that target andoperationId.Review the old/new field values and obtain explicit approval.
Call
rollback_pagewith the same target and operation ID, both returned hashes asexpectedPageHash/expectedOperationHash, and a new stable idempotency key.Re-inspect the draft and report the verified result; never publish.
Rollback only restores a completed, verified patch-only operation while the
page still has its exact post-operation revision. Later changes, uncertain
operations, missing snapshots, changed target configuration and insert operations
are rejected. It preserves component count, order, IDs and media/relation
references. Recorded scalar values may return to their previous empty/null value;
ordinary patches still forbid clearing content. rollbackSupported is eligibility,
not a guarantee that the current page revision permits rollback. There is no
automatic rollback and no component deletion. The REST concurrency limitation
also applies to rollback. These tools require the current source build.
Use alongside the official Strapi MCP
Safe Strapi MCP uses a Content API Token and REST. The official MCP uses an Admin
Token and /mcp; keep their credentials separate. Use this service's opt-in
tools for protected dynamic-zone edits and the official MCP for general content
operations. Where supported, restrict the official token's update permissions on
protected fields: writing through its tools bypasses these preservation checks.
Copy-ready workflows for discovery, JSON insertion, component reordering, long pages, team review drafts, and conflict recovery are available in the prompt cookbook. AI clients can read the same guide directly as Markdown or discover key resources through llms.txt.
Indexes are zero-based. Operations run sequentially, so later indexes refer to the current order. Ambiguous selectors must include an occurrence or index. Writes require revision hashes and idempotency keys; retries with a different operation hash are rejected.
If a create response is lost before its document ID can be recorded, the retry fails closed with WRITE_OUTCOME_UNKNOWN; it never submits another create or adopts a page by slug. Inspect the CMS through its API and use a new operation only after resolving the uncertain result. A lost update response is safely reconciled from the recorded intended content without a second PUT.
Concurrent MCP writes sharing the same audit database are serialized. Draft and source hashes are checked immediately before writes and results are fetched and verified afterward. Owned-draft updates send only the configured dynamic-zone field. Strapi REST does not expose an atomic revision precondition, so a writer outside this MCP can still race inside the final GET/PUT interval; preventing that last race requires a Strapi-side conditional-update endpoint.
Local state
The audit database defaults to .safe-strapi/audit.sqlite inside the selected project; set AUDIT_DB to override it. Preserve this database between sessions. Add .safe-strapi/ and env files to your project's gitignore. Protect them with local filesystem permissions.
Preview and write records contain content snapshots for verification and rollback. Treat the database as private content; losing it also loses recorded rollback history.
Each installation uses its own local credentials and state. The package supports stdio only and does not require or accept an MCP access key.
Development checks
npm install
npm run typecheck
npm run build
npm test
npm pack --dry-runTests include a 100-component preservation scenario, owned-draft checks, idempotency conflicts, lost-response recovery, cross-instance serialization, and env-file onboarding through the real stdio protocol against mocked Strapi REST. Local acceptance additionally exercises a real Strapi 5 API without using the admin UI. These tests do not certify every Strapi plugin or version.
To verify in-place editing with an isolated real Strapi instance, run after build:
node scripts/in-place-acceptance.mjs /absolute/path/to/strapi-project-with-node_modulesThe script reuses installed dependencies but creates a temporary app, SQLite DB and test token; it never loads that project's config, env or customer data. It checks middle/nested patches, component IDs, media/relations, insertion over MCP stdio and preservation of the published version, then removes the temporary app.
Independent tooling; not affiliated with or endorsed by Strapi.
Community and security
Bug reports and contributions are welcome on GitHub. Read CONTRIBUTING.md before opening a pull request. Report vulnerabilities privately as described in SECURITY.md.
Released under the MIT License.
This server cannot be deployed
Maintenance
Related MCP Connectors
Hosted WordPress MCP server for Claude, ChatGPT and Cursor, with each action switched per account.
Publish and schedule content across supported platforms, with publishing state and per-platform adaptation. Built by AutomateLab. Product and documentation: https://automatelab.tech/products/mcp/content-distribution-mcp/
Hosted WooCommerce MCP server for Claude, ChatGPT and Cursor, with each action switched per account.
Manage WordPress blogs and WooCommerce shops from Claude, ChatGPT, Cursor and other MCP apps.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceProvides access to Strapi CMS content through the MCP protocol, allowing users to create, read, update, and delete content entries via Claude.55 npm25MIT
- AlicenseNot gradedqualityDmaintenanceEnables CRUD operations, content management, and media handling for any Strapi content type through standardized tools. Supports internationalization, schema management, and works with Strapi v5's API.MIT
- AlicenseNot gradedqualityBmaintenanceMCP server that exposes Strapi v5 content management, visual layout configuration, schema authoring, media uploads, and GraphQL testing to LLM clients via native Strapi API tokens with security hardening.31 npm1MIT
- AlicenseAqualityDmaintenanceMCP server for Strapi CMS content and media management.1510 npmMIT