ANTIGRAVITY LABJP
Articles/Tips & Best Practices
Tips & Best Practices/2026-03-20Advanced

Antigravity Practical Techniques [Part 2] — Multi-Agent, Production Development & Monetization

Advanced techniques from Antigravity Lab premium articles. Part 2 covers multi-agent orchestration, production app development, custom MCP servers, and SaaS monetization.

Antigravity326practical-techniques2advanced20multi-agent50production71monetization31part-2premium16

Setup and context — Topics in Part 2

Part 1 covered Antigravity fundamentals. Part 2 focuses on production environment complexity, sophisticated system design, and business strategies. Learn advanced implementation patterns essential for real-world work through specific code examples.


Multi-Agent Orchestration Design

Coordinate multiple agents efficiently with clear design patterns.

Router Pattern (TypeScript)

A "router" manages multiple agents, assigning tasks to optimal agents by type.

Architecture

Request
  ↓
Agent Router (classify)
  ├→ Frontend Agent (UI)
  ├→ Backend Agent (API)
  ├→ Data Agent (DB)
  └→ Infra Agent (DevOps)

Implementation

// lib/agent-router.ts
import type { AgentType, Task, TaskResult } from '@/types';
 
interface AgentConfig {
  name: AgentType;
  keywords: string[];
  filePatterns: RegExp[];
  maxConcurrent: number;
}
 
const AGENT_CONFIGS: AgentConfig[] = [
  {
    name: 'frontend',
    keywords: ['component', 'ui', 'page', 'styled'],
    filePatterns: [/src\/(components|app|pages)\//, /\.tsx?$/],
    maxConcurrent: 2,
  },
  {
    name: 'backend',
    keywords: ['api', 'endpoint', 'database', 'query'],
    filePatterns: [/src\/(api|server|db)\//, /route\.(ts|js)$/],
    maxConcurrent: 3,
  },
  {
    name: 'data',
    keywords: ['migration', 'schema', 'index', 'optimize'],
    filePatterns: [/migrations\//, /schema\.(sql|ts)$/],
    maxConcurrent: 1,
  },
  {
    name: 'infra',
    keywords: ['deploy', 'ci', 'github', 'docker'],
    filePatterns: [/\.github/, /dockerfile/i, /\.env/i],
    maxConcurrent: 1,
  },
];
 
export class AgentRouter {
  private activeAgents: Map<AgentType, number> = new Map();
 
  constructor() {
    AGENT_CONFIGS.forEach(config => {
      this.activeAgents.set(config.name, 0);
    });
  }
 
  routeTask(task: Task): AgentType {
    // Extract keywords from task description
    const taskLower = task.description.toLowerCase();
    const matchedAgents = AGENT_CONFIGS
      .filter(config =>
        config.keywords.some(kw => taskLower.includes(kw)) ||
        config.filePatterns.some(pattern =>
          task.affectedFiles?.some(f => pattern.test(f))
        )
      );
 
    if (matchedAgents.length === 0) {
      // Default to frontend
      return 'frontend';
    }
 
    // Select agent with lowest load
    let selectedAgent = matchedAgents[0];
    for (const agent of matchedAgents) {
      const current = this.activeAgents.get(agent.name) || 0;
      const selected = this.activeAgents.get(selectedAgent.name) || 0;
 
      if (current < selected) {
        selectedAgent = agent;
      }
    }
 
    return selectedAgent.name;
  }
 
  async executeTask(task: Task): Promise<TaskResult> {
    const agentType = this.routeTask(task);
    const current = this.activeAgents.get(agentType) || 0;
 
    // Check connection limits
    const config = AGENT_CONFIGS.find(c => c.name === agentType)!;
    if (current >= config.maxConcurrent) {
      throw new Error(
        `Agent ${agentType} at max capacity (${config.maxConcurrent})`
      );
    }
 
    // Start execution
    this.activeAgents.set(agentType, current + 1);
 
    try {
      const result = await this.sendToAgent(agentType, task);
      return result;
    } finally {
      this.activeAgents.set(agentType, current);
    }
  }
 
  private async sendToAgent(
    agentType: AgentType,
    task: Task
  ): Promise<TaskResult> {
    // Call Antigravity Agent API
    const response = await fetch('/api/agents/execute', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ agentType, task }),
    });
 
    return response.json();
  }
}

Pipeline Pattern (TypeScript)

Execute a series of steps sequentially, each using a different agent.

Example: SaaS Deploy Pipeline

// lib/deployment-pipeline.ts
interface PipelineStep {
  name: string;
  agent: AgentType;
  dependencies: string[];
  execute: (context: PipelineContext) => Promise<void>;
}
 
interface PipelineContext {
  commitHash: string;
  environment: 'staging' | 'production';
  artifacts: Map<string, any>;
}
 
export class DeploymentPipeline {
  private steps: PipelineStep[] = [
    {
      name: 'lint',
      agent: 'frontend',
      dependencies: [],
      execute: async (ctx) => {
        console.log('Linting code...');
        // Frontend Agent runs ESLint/Prettier
        ctx.artifacts.set('lint-report', { status: 'pass' });
      },
    },
    {
      name: 'test',
      agent: 'frontend',
      dependencies: ['lint'],
      execute: async (ctx) => {
        console.log('Running tests...');
        // Frontend Agent runs Jest
        ctx.artifacts.set('test-coverage', { percent: 87 });
      },
    },
    {
      name: 'build-frontend',
      agent: 'frontend',
      dependencies: ['test'],
      execute: async (ctx) => {
        console.log('Building frontend...');
        // Frontend Agent builds Next.js
        ctx.artifacts.set('frontend-bundle', { size: '2.3MB' });
      },
    },
    {
      name: 'build-backend',
      agent: 'backend',
      dependencies: [],
      execute: async (ctx) => {
        console.log('Building backend...');
        // Backend Agent builds Node.js
        ctx.artifacts.set('backend-bundle', { size: '1.8MB' });
      },
    },
    {
      name: 'db-migration',
      agent: 'data',
      dependencies: [],
      execute: async (ctx) => {
        console.log('Running database migrations...');
        // Data Agent executes DB migration
        ctx.artifacts.set('migration-status', { applied: 3 });
      },
    },
    {
      name: 'deploy',
      agent: 'infra',
      dependencies: [
        'build-frontend',
        'build-backend',
        'db-migration',
      ],
      execute: async (ctx) => {
        console.log('Deploying to cloud...');
        // Infra Agent deploys to Vercel/Render
        ctx.artifacts.set('deploy-status', {
          url: 'https://staging.app.com',
          time: '3m 45s',
        });
      },
    },
    {
      name: 'e2e-test',
      agent: 'frontend',
      dependencies: ['deploy'],
      execute: async (ctx) => {
        console.log('Running E2E tests...');
        // Frontend Agent runs Playwright
        ctx.artifacts.set('e2e-result', { passed: 45, failed: 0 });
      },
    },
  ];
 
  async run(commitHash: string, environment: 'staging' | 'production') {
    const context: PipelineContext = {
      commitHash,
      environment,
      artifacts: new Map(),
    };
 
    const completed = new Set<string>();
 
    for (const step of this.steps) {
      // Wait for dependencies
      while (!step.dependencies.every(dep => completed.has(dep))) {
        await new Promise(r => setTimeout(r, 100));
      }
 
      try {
        await step.execute(context);
        completed.add(step.name);
        console.log(`✓ ${step.name} completed by ${step.agent}`);
      } catch (error) {
        console.error(`✗ ${step.name} failed:`, error);
        throw error;
      }
    }
 
    return context;
  }
}

Browser Sub-Agent E2E Test Automation

Auto-generate and run complex E2E tests with Playwright + Browser Sub-Agent.

// tests/e2e/checkout.spec.ts
import { test, expect } from '@playwright/test';
 
test('complete checkout flow', async ({ page }) => {
  // Auto-generated by Browser Sub-Agent
 
  // Step 1: Login
  await page.goto('/login');
  await page.fill('input[name="email"]', 'user@example.com');
  await page.fill('input[name="password"]', 'password123');
  await page.click('button[type="submit"]');
  await page.waitForNavigation();
 
  // Step 2: Add to cart
  await page.goto('/products');
  await page.click('text=Add to Cart');
  await expect(page).toHaveURL('/cart');
 
  // Step 3: Checkout
  await page.click('button:has-text("Proceed to Checkout")');
 
  // Step 4: Enter shipping info
  await page.fill('input[name="address"]', '123 Main St');
  await page.select('select[name="country"]', 'US');
 
  // Step 5: Payment
  await page.click('text=Pay with Stripe');
  // Auto-fill card in Stripe iframe
 
  // Step 6: Confirm
  await expect(page).toHaveURL('/order-confirmation');
  await expect(page).toContainText('Thank you for your order');
});

Production App Development

SwiftUI + CloudKit iCloud Sync App

iOS app with automatic iCloud synchronization.

// Models/Note.swift
import Foundation
import CloudKit
 
struct Note: Identifiable {
    let id: UUID
    var title: String
    var content: String
    var modifiedDate: Date
 
    // CloudKit integration
    var cloudKitRecord: CKRecord?
 
    func toCKRecord() -> CKRecord {
        let record = CKRecord(recordType: "Note", recordID: CKRecordID(recordName: id.uuidString))
        record["title"] = title
        record["content"] = content
        record["modifiedDate"] = modifiedDate
        return record
    }
 
    static func fromCKRecord(_ record: CKRecord) -> Note {
        return Note(
            id: UUID(uuidString: record.recordID.recordName) ?? UUID(),
            title: record["title"] as? String ?? "",
            content: record["content"] as? String ?? "",
            modifiedDate: record["modifiedDate"] as? Date ?? Date(),
            cloudKitRecord: record
        )
    }
}
// ViewModels/NoteViewModel.swift
import CloudKit
import Observation
 
@Observable
class NoteViewModel {
    var notes: [Note] = []
    var isLoading = false
 
    private let database = CKContainer.default().publicCloudDatabase
 
    @MainActor
    func fetchNotes() async {
        isLoading = true
        let predicate = NSPredicate(value: true)
        let query = CKQuery(recordType: "Note", predicate: predicate)
 
        do {
            let records = try await database.records(matching: query)
            notes = records.compactMap { Note.fromCKRecord($0) }
        } catch {
            print("Error fetching notes: \(error)")
        }
        isLoading = false
    }
 
    @MainActor
    func saveNote(_ note: Note) async {
        let record = note.toCKRecord()
 
        do {
            _ = try await database.save(record)
            if let index = notes.firstIndex(where: { $0.id == note.id }) {
                notes[index] = Note.fromCKRecord(record)
            } else {
                notes.append(Note.fromCKRecord(record))
            }
        } catch {
            print("Error saving note: \(error)")
        }
    }
}

Android Jetpack Compose + MVI Architecture

Latest Android best practices implementation.

// mvi/PostIntent.kt
sealed class PostIntent {
    data class LoadPosts(val page: Int) : PostIntent()
    data class RefreshPosts : PostIntent()
    data class DeletePost(val id: String) : PostIntent()
    data class FavoritePost(val id: String) : PostIntent()
}
// mvi/PostViewState.kt
data class PostViewState(
    val posts: List<Post> = emptyList(),
    val isLoading: Boolean = false,
    val error: String? = null,
    val selectedPost: Post? = null,
)
// mvi/PostViewModel.kt
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.*
 
class PostViewModel(
    private val postRepository: PostRepository
) : ViewModel() {
 
    private val intentFlow = MutableSharedFlow<PostIntent>()
 
    val viewState: StateFlow<PostViewState> = intentFlow
        .scan(PostViewState()) { state, intent ->
            when (intent) {
                is PostIntent.LoadPosts -> {
                    state.copy(isLoading = true)
                }
                is PostIntent.RefreshPosts -> {
                    state.copy(isLoading = true)
                }
                is PostIntent.DeletePost -> {
                    state.copy(
                        posts = state.posts.filter { it.id != intent.id }
                    )
                }
                is PostIntent.FavoritePost -> {
                    state.copy(
                        posts = state.posts.map { post ->
                            if (post.id == intent.id) {
                                post.copy(isFavorite = !post.isFavorite)
                            } else post
                        }
                    )
                }
            }
        }
        .stateIn(
            viewModelScope,
            SharingStarted.WhileSubscribed(5000),
            PostViewState()
        )
 
    fun sendIntent(intent: PostIntent) {
        viewModelScope.launch {
            intentFlow.emit(intent)
        }
    }
}
// ui/PostScreen.kt
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
 
@Composable
fun PostScreen(viewModel: PostViewModel) {
    val state by viewModel.viewState.collectAsState()
 
    LaunchedEffect(Unit) {
        viewModel.sendIntent(PostIntent.LoadPosts(1))
    }
 
    when {
        state.isLoading -> {
            CircularProgressIndicator()
        }
        state.error != null -> {
            Text("Error: ${state.error}")
        }
        else -> {
            LazyColumn {
                items(state.posts.size) { index ->
                    PostItem(
                        post = state.posts[index],
                        onDelete = {
                            viewModel.sendIntent(PostIntent.DeletePost(state.posts[index].id))
                        },
                        onFavorite = {
                            viewModel.sendIntent(PostIntent.FavoritePost(state.posts[index].id))
                        }
                    )
                }
            }
        }
    }
}

Cloudflare Workers AI Edge AI App

Low-latency AI via Cloudflare Workers integration.

// src/index.ts (Cloudflare Worker)
import { Hono } from 'hono';
 
const app = new Hono();
 
interface AIRequest {
  prompt: string;
  maxTokens?: number;
}
 
app.post('/api/ai/generate', async (c) => {
  const { prompt, maxTokens = 100 } = await c.req.json<AIRequest>();
 
  // @ts-ignore - Cloudflare Workers AI API
  const response = await c.env.AI.run('@cf/mistral/mistral-7b-instruct-v0.1', {
    prompt,
    max_tokens: maxTokens,
  });
 
  return c.json({
    prompt,
    generated: response.response,
    model: '@cf/mistral/mistral-7b-instruct-v0.1',
  });
});
 
app.post('/api/ai/classify', async (c) => {
  const { text } = await c.req.json();
 
  // Sentiment analysis
  const sentiment = await c.env.AI.run('@cf/huggingface/distilbert-sst-2-en', {
    text,
  });
 
  return c.json({
    text,
    sentiment: sentiment.result,
  });
});
 
export default app;

Custom MCP Server Development

Build custom tools for Antigravity.

TypeScript MCP Server

// mcp-server/src/index.ts
import Anthropic from '@anthropic-ai/sdk';
 
interface Tool {
  name: string;
  description: string;
  inputSchema: {
    type: string;
    properties: Record<string, any>;
    required: string[];
  };
}
 
interface Resource {
  uri: string;
  name: string;
  mimeType: string;
  description: string;
}
 
class CustomMCPServer {
  private tools: Tool[] = [
    {
      name: 'fetch_analytics',
      description: 'Fetch analytics from company dashboard',
      inputSchema: {
        type: 'object',
        properties: {
          metric: {
            type: 'string',
            description: 'Metric: revenue, users, retention',
          },
          period: {
            type: 'string',
            description: 'Period: daily, weekly, monthly',
          },
        },
        required: ['metric', 'period'],
      },
    },
    {
      name: 'list_deployments',
      description: 'List recent cloud deployments',
      inputSchema: {
        type: 'object',
        properties: {
          environment: {
            type: 'string',
            enum: ['staging', 'production'],
          },
          limit: {
            type: 'number',
            description: 'Max deployments to return',
          },
        },
        required: ['environment'],
      },
    },
  ];
 
  private resources: Resource[] = [
    {
      uri: 'internal://company/design-system',
      name: 'Design System',
      mimeType: 'text/markdown',
      description: 'Company design system and components',
    },
    {
      uri: 'internal://company/api-spec',
      name: 'API Specification',
      mimeType: 'application/json',
      description: 'OpenAPI spec for company APIs',
    },
  ];
 
  async listTools(): Promise<Tool[]> {
    return this.tools;
  }
 
  async listResources(): Promise<Resource[]> {
    return this.resources;
  }
 
  async callTool(
    toolName: string,
    params: Record<string, string | number>
  ): Promise<string> {
    switch (toolName) {
      case 'fetch_analytics':
        return this.fetchAnalytics(params);
      case 'list_deployments':
        return this.listDeployments(params);
      default:
        throw new Error(`Unknown tool: ${toolName}`);
    }
  }
 
  private async fetchAnalytics(params: Record<string, string | number>) {
    const { metric, period } = params;
 
    // Call analytics dashboard API
    const response = await fetch('https://analytics.company.com/api/metrics', {
      method: 'POST',
      headers: { Authorization: `Bearer ${process.env.ANALYTICS_API_KEY}` },
      body: JSON.stringify({ metric, period }),
    });
 
    const data = await response.json();
    return JSON.stringify(data);
  }
 
  private async listDeployments(params: Record<string, string | number>) {
    const { environment, limit = 10 } = params;
 
    // Call deploy history API
    const response = await fetch(
      `https://deploy.company.com/api/deployments?env=${environment}&limit=${limit}`,
      {
        headers: { Authorization: `Bearer ${process.env.DEPLOY_API_KEY}` },
      }
    );
 
    const data = await response.json();
    return JSON.stringify(data);
  }
}
 
// Start server
const server = new CustomMCPServer();
 
// REST API endpoints
import express from 'express';
const app = express();
 
app.post('/mcp/tools', async (req, res) => {
  res.json({ tools: await server.listTools() });
});
 
app.post('/mcp/resources', async (req, res) => {
  res.json({ resources: await server.listResources() });
});
 
app.post('/mcp/call', async (req, res) => {
  try {
    const { tool, params } = req.body;
    const result = await server.callTool(tool, params);
    res.json({ result });
  } catch (error) {
    res.status(400).json({ error: (error as Error).message });
  }
});
 
app.listen(3001, () => {
  console.log('MCP Server listening on port 3001');
});

SaaS Monetization Pipeline

Antigravity × Stripe Full-Stack SaaS

// app/api/stripe/create-subscription.ts
import Stripe from 'stripe';
import { supabaseAdmin } from '@/lib/supabase';
 
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
 
export async function POST(req: Request) {
  const { userId, priceId } = await req.json();
 
  // 1. Get or create Stripe customer
  const { data: user } = await supabaseAdmin
    .from('users')
    .select('stripe_customer_id, email')
    .eq('id', userId)
    .single();
 
  let customerId = user?.stripe_customer_id;
 
  if (!customerId) {
    const customer = await stripe.customers.create({
      email: user.email,
      metadata: { userId },
    });
    customerId = customer.id;
 
    await supabaseAdmin
      .from('users')
      .update({ stripe_customer_id: customerId })
      .eq('id', userId);
  }
 
  // 2. Create subscription
  const subscription = await stripe.subscriptions.create({
    customer: customerId,
    items: [{ price: priceId }],
    payment_behavior: 'default_incomplete',
    expand: ['latest_invoice.payment_intent'],
  });
 
  // 3. Record in Supabase
  await supabaseAdmin
    .from('subscriptions')
    .insert({
      user_id: userId,
      stripe_subscription_id: subscription.id,
      status: subscription.status,
      current_period_end: new Date(subscription.current_period_end * 1000),
    });
 
  return new Response(JSON.stringify(subscription), {
    status: 200,
    headers: { 'Content-Type': 'application/json' },
  });
}

YouTube Tutorial Video Auto-Production

Antigravity auto-generates YouTube scripts and cutting instructions.

// scripts/generate-tutorial.ts
import { Anthropic } from '@anthropic-ai/sdk';
 
const client = new Anthropic();
 
async function generateYouTubeTutorial(topic: string): Promise<string> {
  const message = await client.messages.create({
    model: 'claude-3-5-sonnet-20241022',
    max_tokens: 4000,
    messages: [
      {
        role: 'user',
        content: `
Generate YouTube tutorial video script.
 
Topic: ${topic}
Target: Beginner to intermediate
Duration: 10 minutes
 
Format:
[Scene 1]
Time: 0:00-0:30
Narration: ...
Visual: ...
 
[Scene 2]
...
`,
      },
    ],
  });
 
  return message.content[0].type === 'text' ? message.content[0].text : '';
}
 
// Execution
const script = await generateYouTubeTutorial(
  'Multi-Agent Development with Antigravity'
);
console.log(script);

Kindle Technical Book Efficient Writing

// scripts/generate-ebook.ts
interface EbookChapter {
  title: string;
  sections: {
    heading: string;
    content: string;
  }[];
}
 
async function generateEbookChapter(chapterTopic: string): Promise<EbookChapter> {
  const response = await fetch('/api/ai/generate', {
    method: 'POST',
    body: JSON.stringify({
      prompt: `
Write Kindle technical book chapter.
 
Chapter: ${chapterTopic}
Style: Technical, practical, code examples included
Length: 3000-4000 words
 
Sections:
1. Overview
2. Fundamentals
3. Implementation (with code)
4. Best Practices
5. FAQ
`,
    }),
  });
 
  const data = (await response.json()) as { generated: string };
 
  // Parse Markdown to structured format
  return parseMarkdownToChapter(data.generated);
}
 
function parseMarkdownToChapter(markdown: string): EbookChapter {
  // Markdown → structured data conversion
  return {
    title: 'Chapter Title',
    sections: [],
  };
}

Summary — Related Premium Articles

Part 2 taught production-level development technology with Antigravity.

Next Premium Article Series

  • "Multi-Agent System Design Pattern Collection"
  • "Production iOS/Android App Operations Techniques"
  • "Achieving Rapid SaaS Growth with AI Automation"
  • "Writing Kindle Bestsellers with Antigravity"

Join Antigravity Lab Premium to experience cutting-edge AI-driven development.

Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Tips2026-03-20
Antigravity Development Best Practices Collection — Essential Techniques from 28 Premium Articles
A curated collection of best practices from all Antigravity Lab premium articles. Covers multi-agent design, production-quality app development, design-to-code conversion, and integrated workflows.
Tips2026-05-05
Designing a Unified Mobile Revenue Funnel with Antigravity: AdMob, IAP, and Subscriptions for Indie Developers
A practical implementation playbook for indie mobile developers to unify AdMob, in-app purchases, and subscriptions into a single revenue funnel. Build automated dashboards and A/B testing pipelines with Antigravity.
Tips2026-03-30
Antigravity × Custom Code Generator — Building Project-Specific Code Generation Pipelines with Template Engines + AI Agents
Learn how to build custom code generators by combining Antigravity's AI agents with template engines. Master Hygen, Plop, and custom TypeScript scaffolding integrated with AGENTS.md to elevate your team's code quality and productivity.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →