> ## 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.

# Subagent as an Agent Tool

> Add WarpGrep as a search subagent to any coding agent

WarpGrep is a subagent your coding agent calls as a tool. The flow:

1. Your agent (Claude, Codex, GPT-4o, Gemini) needs to find code
2. It calls WarpGrep with a natural language query
3. WarpGrep searches in its own isolated context window, multiple turns, 8 parallel tool calls per turn
4. It returns only the relevant file/line-range spans
5. Your agent continues with clean context

The parent agent never sees the intermediate search steps, the rejected files, or the dead-end greps. On SWE-Bench Pro, this makes Opus 15.6% cheaper and 28% faster than searching on its own.

SDK adapters for Anthropic, OpenAI, Vercel AI SDK, and Gemini.

<Tip>
  See [complete agent examples](https://github.com/morphllm/examples/tree/main/warpgrep) for each framework — copy-paste ready.
</Tip>

## 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: process.env.MORPH_API_KEY });
    const anthropic = new Anthropic();

    // Create the tool
    const warpGrepSubagent = morph.anthropic.createWarpGrepTool({ repoRoot: '.' });

    // Use it with your agent
    const response = await anthropic.messages.create({
      model: 'claude-sonnet-4-5-20250929',
      max_tokens: 12000,
      tools: [warpGrepSubagent],
      messages: [{ role: 'user', content: 'Find authentication middleware' }]
    });

    // Execute the tool call
    const toolUse = response.content.find(c => c.type === 'tool_use');
    if (toolUse) {
      const result = await warpGrepSubagent.execute(toolUse.input);
      console.log(warpGrepSubagent.formatResult(result));
    }
    ```
  </Tab>

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

    const morph = new MorphClient({ apiKey: process.env.MORPH_API_KEY });
    const openai = new OpenAI();

    // Create the tool
    const warpGrepSubagent = morph.openai.createWarpGrepTool({ repoRoot: '.' });

    // Use it with your agent
    const response = await openai.chat.completions.create({
      model: 'gpt-4o',
      tools: [warpGrepSubagent],
      messages: [{ role: 'user', content: 'Find authentication middleware' }]
    });

    // Execute the tool call
    const toolCall = response.choices[0].message.tool_calls?.[0];
    if (toolCall) {
      const result = await warpGrepSubagent.execute(toolCall.function.arguments);
      console.log(warpGrepSubagent.formatResult(result));
    }
    ```
  </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: process.env.MORPH_API_KEY });

    // Create the tool
    const warpGrepSubagent = morph.vercel.createWarpGrepTool({ repoRoot: '.' });

    // Vercel AI SDK handles the tool loop automatically
    const result = await generateText({
      model: anthropic('claude-sonnet-4-5-20250929'),
      tools: { grep: warpGrepSubagent },
      prompt: 'Find authentication middleware',
      stopWhen: stepCountIs(5)
    });
    ```
  </Tab>
</Tabs>

### Platform Examples

<Tabs>
  <Tab title="E2B">
    ```typescript theme={null}
    import { Sandbox } from "@e2b/code-interpreter";
    import { MorphClient } from '@morphllm/morphsdk';

    const morph = new MorphClient({ apiKey: "YOUR_API_KEY" });
    const sandbox = await Sandbox.create();
    const repoDir = "/home/user/repo";

    // Clone repo and install ripgrep
    await sandbox.commands.run(`git clone --depth 1 https://github.com/example/repo ${repoDir}`);
    await sandbox.commands.run("apt-get update && apt-get install -y ripgrep");

    const warpGrepSubagent = morph.anthropic.createWarpGrepTool({
      repoRoot: repoDir,
      remoteCommands: {
        grep: async (pattern, path) => {
          const r = await sandbox.commands.run(
            `rg --no-heading --line-number '${pattern}' '${path}'`,
            { cwd: repoDir }
          );
          return r.stdout || '';
        },
        read: async (path, start, end) => {
          const r = await sandbox.commands.run(`sed -n '${start},${end}p' '${path}'`);
          return r.stdout || '';
        },
        listDir: async (path, maxDepth) => {
          const r = await sandbox.commands.run(
            `find '${path}' -maxdepth ${maxDepth} -not -path '*/node_modules/*'`
          );
          return r.stdout || '';
        },
      },
    });

    // Use with Anthropic
    const response = await anthropic.messages.create({
      model: 'claude-sonnet-4-5-20250929',
      tools: [warpGrepSubagent],
      messages: [{ role: 'user', content: 'Find authentication middleware' }]
    });
    ```
  </Tab>

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

    const morph = new MorphClient({ apiKey: "YOUR_API_KEY" });
    const modal = new ModalClient();
    const sandbox = await modal.sandboxes.create(app, image);
    const repoDir = "/home/repo";

    const warpGrepSubagent = morph.openai.createWarpGrepTool({
      repoRoot: repoDir,
      remoteCommands: {
        grep: async (pattern, path) => {
          const proc = await sandbox.exec([
            "rg", "--no-heading", "--line-number", pattern, path
          ]);
          return await proc.stdout.readText();
        },
        read: async (path, start, end) => {
          const proc = await sandbox.exec(["sed", "-n", `${start},${end}p`, path]);
          return await proc.stdout.readText();
        },
        listDir: async (path, maxDepth) => {
          const proc = await sandbox.exec([
            "find", path, "-maxdepth", String(maxDepth)
          ]);
          return await proc.stdout.readText();
        },
      },
    });

    // Use with OpenAI
    const response = await openai.chat.completions.create({
      model: 'gpt-4o',
      tools: [warpGrepSubagent],
      messages: [{ role: 'user', content: 'Find authentication middleware' }]
    });
    ```
  </Tab>

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

    const morph = new MorphClient({ apiKey: "YOUR_API_KEY" });
    const daytona = new Daytona({ apiKey: process.env.DAYTONA_API_KEY });
    const sandbox = await daytona.create({ language: 'python' });
    const repoDir = "/home/daytona/repo";

    const warpGrepSubagent = morph.vercel.createWarpGrepTool({
      repoRoot: repoDir,
      remoteCommands: {
        grep: async (pattern, path) => {
          const r = await sandbox.process.executeCommand(
            `rg --no-heading --line-number '${pattern}' '${path}'`,
            repoDir
          );
          return r.result || '';
        },
        read: async (path, start, end) => {
          const r = await sandbox.process.executeCommand(
            `sed -n '${start},${end}p' '${path}'`,
            repoDir
          );
          return r.result || '';
        },
        listDir: async (path, maxDepth) => {
          const r = await sandbox.process.executeCommand(
            `find '${path}' -maxdepth ${maxDepth}`,
            repoDir
          );
          return r.result || '';
        },
      },
    });

    // Use with Vercel AI SDK
    const result = await generateText({
      model: anthropic('claude-sonnet-4-5-20250929'),
      tools: { grep: warpGrepSubagent },
      prompt: 'Find authentication middleware'
    });
    ```
  </Tab>

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

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

    const sandbox = await Sandbox.create({ runtime: 'node24' });

    // Clone the repo manually (source.type: 'git' is not supported)
    const repoDir = '/home/user/repo';
    await sandbox.runCommand({
      cmd: 'git',
      args: ['clone', '--depth', '1', 'https://github.com/example/repo.git', repoDir],
    });

    // ripgrep isn't in Amazon Linux 2023 repos, download the binary
    await sandbox.runCommand({
      cmd: 'sh',
      args: ['-c', 'curl -sL https://github.com/BurntSushi/ripgrep/releases/download/14.1.1/ripgrep-14.1.1-x86_64-unknown-linux-musl.tar.gz | tar xz -C /tmp && cp /tmp/ripgrep-14.1.1-x86_64-unknown-linux-musl/rg /usr/local/bin/'],
      sudo: true,
    });

    const warpGrepSubagent = morph.anthropic.createWarpGrepTool({
      repoRoot: repoDir,
      remoteCommands: {
        grep: async (pattern, path, glob) => {
          const args = ['--no-heading', '--line-number', '-C', '1', pattern, path];
          if (glob) args.push('--glob', glob);
          const r = await sandbox.runCommand('rg', args);
          return await r.stdout();
        },
        read: async (path, start, end) => {
          const r = await sandbox.runCommand('sed', ['-n', `${start},${end}p`, path]);
          return await r.stdout();
        },
        listDir: async (path, maxDepth) => {
          const r = await sandbox.runCommand('find', [
            path, '-maxdepth', String(maxDepth),
            '-not', '-path', '*/node_modules/*',
            '-not', '-path', '*/.git/*',
          ]);
          return await r.stdout();
        },
      },
    });

    // Use with Anthropic
    const response = await anthropic.messages.create({
      model: 'claude-sonnet-4-5-20250929',
      tools: [warpGrepSubagent],
      messages: [{ role: 'user', content: 'Find authentication middleware' }]
    });
    ```
  </Tab>

  <Tab title="Cloudflare Sandbox">
    ```typescript theme={null}
    // @morphllm/morphsdk uses Node.js builtins (fs, child_process) that are
    // incompatible with the Workers runtime. Use remoteCommands directly instead.
    import { getSandbox } from '@cloudflare/sandbox';

    const sandbox = getSandbox(env.Sandbox, 'code-search');

    // Clone repo (ripgrep is pre-installed via apt-get in the Dockerfile)
    await sandbox.exec('git clone --depth 1 https://github.com/example/repo.git /workspace/repo');

    const repoDir = '/workspace/repo';

    // Pass these remoteCommands to createWarpGrepTool() in your Node.js backend
    const remoteCommands = {
      grep: async (pattern, path, glob) => {
        let cmd = `rg --no-heading --line-number -C 1 '${pattern}' '${path}'`;
        if (glob) cmd += ` --glob '${glob}'`;
        const r = await sandbox.exec(cmd);
        return r.stdout;
      },
      read: async (path, start, end) => {
        const r = await sandbox.exec(`sed -n '${start},${end}p' '${path}'`);
        return r.stdout;
      },
      listDir: async (path, maxDepth) => {
        const r = await sandbox.exec(
          `find '${path}' -maxdepth ${maxDepth} -not -path '*/node_modules/*' -not -path '*/.git/*'`
        );
        return r.stdout;
      },
    };
    ```
  </Tab>

  <Tab title="Docker/SSH">
    ```typescript theme={null}
    import { NodeSSH } from 'node-ssh';
    import { MorphClient } from '@morphllm/morphsdk';

    const morph = new MorphClient({ apiKey: "YOUR_API_KEY" });
    const ssh = new NodeSSH();
    await ssh.connect({ host: 'your-server.com', username: 'user', privateKey: '...' });

    const repoDir = "/home/user/repo";

    const warpGrepSubagent = morph.anthropic.createWarpGrepTool({
      repoRoot: repoDir,
      remoteCommands: {
        grep: async (pattern, path) => {
          const result = await ssh.execCommand(
            `rg --no-heading --line-number '${pattern}' '${path}'`,
            { cwd: repoDir }
          );
          return result.stdout || '';
        },
        read: async (path, start, end) => {
          const result = await ssh.execCommand(`sed -n '${start},${end}p' '${path}'`);
          return result.stdout || '';
        },
        listDir: async (path, maxDepth) => {
          const result = await ssh.execCommand(`find '${path}' -maxdepth ${maxDepth}`);
          return result.stdout || '';
        },
      },
    });

    // Use with Anthropic
    const response = await anthropic.messages.create({
      model: 'claude-sonnet-4-5-20250929',
      tools: [warpGrepSubagent],
      messages: [{ role: 'user', content: 'Find authentication middleware' }]
    });
    ```
  </Tab>
</Tabs>

<Note>
  Your sandbox needs `ripgrep` (`rg`) installed for the grep function. Most sandbox providers support `apt-get install ripgrep` (Ubuntu/Debian). On Amazon Linux 2023 (Vercel Sandbox), download the [static binary](https://github.com/BurntSushi/ripgrep/releases) instead.
</Note>

## Configuration

```typescript theme={null}
const warpGrepSubagent = morph.openai.createWarpGrepTool({
  repoRoot: '.',
  excludes: ['dist', '*.test.ts'],
  includes: ['src/**/*.ts'],
});
```

| Option           | Default                    | Description                                                       |
| ---------------- | -------------------------- | ----------------------------------------------------------------- |
| `repoRoot`       | (required)                 | Root directory of the repository to search                        |
| `excludes`       | (see below)                | Glob patterns to exclude                                          |
| `includes`       | (all files)                | Glob patterns to include (e.g., `['src/**/*.ts', 'lib/**/*.js']`) |
| `name`           | `codebase_search`          | Tool name exposed to the LLM                                      |
| `description`    | (see SDK)                  | Tool description for the LLM                                      |
| `remoteCommands` | (local)                    | Functions for remote sandbox execution                            |
| `morphApiUrl`    | `https://api.morphllm.com` | Override API base URL                                             |
| `timeout`        | `30000`                    | Timeout in ms (also via `MORPH_WARP_GREP_TIMEOUT` env var)        |

### Default Excludes

WarpGrep excludes common non-source directories by default:

* **Dependencies:** `node_modules`, `bower_components`, `.pnpm`, `.yarn`, `vendor`, `Pods`, `.bundle`
* **Build output:** `dist`, `build`, `.next`, `.nuxt`, `out`, `target`, `.output`
* **Python:** `__pycache__`, `.pytest_cache`, `.mypy_cache`, `.ruff_cache`, `.venv`, `venv`, `site-packages`
* **Version control:** `.git`, `.svn`, `.hg`
* **Lock files, minified files, source maps, and common binary formats**

Pass `excludes` to override these defaults. Your list **replaces** the defaults entirely — it does not merge with them.

### Searching node\_modules

By default, `node_modules` is excluded. To search inside dependencies (e.g., debugging a library or finding how a package implements something), pass an empty excludes list:

```typescript theme={null}
const warpGrepSubagent = morph.anthropic.createWarpGrepTool({
  repoRoot: '.',
  excludes: [],
});
```

WarpGrep runs faster with less context to search over. Only include `node_modules` in your search when you need it. See the [node\_modules example](https://github.com/morphllm/examples/tree/main/warpgrep/search-node-modules).

### GitHub Search Options

`createGitHubSearchTool()` accepts:

| Option          | Default                    | Description              |
| --------------- | -------------------------- | ------------------------ |
| `morphApiKey`   | `MORPH_API_KEY` env var    | API key for Morph        |
| `morphApiUrl`   | `https://api.morphllm.com` | Override API base URL    |
| `codeSearchUrl` | `https://morphllm.com`     | Code storage service URL |
| `timeout`       | `30000`                    | Timeout in ms            |

## GitHub Search

Search public GitHub repositories. No local clone or ripgrep needed — Morph indexes the repo remotely. See the [GitHub search example](https://github.com/morphllm/examples/tree/main/warpgrep/github-search).

### Client API

```typescript theme={null}
const morph = new MorphClient({ apiKey: process.env.MORPH_API_KEY });

const result = await morph.warpGrep.searchGitHub({
  searchTerm: 'Find authentication middleware',
  github: 'vercel/next.js',       // or full URL: 'https://github.com/vercel/next.js'
  branch: 'canary',               // optional, defaults to repo's default branch
});

if (result.success) {
  for (const ctx of result.contexts) {
    console.log(`${ctx.file}: ${ctx.content}`);
  }
}
```

### Agent Tool

Each SDK adapter provides `createGitHubSearchTool()`:

```typescript theme={null}
// Same pattern as createWarpGrepTool — just swap the factory:
const githubTool = morph.anthropic.createGitHubSearchTool();
// Or: morph.openai.createGitHubSearchTool()
// Or: morph.vercel.createGitHubSearchTool()
```

Usage is identical to the [Quick Start](#quick-start) above — pass `githubTool` in your `tools` array.

<Tip>
  GitHub search returns the same `WarpGrepResult` format as local search.
</Tip>

## Remote Execution (Sandboxes)

When your code lives in a remote sandbox (E2B, Modal, Daytona, Docker), provide three functions that execute commands remotely. The SDK handles all parsing.

```typescript theme={null}
const warpGrepSubagent = morph.anthropic.createWarpGrepTool({
  repoRoot: '/home/user/repo',
  remoteCommands: {
    grep: async (pattern, path, glob) => {
      const cmd = `rg --no-heading --line-number --color never -C 1 ${glob ? `--glob '${glob}'` : ''} '${pattern}' '${path}'`;
      const r = await sandbox.run(cmd);
      return r.stdout;
    },
    read: async (path, start, end) => {
      const r = await sandbox.run(`sed -n '${start},${end}p' '${path}'`);
      return r.stdout;
    },
    listDir: async (path, maxDepth) => {
      const r = await sandbox.run(`find '${path}' -maxdepth ${maxDepth}`);
      return r.stdout;
    },
  },
});
```

Replace `sandbox.run(...)` with your provider's exec method (E2B's `sandbox.commands.run`, Modal's `sandbox.exec`, etc).

<Tip>
  The SDK parses the raw output for you:

  * `grep` expects ripgrep format (`path:line:content`) with `-C 1` context lines
  * `read` expects raw file content (SDK adds line numbers)
  * `listDir` expects one path per line (from `find` command)

  Your sandbox needs `ripgrep` (`rg`) installed. Most providers support `apt-get install ripgrep`.
</Tip>

## API Reference

**Input** (`WarpGrepInput`):

```typescript theme={null}
{
  searchTerm: string,         // Natural language search query
  repoRoot: string,           // Root directory to search
  excludes?: string[],        // Glob patterns to exclude
  includes?: string[],        // Glob patterns to include
  streamSteps?: boolean,      // Stream progress (see Streaming page)
  provider?: WarpGrepProvider, // Custom file system provider (see Custom Providers page)
  remoteCommands?: {          // For sandbox environments
    grep: (pattern, path, glob?) => Promise<string>,
    read: (path, start, end) => Promise<string>,
    listDir: (path, maxDepth) => Promise<string>,
  },
}
```

**Returns** (`WarpGrepResult`):

```typescript theme={null}
{
  success: boolean,
  contexts?: Array<{
    file: string,     // File path relative to repo root
    content: string,  // Relevant code section
  }>,
  summary?: string,   // Summary of findings
  error?: string,     // Error message if failed
}
```

**Tool methods** (when using as agent tool):

```typescript theme={null}
// Execute the tool with the LLM's input
const result = await warpGrepSubagent.execute(toolInput);

// Format the result as a string to send back to the LLM
// (converts the structured result into a readable text block)
const formatted = warpGrepSubagent.formatResult(result);

```

## Error Handling

```typescript theme={null}
const result = await warpGrepSubagent.execute(toolUse.input);

if (!result.success) {
  console.error(result.error);
  // Common errors:
  // - "Search did not complete" — the model did not call finish within 4 turns
  // - "API error" — authentication or network issue
  // - "timeout" — search took longer than the configured timeout
}
```

## Type Exports

```typescript theme={null}
import {
  warpGrepInputSchema,
  executeToolCall,
  WARP_GREP_TOOL_NAME,
  WARP_GREP_DESCRIPTION,
} from '@morphllm/morphsdk/tools/warp-grep';

import type {
  WarpGrepResult,
  WarpGrepContext,
  WarpGrepInput,
  WarpGrepToolConfig,
  GitHubSearchInput,
  GitHubSearchToolConfig,
  RemoteCommands,
} from '@morphllm/morphsdk';
```
