Skip to content

MCP tool reference

The MCP server exposes 35 stdio tools so an AI agent works from your project’s real values instead of grepping for them. Four layers: Know (read the truth), Understand (health & graph), Config sync through your Strapi (Pro), Refactor (plan → review → apply).

See For your AI agent (MCP) for setup. In VS Code, Cursor, Windsurf and Antigravity the server is bundled and auto-registered — no config.

Choosing a project (multi-project workspaces)

Section titled “Choosing a project (multi-project workspaces)”

Every tool accepts two optional selectors:

  • from — a path inside the target project (e.g. the file you’re editing). Disambiguates a multi-project workspace.
  • project — a project name or root to select among discovered projects.

If a tool returns noProject, the project isn’t indexed yet — call add_project and retry. (from/project only choose among already-discovered projects; they don’t add new roots.)

Register and index a Strapi project on demand from an absolute path — the project root, or any file/folder inside it (the server walks up to the root; a folder is also scanned downward for monorepos). Idempotent, and persisted so it survives server respawns. Returns the projects now known.

ToolParamsWhat it returns
list_projects—The Strapi projects in the workspace (name, root, version, counts). Call first in a multi-project workspace.
project_infofrom? | project?One project in full: Strapi version (installed / declared), TypeScript or JavaScript, package manager, API prefix, counts (APIs, content-types by kind, components, services, controllers, policies, middlewares, routes), every local and installed plugin with whether its refs are verified and, when not, why (no node_modules, rewritten at boot by a project file, a src/extensions override, Enterprise-only code…), health (broken refs, refs into unverified plugins), configSync when the project uses strapi-plugin-config-sync, and freshness. The answer to “why is this plugin ref external?”
list_content_types—Real content-type UIDs (e.g. api::article.article). Use these exact UIDs.
list_components—Real component UIDs (e.g. shared.seo).
list_artifactskind?Real refs of services / controllers / policies / middlewares (optionally one kind).
get_schemauid | uids[]The real attributes (fields, relations, components) of a content-type/component. Batchable. Use instead of guessing field names.
resolverefThe defining file(s) of a reference, tagged by kind.
validate_referencerefvalid | unknown (+ didYouMean) | external: a plugin DevKit can’t verify — not installed, not read completely, rewritten at boot by the project, or still being read just after startup (project_info says which and why). Installed plugins read completely are checked like your own code. Call before writing a magic string.
find_referencesref | refs[], compact?, limit?, offset?Every call-site of a UID / service / controller / handler (or ref#method) in the project’s own code (src/, config/; node_modules and build output aren’t searched). Returns total, then a page of up to limit hits (default 50; truncated: true when more remain): path:line:col [via] snippet.
list_routesplugins?, admin?, method?, path?, handler?, pipeline?The HTTP route table: method, path, mounted fullPath, handler, policies, middlewares, public (auth: false), auth when customized, and where each route is declared (filePath/offset). Explicit routes, auto-CRUD from createCoreRouter, and the routes local and installed plugins declare (origin; plugins: false to leave them out). Admin-panel routes only with admin: true. Filter by method, path (a pattern, part of it, or a concrete URL such as /api/articles/12) or handler. With pipeline: true (needs a filter, at most 20 routes): what a request goes through, in Strapi’s order — the config/middlewares stack (once; a middleware Strapi finds is listed by its name alone), auth, policies, route middlewares, handler — each step ok, not-found-by-strapi (it exists under a name Strapi won’t try: Strapi throws at startup), unknown, unverified, unread or inline; with config-sync, the auth step adds the roles of config/sync that allow the route (syncedRoles). Static, no Strapi boot.
refreshcold?Re-scan every discovered project from disk and rebuild the index; cold: true also drops the cached plugin reads and reads every installed plugin again. Rarely needed (see below). Returns the projects with their freshness.
server_info—The running server: version, licence tier, how many tools are actually callable, the Pro-only tool names, the indexed roots, and how current each project’s index is (freshness). tools/list advertises the Pro tools too — this says which ones will really run. Also the only way for an agent to name a version in a bug report (the version lives in the MCP handshake, which clients don’t always display).

File paths in an answer are relative to the project: the answer gives its root once ("root": "/home/me/my-app", then "filePath": "src/api/article/routes/article.js"). An answer naming the files of several projects keeps them absolute. A path copied from an answer works as input: from (with project when several projects are indexed) and list_unused’s file take it relative to the project root.

Each reference is tagged with a via — how the call reaches the entity: the call form (service, documents, query, schema relation, route handler, relation-field, …) or an internal this / member-var self-call. Run coverage for the full list of indexed forms.

ToolParamsWhat it returns
list_unusedfile?, kinds?Definitions with 0 Strapi references (dead methods, content-types, components, services, policies, middlewares). Counts Strapi refs only — a method called directly in TS still appears, so verify before deleting.
list_broken_refs—Magic strings that resolve to nothing — the inverse of list_unused, and the safety net after a move/rename (target: 0).
coverage—The call forms the engine indexes (+ a via tag) and the ones it does not yet (indexed: false). Use it to know whether find_references/list_unused can be trusted for a pattern.
find_relation_usagesuid, field?Where a content-type’s relation fields are used by name (populate/filters). Indispensable before retargeting or removing a relation.
list_refspatternEntity refs matching a glob — plugin::billing.* for a plugin’s whole surface, api::*, *.
dependenciesref, transitive?What ref uses (outgoing edges: relations, calls). Half of the cut-analysis for modularization.
dependentsref, transitive?What uses ref (incoming edges) — what would break if it moved. The other half of cut-analysis.
config_sync_statuskey?, files?For a project using strapi-plugin-config-sync, read from the files (Strapi is not started, the database is not read): the plugin’s version, its options and where each comes from (with the warnings, such as a short excludedConfig merged index by index with the defaults), the sync directory, and its files by status — ok, excluded, invalid (config-sync can’t read it), or of a type not registered here. files: true lists them with readable names; key returns one file’s content. See Config Sync.
get_model_diagram (Pro)components?, allPlugins?, media?, strapiFields?, focus?[]The data model as a Mermaid erDiagram: content-types (and components) with their fields, one line per association with its cardinalities, media fields linked to the upload plugin’s File, the plugin types they link to; with strapiFields, what Strapi adds to every content-type (createdAt, updatedAt, publishedAt, createdBy/updatedBy → admin::user). focus narrows it to some types and their neighbours. Read from the schemas.
config_sync_diff (Pro)key?, type?, env?The real diff between config/sync and the database, from config-sync itself: each entry different, only-in-db or only-in-sync-dir, with what differs in a few words — what a start of Strapi changes on its own included. With key, every difference, and both sides in full when they fit in one answer; type lists one type only (admin-role, user-role…). Loads the project’s Strapi the way the plugin’s CLI does (runs its code; local development database only).
config_sync_export (Pro)keys?, env?⚠️ Writes config/sync. The plugin’s own export of the entries that differ (keys narrows it): the exact form it compares.
config_sync_import (Pro)keys, reexport?, env?⚠️ Writes the database. The plugin’s own import of keys (required: never everything at once); then the entries still different are exported back in the database’s form. Refused with NODE_ENV=production.

Refactor — plan → review → apply (Pro)

Section titled “Refactor — plan → review → apply (Pro)”

These tools require a Pro licence — without one they stay visible to your agent but return a structured “Pro required” result instead of running, so the agent can relay it rather than fail silently.

Writes are a contract. A plan_* tool computes the exact edits and returns a planId — it writes nothing. Its answer is the plan to review: each edit on one line with the text it replaces (25: "api::restaurant.restaurant" → "api::restaurant.eatery"), the file renames, new files and deletes, paths relative to the project. You review it, then apply_edits applies exactly that plan — only a plan DevKit computed this session, by its planId — after re-verifying every touched file’s fingerprint (a stale plan is rejected), confined to the project root (symlink-resolved), best-effort transactional, and self-verifying for broken refs afterward.

ToolParamsWhat it plans
plan_rename_methodref, method, newNameRename a service/controller method — declaration, every strapi.service(...).method() call-site, and (controllers) the route-handler action segment.
plan_rename_entityref, newNameRename a content-type / service / controller / policy / middleware / component — every call-site & route handler, plus file/folder renames. The plan’s warnings list the UIDs Strapi also stores in the database (permissions, media links, components): the plan edits code and files only, so migrate those rows on a project that already has data. With config-sync, the plan also updates the sync files of those permissions and views (see configSync below).
plan_moveref, toNamespaceMove an artifact, or a whole content-type resource (CT + service + controller + routes), to another namespace; repoints relation targets and strapi.plugin(...) chains.
plan_move_entitiesrefs[], toNamespaceLike plan_move for several refs to one destination, in one coherent pass.
plan_change_relationuid, field, newTargetRetarget a relation field to another content-type (precise schema.json edit). Warns about a now-orphaned inverse.
plan_rename_attributeuid, oldName, newNameRename an attribute in schema.json + (relation) its populate/filters usages. ⚠️ Does not rewrite entity.field object access — always warns.
create_pluginnameScaffold a new local plugin (strapi-server.js, config/plugins).
extract_to_pluginrefs[], nameScaffold a plugin and move artifacts into it — in one plan. The one-pass extraction primitive.
apply_editsplanId⚠️ Writes. Apply a reviewed plan. Verified against disk first; a changed file → nothing is written.
apply_renameref, newName, method?⚠️ Writes. Convenience: plan + apply a rename in one call. Prefer plan_rename_* + apply_edits to review first.

configSync — with strapi-plugin-config-sync, the renames and moves (plan_rename_*, plan_move*, plan_rename_attribute, extract_to_plugin, apply_rename) also update the config/sync files they concern: the roles’ permissions, the admin roles, the content-manager views. Pass configSync: false to leave them (a project whose config/sync comes from another environment). The default is the user’s strapiDevkit.configSync.followRefactors setting — on unless turned off (DEVKIT_CONFIG_SYNC_FOLLOW_REFACTORS=0 for a standalone server). Either way a warnings entry says which sync files the plan updates, or leaves with the old names. Importing them is the user’s call: the local database is shared by every branch.

  • Entities: api::article.article, shared.seo, plugin::billing.invoice.
  • Methods: ref#method (e.g. api::page.notifier#notify) in resolve / validate_reference / find_references.
  • Guarantee, don’t guess: a ref into a plugin DevKit couldn’t read completely is reported as external, never invented; refactors are all-or-nothing and never touch node_modules; warnings are never silent.