Skip to content

Configuration Reference

Configuration Reference

All configuration lives in config/statamic/mcp.php. Most settings can be controlled via environment variables.

Web Endpoint

Controls the HTTP-accessible MCP endpoint.

Key Env Variable Default Description
web.enabled STATAMIC_MCP_WEB_ENABLED true Enable the web MCP endpoint
web.path STATAMIC_MCP_WEB_PATH /mcp/statamic URL path for the endpoint
web.require_https STATAMIC_MCP_WEB_REQUIRE_HTTPS true Reject plain HTTP requests (skipped in local/testing)
web.allowed_origins [] CORS allowed origins for browser-based clients. Empty = no CORS headers
'web' => [
    'enabled' => env('STATAMIC_MCP_WEB_ENABLED', true),
    'path' => env('STATAMIC_MCP_WEB_PATH', '/mcp/statamic'),
    'require_https' => env('STATAMIC_MCP_WEB_REQUIRE_HTTPS', true),
    'allowed_origins' => [], // e.g. ['https://your-app.com'] or ['*']
],

Dashboard

Controls the CP dashboard at Tools > MCP.

Key Env Variable Default Description
dashboard.enabled STATAMIC_MCP_DASHBOARD_ENABLED true Show the MCP dashboard in the CP
'dashboard' => [
    'enabled' => env('STATAMIC_MCP_DASHBOARD_ENABLED', true),
],

Security

Controls authentication enforcement, audit logging, and system hardening.

Key Env Variable Default Description
security.force_web_mode STATAMIC_MCP_FORCE_WEB_MODE false Require token auth even in CLI context
security.audit_logging STATAMIC_MCP_AUDIT_LOGGING true Log all MCP tool calls
security.max_upload_size STATAMIC_MCP_MAX_UPLOAD_SIZE 10485760 Max upload size in bytes (10MB)
security.expose_versions STATAMIC_MCP_EXPOSE_VERSIONS false Include Statamic/Laravel versions in responses
security.max_token_lifetime_days STATAMIC_MCP_MAX_TOKEN_LIFETIME 365 Maximum token lifetime in days
security.tool_timeout_seconds STATAMIC_MCP_TOOL_TIMEOUT 30 Maximum execution time per tool call
security.max_response_size STATAMIC_MCP_MAX_RESPONSE_SIZE 100000 Largest tool response in bytes before it is refused; 0 disables the guard
security.reject_unknown_fields STATAMIC_MCP_REJECT_UNKNOWN_FIELDS true Refuse writes carrying keys that are not field handles inside a set, grid row or group
'security' => [
    'force_web_mode' => env('STATAMIC_MCP_FORCE_WEB_MODE', false),
    'audit_logging' => env('STATAMIC_MCP_AUDIT_LOGGING', true),
    'max_upload_size' => env('STATAMIC_MCP_MAX_UPLOAD_SIZE', 10 * 1024 * 1024),
    'expose_versions' => env('STATAMIC_MCP_EXPOSE_VERSIONS', false),
    'max_token_lifetime_days' => env('STATAMIC_MCP_MAX_TOKEN_LIFETIME', 365),
    'tool_timeout_seconds' => env('STATAMIC_MCP_TOOL_TIMEOUT', 30),
    'max_response_size' => (int) env('STATAMIC_MCP_MAX_RESPONSE_SIZE', 100000),
    'reject_unknown_fields' => env('STATAMIC_MCP_REJECT_UNKNOWN_FIELDS', true),
],

max_response_size

The limit exists to protect the client's context window, not the server: the response has already been built by the time it is measured. The right ceiling therefore depends on the client, which is why it is configurable. Setting it to 0 disables the guard entirely — reasonable for a client with a large context, but a tool call can then return an arbitrarily large payload.

reject_unknown_fields

Inside a replicator set, grid row, bard set or group, a key that is not a field handle is not an error to Statamic. Replicator::processRow() and Grid::processRow() merge the raw row back over the processed one, so the key is written to the content file as inert data that no template reads — and the write reports success. For a client that cannot read the blueprint from the repository, that is undiagnosable, so this refuses the write instead and names the valid handles at that level.

The check does not apply at the top level of a record. An entry legitimately carries keys that are not blueprint fields — template and layout are read back by Entry::template() and Entry::layout(), parent backs structures — and no allowlist can enumerate what every addon adds.

Rate Limiting

Controls request throttling for the web endpoint. Skipped in CLI context.

Key Env Variable Default Description
rate_limit.max_attempts STATAMIC_MCP_RATE_LIMIT_MAX 60 Max requests per minute
'rate_limit' => [
    'max_attempts' => env('STATAMIC_MCP_RATE_LIMIT_MAX', 60),
],

Cache

Whether a write clears Statamic's caches — and it does, because Statamic does not rebuild the indexes that depend on a write. Change a field's max_items and its index keeps the old shape until a query throws on it; change a collection's mount and every entry 404s; remove a taxonomy and whereTaxonomy() goes on returning entries.

What changed in 3.1.0 is when. The clear used to run through Artisan in the middle of the request, resetting the in-memory stores while the call was still using them. On a live multisite the tree repository returned nothing, Statamic padded the empty tree with every entry at root, nested URLs flattened and a random entry became the homepage. It now runs once the tool call is finished — the response is built, nothing further reads Statamic, and the next call starts fresh. Same coverage, without the mechanism that did the damage.

'cache' => [
    'clear_stache_after_write' => env('STATAMIC_MCP_CLEAR_STACHE_AFTER_WRITE', true),
    'clear_static_after_write' => env('STATAMIC_MCP_CLEAR_STATIC_AFTER_WRITE', true),
],

Turn either off if you would rather trade index freshness for speed on a large site and rely on your own invalidation rules. The statamic-system tool's cache_clear action is unaffected — that clear runs immediately, because it was asked for.

Resources

The read-only surface: statamic://blueprints and friends.

These have their own switch because they used to share the tools' one. A site that keeps its content model in Git turns the blueprints tool off precisely because it can create and delete blueprints — and that also removed the only read-only way for an agent to learn a blueprint's fields, while the server's own instructions tell it to read the blueprint before every write.

'resources' => [
    'enabled' => env('STATAMIC_MCP_RESOURCES_ENABLED', true),
    'require_statamic_permission' => env('STATAMIC_MCP_RESOURCES_REQUIRE_PERMISSION', true),
],

require_statamic_permission keeps the Statamic permission check (configure fields, configure collections or configure taxonomies) on top of the token scope and the resource-policy allowlist. Set it to false if your editors hold none of those and you would rather let the token scope you minted decide who may read schema.

Tool Catalog

Controls how the tool list is presented to a client when it connects.

By default the catalog is partial. Only the tools a session usually opens with are listed — statamic-entries, statamic-blueprints and statamic-system-discover. The rest are reached through search_tools and execute_tools.

The reason is token cost: the full catalog is roughly 31 KB of JSON schema that every client loads on every connection, before it has asked anything. Withholding the less-used tools cuts that to about 12.6 KB — a ~60% saving in the client's context window.

Nothing is taken away. The hidden tools are fully available and fully gated — token scopes, resource policy, Statamic permissions and the confirmation gate all apply exactly as they do on a direct call. Only their schemas wait until a client asks.

'catalog' => [
    'searchable' => env('STATAMIC_MCP_SEARCHABLE_CATALOG', true),
],

Set it to false if your MCP client handles search_tools poorly and you would rather it saw every tool listed directly:

STATAMIC_MCP_SEARCHABLE_CATALOG=false

Tool Domains

Enable or disable individual tool domains. When a domain is disabled, its tools are not registered and calls return an error.

Each domain can be toggled via STATAMIC_MCP_TOOL_{NAME}_ENABLED environment variables.

'tools' => [
    'blueprints' => ['enabled' => env('STATAMIC_MCP_TOOL_BLUEPRINTS_ENABLED', true)],
    'entries' => ['enabled' => env('STATAMIC_MCP_TOOL_ENTRIES_ENABLED', true)],
    'terms' => ['enabled' => env('STATAMIC_MCP_TOOL_TERMS_ENABLED', true)],
    'globals' => ['enabled' => env('STATAMIC_MCP_TOOL_GLOBALS_ENABLED', true)],
    'structures' => ['enabled' => env('STATAMIC_MCP_TOOL_STRUCTURES_ENABLED', true)],
    'assets' => ['enabled' => env('STATAMIC_MCP_TOOL_ASSETS_ENABLED', true)],
    'users' => ['enabled' => env('STATAMIC_MCP_TOOL_USERS_ENABLED', true)],
    'system' => ['enabled' => env('STATAMIC_MCP_TOOL_SYSTEM_ENABLED', true)],
    'content-facade' => ['enabled' => env('STATAMIC_MCP_TOOL_CONTENT_FACADE_ENABLED', true)],
],

To disable a domain, set its env var to false:

STATAMIC_MCP_TOOL_USERS_ENABLED=false
STATAMIC_MCP_TOOL_SYSTEM_ENABLED=false

OAuth

Configure the OAuth 2.1 authorization server for browser-based MCP client registration and token exchange using PKCE (RFC 7636).

Key Env Variable Default Description
oauth.enabled STATAMIC_MCP_OAUTH_ENABLED true Enable the OAuth 2.1 authorization server
oauth.driver STATAMIC_MCP_OAUTH_DRIVER BuiltInOAuthDriver::class OAuth driver implementation
oauth.code_ttl STATAMIC_MCP_OAUTH_CODE_TTL 600 Authorization code TTL in seconds (10 min)
oauth.client_ttl STATAMIC_MCP_OAUTH_CLIENT_TTL 2592000 Client registration TTL in seconds (30 days)
oauth.token_ttl STATAMIC_MCP_OAUTH_TOKEN_TTL 604800 Access token TTL in seconds (7 days)
oauth.refresh_token_ttl STATAMIC_MCP_OAUTH_REFRESH_TOKEN_TTL 2592000 Refresh token TTL in seconds (30 days)
oauth.default_scopes STATAMIC_MCP_OAUTH_DEFAULT_SCOPES * Comma-separated default scopes for OAuth tokens
oauth.max_clients STATAMIC_MCP_OAUTH_MAX_CLIENTS 50 Maximum number of registered OAuth clients
oauth.max_clients_per_ip STATAMIC_MCP_OAUTH_MAX_CLIENTS_PER_IP 5 Maximum client registrations per IP address
'oauth' => [
    'enabled' => env('STATAMIC_MCP_OAUTH_ENABLED', true),
    'driver' => env('STATAMIC_MCP_OAUTH_DRIVER', BuiltInOAuthDriver::class),
    'code_ttl' => (int) env('STATAMIC_MCP_OAUTH_CODE_TTL', 600),
    'client_ttl' => (int) env('STATAMIC_MCP_OAUTH_CLIENT_TTL', 2592000),
    'token_ttl' => (int) env('STATAMIC_MCP_OAUTH_TOKEN_TTL', 604800),
    'refresh_token_ttl' => (int) env('STATAMIC_MCP_OAUTH_REFRESH_TOKEN_TTL', 2592000),
    'default_scopes' => array_filter(explode(',', env('STATAMIC_MCP_OAUTH_DEFAULT_SCOPES', '*'))),
    'max_clients' => (int) env('STATAMIC_MCP_OAUTH_MAX_CLIENTS', 50),
    'max_clients_per_ip' => (int) env('STATAMIC_MCP_OAUTH_MAX_CLIENTS_PER_IP', 5),
],

Storage Drivers

Configure which storage backends to use for tokens and audit logs. Swap to database drivers for multi-server or high-availability deployments.

Key Default Description
stores.tokens FileTokenStore::class Token storage driver (FileTokenStore or DatabaseTokenStore)
stores.audit FileAuditStore::class Audit log storage driver (FileAuditStore or DatabaseAuditStore)
'stores' => [
    'tokens' => FileTokenStore::class,
    'audit' => FileAuditStore::class,
],

Storage Paths

File paths used by the file-based storage drivers.

Key Default Description
storage.tokens_path storage_path('statamic-mcp/tokens') Token storage directory
storage.audit_path storage_path('statamic-mcp/audit.log') Audit log file path
storage.oauth_clients_path storage_path('statamic-mcp/oauth/clients') OAuth client registrations
storage.oauth_codes_path storage_path('statamic-mcp/oauth/codes') OAuth authorization codes
storage.oauth_refresh_path storage_path('statamic-mcp/oauth/refresh') OAuth refresh tokens
'storage' => [
    'tokens_path' => storage_path('statamic-mcp/tokens'),
    'audit_path' => storage_path('statamic-mcp/audit.log'),
    'oauth_clients_path' => storage_path('statamic-mcp/oauth/clients'),
    'oauth_codes_path' => storage_path('statamic-mcp/oauth/codes'),
    'oauth_refresh_path' => storage_path('statamic-mcp/oauth/refresh'),
],

Environment Variables Summary

Quick reference for all .env variables:

# Web endpoint
STATAMIC_MCP_WEB_ENABLED=true
STATAMIC_MCP_WEB_PATH="/mcp/statamic"
STATAMIC_MCP_WEB_REQUIRE_HTTPS=true

# Dashboard
STATAMIC_MCP_DASHBOARD_ENABLED=true

# Security
STATAMIC_MCP_FORCE_WEB_MODE=false
STATAMIC_MCP_AUDIT_LOGGING=true
STATAMIC_MCP_EXPOSE_VERSIONS=false
STATAMIC_MCP_MAX_UPLOAD_SIZE=10485760
STATAMIC_MCP_MAX_TOKEN_LIFETIME=365
STATAMIC_MCP_TOOL_TIMEOUT=30
STATAMIC_MCP_MAX_RESPONSE_SIZE=100000
STATAMIC_MCP_REJECT_UNKNOWN_FIELDS=true

# Confirmation flow
# Unset auto-detects: on in production, off in local/dev/testing
STATAMIC_MCP_CONFIRMATION_ENABLED=
STATAMIC_MCP_CONFIRMATION_TTL=300

# Rate limiting
STATAMIC_MCP_RATE_LIMIT_MAX=60

# Tool catalog
STATAMIC_MCP_SEARCHABLE_CATALOG=true

# Cache
STATAMIC_MCP_CLEAR_STACHE_AFTER_WRITE=false
STATAMIC_MCP_CLEAR_STATIC_AFTER_WRITE=true

# Resources
STATAMIC_MCP_RESOURCES_ENABLED=true
STATAMIC_MCP_RESOURCES_REQUIRE_PERMISSION=true

# OAuth 2.1
STATAMIC_MCP_OAUTH_ENABLED=true
STATAMIC_MCP_OAUTH_DRIVER=BuiltInOAuthDriver
STATAMIC_MCP_OAUTH_CODE_TTL=600
STATAMIC_MCP_OAUTH_CLIENT_TTL=2592000
STATAMIC_MCP_OAUTH_TOKEN_TTL=604800
STATAMIC_MCP_OAUTH_REFRESH_TOKEN_TTL=2592000
STATAMIC_MCP_OAUTH_DEFAULT_SCOPES=*
STATAMIC_MCP_OAUTH_MAX_CLIENTS=50
STATAMIC_MCP_OAUTH_MAX_CLIENTS_PER_IP=5

# OAuth: Client ID Metadata Documents
STATAMIC_MCP_OAUTH_CIMD_ENABLED=true
STATAMIC_MCP_OAUTH_CIMD_FETCH_TIMEOUT=5
STATAMIC_MCP_OAUTH_CIMD_MAX_RESPONSE_SIZE=5120
STATAMIC_MCP_OAUTH_CIMD_CACHE_TTL=3600
STATAMIC_MCP_OAUTH_CIMD_BLOCK_PRIVATE_IPS=true

# Tool toggles (set to false to disable)
STATAMIC_MCP_TOOL_BLUEPRINTS_ENABLED=true
STATAMIC_MCP_TOOL_ENTRIES_ENABLED=true
STATAMIC_MCP_TOOL_TERMS_ENABLED=true
STATAMIC_MCP_TOOL_GLOBALS_ENABLED=true
STATAMIC_MCP_TOOL_STRUCTURES_ENABLED=true
STATAMIC_MCP_TOOL_ASSETS_ENABLED=true
STATAMIC_MCP_TOOL_USERS_ENABLED=true
STATAMIC_MCP_TOOL_SYSTEM_ENABLED=true
STATAMIC_MCP_TOOL_CONTENT_FACADE_ENABLED=true