JoyConf 2026 is back. Content Confidence. Human Connection. Save your spot!

Using the Storyblok MCP Server: A Practical Guide

Developers
Daniel Mendoza

The Storyblok MCP server uses a discovery-first approach instead of wrapping every API endpoint. The core tools (search, describe, and three execute tools) plus a specialized asset flow give you access to the Management API. This guide shows you how to use them.

Hint:

New to the Storyblok MCP? Start with the setup documentation. For the architectural deep-dive on the benefits of the discovery approach works, read The Hidden Cost of Flat MCP Tool Lists.

The tools at a glance

Every Storyblok operation follows the same flow: search β†’ describe β†’ execute. Here's what each tool does:

Tool

What it does

When your agent calls it

search

Finds operations by keyword

When you describe what you want to do

describe

Returns the schema for an operation

Before executing (cached after first call)

execute_readonly

Runs read operations

Listing, fetching, querying

execute_mutating

Runs write operations

Creating, updating, publishing

execute_destructive

Runs delete operations

Deleting, purging (requires confirmation)

Plus a two-step asset upload flow (upload_asset and upload_asset_finish) that keeps file bytes out of the MCP transport.

Step 1: Search for operations

Your agent calls search automatically when you describe what you want to do. You never call it directly.

Example:

You: "Add a blog post titled 'Launch week' to my Storyblok space [spaceID]."

The agent searches the API catalog for operations matching "add blog post" or "create story". It gets back candidate operations (like createStory or createApproval) and picks the one that matches your intent.

What this means for you: Describe outcomes, not endpoints. You don't need to know that creating a story uses POST /v2/spaces/{space_id}/stories. Just say what you want.

Step 2: Describe the operation

After finding an operation, your agent calls describe to get its schema. This happens automatically (guardrails enforce it), and the schema gets cached in context for repeat use.

Continuing the example:

You: "Add a blog post titled 'Launch week' to my Storyblok space [spaceId]."

Agent: *...searches for relevant operation...*

[*calls* describe(operationId, "createStory")]

Here's the createStory operation:

  Execute with: execute_mutating

  Parameters:
  - space_id (path, required) β€” integer
  - publish (query) β€” boolean; if true, story is published on creation
  - release_id (query) β€” integer; attach to a release
  - disable_fe_editor (query) β€” boolean; disables visual editor for this story
  - preset_id (query) β€” integer
  - use_default_preset (query) β€” boolean

  Request body (required): UpdateStoryRequest

  Typical body shape for a story:
  {
    "story": {
      "name": "Launch weeks",
      "slug": "launch-weeks",
      "parent_id": 0,
      "content": { "component": "article" },
      "tag_list": [],
      "is_folder": false
    }
  }

  Notes:
  - content.component must be a valid root content type in the space (e.g. article, page).
  - slug must be unique within its parent folder.
  - parent_id places the story in a folder (omit or 0 for root).
  - For publishing on create, add ?publish=1 via the publish param.

The agent learned from describe that:

  • name and slug are required
  • publish is optional (defaults to false for drafts)
  • This routes to execute_mutating (it's a write operation)

Watch for region-based operations: If describe shows no space_id parameter, the operation routes by region (eu, us, cn, ap, ca). Your agent should ask which region to use.

Step 3a: Execute read operations

Read operations (listing, fetching, querying) route through execute_readonly.

Example: List stories with specific fields

You: "Show me the 10 most recently updated stories. I only need their name, slug, and publish status."

Agent: 

	 execute_readonly(operation: "listStories", parameters:
   {"space_id":572524126162802,"sort_by":"updated_at:desc","per_page":10,"page":1}, fields:
   ["stories.name","stories.slug","stories.published","stories.unpublished_changes"])  // [Calls getStories with fields=name,slug,published and per_page=10]
   
Agent: Here are the 10 most recent stories:
  β”Œβ”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  #  β”‚       Name       β”‚       Slug       β”‚  Status   β”‚
  β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚ 1   β”‚ Launch week 21   β”‚ launch-week-21   β”‚ Published β”‚
  β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚ 2   β”‚ Launch week 2026 β”‚ launch-week-2026 β”‚ Published β”‚
  β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  ...

Tips:

  • Be specific about fields: "Give me the names and slugs" lets the agent pass a tighter fields filter and skip unnecessary fields
  • Set limits: "Show me 10 stories" is clearer than "list stories" for paginated operations
  • Not all operations support pagination: check pagination.total_pages in responses

Step 3b: Execute write operations

Write operations (create, update, publish) route through execute_mutating. These are recoverable changes.

Example: Create a story

You: "Create a blog post titled 'Launch week', slug 'launch-week'. Leave it as a draft."

Agent: 

   Storyblok - execute_mutating(operation: "createStory", parameters:
   {"space_id":123,"story":{"name":"Launch
   week","slug":"launch-week","content":{"component":"article"}}}) (MCP)
   Execute a mutating Storyblok API operation (behavior: 'idempotent' or unset).
   
Do you want me to proceed?

You: "Yes" 

Agent: Created "Launch week" as a draft in My Project US.

  - ID: 12345678910
  - Slug: launch-week
  - Status: draft

Tip: Make sure your agent reads the plan back before executing. If it tries to skip confirmation and run immediately, ask it to slow down. Mutating operations can have unintended side effects (publishing early, wrong folder, schema overwrites).

Step 3c: Execute destructive operations

Destructive operations (deletes, purges) route through execute_destructive. These are irreversible and require explicit confirmation.

Example: Delete a story

You: "Delete the story titled 'Launch week'."

Agent: *...searches for relevant operation...*

[*calls* describe(operationId: "deleteStory")]

Agent: 

   Storyblok - execute_destructive(operation: "deleteStory", parameters:
   {"space_id":123,"id":12345678910}) (MCP)
   Execute a destructive Storyblok API operation (behavior: 'destructive'). 
   Data may be permanently lost. 
   
Do you want to proceed?

You: "Yes"

Agent: Deleted "Launch day" (ID 12345678910) from [Space Name].

Always verify before confirming: Re-read the story name and ID the agent quotes back. Make sure it matches what you intended to delete. The MCP won't stop you from deleting the wrong story.

Uploading assets: a two-step flow

Assets use a specialized two-step flow (upload_asset + upload_asset_finish) that keeps file bytes out of the MCP transport.

Example: Upload an image

You: "Upload sunset.jpg to Storyblok with alt text "Image of Sunset""

Agent: 

   Storyblok - upload_asset(space_id: 123, filename: "sunset.jpg", alt: 
   "Image of Sunset") (MCP)
   Create an asset record in Storyblok and get a signed S3 upload URL. 
   Returns a ready-to-use curl command for environments with shell access 
   (Claude Code, Cursor), or a Storyblok asset manager link for manual upload. 
   After the file is uploaded to S3, call `upload_asset_finish` to finalize the
   asset.

 Do you want to proceed?
 ❯ 1. Yes
 
 Agent:
 
 Bash command

   curl -s -o /dev/null -w "%{http_code}\\n" -X POST
   "<https://s3.amazonaws.com/a-us.storyblok.com>" \\
     *...A whole bunch of other stuff...*
     -F "file=@/Users/danielmendoza/sunset.jpg"
   Upload sunset.jpg to the Storyblok S3 bucket

 This command requires approval

 Do you want to proceed?
 ❯ 1. Yes
 
Agent:

Tool use

   Storyblok - upload_asset_finish(space_id: 123, asset_id: 168411926216117) 
   (MP)
   Finalize an asset upload after the file has been uploaded to S3 externally. 
   Call this after using a signed S3 URL (from `upload_asset`) to complete the 
   upload via curl or manual upload.

 Do you want to proceed?
 ❯ 1. Yes
 
Agent: Uploaded sunset.jpg to [Space Name].

  - Asset ID: 168411926216117
  - URL: <https://s3.amazonaws.com/a-us.storyblok.com/f/123/456/sunset.jpg>
  - Alt text: Image of Sunset

How it works:

  1. upload_asset registers the file and returns a signed S3 URL + curl command
  2. The agent (or you) uploads directly to S3
  3. upload_asset_finish finalizes the asset in Storyblok

If your agent has shell access, this is automatic. Otherwise, the agent will show you the curl command to run manually.

Hint:

For additional information, visit the upload and replace assets documentation.

Full example: create and publish a story

Here's what a complete workflow looks like from a single prompt:

You: "Create a blog post titled 'Launch week', slug 'launch-week' in space [spaceId] and publish it."

Agent: 

 Tool use

   Storyblok - search(query: "create story") (MCP)
   Search for available Storyblok API endpoints. Returns the top 3 matching operations with
   their operationId, summary, and behavior.

   Always use this first when you don't know which operation to call. Then call `describe` with
   the operationId to get all parameters, request body schema, and the `executeWith` field that
   tells you which execute tool to call.

 Do you want to proceed?
 ❯ 1. Yes

Agent:

 Tool use

   Storyblok - describe(operationId: "createStory") (MCP)
   Get full details for a Storyblok API operation. Returns all parameters (path, query) with
   descriptions and schemas, the request body schema for write operations, available response
   fields, the `executeWith` tool name to call next, and a `requiresRegion` flag when the user's
    region must be confirmed before executing.

   Always call this after `search` and before executing.

 Do you want to proceed?
 ❯ 1. Yes
 
 Agent: 
 
  Tool use

   Storyblok - execute_mutating(operation: "createStory", parameters:
   {"space_id":572524126162802,"publish":true,"story":{"name":"Launch week
   21","slug":"launch-week-21","content":{"component":"page","body":[]}}}) (MCP)
   Execute a mutating Storyblok API operation (behavior: 'idempotent' or unset).

 Do you want to proceed?
 ❯ 1. Yes

Agent: Created and published. Story ID 168414172465922, live at /launch-week.

What happened behind the scenes:

  • 1Γ— search (find createStory): search(query: "create story")
  • 1Γ— describe (get schema for operation): describe(operationId: "createStory")
  • 1Γ— execute_mutating (create and publish)

You described the outcome. The agent handled the discovery and execution.

General tips for working with the Storyblok MCP

Be specific in your prompts

  • "Show me the 10 most recent stories with names and slugs" is better than "list stories"
  • Explicit field lists help the agent pass tighter filters to Storyblok

Watch for confirmations

  • Mutating operations should show you the plan before running
  • Destructive operations require explicit confirmation - always verify the ID

Region-based operations

  • When needed your agent will ask for a region (eu, us, cn, ap, ca)

The server is hosted

  • New API endpoints appear automatically in search results
  • No client updates needed when Storyblok adds new operations