> ## Documentation Index
> Fetch the complete documentation index at: https://docs.morphllm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Fast Apply

> AI file editing at 10,500 tokens/s - 1.8x faster, 40% fewer tokens

Coding agents rewrite entire files to change a few lines. A 500-line file costs \~\$0.12 in tokens and takes 8 seconds to regenerate. Fast Apply merges just the changed lines at 10,500 tok/s, 98% accuracy.

The agent outputs only the edited lines using `// ... existing code ...` markers. Morph merges them server-side and returns the complete file. Token usage drops 40% compared to full-file rewrites.

## Installation

```bash theme={null}
npm install @morphllm/morphsdk
```

## Quick Start

<Tabs>
  <Tab title="Anthropic">
    ```typescript theme={null}
    import Anthropic from '@anthropic-ai/sdk';
    import { MorphClient } from '@morphllm/morphsdk';

    const morph = new MorphClient({ apiKey: "YOUR_API_KEY" });
    const anthropic = new Anthropic();

    // Tool inherits API key from MorphClient
    const tool = morph.anthropic.createEditFileTool();

    const response = await anthropic.messages.create({
      model: "claude-sonnet-4-5-20250929",
      max_tokens: 12000,
      tools: [tool],
      messages: [{ 
        role: "user", 
        content: "Add error handling to src/auth.ts" 
      }]
    });
    ```
  </Tab>

  <Tab title="OpenAI">
    ```typescript theme={null}
    import OpenAI from 'openai';
    import { MorphClient } from '@morphllm/morphsdk';

    const morph = new MorphClient({ apiKey: "YOUR_API_KEY" });
    const openai = new OpenAI();

    // Tool inherits API key from MorphClient
    const tool = morph.openai.createEditFileTool();

    const response = await openai.chat.completions.create({
      model: "gpt-5-high",
      tools: [tool],
      messages: [{ 
        role: "user", 
        content: "Add error handling to src/auth.ts" 
      }]
    });
    ```

    <Tip>
      OpenAI high thinking models often output in patch format. Morph handles this automatically. If you see patch-style outputs, tune your system prompt to prefer `// ... existing code ...` markers for better results.
    </Tip>
  </Tab>

  <Tab title="Vercel AI SDK">
    ```typescript theme={null}
    import { generateText, stepCountIs } from 'ai';
    import { anthropic } from '@ai-sdk/anthropic';
    import { MorphClient } from '@morphllm/morphsdk';

    const morph = new MorphClient({ apiKey: "YOUR_API_KEY" });

    // Create the tool that is compatible with the Vercel AI SDK
    const editFileTool = morph.vercel.createEditFileTool();

    const result = await generateText({
      model: anthropic('claude-sonnet-4-5-20250929'),
      tools: { editFile: editFileTool },
      prompt: "Add error handling to src/auth.ts",
      stopWhen: stepCountIs(5)
    });
    ```
  </Tab>

  <Tab title="MorphClient">
    ```typescript theme={null}
    import { MorphClient } from '@morphllm/morphsdk';

    const morph = new MorphClient({ apiKey: "YOUR_API_KEY" });

    // Direct execution
    const result = await morph.fastApply.execute({
      target_filepath: 'src/auth.ts',
      instructions: 'I will add null check',
      code_edit: '// ... existing code ...\nif (!user) throw new Error("Not found");\n// ... existing code ...'
    });

    console.log(result.success); // true
    console.log(`+${result.changes.linesAdded} -${result.changes.linesRemoved}`);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from openai import OpenAI

    client = OpenAI(
        api_key="YOUR_API_KEY",
        base_url="https://api.morphllm.com/v1",
    )

    original_code = open("src/auth.ts").read()
    code_edit = "// ... existing code ...\nif (!user) throw new Error('Not found');\n// ... existing code ..."
    instruction = "I will add null check"

    response = client.chat.completions.create(
        model="morph-v3-fast",
        messages=[
            {
                "role": "user",
                "content": f"<instruction>{instruction}</instruction>\n<code>{original_code}</code>\n<update>{code_edit}</update>"
            }
        ],
    )

    merged_code = response.choices[0].message.content

    with open("src/auth.ts", "w") as f:
        f.write(merged_code)
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST "https://api.morphllm.com/v1/chat/completions" \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "morph-v3-fast",
        "messages": [
          {
            "role": "user",
            "content": "<instruction>I will add null check</instruction>\n<code>async function login(email: string, password: string) {\n  const user = await db.findUser(email);\n  return createSession(user);\n}</code>\n<update>// ... existing code ...\nif (!user) throw new Error(\"Not found\");\n// ... existing code ...</update>"
          }
        ]
      }'
    ```

    The response is an OpenAI-compatible chat completion. The merged code is in `choices[0].message.content`.
  </Tab>
</Tabs>

<Note>
  The `instructions` parameter provides context for ambiguous edits, helping the apply model make correct decisions and reach 98% accuracy. Have the parent model generate the instructions.
</Note>

## How It Works

<Steps>
  <Step title="Add an edit_file tool to your agent">
    Add the `edit_file` tool to your agent. Use one of the formats below.

    <Tabs>
      <Tab title="General Prompt">
        ```
        When editing files, use the edit_file tool with these parameters:
        - target_filepath: Path of the file to modify
        - instructions: Brief first-person description of what you're changing
        - code_edit: Only the changed lines with // ... existing code ... markers

        Use "// ... existing code ..." to represent unchanged code blocks. Include
        just enough surrounding context to locate each edit precisely.

        Example format:
        // ... existing code ...
        FIRST_EDIT
        // ... existing code ...
        SECOND_EDIT
        // ... existing code ...

        Rules:
        - ALWAYS use "// ... existing code ..." for unchanged sections (omitting
          this marker will cause deletions)
        - Include minimal context around edits only when needed for disambiguation
        - Preserve exact indentation
        - For deletions: show context before and after, omit the deleted lines
        - Batch multiple edits to the same file in one call
        ```
      </Tab>

      <Tab title="JSON Tool (Claude)">
        Pass this tool definition directly to Claude or any model that supports JSON tool schemas.

        ```json theme={null}
        {
          "name": "edit_file",
          "description": "Edit an existing file by showing only the changed lines. Use // ... existing code ... to represent unchanged sections. Include just enough surrounding context to locate each edit precisely. ALWAYS use the marker for unchanged sections (omitting it will cause deletions). Preserve exact indentation. For deletions, show context before and after. Batch multiple edits to the same file in one call.",
          "input_schema": {
            "type": "object",
            "properties": {
              "target_filepath": {
                "type": "string",
                "description": "Path of the file to modify"
              },
              "instructions": {
                "type": "string",
                "description": "A single sentence written in the first person describing what the agent is changing. Used to help disambiguate uncertainty in the edit."
              },
              "code_edit": {
                "type": "string",
                "description": "Specify ONLY the precise lines of code that you wish to edit. Use // ... existing code ... for unchanged sections."
              }
            },
            "required": ["target_filepath", "instructions", "code_edit"]
          }
        }
        ```
      </Tab>

      <Tab title="Output Parsing (No Tool)">
        If your model doesn't support tool use, have it output edits in a structured format you parse yourself.

        ````
        When editing files, output a fenced code block with the filepath as the language tag:

        ```src/auth.ts
        // ... existing code ...
        if (!user) throw new Error("Not found");
        // ... existing code ...
        ```

        Before each block, write a one-line instruction starting with "I will":
        I will add a null check before creating the session.
        ````

        Parse the filepath from the code fence, the instruction from the preceding line, and the code edit from the block contents. Then send all three to Morph.
      </Tab>
    </Tabs>

    **Parameters:**

    | Parameter         | Type   | Required | Description                                                                                         |
    | ----------------- | ------ | -------- | --------------------------------------------------------------------------------------------------- |
    | `target_filepath` | string | yes      | Path of the file to modify                                                                          |
    | `instructions`    | string | yes      | Brief first-person description of what you're changing (helps disambiguate uncertainty in the edit) |
    | `code_edit`       | string | yes      | Only the changed lines with `// ... existing code ...` markers for unchanged sections               |

    <Warning>
      The `instructions` param should be generated by the model, not hardcoded. Example: *"I am adding error handling to the user auth and removing the old auth functions"*
    </Warning>

    <Accordion title="Why do I need the instructions to be generated by the model?">
      The `instructions` parameter provides crucial context for ambiguous edits, helping the apply model make correct decisions and achieve near 100% accuracy even in edge cases.
    </Accordion>
  </Step>

  <Step title="Merge with Morph Fast Apply">
    When the agent calls your tool, send the original file + the edit snippet to Morph's API. It returns the merged file. Write it to disk.

    <Accordion title="What to add to your System Prompt">
      ```
      When editing code, use the edit_file tool. Output only the changed sections and use
      `// ... existing code ...` markers to skip over unchanged code. Do not reread a file
      before editing. The edit is applied semantically, so you do not need the file's exact
      current contents to make a correct edit.
      ```
    </Accordion>

    <CodeGroup>
      ```typescript TypeScript theme={null}
      import OpenAI from "openai";

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

      const originalCode = await fs.readFile(toolCall.target_filepath, "utf-8");

      const response = await openai.chat.completions.create({
        model: "morph-v3-fast",
        messages: [
          {
            role: "user",
            content: `<instruction>${toolCall.instructions}</instruction>\n<code>${originalCode}</code>\n<update>${toolCall.code_edit}</update>`,
          },
        ],
      });

      const mergedCode = response.choices[0].message.content;
      ```

      ```python Python theme={null}
      from openai import OpenAI

      client = OpenAI(
          api_key="YOUR_API_KEY",
          base_url="https://api.morphllm.com/v1",
      )

      original_code = open(tool_call["target_filepath"]).read()

      response = client.chat.completions.create(
          model="morph-v3-fast",
          messages=[{
              "role": "user",
              "content": f"<instruction>{tool_call['instructions']}</instruction>\n<code>{original_code}</code>\n<update>{tool_call['code_edit']}</update>"
          }],
      )

      merged_code = response.choices[0].message.content
      ```

      ```bash cURL theme={null}
      curl -X POST "https://api.morphllm.com/v1/chat/completions" \
        -H "Authorization: Bearer YOUR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "model": "morph-v3-fast",
          "messages": [{
            "role": "user",
            "content": "<instruction>Add null check</instruction>\n<code>async function login(email, password) {\n  const user = await db.findUser(email);\n  return createSession(user);\n}</code>\n<update>// ... existing code ...\nif (!user) throw new Error(\"Not found\");\n// ... existing code ...</update>"
          }]
        }'
      ```
    </CodeGroup>
  </Step>

  <Step title="Handle the Response">
    The merged code is in `response.choices[0].message.content`. Write it to the target file.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const finalCode = response.choices[0].message.content;
      await fs.writeFile(toolCall.target_filepath, finalCode);
      ```

      ```python Python theme={null}
      final_code = response.choices[0].message.content
      with open(tool_call["target_filepath"], "w") as f:
          f.write(final_code)
      ```

      ```bash cURL theme={null}
      # Parse choices[0].message.content from the JSON response
      # and write it to the target file
      ```
    </CodeGroup>
  </Step>

  <Step title="Verifying Edits (Optional but Recommended)">
    Pass the diff back to the agent so it can verify the changes match its intent. To save tokens, you can limit this to cases where the linter reports errors.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      import { createTwoFilesPatch } from 'diff';

      const udiff = createTwoFilesPatch(
        toolCall.target_filepath,
        toolCall.target_filepath,
        originalCode,
        mergedCode,
        '',
        ''
      );

      // Send back to agent for verification
      console.log("Changes applied:", udiff);
      ```

      ```python Python theme={null}
      import difflib

      udiff = difflib.unified_diff(
          original_code.splitlines(keepends=True),
          merged_code.splitlines(keepends=True),
          fromfile=tool_call["target_filepath"],
          tofile=tool_call["target_filepath"],
      )

      # Send back to agent for verification
      print("Changes applied:", "".join(udiff))
      ```

      ```bash Bash theme={null}
      # If you saved the original to a temp file:
      diff -u original.ts modified.ts
      ```
    </CodeGroup>

    This catches unexpected changes before they hit disk.
  </Step>
</Steps>

## Direct Usage

Use without an agent:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    const result = await morph.fastApply.execute({
      target_filepath: 'src/auth.ts',
      instructions: 'I will add null check',
      code_edit: '// ... existing code ...\nif (!user) throw new Error("Not found");\n// ... existing code ...'
    });

    console.log(result.success); // true
    console.log(`+${result.changes.linesAdded} -${result.changes.linesRemoved}`);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from openai import OpenAI

    client = OpenAI(
        api_key="YOUR_API_KEY",
        base_url="https://api.morphllm.com/v1",
    )

    original_code = open("src/auth.ts").read()

    response = client.chat.completions.create(
        model="morph-v3-fast",
        messages=[
            {
                "role": "user",
                "content": f"<instruction>I will add null check</instruction>\n<code>{original_code}</code>\n<update>// ... existing code ...\nif (!user) throw new Error('Not found');\n// ... existing code ...</update>"
            }
        ],
    )

    merged_code = response.choices[0].message.content
    with open("src/auth.ts", "w") as f:
        f.write(merged_code)
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST "https://api.morphllm.com/v1/chat/completions" \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "morph-v3-fast",
        "messages": [
          {
            "role": "user",
            "content": "<instruction>I will add null check</instruction>\n<code>async function login(email, password) {\n  const user = await db.findUser(email);\n  return createSession(user);\n}</code>\n<update>// ... existing code ...\nif (!user) throw new Error(\"Not found\");\n// ... existing code ...</update>"
          }
        ]
      }'
    ```
  </Tab>
</Tabs>

## Code-in/Code-out (Sandbox Support)

Use `applyEdit` when you manage your own filesystem or work in sandboxes like E2B, Modal, or Daytona:

<Tabs>
  <Tab title="applyEdit Function">
    ```typescript theme={null}
    import { applyEdit } from '@morphllm/morphsdk';

    // Read file yourself (from sandbox, memory, etc.)
    const originalCode = await sandbox.readFile('src/auth.ts');

    const result = await applyEdit({
      originalCode,
      codeEdit: '// ... existing code ...\nif (!user) throw new Error("Not found");\n// ... existing code ...',
      instructions: 'Add null check',
    });

    if (result.success) {
      // Write file yourself
      await sandbox.writeFile('src/auth.ts', result.mergedCode);
      console.log(result.udiff);  // View the diff
    }
    ```
  </Tab>

  <Tab title="MorphClient Method">
    ```typescript theme={null}
    import { MorphClient } from '@morphllm/morphsdk';

    const morph = new MorphClient({ apiKey: "YOUR_API_KEY" });

    const result = await morph.fastApply.applyEdit({
      originalCode: 'function hello() { return "world"; }',
      codeEdit: 'function hello() { return "universe"; }',
      instructions: 'Change return value'
    });

    console.log(result.mergedCode);
    // function hello() { return "universe"; }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from openai import OpenAI

    client = OpenAI(
        api_key="YOUR_API_KEY",
        base_url="https://api.morphllm.com/v1",
    )

    original_code = sandbox.read_file("src/auth.ts")
    code_edit = "// ... existing code ...\nif (!user) throw new Error('Not found');\n// ... existing code ..."

    response = client.chat.completions.create(
        model="morph-v3-fast",
        messages=[
            {
                "role": "user",
                "content": f"<instruction>Add null check</instruction>\n<code>{original_code}</code>\n<update>{code_edit}</update>"
            }
        ],
    )

    merged_code = response.choices[0].message.content
    sandbox.write_file("src/auth.ts", merged_code)
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST "https://api.morphllm.com/v1/chat/completions" \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "morph-v3-fast",
        "messages": [
          {
            "role": "user",
            "content": "<instruction>Add null check</instruction>\n<code>async function login(email, password) {\n  const user = await db.findUser(email);\n  return createSession(user);\n}</code>\n<update>// ... existing code ...\nif (!user) throw new Error(\"Not found\");\n// ... existing code ...</update>"
          }
        ]
      }'
    ```

    Parse `choices[0].message.content` for the merged code, then write it back to your sandbox filesystem.
  </Tab>
</Tabs>

<Info>
  `applyEdit` returns `mergedCode` instead of writing to disk. Use it in sandbox environments where you control file I/O.
</Info>

## Configuration

```typescript theme={null}
import { MorphClient } from '@morphllm/morphsdk';

const morph = new MorphClient({ apiKey: "YOUR_API_KEY" });

const tool = morph.openai.createEditFileTool({
  baseDir: './src',       // Default: process.cwd()
  autoWrite: true,        // Auto-write files (default: true)
  generateUdiff: true     // Return diff (default: true)
});
```

## API

<Tabs>
  <Tab title="execute (file-based)">
    **Input** (`EditFileInput`):

    ```typescript theme={null}
    {
      target_filepath: string,  // Relative to baseDir
      instructions: string,     // What the model is changing
      code_edit: string         // Code with // ... existing code ...
    }
    ```

    **Returns** (`EditFileResult`):

    ```typescript theme={null}
    {
      success: boolean,
      filepath: string,
      changes: { linesAdded, linesRemoved, linesModified },
      udiff?: string,
      error?: string
    }
    ```
  </Tab>

  <Tab title="applyEdit (code-based)">
    **Input** (`ApplyEditInput`):

    ```typescript theme={null}
    {
      originalCode: string,     // Current file contents
      codeEdit: string,         // Code with // ... existing code ...
      instructions: string      // What the model is changing
    }
    ```

    **Returns** (`ApplyEditResult`):

    ```typescript theme={null}
    {
      success: boolean,
      mergedCode?: string,      // The merged result
      changes: { linesAdded, linesRemoved, linesModified },
      udiff?: string,
      error?: string
    }
    ```
  </Tab>
</Tabs>

<Tip>
  All types are exported from the SDK root:

  ```typescript theme={null}
  import type { 
    EditFileInput, 
    EditFileResult, 
    ApplyEditInput, 
    ApplyEditResult,
    EditChanges 
  } from '@morphllm/morphsdk';
  ```
</Tip>

## Edge / Cloudflare Workers

Use `@morphllm/morphsdk/edge` for edge environments (Cloudflare Workers, Vercel Edge, Deno):

```typescript theme={null}
import { applyEdit } from '@morphllm/morphsdk/edge';

export default {
  async fetch(request: Request, env: Env) {
    const { originalCode, codeEdit, instructions } = await request.json();

    const result = await applyEdit({
      originalCode,
      codeEdit,
      instructions,
    }, {
      morphApiKey: env.MORPH_API_KEY
    });

    return Response.json({
      success: result.success,
      mergedCode: result.mergedCode,
      udiff: result.udiff
    });
  }
};
```

<Note>
  The edge entry point has zero Node.js dependencies. It exports `applyEdit`, `generateUdiff`, `countChanges`, and `callMorphAPI`.
</Note>

## When to Use

**Use Fast Apply when:**

* Your agent edits existing files (the primary use case)
* Batching multiple edits to the same file in one call
* Running in CI pipelines where token cost and latency matter
* Working in sandboxed environments (E2B, Modal, Daytona) via `applyEdit`

**Don't use Fast Apply when:**

* Creating new files. Write them directly, there's nothing to merge.
* The entire file needs rewriting (rare). Full generation is simpler.
* Editing non-code files like images or binaries.

## Error Handling

```typescript theme={null}
if (!result.success) {
  console.error(result.error);
  // "File not found" | "Invalid filepath" | "API error"
}
```
