Copied to clipboard!

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 syntax
  • number: 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, or archived
  • 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 like a5803364-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 to en-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 published
  • published: Live content visible in published versions
  • archived: 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!