server.ts•44.4 kB
#!/usr/bin/env node
/**
* Base Mini App Builder MCP Server
* A TypeScript MCP server for building Base mini apps from start to finish.
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
// Base Mini App Builder MCP Server
// Initialize the MCP server
const server = new Server({
name: 'base-mini-app-builder',
version: '1.0.0',
});
// Base Mini App Builder Tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'generate_mini_app_manifest',
description: 'Generate a complete Base mini app manifest with all required fields.',
inputSchema: {
type: 'object',
properties: {
app_name: {
type: 'string',
description: 'Name of your mini app (max 32 chars)',
},
description: {
type: 'string',
description: 'App description (max 170 chars)',
},
category: {
type: 'string',
description: 'Primary category for the app',
enum: ['games', 'social', 'finance', 'utility', 'productivity', 'health-fitness', 'news-media', 'music', 'shopping', 'education', 'developer-tools', 'entertainment', 'art-creativity'],
},
domain: {
type: 'string',
description: 'Your app domain (e.g., myapp.vercel.app)',
},
tags: {
type: 'array',
items: { type: 'string' },
description: 'Search tags (max 5, lowercase, no spaces)',
},
},
required: ['app_name', 'description', 'category', 'domain'],
},
},
{
name: 'generate_mini_app_code',
description: 'Generate starter code for a Base mini app with MiniKit integration.',
inputSchema: {
type: 'object',
properties: {
app_type: {
type: 'string',
description: 'Type of mini app to generate',
enum: ['nextjs', 'vanilla', 'react'],
},
features: {
type: 'array',
items: { type: 'string' },
description: 'Features to include',
enum: ['authentication', 'wallet_connect', 'transactions', 'notifications', 'sharing'],
},
app_name: {
type: 'string',
description: 'Name of your app',
},
},
required: ['app_type', 'app_name'],
},
},
{
name: 'get_base_deployment_guide',
description: 'Get step-by-step deployment guide for Base mini apps.',
inputSchema: {
type: 'object',
properties: {
platform: {
type: 'string',
description: 'Deployment platform',
enum: ['vercel', 'netlify', 'custom'],
},
has_domain: {
type: 'boolean',
description: 'Whether you have a custom domain',
},
},
required: ['platform'],
},
},
{
name: 'validate_mini_app_requirements',
description: 'Check if your mini app meets Base featured placement requirements.',
inputSchema: {
type: 'object',
properties: {
manifest_url: {
type: 'string',
description: 'URL to your manifest file',
},
app_url: {
type: 'string',
description: 'URL to your mini app',
},
},
required: ['app_url'],
},
},
{
name: 'get_base_account_guide',
description: 'Get guide for implementing Base Account features like sponsored gas and batch transactions.',
inputSchema: {
type: 'object',
properties: {
feature: {
type: 'string',
description: 'Specific Base Account feature to learn about',
enum: ['sponsored_gas', 'batch_transactions', 'passkey_auth', 'capabilities_detection'],
},
},
required: [],
},
},
{
name: 'generate_embed_metadata',
description: 'Generate embed metadata for social sharing of your mini app.',
inputSchema: {
type: 'object',
properties: {
app_name: {
type: 'string',
description: 'Name of your app',
},
app_url: {
type: 'string',
description: 'URL of your app',
},
image_url: {
type: 'string',
description: 'Preview image URL (3:2 aspect ratio)',
},
button_text: {
type: 'string',
description: 'Button text (max 32 chars)',
},
},
required: ['app_name', 'app_url'],
},
},
{
name: 'get_design_guidelines',
description: 'Get comprehensive design guidelines for Base mini apps including colors, typography, spacing, and navigation.',
inputSchema: {
type: 'object',
properties: {
category: {
type: 'string',
description: 'Design category to focus on',
enum: ['colors', 'typography', 'spacing', 'navigation', 'components', 'app_icon', 'all'],
},
},
required: [],
},
},
{
name: 'get_debugging_guide',
description: 'Get comprehensive debugging guide for Base mini app development issues.',
inputSchema: {
type: 'object',
properties: {
issue_type: {
type: 'string',
description: 'Type of issue to debug',
enum: ['discovery', 'embed_rendering', 'wallet_connection', 'manifest', 'mobile_testing', 'all'],
},
},
required: [],
},
},
{
name: 'get_base_app_compatibility',
description: 'Get Base App compatibility information and feature support status.',
inputSchema: {
type: 'object',
properties: {
feature: {
type: 'string',
description: 'Specific feature to check compatibility for',
enum: ['wallet_integration', 'navigation', 'notifications', 'actions', 'all'],
},
},
required: [],
},
},
{
name: 'get_search_discovery_guide',
description: 'Get guide for optimizing Base mini app search and discovery.',
inputSchema: {
type: 'object',
properties: {
focus_area: {
type: 'string',
description: 'Area to focus on for discovery optimization',
enum: ['search_indexing', 'category_optimization', 'metadata_optimization', 'ranking', 'all'],
},
},
required: [],
},
},
],
};
});
// Tool implementations
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case 'generate_mini_app_manifest': {
const { app_name, description, category, domain, tags = [] } = args as any;
const manifest = {
accountAssociation: {
header: "",
payload: "",
signature: ""
},
baseBuilder: {
allowedAddresses: [""]
},
miniapp: {
version: "1",
name: app_name,
homeUrl: `https://${domain}`,
iconUrl: `https://${domain}/icon.png`,
splashImageUrl: `https://${domain}/splash.png`,
splashBackgroundColor: "#000000",
webhookUrl: `https://${domain}/api/webhook`,
subtitle: description.substring(0, 30),
description: description,
screenshotUrls: [
`https://${domain}/screenshot1.png`,
`https://${domain}/screenshot2.png`,
`https://${domain}/screenshot3.png`
],
primaryCategory: category,
tags: tags.length > 0 ? tags : ["base", "miniapp", "web3"],
heroImageUrl: `https://${domain}/hero.png`,
tagline: "Built on Base",
ogTitle: app_name,
ogDescription: description,
ogImageUrl: `https://${domain}/og.png`,
noindex: true
}
};
return {
content: [
{
type: 'text',
text: `# Base Mini App Manifest Generated
## Next Steps:
1. Save this as \`/public/.well-known/farcaster.json\` in your project
2. Deploy your app to get a live URL
3. Use Base Build to generate accountAssociation credentials
4. Replace the empty accountAssociation fields
## Manifest JSON:
\`\`\`json
${JSON.stringify(manifest, null, 2)}
\`\`\`
## Required Images:
- Icon: 1024x1024px PNG
- Splash: 200x200px PNG
- Screenshots: 1284x2778px PNG (portrait)
- Hero: 1200x630px PNG
- OG Image: 1200x630px PNG
## Validation:
Use https://base.dev/preview to validate your manifest before publishing.`,
},
],
};
}
case 'generate_mini_app_code': {
const { app_type, features = [], app_name } = args as any;
let code = '';
let setupInstructions = '';
if (app_type === 'nextjs') {
code = `// app/layout.tsx
import { Metadata } from 'next';
import { MiniKitProvider } from '@coinbase/onchainkit/minikit';
export const metadata: Metadata = {
title: '${app_name}',
description: 'A Base Mini App built with Next.js',
other: {
'fc:miniapp': JSON.stringify({
version: 'next',
imageUrl: 'https://your-domain.com/preview.png',
button: {
title: 'Launch ${app_name}',
action: {
type: 'launch_frame',
url: 'https://your-domain.com'
}
}
})
}
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<MiniKitProvider>
{children}
</MiniKitProvider>
</body>
</html>
);
}
// app/page.tsx
'use client';
import { useMiniKit, useAuthenticate } from '@coinbase/onchainkit/minikit';
import { useEffect } from 'react';
export default function HomePage() {
const { context } = useMiniKit();
const { user } = useAuthenticate();
useEffect(() => {
// Hide loading screen when app is ready
sdk.actions.ready();
}, []);
return (
<div className="min-h-screen bg-black text-white p-4">
<h1 className="text-2xl font-bold mb-4">Welcome to ${app_name}!</h1>
{user ? (
<div>
<p>Hello, {user.displayName || user.username}!</p>
<p>FID: {user.fid}</p>
</div>
) : (
<p>Please sign in to continue</p>
)}
</div>
);
}`;
setupInstructions = `# Next.js Setup Instructions
1. Create a new Next.js app:
\`\`\`bash
npx create-next-app@latest ${app_name.toLowerCase().replace(/\s+/g, '-')} --typescript --tailwind --app
cd ${app_name.toLowerCase().replace(/\s+/g, '-')}
\`\`\`
2. Install dependencies:
\`\`\`bash
npm install @coinbase/onchainkit @farcaster/miniapp-sdk
\`\`\`
3. Add the code above to your app
4. Create the manifest file at \`/public/.well-known/farcaster.json\`
5. Deploy to Vercel:
\`\`\`bash
npx vercel --prod
\`\`\``;
} else if (app_type === 'vanilla') {
code = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${app_name}</title>
<meta name="fc:miniapp" content='{
"version": "next",
"imageUrl": "https://your-domain.com/preview.png",
"button": {
"title": "Launch ${app_name}",
"action": {
"type": "launch_frame",
"url": "https://your-domain.com"
}
}
}' />
<script type="module" src="https://unpkg.com/@farcaster/miniapp-sdk@latest/dist/index.js"></script>
</head>
<body>
<div id="app">
<h1>Welcome to ${app_name}!</h1>
<div id="user-info"></div>
</div>
<script type="module">
import { sdk } from '@farcaster/miniapp-sdk';
async function initApp() {
try {
const isInMiniApp = await sdk.isInMiniApp();
if (isInMiniApp) {
const context = await sdk.context;
document.getElementById('user-info').innerHTML = \`
<p>Hello, \${context.user.displayName || context.user.username}!</p>
<p>FID: \${context.user.fid}</p>
\`;
}
// Hide loading screen
await sdk.actions.ready();
} catch (error) {
console.error('Error initializing app:', error);
}
}
initApp();
</script>
</body>
</html>`;
setupInstructions = `# Vanilla JS Setup Instructions
1. Create a new directory:
\`\`\`bash
mkdir ${app_name.toLowerCase().replace(/\s+/g, '-')}
cd ${app_name.toLowerCase().replace(/\s+/g, '-')}
\`\`\`
2. Create the HTML file above as \`index.html\`
3. Create the manifest file at \`/.well-known/farcaster.json\`
4. Deploy to any static hosting service (Vercel, Netlify, etc.)`;
}
return {
content: [
{
type: 'text',
text: `# ${app_name} - ${app_type.toUpperCase()} Code Generated
## Generated Code:
\`\`\`${app_type === 'nextjs' ? 'tsx' : 'html'}
${code}
\`\`\`
${setupInstructions}
## Features Included:
${features.length > 0 ? features.map((f: string) => `- ${f}`).join('\n') : '- Basic MiniKit integration\n- User context\n- Ready action'}
## Next Steps:
1. Set up your project using the instructions above
2. Generate your manifest using the manifest generator tool
3. Deploy your app
4. Test with Base Build preview tool`,
},
],
};
}
case 'get_base_deployment_guide': {
const { platform, has_domain = false } = args as any;
let guide = '';
if (platform === 'vercel') {
guide = `# Vercel Deployment Guide for Base Mini Apps
## Step 1: Prepare Your App
1. Ensure your app has a \`/public/.well-known/farcaster.json\` manifest file
2. Add embed metadata to your HTML head
3. Test locally with \`npm run dev\`
## Step 2: Deploy to Vercel
\`\`\`bash
# Install Vercel CLI
npm i -g vercel
# Deploy
vercel --prod
# Or connect your GitHub repo to Vercel dashboard
\`\`\`
## Step 3: Configure Domain
${has_domain ?
'1. Add your custom domain in Vercel dashboard\n2. Update DNS records as instructed\n3. Update manifest URLs to use your domain' :
'1. Use the provided .vercel.app domain\n2. Update manifest URLs to use your Vercel domain\n3. Consider upgrading to custom domain later'}
## Step 4: Generate Account Association
1. Go to https://base.dev/preview?tab=account
2. Enter your app URL (e.g., myapp.vercel.app)
3. Click "Submit" and "Verify"
4. Copy the generated accountAssociation fields
5. Update your manifest with the new credentials
## Step 5: Validate & Test
1. Use https://base.dev/preview to validate your manifest
2. Test the launch button
3. Check embed metadata
4. Verify account association
## Step 6: Publish
1. Create a post in Base app with your app URL
2. Your mini app will be indexed and discoverable
## Troubleshooting
- Ensure manifest is accessible at \`/.well-known/farcaster.json\`
- Check all image URLs are valid and accessible
- Verify embed metadata is properly formatted
- Test on both mobile and desktop`;
} else if (platform === 'netlify') {
guide = `# Netlify Deployment Guide for Base Mini Apps
## Step 1: Prepare Your App
1. Ensure your app has a \`/public/.well-known/farcaster.json\` manifest file
2. Add embed metadata to your HTML head
3. Test locally
## Step 2: Deploy to Netlify
\`\`\`bash
# Install Netlify CLI
npm i -g netlify-cli
# Deploy
netlify deploy --prod --dir=dist
# Or connect your GitHub repo to Netlify dashboard
\`\`\`
## Step 3: Configure Domain
${has_domain ?
'1. Add your custom domain in Netlify dashboard\n2. Update DNS records\n3. Update manifest URLs' :
'1. Use the provided .netlify.app domain\n2. Update manifest URLs\n3. Consider custom domain upgrade'}
## Step 4-6: Same as Vercel guide above`;
}
return {
content: [
{
type: 'text',
text: guide,
},
],
};
}
case 'validate_mini_app_requirements': {
const { app_url, manifest_url } = args as any;
const requirements = {
design_guidelines: {
status: 'check_required',
criteria: [
'Mobile-first responsive design',
'Clear primary actions',
'Accessible contrast ratios',
'Touch-friendly interface elements'
]
},
complete_metadata: {
status: 'check_required',
criteria: [
'Manifest accessible at /.well-known/farcaster.json',
'All required fields present',
'Valid image URLs and formats',
'Proper text field lengths'
]
},
in_app_authentication: {
status: 'check_required',
criteria: [
'No external redirects',
'No email/phone verification',
'Users can explore before sign-in',
'Uses SIWF or wallet auth'
]
},
client_agnostic: {
status: 'check_required',
criteria: [
'No hardcoded client-specific URLs',
'Neutral language in UI',
'No deeplinks to other clients',
'Works in Base app'
]
},
gasless_transactions: {
status: 'recommended',
criteria: [
'Uses paymaster for gas sponsorship',
'Transactions work without ETH',
'Reduces user friction'
]
},
batch_transactions: {
status: 'recommended',
criteria: [
'Combines sequential actions',
'Uses EIP-5792 capabilities',
'Minimizes user signatures'
]
},
mainstream_ready: {
status: 'check_required',
criteria: [
'Avoids crypto jargon',
'Shows usernames not addresses',
'Plain language explanations',
'Confidence-building copy'
]
},
user_onboarding: {
status: 'check_required',
criteria: [
'Guest mode available',
'Optional lightweight onboarding',
'Uses context data for personalization',
'Clear tutorial or CTA'
]
}
};
return {
content: [
{
type: 'text',
text: `# Base Mini App Requirements Validation
## App URL: ${app_url}
${manifest_url ? `## Manifest URL: ${manifest_url}` : ''}
## Requirements Checklist:
### ✅ Required for Featured Placement:
**Design Guidelines**
- Mobile-first responsive design
- Clear primary actions
- Accessible contrast ratios
- Touch-friendly interface elements
**Complete Metadata**
- Manifest accessible at /.well-known/farcaster.json
- All required fields present
- Valid image URLs and formats
- Proper text field lengths
**In-App Authentication**
- No external redirects
- No email/phone verification
- Users can explore before sign-in
- Uses SIWF or wallet auth
**Client-Agnostic**
- No hardcoded client-specific URLs
- Neutral language in UI
- No deeplinks to other clients
- Works in Base app
**Mainstream Ready**
- Avoids crypto jargon
- Shows usernames not addresses
- Plain language explanations
- Confidence-building copy
**User Onboarding**
- Guest mode available
- Optional lightweight onboarding
- Uses context data for personalization
- Clear tutorial or CTA
### 🚀 Recommended for Best Experience:
**Gasless Transactions**
- Uses paymaster for gas sponsorship
- Transactions work without ETH
- Reduces user friction
**Batch Transactions**
- Combines sequential actions
- Uses EIP-5792 capabilities
- Minimizes user signatures
## Validation Tools:
- Use https://base.dev/preview to test your app
- Check manifest validation
- Test launch button functionality
- Verify embed metadata
## Next Steps:
1. Address any missing required features
2. Implement recommended features for better UX
3. Test thoroughly before submission
4. Submit for featured placement at https://buildonbase.deform.cc/getstarted/`,
},
],
};
}
case 'get_base_account_guide': {
const { feature } = args as any;
let guide = '';
if (feature === 'sponsored_gas') {
guide = `# Sponsored Gas Implementation Guide
## Overview
Base Accounts support sponsored gas transactions, allowing your app to pay gas fees for users. This removes friction and enables users to transact without owning ETH.
## Implementation Steps
### 1. Check for Paymaster Capability
\`\`\`javascript
const capabilities = await publicClient.request({
method: 'wallet_getCapabilities',
params: [address]
});
const hasPaymaster = capabilities['0x2105']?.paymasterService?.supported;
\`\`\`
### 2. Configure Paymaster Service
\`\`\`javascript
const capabilities = {
paymasterService: {
url: 'https://api.developer.coinbase.com/rpc/v1/base/v7HqDLjJY4e28qgIDAAN4JNYXnz88mJZ'
}
};
\`\`\`
### 3. Use in Transactions
\`\`\`javascript
import { useWriteContracts } from 'wagmi/experimental'
function SponsoredTransactionButton() {
const { writeContracts } = useWriteContracts()
const handleSponsoredMint = () => {
writeContracts({
contracts: [{
address: '0x...',
abi: contractAbi,
functionName: 'mint',
args: [account.address],
}],
capabilities, // This enables sponsored gas
})
}
return <button onClick={handleSponsoredMint}>Mint NFT (Gas Free)</button>
}
\`\`\`
## Benefits
- Users can transact with zero ETH balance
- Reduces drop-off rates
- Better user experience
- Enables new user onboarding
## Best Practices
- Always check capability before offering gas-free transactions
- Handle cases where paymaster is unavailable
- Test with Base Accounts that have zero ETH balance`;
} else if (feature === 'batch_transactions') {
guide = `# Batch Transactions (EIP-5792) Guide
## Overview
Base Accounts support atomic batch transactions, allowing multiple operations in a single transaction. This reduces user friction and gas costs.
## Implementation Steps
### 1. Check for Atomic Batch Capability
\`\`\`javascript
const capabilities = await publicClient.request({
method: 'wallet_getCapabilities',
params: [address]
});
const hasAtomicBatch = capabilities['0x2105']?.atomicBatch?.supported;
\`\`\`
### 2. Use wallet_sendCalls for Batching
\`\`\`javascript
const calls = [
{
to: tokenContract,
data: approveCallData,
},
{
to: swapContract,
data: swapCallData,
}
];
const result = await window.ethereum.request({
method: 'wallet_sendCalls',
params: [{
calls,
capabilities: {
atomicBatch: {
supported: true
}
}
}]
});
\`\`\`
### 3. UI Pattern for Batch Operations
\`\`\`javascript
function BatchTransactionButton() {
const { atomicBatch } = useBaseAccountCapabilities(address);
if (atomicBatch) {
return <OneClickPurchaseFlow />; // Single confirmation
} else {
return <MultiStepPurchaseFlow />; // Multiple confirmations
}
}
\`\`\`
## Use Cases
- Approve + Transfer in one transaction
- Multiple NFT mints
- Complex DeFi operations
- Multi-step workflows
## Benefits
- Single user confirmation
- Lower gas costs
- Better UX
- Atomic operations (all succeed or all fail)`;
} else if (feature === 'passkey_auth') {
guide = `# Passkey Authentication Guide
## Overview
Base Accounts use passkeys for authentication, providing a more secure and user-friendly experience than traditional private keys.
## Implementation
### 1. Detect Passkey Support
\`\`\`javascript
const capabilities = await publicClient.request({
method: 'wallet_getCapabilities',
params: [address]
});
const hasPasskey = capabilities['0x2105']?.passkeyAuth?.supported;
\`\`\`
### 2. Handle Authentication
\`\`\`javascript
import { useAuthenticate } from '@coinbase/onchainkit/minikit';
function AuthComponent() {
const { user, isAuthenticated } = useAuthenticate();
if (isAuthenticated) {
return <div>Welcome, {user.displayName}!</div>;
} else {
return <button>Sign In with Passkey</button>;
}
}
\`\`\`
## Benefits
- No private key management
- Biometric authentication
- More secure than passwords
- Better user experience
- Works across devices`;
} else {
guide = `# Base Account Capabilities Detection
## Overview
Base Accounts offer enhanced features that traditional wallets don't support. Always detect capabilities before implementing features.
## Implementation
### 1. Detect All Capabilities
\`\`\`javascript
function useBaseAccountCapabilities(address) {
const [capabilities, setCapabilities] = useState({});
useEffect(() => {
async function detect() {
const caps = await publicClient.request({
method: 'wallet_getCapabilities',
params: [address]
});
setCapabilities({
atomicBatch: caps['0x2105']?.atomicBatch?.supported,
paymasterService: caps['0x2105']?.paymasterService?.supported,
auxiliaryFunds: caps['0x2105']?.auxiliaryFunds?.supported,
passkeyAuth: caps['0x2105']?.passkeyAuth?.supported
});
}
if (address) detect();
}, [address]);
return capabilities;
}
\`\`\`
### 2. Adapt UI Based on Capabilities
\`\`\`javascript
function MiniAppWorkflow() {
const { address } = useAccount();
const { atomicBatch, paymasterService } = useBaseAccountCapabilities(address);
if (atomicBatch && paymasterService) {
return <BaseAccountOptimizedFlow />;
} else {
return <TraditionalWalletFlow />;
}
}
\`\`\`
## Available Capabilities
- **atomicBatch**: Batch multiple transactions
- **paymasterService**: Sponsored gas transactions
- **auxiliaryFunds**: Additional funding options
- **passkeyAuth**: Passkey authentication
## Best Practices
- Always check capabilities before using features
- Provide fallback experiences for traditional wallets
- Test with both Base Accounts and traditional wallets
- Gracefully handle unsupported features`;
}
return {
content: [
{
type: 'text',
text: guide,
},
],
};
}
case 'generate_embed_metadata': {
const { app_name, app_url, image_url, button_text } = args as any;
const embedMetadata = {
version: "next",
imageUrl: image_url || `${app_url}/preview.png`,
button: {
title: button_text || `Launch ${app_name}`,
action: {
type: "launch_frame",
name: app_name,
url: app_url,
splashImageUrl: `${app_url}/splash.png`,
splashBackgroundColor: "#000000"
}
}
};
const htmlMeta = `<!-- Add this to your HTML <head> -->
<meta name="fc:miniapp" content='${JSON.stringify(embedMetadata)}' />`;
const nextjsMeta = `// For Next.js apps - add to generateMetadata()
export async function generateMetadata(): Promise<Metadata> {
return {
title: '${app_name}',
description: 'A Base Mini App',
other: {
'fc:miniapp': JSON.stringify(${JSON.stringify(embedMetadata, null, 2)})
}
};
}`;
return {
content: [
{
type: 'text',
text: `# Embed Metadata Generated for ${app_name}
## HTML Meta Tag:
\`\`\`html
${htmlMeta}
\`\`\`
## Next.js Metadata:
\`\`\`typescript
${nextjsMeta}
\`\`\`
## Requirements:
- **Image**: 3:2 aspect ratio, max 10MB, max 1024 chars
- **Button Text**: Max 32 characters
- **Action URL**: Max 1024 characters
- **Splash Image**: 200x200px (optional)
## Testing:
1. Add the metadata to your app
2. Deploy your changes
3. Test with https://base.dev/preview
4. Share your app URL to see the embed in action
## Best Practices:
- Use high-quality preview images
- Keep button text clear and action-oriented
- Test embeds on both mobile and desktop
- Ensure all URLs are accessible and HTTPS`,
},
],
};
}
case 'get_design_guidelines': {
const { category = 'all' } = args as any;
let guidelines = '';
if (category === 'colors' || category === 'all') {
guidelines += `# Colors & Theming
## Primary Colors
- Use your brand color for CTAs, active states, and key interactive elements
- Secondary colors for supporting actions and background accents
- Neutral colors for text, backgrounds, and structure
## Theme Support
- Design for both light and dark modes
- Use CSS custom properties for consistent theming
- Maintain proper contrast ratios in both themes
## Implementation
\`\`\`css
:root {
--color-primary: #578BFA;
--color-background: #FFFFFF;
--color-text-primary: #1A1A1A;
}
[data-theme="dark"] {
--color-background: #0F0F0F;
--color-text-primary: #FFFFFF;
}
\`\`\`
`;
}
if (category === 'typography' || category === 'all') {
guidelines += `# Typography
## Font Selection
- Use system fonts for cross-platform compatibility
- Ensure clarity at small sizes (16px minimum for mobile)
- Maintain consistent hierarchy with size, weight, and spacing
## Hierarchy
- H1: 32px-48px for page titles
- H2: 24px-32px for section titles
- Body: 16px for standard content
- Caption: 12px-14px for labels
## Implementation
\`\`\`css
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
line-height: 1.4-1.6 for body text;
\`\`\`
`;
}
if (category === 'spacing' || category === 'all') {
guidelines += `# Spacing
## Base Unit
- Use 4px or 8px as base spacing unit
- Create consistent scale: 4px, 8px, 16px, 24px, 32px, 48px, 64px
## Mobile Considerations
- 44px minimum touch targets
- 8px minimum spacing between touch targets
- Consider thumb reach zones
## Implementation
\`\`\`css
:root {
--space-xs: 4px;
--space-sm: 8px;
--space-md: 16px;
--space-lg: 24px;
--space-xl: 32px;
}
\`\`\`
`;
}
if (category === 'navigation' || category === 'all') {
guidelines += `# Navigation
## Mobile-First Patterns
- Bottom tab navigation for primary sections (3-5 max)
- Hamburger menu for secondary actions
- Clear visual hierarchy and active states
## Best Practices
- 44px minimum touch targets
- Thumb-friendly placement
- Clear navigation states
- Performance optimized animations
## Implementation
- Use OnchainKit navigation components
- Implement proper URL synchronization
- Test across different screen sizes
`;
}
if (category === 'app_icon' || category === 'all') {
guidelines += `# App Icon
## Specifications
- Dimension: 1024x1024px
- Format: PNG (no alpha)
- Clarity at 16x16px
- High contrast and simple shapes
## Best Practices
- Reflect core functionality or brand identity
- Work in both light and dark themes
- Stand out among other mini apps
- Maintain visual consistency
`;
}
if (category === 'components' || category === 'all') {
guidelines += `# Components
## OnchainKit Integration
- Use pre-built React components
- Follow Base design guidelines
- Ensure consistency across your app
## Installation
\`\`\`bash
npx create-onchain@latest --mini
\`\`\`
## Available Components
- Wallet connection
- Transaction handling
- UI components following Base design system
`;
}
return {
content: [
{
type: 'text',
text: `# Base Mini App Design Guidelines
${guidelines}
## Resources
- [OnchainKit Components](/onchainkit/getting-started)
- [Base Design System](https://base.docs)
- [Figma Specifications](https://www.figma.com/design/4wx6s24NB0KLgprQAyMT8R/TBA-Mini-App-Specs)
## Testing
- Use Base Mini App Preview Tool for validation
- Test on multiple devices and screen sizes
- Verify accessibility compliance`,
},
],
};
}
case 'get_debugging_guide': {
const { issue_type = 'all' } = args as any;
let guide = '';
if (issue_type === 'discovery' || issue_type === 'all') {
guide += `# App Discovery & Indexing Issues
## Problem: App doesn't appear in search
**Root Cause:** Missing or incomplete manifest configuration
**Solution:**
1. Ensure manifest includes all required fields
2. Set \`primaryCategory\` for searchability
3. Complete \`accountAssociation\` for verification
4. Share your Mini App URL in a post
5. Wait ~10 minutes for indexing
**Caching Issues:**
- Farcaster clients cache manifest data for up to 24 hours
- Re-share to trigger refresh
- Allow ~10 minutes for changes to appear
`;
}
if (issue_type === 'embed_rendering' || issue_type === 'all') {
guide += `# Embed Rendering Issues
## Problem: URL doesn't render as rich embed
**Root Cause:** Incorrect or missing fc:frame metadata
**Solution:**
1. Use \`name="fc:frame"\` meta tag in <head>
2. Validate using Base Build Preview Tool
3. Ensure proper JSON structure
4. Test with different clients
`;
}
if (issue_type === 'wallet_connection' || issue_type === 'all') {
guide += `# Wallet Connection Problems
## Best Practices
- Always use user's connected wallet
- Use OnchainKit Wallet component or Wagmi hooks
- Provide fallback for unsupported features
**Implementation:**
\`\`\`tsx
import { useAccount } from 'wagmi';
function MyComponent() {
const { address, isConnected } = useAccount();
// Use wallet data for secure operations
}
\`\`\`
`;
}
if (issue_type === 'manifest' || issue_type === 'all') {
guide += `# Manifest Configuration Problems
## Image Display Issues
1. Test image accessibility in incognito
2. Verify image format (PNG, JPG, WebP)
3. Check dimensions match requirements
4. Ensure HTTPS URLs only
## Required Structure
\`\`\`json
{
"accountAssociation": { "header": "", "payload": "", "signature": "" },
"baseBuilder": { "allowedAddresses": ["0x..."] },
"miniapp": {
"version": "1",
"name": "Your App",
"description": "App description",
"iconUrl": "https://yourapp.com/icon.png",
"homeUrl": "https://yourapp.com",
"primaryCategory": "social"
}
}
\`\`\`
`;
}
if (issue_type === 'mobile_testing' || issue_type === 'all') {
guide += `# Mobile Testing & Debugging
## Eruda Mobile Console Setup
\`\`\`tsx
import { useEffect } from 'react';
export default function App() {
useEffect(() => {
if (typeof window !== 'undefined' &&
process.env.NODE_ENV === 'development' &&
!window.location.hostname.includes('localhost')) {
import('eruda').then((eruda) => eruda.default.init());
}
}, []);
}
\`\`\`
## Testing Workflow
1. Deploy to production or use ngrok
2. Share mini app in Farcaster DM
3. Open in mobile client (Base App, Farcaster)
4. Use Eruda console for debugging
5. Test across multiple clients
`;
}
return {
content: [
{
type: 'text',
text: `# Base Mini App Debugging Guide
${guide}
## Quick Diagnostic Workflow
1. Use [Base Build Preview Tool](https://base.dev/preview)
2. Check manifest accessibility at \`/.well-known/farcaster.json\`
3. Validate JSON syntax
4. Test app loading without console errors
## Common Issues Checklist
- [ ] App appears in search results
- [ ] Embeds render correctly when shared
- [ ] Wallet connection works properly
- [ ] Images load and display correctly
- [ ] App works on mobile devices
- [ ] No console errors
## Tools & Resources
- [Base Build Preview Tool](https://base.dev/preview)
- [JSONLint](https://jsonlint.com/)
- [Eruda Mobile Console](https://github.com/liriliri/eruda)
- Base Discord #minikit channel`,
},
],
};
}
case 'get_base_app_compatibility': {
const { feature = 'all' } = args as any;
let compatibility = '';
if (feature === 'wallet_integration' || feature === 'all') {
compatibility += `# Wallet Integration
## Supported Methods
### Method 1: OnchainKit (Recommended)
\`\`\`tsx
import { ConnectWallet } from '@coinbase/onchainkit/wallet';
function WalletConnection() {
return <ConnectWallet />;
}
\`\`\`
### Method 2: Wagmi Hooks
\`\`\`tsx
import { useConnect } from 'wagmi';
function WalletConnect() {
const { connect, connectors } = useConnect();
// Base App connector available automatically
}
\`\`\`
### Method 3: Direct Provider Access
\`\`\`tsx
async function connectWallet() {
if (window.ethereum) {
await window.ethereum.request({
method: 'eth_requestAccounts'
});
}
}
\`\`\`
`;
}
if (feature === 'navigation' || feature === 'all') {
compatibility += `# Navigation & Links
## Use MiniKit Hooks
- ✅ \`useOpenUrl()\` instead of manual URLs
- ✅ \`useComposeCast()\` instead of composer URLs
- ✅ \`useViewProfile()\` instead of profile deeplinks
**Implementation:**
\`\`\`tsx
import { useOpenUrl, useComposeCast } from '@coinbase/onchainkit/minikit';
function Navigation() {
const openUrl = useOpenUrl();
const composeCast = useComposeCast();
const handleExternalLink = () => openUrl('https://example.com');
const handleShare = () => composeCast({
text: "Check this out!",
embeds: [window.location.href]
});
}
\`\`\`
`;
}
if (feature === 'notifications' || feature === 'all') {
compatibility += `# Notifications
## Current Status
- **Not yet supported** in Base App
- Coming soon in future updates
- Use alternative feedback methods for now
## Alternatives
- In-app notifications
- Toast messages
- Status indicators
- Progress feedback
`;
}
if (feature === 'actions' || feature === 'all') {
compatibility += `# Mini App Actions
## Current Status
- **Not yet supported** in Base App
- ETA: Coming soon
- Use standard web interactions for now
## Base App Detection
\`\`\`tsx
import { useMiniKit } from '@coinbase/onchainkit/minikit';
function MyComponent() {
const { context } = useMiniKit();
const isBaseApp = context.client.clientFid === 309857;
if (isBaseApp) {
console.log('Running in Base App');
}
}
\`\`\`
`;
}
return {
content: [
{
type: 'text',
text: `# Base App Compatibility Guide
${compatibility}
## Supported Chains
- Base
- Mainnet
- Optimism
- Arbitrum
- Polygon
- Zora
- BNB
- Avalanche C-Chain
## Development Notes
- Use \`openUrl()\` for external navigation
- Use \`composeCast()\` instead of composer URLs
- Provide alternatives for haptic feedback
- Avoid relying on location context for core flows
- Detect Base App with \`context.client.clientFid === 309857\`
## Currently Unsupported
- Notifications (coming soon)
- Mini App actions (ETA coming soon)
We are actively expanding compatibility and will update as support increases.`,
},
],
};
}
case 'get_search_discovery_guide': {
const { focus_area = 'all' } = args as any;
let guide = '';
if (focus_area === 'search_indexing' || focus_area === 'all') {
guide += `# Search Indexing
## How Search Works
- Users discover Mini Apps through direct search in Base App
- Apps appear in search after being indexed
- Indexing takes ~10 minutes after first share
## Indexing Requirements
1. Complete manifest setup with all required fields
2. Share your Mini App URL in a Base App post
3. Wait ~10 minutes for indexing
4. Verify appearance in app catalogs
## Managing Indexing
- Development: Add \`"noindex": true\` to prevent dev/staging from appearing
- Production: Remove or set \`"noindex": false\`
- To remove from search: Invalidate your manifest
`;
}
if (focus_area === 'category_optimization' || focus_area === 'all') {
guide += `# Category Optimization
## Strategy
- Choose \`primaryCategory\` strategically based on target audience
- Monitor category rankings and adjust strategy
- Consider seasonal trends affecting popularity
## Available Categories
- games, social, finance, utility, productivity
- health-fitness, news-media, music, shopping
- education, developer-tools, entertainment, art-creativity
## Ranking Factors
- Base app uses 7-day rolling window for dynamic rankings
- Based on user engagement and activity
- Optimize for your chosen category
`;
}
if (focus_area === 'metadata_optimization' || focus_area === 'all') {
guide += `# Metadata Optimization
## App Icon
- 1024x1024px PNG (no alpha)
- Clear and recognizable at small sizes
- High contrast and simple shapes
- Reflect core functionality or brand
## Description
- Under 130 characters
- Clearly communicate value proposition
- Use relevant keywords
- Avoid crypto jargon
## Tags
- Max 5 tags, lowercase, no spaces
- Match user search behavior
- Use relevant, descriptive terms
- Include "base" and "miniapp" tags
## OG Image
- 1200x630px for social sharing
- Clear visual hierarchy
- Compelling preview of your app
`;
}
if (focus_area === 'ranking' || focus_area === 'all') {
guide += `# Ranking Optimization
## Discovery Surfaces
### Saved Apps
- Personal launcher and quick access
- Appears in user's saved Mini Apps
- Recently used applications
- Prompt users to save via Add Frame flow
### App Categories
- Browsable directory organized by interest
- Choose primaryCategory carefully
- Optimize for category-specific ranking
- Monitor performance and adjust strategy
## Optimization Checklist
- [ ] High-quality icon (1024x1024, clear at small sizes)
- [ ] Compelling description under 130 characters
- [ ] Relevant category selection
- [ ] OG image optimized for sharing (1200x630)
- [ ] Test metadata rendering across clients
- [ ] Implement proper manifest with correct categorization
- [ ] Create shareable moments that encourage shares
- [ ] Design compelling embeds with clear CTAs
- [ ] Encourage saves for easy return access
`;
}
return {
content: [
{
type: 'text',
text: `# Base Mini App Search & Discovery Guide
${guide}
## Visual Specifications
For detailed visual mapping of metadata to UI elements, see the [Figma specification file](https://www.figma.com/design/4wx6s24NB0KLgprQAyMT8R/TBA-Mini-App-Specs).
## Testing Your App
1. Use [Base Mini App Preview Tool](https://base.dev/preview) to validate metadata
2. Test how your app appears in different discovery surfaces
3. Share your app URL to see embed in action
4. Check search functionality with relevant keywords
## Complete Implementation
- [Manifest Configuration](/mini-apps/core-concepts/manifest)
- [Embed Implementation](/mini-apps/core-concepts/embeds-and-previews)
- [Sharing & Embeds](/mini-apps/core-concepts/embeds-and-previews)
## Resources
- [Base Build Preview Tool](https://base.dev/preview)
- [Figma Specifications](https://www.figma.com/design/4wx6s24NB0KLgprQAyMT8R/TBA-Mini-App-Specs)`,
},
],
};
}
default:
throw new Error(`Unknown tool: ${name}`);
}
});
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Base Mini App Builder MCP server running on stdio');
}
if (require.main === module) {
main().catch((error) => {
console.error('Server error:', error);
process.exit(1);
});
}