"use strict";
/**
* Get Started Tool
*
* Intelligent entry point for the MCP server that performs:
* 1. State Detection - Understand project state automatically
* 2. Intent Inference - Determine what user wants to do
* 3. Guided Execution - Provide exact tool calls with real values
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getStarted = getStarted;
const context_fetcher_1 = require("../lib/context-fetcher");
const device_flow_1 = require("../lib/device-flow");
const api_client_1 = require("../lib/api-client");
// ============ Response Builders ============
/**
* Build the explore response - overview of all projects
*/
function buildExploreResponse(context) {
let output = `# Fast Mode MCP - Project Overview
## Authentication
Status: ✓ Authenticated
`;
if (context.projects.length === 0) {
output += `## No Projects Yet
You don't have any projects. Let's create one!
### Create Your First Project
\`\`\`
create_site(name: "My Website")
\`\`\`
Or get help converting an existing website:
\`\`\`
get_started(intent: "convert")
\`\`\`
`;
return output;
}
output += `## Your Projects (${context.projects.length})
| Project | URL | Collections | Items | Status |
|---------|-----|-------------|-------|--------|
`;
for (const project of context.projects) {
const url = project.customDomain || `${project.subdomain}.fastmode.ai`;
output += `| ${project.name} | ${url} | ${project.collectionCount} | ${project.totalItems} | ${project.status} |\n`;
}
output += `
## Quick Actions
`;
// Suggest actions based on project state
const firstProject = context.projects[0];
output += `### See project details
\`\`\`
get_started(intent: "explore", projectId: "${firstProject.id}")
\`\`\`
### Add content
\`\`\`
get_started(intent: "add_content", projectId: "${firstProject.id}")
\`\`\`
### Update schema (add collections/fields)
\`\`\`
get_started(intent: "update_schema", projectId: "${firstProject.id}")
\`\`\`
### Deploy a new website
\`\`\`
get_started(intent: "deploy", projectId: "${firstProject.id}")
\`\`\`
### Create a new site from scratch
\`\`\`
get_started(intent: "convert")
\`\`\`
## Available Intents
| Intent | Description |
|--------|-------------|
| \`explore\` | See what projects and content exist |
| \`add_content\` | Create/edit CMS items (blog posts, team, etc.) |
| \`update_schema\` | Add collections or fields |
| \`convert\` | Build a new website from scratch |
| \`deploy\` | Push changes to an existing site |
`;
return output;
}
/**
* Build the add_content response with schema details
*/
function buildAddContentResponse(project) {
let output = `# Fast Mode MCP - Add Content
## Project: ${project.name}
**URL:** https://${project.subdomain}.fastmode.ai
**Status:** ${project.status}
`;
if (project.collections.length === 0) {
output += `## No Collections Yet
This project doesn't have any collections. Create collections first:
\`\`\`
get_started(intent: "update_schema", projectId: "${project.id}")
\`\`\`
`;
return output;
}
output += `## Collections
`;
for (const collection of project.collections) {
output += buildCollectionTable(collection);
}
output += `---
## Workflow: Add Content
### Step 1: See existing content
\`\`\`
list_cms_items(projectId: "${project.id}", collectionSlug: "${project.collections[0]?.slug || 'posts'}")
\`\`\`
### Step 2: Create new item
\`\`\`
create_cms_item(
projectId: "${project.id}",
collectionSlug: "${project.collections[0]?.slug || 'posts'}",
name: "Item Title",
data: {
${buildExampleData(project.collections[0])}
}
)
\`\`\`
### Step 3: Update existing item
\`\`\`
update_cms_item(
projectId: "${project.id}",
collectionSlug: "${project.collections[0]?.slug || 'posts'}",
itemSlug: "existing-item-slug",
data: { fieldToUpdate: "new value" }
)
\`\`\`
---
## ⚠️ Common Mistakes to Avoid
### Rich text fields
Rich text fields (type: \`richText\`) accept HTML content:
\`\`\`
data: { body: "<p>Your content with <strong>formatting</strong></p>" }
\`\`\`
### Relation fields
Relation fields require the **ID** of the related item, not the name:
\`\`\`
// WRONG:
data: { author: "John Smith" }
// CORRECT:
data: { author: "abc-123-uuid-of-john" }
\`\`\`
To get the ID, first list items in the related collection:
\`\`\`
list_cms_items(projectId: "${project.id}", collectionSlug: "authors")
\`\`\`
### Delete requires confirmation
Always ask the user before deleting:
\`\`\`
delete_cms_item(projectId: "...", collectionSlug: "...", itemSlug: "...", confirmDelete: true)
\`\`\`
`;
return output;
}
/**
* Build a table for a collection's fields
*/
function buildCollectionTable(collection) {
let output = `### ${collection.name} (${collection.itemCount} items)
| Field | Type | Required | Token |
|-------|------|----------|-------|
`;
for (const field of collection.fields) {
const token = field.type === 'richText'
? `\`{{{${field.slug}}}}\``
: field.type === 'relation'
? `\`{{${field.slug}.name}}\``
: `\`{{${field.slug}}}\``;
const typeDisplay = field.type === 'relation' && field.referenceCollection
? `relation→${field.referenceCollection}`
: field.type;
output += `| ${field.name} | ${typeDisplay} | ${field.isRequired ? 'yes' : 'no'} | ${token} |\n`;
}
output += '\n';
return output;
}
/**
* Build example data object for a collection
*/
function buildExampleData(collection) {
if (!collection || collection.fields.length === 0) {
return ' // Add your field values here';
}
const lines = [];
for (const field of collection.fields.slice(0, 5)) {
let exampleValue;
switch (field.type) {
case 'text':
exampleValue = `"Your ${field.name.toLowerCase()}"`;
break;
case 'richText':
exampleValue = `"<p>Your ${field.name.toLowerCase()} content here...</p>"`;
break;
case 'number':
exampleValue = '0';
break;
case 'boolean':
exampleValue = 'true';
break;
case 'image':
case 'url':
exampleValue = '"https://example.com/..."';
break;
case 'relation':
exampleValue = '"RELATED_ITEM_ID" // Get ID from list_cms_items';
break;
default:
exampleValue = `"..."`;
}
lines.push(` ${field.slug}: ${exampleValue}`);
}
return lines.join(',\n');
}
/**
* Build the update_schema response
*/
function buildUpdateSchemaResponse(project) {
let output = `# Fast Mode MCP - Update Schema
## Project: ${project.name}
**Current Collections:** ${project.collections.map(c => c.slug).join(', ') || 'none'}
`;
if (project.collections.length > 0) {
output += `## Current Schema Summary
| Collection | Fields | Items |
|------------|--------|-------|
`;
for (const col of project.collections) {
const fieldNames = col.fields.slice(0, 4).map(f => f.slug).join(', ');
const more = col.fields.length > 4 ? ` +${col.fields.length - 4} more` : '';
output += `| ${col.slug} | ${fieldNames}${more} | ${col.itemCount} |\n`;
}
output += '\n';
}
output += `---
## Workflow: Add New Schema
### Option A: Add fields to existing collection
\`\`\`
sync_schema(
projectId: "${project.id}",
fieldsToAdd: [{
collectionSlug: "${project.collections[0]?.slug || 'posts'}",
fields: [
{ slug: "new_field", name: "New Field", type: "text" },
{ slug: "featured", name: "Featured", type: "boolean" }
]
}]
)
\`\`\`
### Option B: Create new collection
\`\`\`
sync_schema(
projectId: "${project.id}",
collections: [{
slug: "testimonials",
name: "Testimonials",
nameSingular: "Testimonial",
fields: [
{ slug: "quote", name: "Quote", type: "richText" },
{ slug: "company", name: "Company", type: "text" },
{ slug: "photo", name: "Photo", type: "image" }
]
}]
)
\`\`\`
### After creating schema, optionally add sample content:
\`\`\`
generate_sample_items(projectId: "${project.id}", collectionSlugs: ["testimonials"])
\`\`\`
---
## ⚠️ Field Type Guidance
### For Video Content
Use \`videoEmbed\` (NOT \`url\` or \`text\`):
\`\`\`json
{ "slug": "video", "name": "Video", "type": "videoEmbed" }
\`\`\`
Supports: YouTube, Vimeo, Wistia, Loom
### For Linked Content
Use \`relation\` (NOT \`text\`):
\`\`\`json
{
"slug": "author",
"name": "Author",
"type": "relation",
"referenceCollection": "authors"
}
\`\`\`
---
## Available Field Types
| Type | Description | Token Usage |
|------|-------------|-------------|
| text | Short text | \`{{field}}\` |
| richText | HTML content | \`{{{field}}}\` (triple braces!) |
| number | Numeric value | \`{{field}}\` |
| boolean | True/false | \`{{#if field}}...{{/if}}\` |
| date | Date picker | \`{{field}}\` |
| image | Image URL | \`{{field}}\` in src |
| url | Web link | \`{{field}}\` in href |
| email | Email address | \`{{field}}\` |
| videoEmbed | YouTube/Vimeo/etc | \`{{#videoEmbed field}}{{/videoEmbed}}\` |
| select | Single choice | \`{{field}}\` (requires \`options\`) |
| multiSelect | Multiple choices | \`{{#each field}}...{{/each}}\` |
| relation | Link to collection | \`{{field.name}}\`, \`{{field.url}}\` |
`;
return output;
}
/**
* Build the convert response for new websites
*/
function buildConvertResponse(context) {
let output = `# Fast Mode MCP - Convert Website
## Getting Started
`;
if (context.projects.length > 0) {
output += `### Your existing projects:
`;
for (const p of context.projects.slice(0, 5)) {
output += `- ${p.name} (\`${p.id}\`)\n`;
}
output += `
**Ask the user:** "Should I add this website to an existing project, or create a new one?"
`;
}
output += `### To create a new project:
\`\`\`
create_site(name: "New Website Name")
\`\`\`
---
## ⚠️ REQUIRED: Read Before Building
**DO NOT SKIP THIS STEP.** The conversion guide contains CRITICAL information that prevents common failures:
### Step 1: Read common mistakes FIRST
\`\`\`
get_conversion_guide(section: "common_mistakes")
\`\`\`
This section covers:
- **Asset paths** - Why CSS/JS might 404 (MUST use /public/ prefix!)
- **Form handling** - Why forms might not work (MUST remove original handlers!)
- **data-edit-key** - Why text won't be editable (REQUIRED for static pages!)
### Step 2: Read the full guide
\`\`\`
get_conversion_guide(section: "full")
\`\`\`
All sections:
- \`first_steps\` - Required initial setup
- \`structure\` - Package folder structure
- \`manifest\` - manifest.json configuration
- \`templates\` - Template creation patterns
- \`tokens\` - CMS token reference
- \`forms\` - Form submission setup
- \`common_mistakes\` - Critical errors to avoid
### Step 2: Build and validate as you go
\`\`\`
validate_manifest(manifest: "{ ... }")
validate_template(html: "...", templateType: "custom_index", collectionSlug: "posts")
validate_package(fileList: [...], manifestContent: "...")
\`\`\`
### Step 3: Create schema (if new collections needed)
\`\`\`
sync_schema(projectId: "PROJECT_ID", collections: [...])
\`\`\`
### Step 4: Deploy
\`\`\`
deploy_package(packagePath: "./my-site.zip", projectId: "PROJECT_ID")
\`\`\`
---
## Key Resources
| Example | Description |
|---------|-------------|
| \`get_example("manifest_basic")\` | Basic manifest.json |
| \`get_example("blog_post_template")\` | Blog post detail template |
| \`get_example("blog_index_template")\` | Blog listing template |
| \`get_example("form_handling")\` | Form submission setup |
| \`get_example("common_mistakes")\` | What to avoid |
| \`get_example("relation_fields")\` | Linking collections |
`;
return output;
}
/**
* Build the deploy response
*/
function buildDeployResponse(project) {
let output = `# Fast Mode MCP - Deploy Changes
## Project: ${project.name}
**URL:** https://${project.subdomain}.fastmode.ai
**Status:** ${project.status}
## Pre-Deploy Checklist
`;
if (project.githubConnected) {
output += `⚠️ **GitHub Connected:** Yes
Consider pushing to GitHub instead for automatic deployment.
`;
}
else {
output += `✓ **GitHub Connected:** No (manual deploy available)
`;
}
if (project.hasPendingChanges) {
output += `⚠️ **Pending Changes:** Yes
There are unsaved changes in the editor.
`;
}
else {
output += `✓ **Pending Changes:** None
`;
}
output += `---
## Workflow: Deploy
### Step 1: Validate your package first
\`\`\`
validate_package(
fileList: ["manifest.json", "pages/index.html", "public/css/style.css", ...],
manifestContent: "{ ... }"
)
\`\`\`
### Step 2: If validation passes, deploy
\`\`\`
deploy_package(
packagePath: "./my-site.zip",
projectId: "${project.id}"
)
\`\`\`
---
## Deployment Notes
- **Pre-deploy validation:** deploy_package now validates automatically and blocks if there are errors
- **Incremental deploys:** CMS item changes trigger automatic page rebuilds
- **Full deploys:** Package uploads trigger full site rebuild
- **CDN cache:** Automatically purged after successful deploy
`;
return output;
}
/**
* Build unauthenticated response
*/
function buildUnauthenticatedResponse() {
return `# Fast Mode MCP - Authentication Required
You need to authenticate before using Fast Mode tools.
## How to Authenticate
### Option 1: Environment Variable (Recommended for CI/CD)
Set the \`FASTMODE_AUTH_TOKEN\` environment variable with your API token.
### Option 2: Browser Login
Run any authenticated command (like \`list_projects\`) and follow the browser-based login flow.
## Try It Now
\`\`\`
list_projects()
\`\`\`
This will prompt you to authenticate if needed.
## After Authentication
Call \`get_started()\` again to see your projects and available actions.
`;
}
// ============ Main Function ============
/**
* Get Started - Intelligent entry point for the MCP server
*
* @param input.intent - What the user wants to do (explore, add_content, update_schema, convert, deploy)
* @param input.projectId - Specific project to get details for
*/
async function getStarted(input) {
const { intent, projectId } = input;
// Check authentication first
if (await (0, api_client_1.needsAuthentication)()) {
// Try to authenticate
const authResult = await (0, device_flow_1.ensureAuthenticated)();
if (!authResult.authenticated) {
return buildUnauthenticatedResponse();
}
}
// Fetch project context
const context = await (0, context_fetcher_1.fetchProjectContext)(projectId);
if (!context.isAuthenticated) {
return buildUnauthenticatedResponse();
}
// Route based on intent
switch (intent) {
case 'add_content':
if (!context.selectedProject) {
if (!projectId) {
return `# Project Required
To add content, specify which project:
\`\`\`
get_started(intent: "add_content", projectId: "your-project-id")
\`\`\`
${buildExploreResponse(context)}`;
}
return `# Project Not Found
Could not find project: "${projectId}"
Use \`list_projects\` to see available projects.`;
}
return buildAddContentResponse(context.selectedProject);
case 'update_schema':
if (!context.selectedProject) {
if (!projectId) {
return `# Project Required
To update schema, specify which project:
\`\`\`
get_started(intent: "update_schema", projectId: "your-project-id")
\`\`\`
${buildExploreResponse(context)}`;
}
return `# Project Not Found
Could not find project: "${projectId}"
Use \`list_projects\` to see available projects.`;
}
return buildUpdateSchemaResponse(context.selectedProject);
case 'convert':
return buildConvertResponse(context);
case 'deploy':
if (!context.selectedProject) {
if (!projectId) {
return `# Project Required
To deploy, specify which project:
\`\`\`
get_started(intent: "deploy", projectId: "your-project-id")
\`\`\`
${buildExploreResponse(context)}`;
}
return `# Project Not Found
Could not find project: "${projectId}"
Use \`list_projects\` to see available projects.`;
}
return buildDeployResponse(context.selectedProject);
case 'explore':
default:
// If projectId provided, show detailed project info
if (context.selectedProject) {
return buildAddContentResponse(context.selectedProject);
}
return buildExploreResponse(context);
}
}