Chapter 2: First Steps with Vibe CMS
Welcome to your first hands-on experience with Vibe CMS! In this chapter, you'll learn the core concepts and workflows for managing content through the MCP server. By the end, you'll be comfortable creating collections, managing content items, working with translations, and handling media assets.
Prerequisites
Before starting this chapter, ensure you have:
- Completed Chapter 1: MCP Server Installation
- Vibe CMS MCP server configured and working in Claude Code
- Basic familiarity with Claude Code and MCP tools
- Access to your Vibe CMS project
Understanding Collections and Content Structure
What Are Collections?
Collections are the foundation of content organization in Vibe CMS. Think of a collection as a content type definition - a blueprint that defines what fields your content contains and how they should be structured.
For example, a blog-posts collection might define fields like:
title(text)author(text)body(markdown)published_date(text)featured_image(file)
Singleton vs Multi-Item Collections
Vibe CMS supports two types of collections:
Multi-Item Collections (default):
- Contain multiple content items
- Examples: blog posts, product listings, team members
- Each item has a unique ID
- Used for repeating content structures
Singleton Collections:
- Contain exactly one content item
- Examples: homepage, site settings, global footer
- Perfect for unique pages or configuration
- Accessed with
.first()in SDK queries
Collection Schema and Fields
Every collection defines a schema through its fields. Each field has:
Field Types (Data Storage)
The available field types determine how data is stored in the database:
text: Plain text strings (names, titles, URLs)markdown: Rich formatted text with markdown syntaxnumber: Numeric values (prices, quantities, order)boolean: True/false flags (featured, published, enabled)file: References to uploaded media assets
Interface Types (Editor UI)
Interface types control how editors interact with fields in the admin:
input: Single-line text input (for text fields)textarea: Multi-line text area (for longer text)markdown: Rich markdown editor with preview (for markdown fields)single_file: Single file picker (for file fields)multiple_files: Multiple file selection (for file fields with arrays)
Required vs Optional Fields
Fields can be marked as required or optional:
- Required fields: Must have a value before content can be published
- Optional fields: Can be left empty
Viewing Collections
To see all collections in your project:
// List all collections
mcp__vibe-cms-docs__collections({ slug: null })
// Get specific collection with fields
mcp__vibe-cms-docs__collections({ slug: "blog-posts" })
The detailed view shows:
- Collection name and slug
- Whether it's a singleton
- All fields with their types, interfaces, and requirements
- Field ordering (sort_order)
Creating Your First Content Items
Understanding Content Item Metadata
Before diving into content creation, understand that Vibe CMS separates content metadata from translation data:
Content Item (metadata):
- Unique ID (UUID)
- Status:
draft,published, orarchived - Description (optional organizational note)
- Exists once per logical content piece
Translation (actual content):
- Locale code (en-US, fr-FR, de-DE, etc.)
- Field data matching the collection schema
- Multiple translations per content item
Step 1: Create Content Item Metadata
First, create the content item shell:
mcp__vibe-cms-docs__create_content({
collection_slug: "blog-posts",
description: "Introduction to Vibe CMS features",
status: "draft"
})
This returns a new content item with:
- A unique
id(UUID likea5803364-06f5-4fc4-a921-c93cf8aefb28) - Initial status set to
draft - No translations yet
Important: Save this ID - you'll need it for the next step!
Step 2: Create Translation with Content
Now add the actual content in your desired locale:
mcp__vibe-cms-docs__update_content_translation({
content_item_id: "a5803364-06f5-4fc4-a921-c93cf8aefb28",
locale: "en-US",
data: {
title: "Getting Started with Vibe CMS",
author: "Documentation Team",
body: "# Welcome\n\nThis is your first blog post...",
published_date: "2025-01-15",
featured_image: "file-uuid-from-upload"
}
})
The data object must match your collection's schema exactly:
- Field names must match collection field names
- Field values must match the expected types
- Required fields must be included
- Extra fields will be rejected
Viewing Content Items
To see all content in a collection:
// List all items
mcp__vibe-cms-docs__content({
collection_slug: "blog-posts"
})
// Get single item with all translations
mcp__vibe-cms-docs__content({
collection_slug: "blog-posts",
content_item_id: "a5803364-06f5-4fc4-a921-c93cf8aefb28"
})
// Get single item, filtered to one locale
mcp__vibe-cms-docs__content({
collection_slug: "blog-posts",
content_item_id: "a5803364-06f5-4fc4-a921-c93cf8aefb28",
locale: "en-US"
})
Working with Translations and Locales
Understanding Locales
Locales define the languages and regional variants available in your project. They follow the BCP 47 standard:
en-US- English (United States)en-GB- English (United Kingdom)fr-FR- French (France)de-DE- German (Germany)es-ES- Spanish (Spain)ja-JP- Japanese (Japan)
Each project has one default locale that serves as the primary language.
Managing Locales
View all configured locales:
mcp__vibe-cms-docs__locales({})
Create a new locale:
mcp__vibe-cms-docs__manage_locale({
action: "create",
locale_code: "fr-FR",
display_name: "French (France)",
is_active: true,
is_default: false
})
Update an existing locale:
mcp__vibe-cms-docs__manage_locale({
action: "update",
locale_code: "fr-FR",
display_name: "Français",
is_active: true
})
Creating Multiple Translations
To make content available in multiple languages, create separate translations:
// English version
mcp__vibe-cms-docs__update_content_translation({
content_item_id: "a5803364-06f5-4fc4-a921-c93cf8aefb28",
locale: "en-US",
data: {
title: "Getting Started with Vibe CMS",
body: "Welcome to Vibe CMS..."
}
})
// French version
mcp__vibe-cms-docs__update_content_translation({
content_item_id: "a5803364-06f5-4fc4-a921-c93cf8aefb28",
locale: "fr-FR",
data: {
title: "Commencer avec Vibe CMS",
body: "Bienvenue sur Vibe CMS..."
}
})
The same content item ID is used for all translations - Vibe CMS automatically links them together.
Locale Fallback Behavior
When requesting content:
- If the requested locale exists, it's returned
- If the requested locale doesn't exist, no automatic fallback occurs
- Your application should implement fallback logic (e.g., try
fr-FR, then fall back toen-US)
Managing Files and Media Assets
Understanding File Management
Vibe CMS includes a built-in asset manager for handling files and media. Key features:
- Deduplication: Files are identified by SHA-256 hash - uploading the same file twice reuses the existing copy
- Folder organization: Organize files in a hierarchical folder structure
- Rich metadata: Add titles, descriptions, alt text, and SEO keywords
- Image transformations: Automatic image variants and transformations
- CDN delivery: Fast global file delivery
Managing Folders
Create a folder structure:
mcp__vibe-cms-docs__manage_folder({
action: "create",
path: "/images"
})
mcp__vibe-cms-docs__manage_folder({
action: "create",
path: "/images/blog"
})
mcp__vibe-cms-docs__manage_folder({
action: "create",
path: "/documents"
})
List folders:
// All folders
mcp__vibe-cms-docs__folders({})
// Folders in a specific path
mcp__vibe-cms-docs__folders({
parent_path: "/images"
})
Rename a folder:
mcp__vibe-cms-docs__manage_folder({
action: "update",
path: "/images/blog",
new_path: "/images/articles"
})
Uploading Files
Method 1: Upload from URL
The easiest way to add files is from a public URL:
mcp__vibe-cms-docs__upload_file_from_url({
url: "https://images.unsplash.com/photo-1234567890",
folder_path: "/images/blog",
title: "Hero Image",
alt_text: "Modern office workspace with laptop",
caption: "Photo by Unsplash",
description: "Hero image for blog post about productivity",
focus_keyword: "productivity workspace"
})
This returns file metadata including the file ID and public URL.
Method 2: Request Upload Token
For uploading local files via HTTP PUT:
// Step 1: Request token
mcp__vibe-cms-docs__request_upload_token({
filename: "document.pdf",
mime_type: "application/pdf",
file_size: 2048576, // bytes
folder_path: "/documents",
title: "Product Guide"
})
// Step 2: Use returned upload_url and token to PUT file
// Step 3: File is automatically created after successful upload
Working with File Metadata
List files:
// All files
mcp__vibe-cms-docs__files({})
// Files in a folder
mcp__vibe-cms-docs__files({
folder_path: "/images/blog"
})
// Files by MIME type
mcp__vibe-cms-docs__files({
mime_type: "image/" // All images
})
// Get specific file
mcp__vibe-cms-docs__files({
file_id: "file-uuid"
})
Update file metadata:
mcp__vibe-cms-docs__manage_file({
action: "update",
file_id: "file-uuid",
title: "Updated Title",
alt_text: "Descriptive alt text for accessibility",
folder_path: "/images/featured",
focus_keyword: "main topic"
})
Image Previews
Generate a 512x512 preview for images:
mcp__vibe-cms-docs__get_file_preview({
file_id: "image-file-uuid"
})
This returns base64-encoded image data for quick previews.
Using Files in Content
Reference uploaded files in your content by file ID:
mcp__vibe-cms-docs__update_content_translation({
content_item_id: "content-uuid",
locale: "en-US",
data: {
title: "My Blog Post",
featured_image: "file-uuid", // Single file field
gallery: ["file-uuid-1", "file-uuid-2"] // Multiple files field
}
})
Publishing and Draft Workflows
Understanding the Draft System
Vibe CMS uses a single draft workflow:
- Each project has one draft version at a time
- The draft contains all unpublished changes across all content
- Publishing creates a versioned snapshot of all content
- You can rollback to any previous version
This is different from per-item drafts - changes to any content exist in the shared draft until published.
Status Types
Content items have three status values:
draft: New content, not yet publishedpublished: Live content visible in published versionsarchived: Removed from active use but preserved
Creating Draft Content
When you create or update content, changes exist in draft:
// Create new content (automatically in draft)
mcp__vibe-cms-docs__create_content({
collection_slug: "blog-posts",
status: "draft"
})
// Update existing content (changes go to draft)
mcp__vibe-cms-docs__update_content_translation({
content_item_id: "content-uuid",
locale: "en-US",
status: "published", // Status of the content item
data: {
title: "Updated title" // This change is in draft
}
})
Publishing the Draft
Publishing creates a new version with all draft changes:
# Publishing is typically done through the admin UI or API
# It creates a versioned snapshot with a commit message
After publishing:
- A new version is created (e.g., v1, v2, v3)
- The draft becomes empty (ready for new changes)
- The published version is immutable
- Previous versions remain available
Version History and Rollback
Each published version includes:
- Version number
- Timestamp
- Commit message describing changes
- Complete snapshot of all content at that point
Rollback capabilities:
- View any previous version
- Restore content to any previous state
- Compare versions to see what changed
Archiving Content
Remove content from active use:
mcp__vibe-cms-docs__update_content_translation({
content_item_id: "content-uuid",
locale: "en-US",
status: "archived",
data: { /* existing data */ }
})
Archived content:
- Remains in the database
- Not returned in normal queries
- Can be unarchived by changing status back
Basic Content Queries
Using MCP Tools
Query content through MCP tools during development:
// List all items in a collection
mcp__vibe-cms-docs__content({
collection_slug: "blog-posts"
})
// Get single item with all translations
mcp__vibe-cms-docs__content({
collection_slug: "blog-posts",
content_item_id: "content-uuid"
})
// Get single item in specific locale
mcp__vibe-cms-docs__content({
collection_slug: "blog-posts",
content_item_id: "content-uuid",
locale: "en-US"
})
Using the TypeScript SDK
In your application code, use the SDK for content retrieval:
import { cms } from './lib/cms';
// Query singleton (e.g., homepage)
const homepageResult = await cms.collection('homepage').first();
const homepage = homepageResult.data.data;
// Query all items in a collection
const allPosts = await cms.collection('blog-posts').all();
// Query by slug
const post = await cms.collection('blog-posts').slug('getting-started');
// Query with filters
const posts = await cms.collection('blog-posts')
.where('status', 'published')
.locale('en-US')
.many();
SDK Response Structure
The SDK returns nested structures - use helper functions to extract data:
import { extractData, extractCollection } from './lib/cms';
// Singleton query
const heroResult = await cms.collection('landing-hero').first();
const heroData = extractData<LandingHero>(heroResult);
// Now heroData = { title, subtitle, cta_text, ... }
// Collection query
const postsRaw = await cms.collection('blog-posts').all();
const posts = extractCollection<BlogPost>(postsRaw);
// Now posts = [{ title, body, ... }, { title, body, ... }]
Important: Always use the helper functions to handle the nested result.data.data structure correctly.
Practical Exercise
Let's put everything together with a complete example:
1. Create a Team Members Collection
// Create collection
mcp__vibe-cms-docs__manage_collection({
action: "create",
slug: "team-members",
name: "Team Members",
description: "Company team member profiles"
})
// Add fields
mcp__vibe-cms-docs__add_collection_field({
collection_slug: "team-members",
name: "full_name",
field_type: "text",
interface_type: "input",
is_required: true,
sort_order: 1
})
mcp__vibe-cms-docs__add_collection_field({
collection_slug: "team-members",
name: "role",
field_type: "text",
interface_type: "input",
is_required: true,
sort_order: 2
})
mcp__vibe-cms-docs__add_collection_field({
collection_slug: "team-members",
name: "bio",
field_type: "markdown",
interface_type: "markdown",
is_required: false,
sort_order: 3
})
mcp__vibe-cms-docs__add_collection_field({
collection_slug: "team-members",
name: "photo",
field_type: "file",
interface_type: "single_file",
is_required: false,
sort_order: 4
})
2. Upload a Profile Photo
mcp__vibe-cms-docs__upload_file_from_url({
url: "https://images.unsplash.com/photo-profile-example",
folder_path: "/images/team",
title: "Jane Doe Profile Photo",
alt_text: "Professional headshot of Jane Doe"
})
// Save the returned file_id
3. Create a Team Member
// Create content item
mcp__vibe-cms-docs__create_content({
collection_slug: "team-members",
description: "Jane Doe - CEO",
status: "draft"
})
// Save the returned content_item_id
// Add English translation
mcp__vibe-cms-docs__update_content_translation({
content_item_id: "saved-content-id",
locale: "en-US",
data: {
full_name: "Jane Doe",
role: "Chief Executive Officer",
bio: "Jane has 15 years of experience in content management...",
photo: "saved-file-id"
}
})
// Add French translation
mcp__vibe-cms-docs__update_content_translation({
content_item_id: "saved-content-id",
locale: "fr-FR",
data: {
full_name: "Jane Doe",
role: "Directrice Générale",
bio: "Jane possède 15 ans d'expérience dans la gestion de contenu...",
photo: "saved-file-id"
}
})
4. Query the Content
// List all team members
mcp__vibe-cms-docs__content({
collection_slug: "team-members"
})
// Get specific member in French
mcp__vibe-cms-docs__content({
collection_slug: "team-members",
content_item_id: "saved-content-id",
locale: "fr-FR"
})
Next Steps
Congratulations! You now understand the fundamentals of working with Vibe CMS:
- ✅ Collections and content structure
- ✅ Creating and managing content items
- ✅ Working with translations and locales
- ✅ File and media management
- ✅ Publishing and draft workflows
- ✅ Basic content queries
In Chapter 3: Advanced Content Management, you'll learn:
- Complex collection schemas
- Content relationships and references
- Advanced querying and filtering
- Bulk operations and automation
- SEO and metadata optimization
- API authentication and security
Continue practicing with the MCP tools in Claude Code, and don't hesitate to experiment - the draft system makes it safe to try things out!