Chapter 7: Working with Claude Code
Vibe CMS includes a powerful Model Context Protocol (MCP) server that enables AI agents like Claude Code to manage content, collections, files, and locales programmatically. This chapter covers the MCP architecture, available tools, and common workflows for AI-assisted content management.
Prerequisites
Before proceeding, ensure you have:
- Completed Chapter 1: MCP Server Installation
- API key configured in your MCP client
- Familiarity with Claude Code or another MCP-compatible AI agent
MCP Server Architecture
Vibe CMS implements the Model Context Protocol using FastMCP, a production-ready framework for building MCP servers with FastAPI integration.
Core Components
1. FastMCP Application (/backend/app/services/mcp/app.py)
The MCP server is defined using FastMCP and includes:
from fastmcp import FastMCP
mcp = FastMCP(
"Vibe CMS Server",
instructions="""CMS with project-scoped operations.
Collections contain content items with translations per locale.
All content item edits are performed on a draft version.
After content item edits, you must ask the user to approve the
changes going to the web app and publishing the draft version.
Files stored in folders with metadata tracking.""",
)
The server registers 6 categories of tools:
- CollectionTools - Manage collections and schemas
- FieldTools - Add, update, reorder, and delete fields
- ContentItemTools - Create and manage content with translations
- FileTools - Upload, manage, and retrieve files
- FolderTools - Organize files in folder structures
- LocaleTools - Configure supported languages
2. Authentication Middleware (/backend/app/services/mcp/middleware.py)
The MCPAuthMiddleware intercepts all requests to /mcp/* endpoints and:
- Extracts API key from headers (
X-API-KeyorAuthorization: Bearer) - Validates the key via
authenticate_with_api_key() - Creates an authenticated
SupabaseServiceinstance - Stores project context in
request.state
class MCPAuthMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
if not request.url.path.startswith("/mcp"):
return await call_next(request)
api_key = extract_api_key_from_headers(request.headers)
if not api_key:
return JSONResponse(status_code=401, content={"error": "Authentication required"})
service, key_info = await authenticate_with_api_key(api_key)
request.state.supabase = service
request.state.project_id = key_info.get("project_id")
return await call_next(request)
3. Database-Layer Security
Vibe CMS implements a zero-trust architecture where every database operation independently validates the API key:
CREATE OR REPLACE FUNCTION public.get_collections_for_project(
p_project_id uuid,
p_api_key text DEFAULT NULL
)
RETURNS TABLE (...) AS $$
BEGIN
-- Validate API key and set session context
PERFORM ensure_authenticated(p_api_key);
-- Query with RLS enforcement
RETURN QUERY SELECT * FROM collections WHERE project_id = p_project_id;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
Key security features:
- API key validation:
ensure_authenticated(p_api_key)uses bcrypt (timing-safe) - Session variables: Sets
app.current_project_id,app.current_account_id,app.auth_type - RLS policies: Enforce project-scoped access on all tables
- No session pooling: Each RPC call is independently authenticated
Request Flow
Claude Code
↓
↓ HTTP POST /mcp/ (with X-API-Key header)
↓
MCPAuthMiddleware
↓ Extract & validate API key
↓ Create authenticated SupabaseService
↓
FastMCP Tool Handler
↓ Get service from request.state.supabase
↓
SupabaseService Method
↓ Add p_api_key to RPC params
↓
Database RPC Function
↓ Call ensure_authenticated(p_api_key)
↓ Execute query with RLS enforcement
↓
Response to Claude Code
Available MCP Tools
Vibe CMS provides 18+ tools organized into 6 functional categories.
Collections Tools
collections(slug?)
Query collections with optional slug filter.
Parameters:
slug(optional): Collection slug (e.g., 'blog-posts'). If provided, returns single collection with fields. If omitted, lists all collections.
Response (list all):
{
"collections": [
{
"name": "Blog Posts",
"slug": "blog-posts",
"description": "Main blog content",
"is_singleton": false,
"fields": [
{
"field_name": "title",
"field_type": "text",
"interface_type": "input",
"is_required": true,
"sort_order": 0
}
]
}
],
"count": 1
}
Response (single collection):
{
"collection": {
"name": "Blog Posts",
"slug": "blog-posts",
"description": "Main blog content",
"is_singleton": false,
"fields": [...]
},
"message": "Collection 'blog-posts' retrieved"
}
manage_collection(action, slug, ...)
Create, update, or delete collection.
Parameters:
action(required): "create", "update", or "delete"slug(required): Collection slugname(required for create/update): Display namedescription(optional): Collection descriptionis_singleton(optional): Single-item collection flagconfirm_delete(required for delete): Must betrueto confirm
Example (create):
{
"action": "create",
"slug": "faq",
"name": "FAQ Items",
"description": "Frequently asked questions",
"is_singleton": false
}
Field Tools
add_collection_field(collection_slug, name, field_type, interface_type, ...)
Add a new field to a collection.
Parameters:
collection_slug(required): Collection slug (e.g., 'blog-posts')name(required): Field name (lowercase, letters/numbers/underscores, must start with letter)field_type(required): Data type - "text", "markdown", "number", "boolean", or "file"interface_type(required): UI interface - "input", "textarea", "markdown", "single_file", or "multiple_files"is_required(optional, default false): Whether field is requiredsort_order(optional, default 0): Field ordering
Field Type Compatibility:
text: "input", "textarea"markdown: "markdown"number: "input"boolean: "input"file: "single_file", "multiple_files"
Reserved Field Names: Cannot use id, created_at, updated_at, data, select, from, where, and, or, not, null, true, false, table, column
update_collection_field(collection_slug, field_name, ...)
Update field interface, required status, or sort order.
Parameters:
collection_slug(required): Collection slugfield_name(required): Name of field to updateinterface_type(optional): New UI interface (must be compatible with field type)is_required(optional): New required statussort_order(optional): New sort order
Note: Cannot change field name or field type after creation.
reorder_collection_fields(collection_slug, field_names)
Reorder all fields in a collection.
Parameters:
collection_slug(required): Collection slugfield_names(required): List of ALL field names in desired order (first to last)
Example:
{
"collection_slug": "blog-posts",
"field_names": ["title", "slug", "content", "author", "published_date"]
}
delete_collection_field(collection_slug, field_name, confirm)
Delete field from collection permanently with all data.
Parameters:
collection_slug(required): Collection slugfield_name(required): Name of field to deleteconfirm(required): Must betrueto confirm deletion
Warning: This permanently deletes the field and all its data across all content items.
Content Tools
content(collection_slug, content_item_id?, locale?)
Get content items - list all in collection or get single item with translations.
Parameters:
collection_slug(required): Collection slug (e.g., 'blog-posts')content_item_id(optional): Content item UUID. If provided, returns single item with translations.locale(optional): Locale code filter (e.g., 'en-US'). Only used withcontent_item_id.
Response (list all):
{
"items": [
{
"id": "uuid-here",
"collection_slug": "blog-posts",
"status": "published",
"description": "My first post"
}
],
"count": 1,
"collection_slug": "blog-posts"
}
Response (single item):
{
"id": "uuid-here",
"collection_slug": "blog-posts",
"status": "published",
"description": "My first post",
"translations": [
{
"locale": "en-US",
"data": {
"title": "Hello World",
"content": "Welcome to my blog..."
}
}
]
}
create_content(collection_slug, description?, status?)
Create new content item metadata without translation data.
Parameters:
collection_slug(required): Collection slugdescription(optional): Content item descriptionstatus(optional, default "draft"): "draft", "published", or "archived"
Response:
{
"id": "uuid-here",
"collection_slug": "blog-posts",
"status": "draft",
"message": "Content item created successfully. Use update_content_translation to add content data for each locale."
}
update_content_translation(content_item_id, locale, data, ...)
Create or update content item translation with schema validation.
Parameters:
content_item_id(required): Content item UUIDlocale(required): Locale code (e.g., 'en-US', 'de-DE')data(required): Content data matching collection field schemastatus(optional): Update status ("draft", "published", "archived")description(optional): Update description
Example:
{
"content_item_id": "uuid-here",
"locale": "en-US",
"data": {
"title": "My First Blog Post",
"slug": "first-post",
"content": "# Welcome\n\nThis is my first post...",
"author": "John Doe",
"published_date": "2024-01-15"
},
"status": "published"
}
Validation:
- Checks all fields match collection schema
- Validates required fields on create
- Prevents setting required fields to null on update
- Returns clear error messages for validation failures
delete_content(content_item_id, locale?, confirm_delete)
Delete content item or specific translation.
Parameters:
content_item_id(required): Content item UUIDlocale(optional): Locale code. If provided, deletes only that translation. If omitted, deletes entire item.confirm_delete(required): Must betrueto confirm
Note: If you delete the last translation, the content item is automatically deleted.
File Tools
files(file_id?, folder_path?, mime_type?)
Get file metadata by ID or list files with optional filters.
Parameters:
file_id(optional): File ID to get metadata for. If provided, returns single file metadata with public URL.folder_path(optional): Folder path filter for listing (e.g., '/images', '/documents/2024')mime_type(optional): MIME type filter for listing (e.g., 'image/png', 'image/')
Response (single file):
{
"id": "file-uuid",
"filename": "hero.jpg",
"mime_type": "image/jpeg",
"file_size": 245678,
"width": 1920,
"height": 1080,
"alt_text": "Hero image",
"title": "Homepage Hero",
"public_url": "https://cdn.example.com/files/hero.jpg"
}
manage_file(action, file_id, ...)
Update file metadata or delete file permanently.
Parameters:
action(required): "update" or "delete"file_id(required): File ID to managetitle(optional, update only): File titlealt_text(optional, update only): SEO alt textcaption(optional, update only): File captiondescription(optional, update only): File descriptionfocus_keyword(optional, update only): SEO focus keywordfolder_path(optional, update only): New folder pathconfirm_delete(required for delete): Must betrueto confirm
get_file_preview(file_id)
Generate 512x512 preview image for image files only.
Parameters:
file_id(required): File ID
Returns: Base64-encoded preview image or error if file is not an image.
upload_file_from_url(url, ...)
Download file from URL and upload to CMS.
Parameters:
url(required): URL to download file fromfilename(optional): Override filename (auto-detected if not provided)mime_type(optional): Override MIME type (auto-detected if not provided)folder_path(optional): Folder path (e.g., '/images', '/documents/2024')title(optional): File titlealt_text(optional): SEO alt text for imagescaption(optional): File captiondescription(optional): File descriptionfocus_keyword(optional): SEO focus keyword
Example:
{
"url": "https://images.unsplash.com/photo-123",
"filename": "hero-image.jpg",
"folder_path": "/images/heroes",
"alt_text": "Beautiful mountain landscape",
"title": "Hero Image - Mountains"
}
request_upload_token(filename, mime_type, file_size, ...)
Request temporary token for direct HTTP PUT file upload.
Parameters:
filename(required): Filename to upload (e.g., 'document.pdf', 'image.jpg')mime_type(required): File MIME type (e.g., 'application/pdf', 'image/jpeg')file_size(required): File size in bytes (max 52428800 = 50MB)folder_path(optional): Folder pathtitle(optional): File titlealt_text(optional): SEO alt text
Response:
{
"upload_url": "https://storage.example.com/upload?token=...",
"file_id": "uuid-here",
"expires_at": "2024-01-15T12:30:00Z"
}
Usage: Use the upload_url to perform a direct HTTP PUT request with the file contents.
Folder Tools
folders(parent_path?)
List all folders with optional parent path filter.
Parameters:
parent_path(optional): Parent path to filter folders (e.g., '/images', '/documents/2024')
Response:
{
"folders": [
{
"name": "heroes",
"path": "/images/heroes",
"parent_path": "/images"
}
],
"count": 1,
"filter_applied": "/images"
}
manage_folder(action, path, ...)
Create, update (rename), or delete folder.
Parameters:
action(required): "create", "update", or "delete"path(required): Folder path (e.g., '/images', '/teams')new_path(optional, required for update): New path for renameconfirm_delete(required for delete): Must betrueto confirm
Example (create):
{
"action": "create",
"path": "/images/heroes"
}
Locale Tools
locales()
List all locales for project.
Response:
{
"locales": [
{
"locale_code": "en-US",
"display_name": "English (US)",
"is_default": true,
"is_active": true
},
{
"locale_code": "de-DE",
"display_name": "German",
"is_default": false,
"is_active": true
}
],
"default_locale": {
"locale_code": "en-US",
"display_name": "English (US)",
"is_default": true,
"is_active": true
},
"count": 2,
"active_count": 2
}
manage_locale(action, locale_code, ...)
Create, update, or delete locale.
Parameters:
action(required): "create", "update", or "delete"locale_code(required): BCP 47 locale code (e.g., 'en-US', 'fr-FR', 'de-DE')display_name(required for create/update): Human-readable locale nameis_default(optional): Whether this should be the default localeis_active(optional): Whether this locale is activeconfirm_delete(required for delete): Must betrueto confirm
Example (create):
{
"action": "create",
"locale_code": "es-ES",
"display_name": "Spanish (Spain)",
"is_active": true
}
Authentication and Security
API Key Format
Vibe CMS uses project-scoped API keys with the format:
sk_[random_string]
Creating an API Key
Via Frontend UI:
- Login to Vibe CMS
- Navigate to Project Settings → API Keys
- Click "Create New API Key"
- Provide name and optional expiration
- Copy the key (shown only once!)
Via Database Seed (development only):
npm run db:seed
cat /tmp/vibe_cms_test_api_key_project.txt
Using API Keys with MCP
API keys can be provided in two ways:
Option 1: X-API-Key Header
{
"headers": {
"X-API-Key": "sk_your_api_key_here"
}
}
Option 2: Authorization Bearer Header
{
"headers": {
"Authorization": "Bearer sk_your_api_key_here"
}
}
Security Model
Vibe CMS implements a zero-trust security architecture:
- No Session State: Each request is independently authenticated
- Database-Layer Validation: Every RPC call validates the API key via
ensure_authenticated(p_api_key) - Bcrypt Hashing: API keys are hashed using bcrypt (timing-safe comparison)
- Session Variables: Authentication sets PostgreSQL session variables:
app.auth_type = 'api_key'app.current_project_id = <project_id>app.current_account_id = <account_id>
- RLS Enforcement: Row-Level Security policies use session variables for access control
- Project Scoping: API keys only access resources within their project
- Instant Revocation: Delete API key to immediately terminate all access
Best Practices
- Never commit API keys to version control
- Use environment variables for API key storage
- Rotate keys regularly for production environments
- Set expiration dates for temporary access
- Use descriptive names to identify key purpose
- Monitor API key usage via audit logs
Using MCP Tools in Claude Code
Tool Invocation Syntax
Claude Code automatically detects available MCP tools and can invoke them based on natural language requests.
Example Conversations:
User: "List all collections in my CMS"
Claude Code: Invokes collections() without parameters
User: "Show me the schema for the blog-posts collection"
Claude Code: Invokes collections(slug="blog-posts")
User: "Create a new collection called 'Team Members' with slug 'team'"
Claude Code: Invokes manage_collection(action="create", slug="team", name="Team Members")
Response Handling
All MCP tools return JSON responses with consistent structure:
Success Response:
{
"success": true,
"data": { ... },
"message": "Operation completed successfully"
}
Error Response:
{
"error": "Error message",
"suggestion": "How to fix this error"
}
Claude Code automatically:
- Parses JSON responses
- Displays results in human-readable format
- Uses suggestions to retry failed operations
- Chains multiple tool calls for complex workflows
Error Handling
Common error scenarios and how to handle them:
Invalid Field Name:
{
"error": "Field name must start with lowercase letter and contain only lowercase letters, numbers, and underscores",
"suggestion": "Use lowercase letters, numbers, and underscores only. Must start with a letter."
}
Missing Required Parameters:
{
"error": "name is required for create action",
"suggestion": "Provide a name for the collection"
}
Resource Not Found:
{
"error": "Collection 'blog-posts' not found",
"suggestion": "Use collections() to see available collections"
}
Permission Denied:
{
"error": "Insufficient permissions to delete this field",
"suggestion": "Ensure your API key has write access to this project"
}
Common Workflows
1. Creating a New Collection with Fields
Goal: Set up a "Team Members" collection with name, role, bio, and photo fields.
Prompt to Claude Code:
"Create a new collection called 'Team Members' with slug 'team'. Add fields: name (text input, required), role (text input, required), bio (markdown), and photo (single file)."
Claude Code executes:
# Step 1: Create collection
manage_collection(
action="create",
slug="team",
name="Team Members",
description="Company team member profiles"
)
# Step 2: Add name field
add_collection_field(
collection_slug="team",
name="name",
field_type="text",
interface_type="input",
is_required=True,
sort_order=0
)
# Step 3: Add role field
add_collection_field(
collection_slug="team",
name="role",
field_type="text",
interface_type="input",
is_required=True,
sort_order=1
)
# Step 4: Add bio field
add_collection_field(
collection_slug="team",
name="bio",
field_type="markdown",
interface_type="markdown",
is_required=False,
sort_order=2
)
# Step 5: Add photo field
add_collection_field(
collection_slug="team",
name="photo",
field_type="file",
interface_type="single_file",
is_required=False,
sort_order=3
)
2. Managing Files and Media
Goal: Upload a hero image from Unsplash and organize it in a folder.
Prompt to Claude Code:
"Create a folder at /images/heroes, then download this Unsplash image and upload it to that folder with alt text 'Mountain landscape at sunset'"
Claude Code executes:
# Step 1: Create folder
manage_folder(
action="create",
path="/images/heroes"
)
# Step 2: Upload image from URL
upload_file_from_url(
url="https://images.unsplash.com/photo-1234567890",
filename="hero-mountains.jpg",
folder_path="/images/heroes",
alt_text="Mountain landscape at sunset",
title="Hero Image - Mountains"
)
3. Creating Multi-Locale Content
Goal: Create a blog post with English and German translations.
Prompt to Claude Code:
"Create a new blog post titled 'Welcome to Our Blog' in English and 'Willkommen in unserem Blog' in German"
Claude Code executes:
# Step 1: Create content item
result = create_content(
collection_slug="blog-posts",
description="Welcome blog post",
status="draft"
)
content_item_id = result["id"]
# Step 2: Add English translation
update_content_translation(
content_item_id=content_item_id,
locale="en-US",
data={
"title": "Welcome to Our Blog",
"slug": "welcome",
"content": "# Welcome\n\nThanks for visiting our blog..."
}
)
# Step 3: Add German translation
update_content_translation(
content_item_id=content_item_id,
locale="de-DE",
data={
"title": "Willkommen in unserem Blog",
"slug": "willkommen",
"content": "# Willkommen\n\nDanke für Ihren Besuch in unserem Blog..."
}
)
4. Schema Modifications
Goal: Add a new "tags" field to an existing collection and reorder fields.
Prompt to Claude Code:
"Add a 'tags' text field to the blog-posts collection, then reorder fields so tags appears after title"
Claude Code executes:
# Step 1: Check current schema
schema = collections(slug="blog-posts")
print(f"Current fields: {[f['field_name'] for f in schema['collection']['fields']]}")
# Step 2: Add tags field
add_collection_field(
collection_slug="blog-posts",
name="tags",
field_type="text",
interface_type="input",
is_required=False
)
# Step 3: Reorder fields
reorder_collection_fields(
collection_slug="blog-posts",
field_names=["title", "tags", "slug", "content", "author", "published_date"]
)
5. Content Queries and Updates
Goal: Find all draft blog posts and publish them.
Prompt to Claude Code:
"List all draft blog posts and publish them"
Claude Code executes:
# Step 1: Get all blog posts
result = content(collection_slug="blog-posts")
# Step 2: Filter draft items
draft_items = [item for item in result["items"] if item["status"] == "draft"]
print(f"Found {len(draft_items)} draft posts")
# Step 3: Publish each draft
for item in draft_items:
# Get full item with translations
full_item = content(
collection_slug="blog-posts",
content_item_id=item["id"]
)
# Update each translation to published status
for translation in full_item["translations"]:
update_content_translation(
content_item_id=item["id"],
locale=translation["locale"],
data=translation["data"],
status="published"
)
print(f"Published: {item['description']}")
Advanced Patterns
Batch Operations
When performing multiple operations, optimize by batching related calls:
Anti-pattern (slow):
for field_name in field_names:
add_collection_field(collection_slug, field_name, ...)
# Wait for response
# Next iteration
Best practice (fast):
# Prepare all field configurations
fields = [
{"name": "title", "field_type": "text", "interface_type": "input"},
{"name": "content", "field_type": "markdown", "interface_type": "markdown"},
# ...
]
# Execute in sequence (Claude Code handles this automatically)
for field in fields:
add_collection_field(collection_slug="blog", **field)
Complex Content Creation
Pattern: Content with file references
# Step 1: Upload referenced files first
hero_image = upload_file_from_url(
url="https://example.com/hero.jpg",
folder_path="/images/posts",
alt_text="Post hero image"
)
# Step 2: Create content with file ID reference
content_item = create_content(
collection_slug="blog-posts",
description="New post with hero image"
)
# Step 3: Add translation with file reference
update_content_translation(
content_item_id=content_item["id"],
locale="en-US",
data={
"title": "My Post",
"content": "Post content...",
"hero_image": hero_image["id"] # Reference uploaded file
}
)
Multi-Locale Content Management
Pattern: Bulk translation creation
# Define content in multiple locales
translations = {
"en-US": {"title": "Welcome", "content": "Welcome to our site..."},
"de-DE": {"title": "Willkommen", "content": "Willkommen auf unserer Seite..."},
"fr-FR": {"title": "Bienvenue", "content": "Bienvenue sur notre site..."},
"es-ES": {"title": "Bienvenido", "content": "Bienvenido a nuestro sitio..."}
}
# Create content item
item = create_content(
collection_slug="pages",
description="Homepage",
status="draft"
)
# Add all translations
for locale, data in translations.items():
update_content_translation(
content_item_id=item["id"],
locale=locale,
data=data
)
Automated Content Generation
Pattern: Generate content from external data
# External data source (e.g., team members from API)
team_data = [
{"name": "Alice Johnson", "role": "CEO", "bio": "Founder with 15 years experience..."},
{"name": "Bob Smith", "role": "CTO", "bio": "Tech lead specializing in..."},
# ...
]
# Generate content items for each team member
for member in team_data:
# Create content item
item = create_content(
collection_slug="team",
description=f"{member['name']} - {member['role']}"
)
# Add content data
update_content_translation(
content_item_id=item["id"],
locale="en-US",
data={
"name": member["name"],
"role": member["role"],
"bio": member["bio"]
}
)
print(f"Created profile for {member['name']}")
Integration with Other Systems
Pattern: Sync content from external CMS
# Fetch content from external API
import requests
external_posts = requests.get("https://api.external-cms.com/posts").json()
# Sync to Vibe CMS
for post in external_posts:
# Create content item
item = create_content(
collection_slug="blog-posts",
description=post["title"],
status="published"
)
# Download and upload featured image
if post.get("featured_image_url"):
image = upload_file_from_url(
url=post["featured_image_url"],
folder_path="/images/posts",
alt_text=post.get("image_alt", "")
)
featured_image_id = image["id"]
else:
featured_image_id = None
# Add content with image reference
update_content_translation(
content_item_id=item["id"],
locale="en-US",
data={
"title": post["title"],
"slug": post["slug"],
"content": post["content"],
"author": post["author"],
"published_date": post["published_at"],
"featured_image": featured_image_id
}
)
print(f"Synced: {post['title']}")
Troubleshooting
"Authentication required"
Problem: MCP endpoint returns 401 error.
Solutions:
- Verify API key is included in headers (
X-API-KeyorAuthorization: Bearer) - Check API key is valid and not expired
- Ensure key was created for correct project
- Regenerate API key if needed
"Invalid or expired API key"
Problem: Tool execution fails with authentication error.
Solutions:
- Generate new API key via UI
- Verify key hasn't been deleted
- Check key expiration date
- Ensure key is for correct project
"Permission denied"
Problem: API key cannot access resources.
Solutions:
- Verify API key is scoped to correct project
- Check if collection belongs to API key's project
- Review database RLS policies
- Check database logs for detailed error
Tools not appearing in Claude Code
Problem: Hammer icon missing or tools not listed.
Solutions:
- Restart Claude Code completely
- Check MCP configuration file syntax (valid JSON)
- Verify URL has trailing slash (
/mcp/) - Test endpoint:
curl -H "X-API-Key: sk_..." https://your-cms.com/mcp/ - Check backend logs for authentication attempts
"Field name validation failed"
Problem: Cannot create field with desired name.
Solutions:
- Use lowercase letters only (not camelCase or PascalCase)
- Start field name with letter (not number or underscore)
- Avoid reserved words (id, created_at, select, from, where, etc.)
- Use underscores instead of hyphens or spaces
"Interface not compatible with field type"
Problem: Cannot set interface type for field.
Solutions:
- Check field type compatibility:
- text → input, textarea
- markdown → markdown
- number → input
- boolean → input
- file → single_file, multiple_files
- Cannot change field type after creation
- Delete and recreate field if type change needed
Next Steps
Additional Resources
- Chapter 1: MCP Server Installation - Initial setup and configuration
- FastMCP Documentation - Framework reference
- MCP Specification - Protocol details
- Claude Code Documentation - AI agent usage
Advanced Topics
Phase 2: Enhanced Read Operations
- Advanced filtering and search
- Pagination for large datasets
- Aggregation queries
- Full-text search
Phase 3: Workflow Automation
- Content review and approval workflows
- Scheduled publishing
- Automated translations
- SEO optimization
Phase 4: Integration Features
- Webhooks for content changes
- Export/import operations
- Backup and restore
- Analytics and reporting
Questions or Issues?
If you encounter problems or have questions about the MCP server:
- Check the Troubleshooting section above
- Review backend logs for detailed error messages
- Consult the FastMCP documentation for framework-specific issues
- Open an issue on the Vibe CMS GitHub repository
This documentation reflects MCP Server v2.0 with database-layer security and zero-trust architecture.