Morph + Freestyle: Perfect for AI App Builders

Morph Fast Apply integrates seamlessly with Freestyle, the cloud platform for AI App Builders. This combination gives you the best of both worlds: Freestyle’s managed dev servers and git infrastructure, plus Morph’s lightning-fast code editing.

Why Use Morph with Freestyle?

Freestyle provides excellent infrastructure for AI App Builders. The default file editing uses search-and-replace which can be slow and error-prone. Morph replaces this with semantic code merging:
  • Freestyle default: Search-and-replace editing - 86% accurate, 35s per edit
  • Morph + Freestyle: Semantic merging - 98% accurate, 6s per edit
Perfect for AI App Builders built on Freestyle that need:
  • Faster user experiences during code generation
  • Higher accuracy with fewer correction loops
  • Better handling of complex, multi-location edits
  • Reduced hallucinations and formatting errors

Prerequisites

This guide assumes you have a working Freestyle AI App Builder. If you’re new to Freestyle, check out their getting started guide first.

How to Integrate Morph with Freestyle

1. Get Your Morph API Key

First, grab your API key from the Morph dashboard and add it to your environment:
MORPH_API_KEY=your_morph_api_key_here

2. Create the Morph-Freestyle Tool

Morph works by replacing Freestyle’s default edit_file tool. Create a new tool that uses Morph’s semantic merging with Freestyle’s filesystem interface:
import { createTool } from "@mastra/core/tools";
import { z } from "zod";
import OpenAI from "openai";
import { FreestyleDevServerFilesystem } from "freestyle-sandboxes";

const openai = new OpenAI({
  apiKey: process.env.MORPH_API_KEY,
  baseURL: "https://api.morphllm.com/v1",
});

export const morphTool = (fs: FreestyleDevServerFilesystem) =>
  createTool({
    id: "edit_file",
    description:
      "Use this tool to make an edit to an existing file.\n\nThis will be read by a less intelligent model, which will quickly apply the edit. You should make it clear what the edit is, while also minimizing the unchanged code you write.\nWhen writing the edit, you should specify each edit in sequence, with the special comment // ... existing code ... to represent unchanged code in between edited lines.\n\nFor example:\n\n// ... existing code ...\nFIRST_EDIT\n// ... existing code ...\nSECOND_EDIT\n// ... existing code ...\nTHIRD_EDIT\n// ... existing code ...\n\nYou should still bias towards repeating as few lines of the original file as possible to convey the change.\nBut, each edit should contain sufficient context of unchanged lines around the code you're editing to resolve ambiguity.\nDO NOT omit spans of pre-existing code (or comments) without using the // ... existing code ... comment to indicate its absence. If you omit the existing code comment, the model may inadvertently delete these lines.\nIf you plan on deleting a section, you must provide context before and after to delete it. If the initial code is ```code \\n Block 1 \\n Block 2 \\n Block 3 \\n code```, and you want to remove Block 2, you would output ```// ... existing code ... \\n Block 1 \\n  Block 3 \\n // ... existing code ...```.\nMake sure it is clear what the edit should be, and where it should be applied.\nMake edits to a file in a single edit_file call instead of multiple edit_file calls to the same file. The apply model can handle many distinct edits at once.",
    inputSchema: z.object({
      target_file: z.string().describe("The target filepath to modify."),
      instructions: z
        .string()
        .describe(
          "A single sentence instruction describing what you are going to do for the sketched edit. This is used to assist the less intelligent model in applying the edit. Use the first person to describe what you are going to do. Use it to disambiguate uncertainty in the edit."
        ),
      code_edit: z
        .string()
        .describe(
          "Specify ONLY the precise lines of code that you wish to edit. NEVER specify or write out unchanged code. Instead, represent all unchanged code using the comment of the language you're editing in - example: // ... existing code ..."
        ),
    }),
    execute: async ({
      context: { target_file, instructions, code_edit: editSnippet },
    }) => {
      let file;
      try {
        file = await fs.readFile(target_file);
      } catch (error) {
        throw new Error(
          `File not found: ${target_file}. Error message: ${error instanceof Error ? error.message : String(error)}`
        );
      }
      const response = await openai.chat.completions.create({
        model: "morph-v3-large",
        messages: [
          {
            role: "user",
            content: `<instruction>${instructions}</instruction>\n<code>${file}</code>\n<update>${editSnippet}</update>`,
          },
        ],
      });

      const finalCode = response.choices[0].message.content;

      if (!finalCode) {
        throw new Error("No code returned from Morph API.");
      }
      // Write to file or return to your application
      await fs.writeFile(target_file, finalCode);
    },
  });

3. Update Your Freestyle Chat API

In your existing Freestyle app’s app/api/chat/route.ts, replace the default edit tool with your Morph-powered version:
// app/api/chat/route.ts
import { streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { FreestyleSandboxes } from "freestyle-sandboxes";
import { morphTool } from '../../../lib/morph-tool';

const freestyle = new FreestyleSandboxes({
  apiKey: process.env.FREESTYLE_API_KEY!,
});

export async function POST(req: Request) {
  const repoId = req.headers.get("Repo-Id");
  const { messages } = await req.json();

  const { ephemeralUrl, mcpEphemeralUrl } = await freestyle.requestDevServer({
    repoId: repoId,
  });

  // Get the filesystem interface from the dev server
  const devServerMcp = await createMCPClient({
    transport: new StreamableHTTPClientTransport(new URL(mcpEphemeralUrl)),
  });
  
  // Get default tools but replace edit_file with Morph version
  const defaultTools = await devServerMcp.getTools();
  const morphEditTool = morphTool(devServerMcp.fs); // fs interface from MCP client
  
  const tools = {
    ...defaultTools,
    edit_file: morphEditTool, // Override default with Morph version
  };

  const response = await streamText({
    model: anthropic('claude-3-5-sonnet-20241022'),
    maxSteps: 100,
    tools: tools,
    toolCallStreaming: true,
    messages: [
      {
        role: "system",
        content: `You are an AI App Builder. Edit the app in /template directory based on user requests and commit changes incrementally.`,
      },
      ...messages,
    ],
  });

  result.consumeStream();
  return result.toDataStreamResponse();
}

Why Morph + Freestyle?

Freestyle provides fast and cost-effective serverless code execution on the market, while Morph delivers the most accurate and efficient code editing. Together, they create the ideal environment for AI app builders - each tool perfectly suited for its purpose.
  • The Right Tool for Code Editing: While Freestyle excels at execution, Morph is purpose-built for code edits, delivering 4x faster file modifications (35+ seconds → ~6 seconds)
  • Seamless Integration: Drop-in replacement for Freestyle’s default edit tool - no changes to your AI logic required
  • Perfect Pairing: Freestyle’s blazing-fast execution + Morph’s precise editing = the complete AI development stack
  • Cost Effective: Morph’s efficiency reduces expensive model correction loops, often saving more than its service cost

What’s Next?

Once integrated, your Freestyle AI App Builder will have the complete toolkit for rapid, accurate development. Users will experience:
  • Faster response times when making app changes
  • Fewer “let me fix that” moments from the AI
  • More reliable complex edits across multiple files
  • The snappiest AI development experience available
For more advanced use cases and examples, check out our API documentation or explore other Morph integrations.