✨ Strapi MCP is now Generally Available - let your agents manage your Strapi content ✨

Ecosystem15 min read

How to Use Strapi MCP to Bulk-Create, Update, and Migrate Content

September 3, 2026
How to Use Strapi MCP to Bulk-Create, Update, and Migrate Content

Bulk content operations through the Strapi MCP server use an artificial intelligence (AI) client to execute changes entry by entry. In Strapi, an open-source headless content management system (CMS), this workflow can move 2,000 articles from a legacy system by Friday or replace an old product name across hundreds of entries.

Narrow scoping and verification keep a bad prompt from torching your production content in a Model Context Protocol (MCP) server workflow. You describe the change, and the agent executes it entry by entry through Strapi's MCP tools.

In brief:

  • The Strapi MCP server exposes single-document tools (list, get, create, update, delete, publish, unpublish, discard_draft); the agent creates bulk scale by calling them in a loop.
  • Filters, sorting, and pagination (pageSize max 100) scope every run to exactly the entries you intend to touch. Always test on a narrow filter first.
  • MCP can't upload files, so media goes through the Media Library or Upload application programming interface (API) first and gets referenced by document ID.
  • A dedicated, least-privilege Admin token limits the blast radius of a bad prompt.

Together, these constraints define a safe list-act-verify workflow.

Before You Start

You need Strapi 5.47.0 or later with the MCP server active in config/server.js and an AI client (Claude Desktop, Claude Code, Cursor, or Windsurf) connected to http://localhost:1337/mcp with an Admin token. Use the MCP server configuration walkthrough to configure each client. The MCP activation setting defaults to false, so nothing is exposed until you explicitly turn it on.

The Mental Model: List, Act, Verify

Every bulk task through MCP follows the same loop. The agent calls list to find the entries in scope, acts on each one with create, update, delete, or publish, then calls list again to confirm the result.

The available tools map directly onto this loop. Collection Types get up to eight tools: list, get, create, update, delete, plus publish, unpublish, and discard_draft when Draft and Publish is active on the Content-Type. Single Types get up to six, including a write tool that creates the document if none exists and updates the draft otherwise.

Why There Is No Batch Endpoint

The content management tools are all single-document, single-operation. A GitHub Request for Comments (RFC) (#25398) proposes batch-aware events for bulk operations. Batch-aware events remain proposed and unshipped.

The agent creates scale through orchestration. It pages through entries with list, reasons about each one, and issues individual create, update, or delete calls. That per-entry reasoning is the trade-off against an import script or the data management CLI: strapi import moves a pre-validated archive as-is.

An MCP agent can apply a change like "swap 'Foo' for 'Bar' in the title and any URLs while preserving the meaning of every sentence" to every entry it touches. You get flexibility; you give up the atomicity of a one-shot archive restore. That's exactly why the verify step exists.

Selecting the Right Entries With Filters

The list tool takes filters, sort, page, and pageSize parameters, and getting these right is most of the safety story. The filtering documentation covers the full operator set you know from Strapi's query APIs: $eq, $ne, $in, $notIn, $lt, $lte, $gt, $gte, $between, $contains, $startsWith, $endsWith, $null, $notNull, case-insensitive variants like $containsi, and the logical operators $and, $or, and $not. Passing { "title": "Hello" } is shorthand for { "title": { "$eq": "Hello" } }.

A filter that scopes a run to unpublished news drafts looks like this:

{
  "$and": [
    { "publishedAt": { "$null": true } },
    { "category": "news" }
  ]
}

Plan the run around two constraints. Filters and sorting work on scalar attributes only: strings, numbers, booleans, dates, and enumerations. Selection criteria stored in relations, components, Dynamic Zones, media, or JSON fields require the agent to list more broadly and check each entry.

Pagination caps at pageSize: 100, and page starts at 1. A 2,000-entry job is at minimum 20 list calls, and sorting on a stable field like createdAt keeps the ordering predictable across pages so nothing gets skipped or double-processed.

Dry-Run First: Test on a Filtered Subset

Before any bulk prompt touches real content, run it against a filter that matches a handful of entries and inspect the output. There's no transaction wrapping an agent run; if the agent misunderstands your instruction on entry three of 800, you find out 797 entries too late.

Run the exact prompt you plan to use with a narrow constraint. "Update only articles where the slug starts with test-" or "process just the first five entries sorted by createdAt, then stop and show me what you changed."

Read the results in the Content Manager user interface (UI) and confirm the field values and relations look right. Check the locales, then widen the filter scope to the full set. Thirty seconds of inspection here beats an evening of cleanup.

Bulk-Create: Seeding and Importing Entries

Creating many entries is a single prompt describing the set. The prompt-driven workflow maps natural language to tool calls, so something like this works as-is:

"I just created a new Content-Type called case-study. Read its schema, then create five stub drafts so I can see how a populated listing page would look. Use realistic-sounding company names with obviously fake numbers."

When Draft and Publish is active, entries land as drafts. This keeps everything offline until you've verified it. MCP also requires explicit values for uid fields, unlike the Content Manager UI, so a slug field follows the documented slug behavior. Tell the agent to generate slugs from titles as part of the prompt, or your listing pages will break later.

For volume, the Strapi team provides batching guidance that recommends groups of five to ten entries per prompt. Asking for 50 at once increases the risk of timeouts. Smaller batches keep each request inside timeout limits and give you natural checkpoints to inspect progress.

Making Create Runs Idempotent

Re-running a create prompt after an interruption will happily duplicate everything it already made unless you build a check into the instruction. Before each create call, use list to check a unique field:

  1. For each source item, list with a filter on the idempotency key, such as a slug or an sku. An external ID stored in a string field also works.
  2. If a match comes back, skip the create or issue an update instead. If nothing matches, create.

In the prompt, that's one sentence: "Before creating each entry, list entries filtered by slug and skip any that already exist."

Use the filter check to catch duplicates because unique: true in the schema does not reliably catch duplicate drafts. When Draft and Publish is active, Strapi's draft uniqueness rules skip unique validations on draft saves, so duplicate drafts sail through and only collide with the database constraint at publish time. The filter check catches them at create time, which is where you want the failure.

Bulk-Update: Changing Fields Across Many Entries

For a mass field change, page through matching entries and call update for each document ID. A find-and-replace prompt can use this structure:

"Update every Article that references our old product name 'Foo' to use 'Bar.' Preserve the meaning of each sentence while swapping the name and any obvious derivatives throughout the content, including URLs. Update headings and CTAs too. Update them as drafts so I can review."

The "update them as drafts so I can review" clause is doing real safety work. Keep it in your own prompts.

Relation changes ride along in the same update calls through three keys documented under relation operators:

  • connect adds relations without touching existing ones. It takes document ID strings or objects with an optional position ({ before?, after?, start?, end? }). The default position is { end: true }.
  • disconnect removes only the relations you name.
  • set replaces the entire relation list, and set: null clears it. set is mutually exclusive with connect and disconnect.

These operators let you preserve existing relations unless replacement is intentional.

Adding a category to every matched article looks like this per entry:

{
  "documentId": "<article-documentId>",
  "data": {
    "categories": {
      "connect": ["abc123"]
    }
  }
}

Reach for connect and disconnect in bulk runs, and treat set with suspicion. A set that the agent builds from an incomplete list silently drops every relation it didn't know about, while a connect can only add. The REST relations docs state that connect is not officially supported for media attributes and can break. Assign media by setting the field value directly.

A Content Migration Walkthrough, End to End

Content migration with an AI agent works best as five deliberate passes. Each step below is its own conversation with the agent, verified before the next one starts. Each conversation uses the same prompt-driven tools as everything above. The scenario: moving a few thousand posts from an exported dataset into a Strapi article Content-Type.

Step 1: Map Source Data to Your Content-Types

Use this read-only step to give the agent your source data (or a representative sample) and your Strapi schema, then have it produce a field mapping you can read and correct. Apply Strapi 5's document model to the following mapping decisions:

  • Every document gets a 24-character alphanumeric documentId that persists across locales and draft/published versions.
  • Store your source system's ID in a plain string field so you can trace every migrated entry back to its origin. That field also becomes your idempotency key.
  • Match source fields to Strapi's attribute types deliberately. Decide whether text belongs in string or text. Use richtext where needed. Distinguish date from datetime. Use enumeration for controlled vocabularies.
  • The names meta, status, entryId, and anything prefixed with strapi are reserved in Strapi 5. Rename any source fields that collide before you touch the schema.

Ambiguities surface here, in a spreadsheet, instead of mid-run as malformed entries.

Step 2: Recreate Entries Through Prompts

With the mapping agreed, instruct the agent to create the mapped entries in batches, drafts first, slugs explicitly generated, check-before-create on your source-ID field. Work through the source in chunks and spot-check between chunks.

Localized content needs one extra habit. When Internationalization is active, the tools accept an optional locale parameter that defaults to your configured default locale when omitted. The docs are specific about how to prompt for it: name the target language explicitly.

"Create an article in French with the title 'Bonjour le monde'" passes locale: "fr", while "Create an article titled Bonjour" may land in the default locale because the agent has nothing to key off. For migrating existing translations, reference the document: "Create a French version of article abc123 with title '...'" attaches the locale to the same documentId.

Step 3: Handle Media References

The MCP server cannot upload files. Media fields accept references to existing assets only. Media therefore requires a pre-pass before entry creation:

  1. Upload the files through the Media Library or the Upload API at /api/upload, scripted outside MCP.
  2. Record each file's documentId. The GraphQL API returns documentId for media files if you need to look them up after the fact.
  3. Include those document IDs in your source mapping so the agent passes them as media field values during create and update calls.

Build a lookup table of source-URL to Strapi documentId during the upload pass. Handing that table to the agent alongside the source data lets step 2 wire images correctly.

Step 4: Verify Counts and Spot-Check

Before anything publishes, have the agent count what it made and compare against the source. A filtered list on your source-ID field ({ "legacyId": { "$notNull": true } }) paged to the end gives you a migrated total to set against the source count. A mismatch means silent drops, and a filter on the missing IDs tells you exactly which entries to re-run.

Also pull a random sample of entries with get and check them field by field against the source: rich text intact, dates parsed into the right format, relations connected, media resolving, locales attached to the right documents. The list and get tools don't support nested population parameters for relations, so verify deep relation chains in the Content Manager.

Step 5: Publish in Bulk

Once the counts reconcile and the spot-checks pass, move the verified drafts live. The publish tool operates on one entry per call by documentId, so bulk publishing is the same loop as everything else: list the verified drafts, publish each one, in batches. A prompt like "List all article drafts where legacyId is not null. Publish them ten at a time and report after each batch" keeps the run observable.

If you'd rather do this step by hand, the Content Manager has its own select-and-publish bulk action. It's a UI feature. With Internationalization installed, it applies only to the currently selected locale, so a multi-locale migration needs a pass per locale.

Recovering From a Half-Finished Run

Interrupted runs are normal at migration scale. A timeout or closed laptop can stop a run, and an agent can also hit its context limit. Because you built idempotency in (step 2), recovery starts with a re-filter. list on your idempotency key to collect what already exists, diff against the source, and resume creates for the remainder only. The check-before-create guard means that even a sloppy resume that overlaps the completed range creates no duplicates.

Reversing a bad run depends on what state the bad entries are in, and the tool semantics differ in ways that bite if you pick wrong:

  • Never-published drafts (the default output of a create run): use delete. discard_draft requires an existing published version to revert to and errors on never-published entries.
  • Published entries with good history: discard_draft drops the bad draft changes and reverts to the published version.
  • Entries published during the bad run: unpublish to pull them off the live API, then delete if they shouldn't exist at all.

Cleanup is iterative too. The Document Service has a deleteMany() method internally, while the MCP server provides single-entry deletion. The agent removes bad entries one delete call at a time, scoped by the same filters you used to find them. Which is another argument for dry-runs: deleting five bad test entries is a chore, deleting 800 is an afternoon.

Timeouts and Long-Running Jobs

Two advanced options in the mcp config block govern how long requests can run. connectTimeoutMs (default 5000) caps how long the internal MCP transport gets to connect, and requestTimeoutMs (default 60000) caps a single MCP request. That 60-second default is the one bulk jobs trip over: any individual tool call that runs longer times out, which is how half-finished runs happen.

For migration work, raise both:

// config/server.js
module.exports = () => ({
  mcp: {
    enabled: true,
    connectTimeoutMs: 10000,  // 10 seconds
    requestTimeoutMs: 120000, // 2 minutes
  },
});

Restart Strapi after changing the config. Keep each unit of work small even after increasing the timeouts. Page at pageSize: 100 or below, batch creates in the five-to-ten range, and instruct the agent to checkpoint between batches. Small batches mean any single failure costs you seconds of rework instead of the whole run.

Guardrails: Scoped Tokens for Destructive Work

A bulk run is only as dangerous as the token behind it, and the MCP server gives you precise control there. First, use the right token type: MCP accepts Admin tokens created under Settings > Admin Tokens, and rejects Content API tokens with a 401. The plaintext key is shown only once at creation, so store it immediately.

The permission boundaries enforce the token's scope at four levels: only tools the token can access are exposed to the AI client at all, input and output schemas are narrowed to permitted fields, the locale parameter is narrowed per action when i18n is active, and every handler re-checks permissions at runtime.

These runtime checks cover condition-based rules like "only update entries you own." If the token lacks delete on Article, the agent never even sees a delete tool for articles. Tokens also inherit their owner's RBAC permissions as a ceiling, get re-clamped when the owner's roles change, and are revoked if the owner is deactivated.

The official guidance is blunt: "Give the Admin token the least access it needs, and add permissions as you go." For migration work, that translates to a dedicated token per job. A seeding run gets create and read on the target Content-Type and nothing else. The publish pass gets read and publish. Grant delete only when you're actively cleaning up. On Strapi 5.52.0+ with an Enterprise plan, MCP write actions also land in Audit Logs with origin: "mcp", so you can reconstruct what an agent did after the fact.

Put the List, Act, Verify Loop to Work

Bulk work through the Strapi MCP server rewards a specific discipline: use filters to scope work and process it in small batches. Verify the results with a fresh list before moving on. Add dry-runs against a handful of entries and idempotency keys so re-runs are safe. A least-privilege Admin token per job further limits the impact of errors. With these controls, an AI agent becomes a fast way to seed, change, and migrate content with recoverable failure modes.

Build confidence in the loop with a real dataset. Create a Strapi project with Strapi 5, turn on the MCP server, and point the agent at a migration you've been putting off. Start with five entries.

Paul BratslavskyDeveloper Advocate

Related Posts

How to Build Ecommerce with Next.js Template + Strapi
Ecosystem·17 min read

How to Build Ecommerce with Next.js Template + Strapi

Learn how to build a full ecommerce site with Strapi 5, Next.js 16, and Stripe. Covers content models, cart, checkout, and deployment step by step.

·September 9, 2026
How to Build a Coaching and Tutor Marketplace with Strapi 5
Ecosystem·17 min read

How to Build a Coaching and Tutor Marketplace with Strapi 5

Learn how to build a coaching and tutor marketplace with Strapi 5 and Next.js 16. Covers content modeling, the REST API, JWT auth, and Server Actions.

·August 11, 2026