Skip to main content

MorphClient

Unified client for all Morph tools.
import { MorphClient } from '@morphllm/morphsdk';

const morph = new MorphClient({
  apiKey?: string;          // Default: process.env.MORPH_API_KEY
  debug?: boolean;          // Default: false (enables logging)
  timeout?: number;         // Default: varies by tool
  retryConfig?: RetryConfig; // Optional retry configuration
});

Namespaces

morph.fastApply         // FastApplyClient
morph.codebaseSearch    // CodebaseSearchClient
morph.browser           // BrowserClient
morph.git               // MorphGit

Standalone Clients (Advanced)

Need custom configuration per tool? Use individual clients:
import { BrowserClient, FastApplyClient } from '@morphllm/morphsdk';

// Browser with custom API URL
const browser = new BrowserClient({
  apiKey: process.env.MORPH_API_KEY,
  apiUrl: 'http://localhost:8000',  // Custom endpoint
  timeout: 180000
});

// FastApply with different settings
const fastApply = new FastApplyClient({
  apiKey: process.env.MORPH_API_KEY,
  timeout: 60000
});
Use when: You need tool-specific configuration that differs from defaults (custom URLs, different timeouts, etc.).

Fast Apply

morph.fastApply.execute(input, overrides?)

Edit files with AI-powered merge.
const result = await morph.fastApply.execute({
  target_filepath: 'src/auth.ts',
  baseDir: './my-project',      // Optional: defaults to cwd
  instructions: 'Add error handling',
  code_edit: '// ... existing code ...\nif (!user) throw new Error("Invalid");\n// ... existing code ...'
}, {
  // Optional overrides
  generateUdiff: true,
  autoWrite: true,
  timeout: 60000
});

console.log(result.udiff);
console.log(result.changes);  // { linesAdded, linesRemoved, linesModified }

Framework Adapters

import { createEditFileTool } from '@morphllm/morphsdk/tools/fastapply/anthropic';

const tool = createEditFileTool(morph.fastApply);
// OR with config: createEditFileTool({ morphApiKey: '...' })

Types

interface EditFileInput {
  target_filepath: string;
  instructions: string;
  code_edit: string;
}

interface EditFileResult {
  success: boolean;
  filepath: string;
  udiff?: string;
  changes: {
    linesAdded: number;
    linesRemoved: number;
    linesModified: number;
  };
  error?: string;
}

morph.codebaseSearch.search(input, overrides?)

Semantic code search with 2-stage retrieval.
const result = await morph.codebaseSearch.search({
  query: 'How does user authentication work?',
  repoId: 'my-project',             // Required per search
  target_directories: ['src/auth'], // or [] for entire repo
  explanation: 'Finding auth logic',
  limit: 10
}, {
  // Optional overrides
  timeout: 60000,
  searchUrl: 'https://custom-search.example.com'
});

console.log(result.results);  // Top 10 code chunks
console.log(result.stats);    // { totalResults, candidatesRetrieved, searchTimeMs }

Framework Adapters

import { createCodebaseSearchTool } from '@morphllm/morphsdk/tools/codebase-search/anthropic';

const tool = createCodebaseSearchTool({ 
  client: morph.codebaseSearch, 
  repoId: 'my-project' 
});
// OR with config: createCodebaseSearchTool({ repoId: 'my-project' })

Types

interface CodebaseSearchInput {
  query: string;
  target_directories: string[];
  explanation: string;
  limit?: number;  // Default: 10
}

interface CodeSearchResult {
  filepath: string;              // "auth.ts::AuthService.login@L10-L25"
  symbolPath: string;            // "AuthService.login"
  content: string;               // Function/class code
  language: string;              // "typescript"
  startLine: number;
  endLine: number;
  embeddingSimilarity: number;   // 0-1
  rerankScore: number;           // 0-1 (higher = more relevant)
}

interface CodebaseSearchResult {
  success: boolean;
  results: CodeSearchResult[];
  stats: {
    totalResults: number;
    candidatesRetrieved: number;
    searchTimeMs: number;
  };
  error?: string;
}
Requires git push: Code must be pushed with MorphGit to generate embeddings before searching.

Git Operations

morph.git.*

Access the MorphGit client via morph.git.
// All standard git operations available
await morph.git.init({ repoId: 'my-project', dir: './project' });
await morph.git.clone({ repoId: 'my-project', dir: './project' });
await morph.git.add({ dir: './project', filepath: '.' });
await morph.git.commit({ dir: './project', message: 'Update' });
await morph.git.push({ dir: './project' });
await morph.git.pull({ dir: './project' });

Repository Management

// Initialize new repository
await morph.git.init({
  repoId: string;
  dir: string;
  defaultBranch?: string;  // Default: 'main'
});

// Clone existing repository
await morph.git.clone({
  repoId: string;
  dir: string;
  branch?: string;
  depth?: number;
  singleBranch?: boolean;  // Default: true
});

Basic Operations

// Stage files
await morph.git.add({
  dir: string;
  filepath: string;  // Use '.' for all files
});

// Commit changes
const sha = await morph.git.commit({
  dir: string;
  message: string;
  author?: { name: string; email: string; };
});

// Push to remote (triggers auto-embedding)
await morph.git.push({
  dir: string;
  remote?: string;   // Default: 'origin'
  branch?: string;
});

// Pull from remote
await morph.git.pull({
  dir: string;
  remote?: string;
  branch?: string;
});

Status & History

// Get file status
const status = await morph.git.status({
  dir: string;
  filepath: string;
});
// Returns: 'modified' | '*added' | 'deleted' | 'unmodified' | 'absent'

// Get all file statuses
const matrix = await morph.git.statusMatrix({ dir: string });
// Returns: { filepath: string; status: string; }[]

// Get commit history
const commits = await morph.git.log({
  dir: string;
  depth?: number;
  ref?: string;
});

Branching

// Create branch
await morph.git.branch({
  dir: string;
  name: string;
  checkout?: boolean;  // Default: false
});

// Checkout branch/commit
await morph.git.checkout({
  dir: string;
  ref: string;
});

// List all branches
const branches = await morph.git.listBranches({ dir: string });

// Get current branch
const current = await morph.git.currentBranch({ dir: string });

// Get commit hash
const hash = await morph.git.resolveRef({ dir: string; ref: 'HEAD' });
Auto-embedding on push: Every git.push() triggers automatic embedding generation for semantic search (~8 seconds in background).

Browser Automation

morph.browser.execute(input)

Execute browser automation tasks.
const result = await morph.browser.execute({
  task: 'Test login with email test@example.com',
  url: 'https://3000-xyz.e2b.dev',
  max_steps: 15,
  model: 'morph-computer-use-v0',  // or 'gemini-flash-latest'
  record_video: false,
  viewport_width: 1280,
  viewport_height: 720
});

console.log(result.success);
console.log(result.result);
console.log(result.steps_taken);

morph.browser.executeWithRecording(input)

Execute with recording and wait for video to be ready.
const result = await morph.browser.executeWithRecording({
  task: 'Test checkout flow',
  url: 'https://staging.myapp.com',
  max_steps: 20,
  record_video: true,
  video_width: 1280,
  video_height: 720,
  repo_id: 'my-app',         // Optional
  commit_id: 'commit-uuid'   // Optional
});

// Video is ready!
console.log(result.recording?.video_url);
console.log(result.recording?.file_size);
console.log(result.recording?.status);  // 'COMPLETED'

Recording Management

// Get recording status
const recording = await morph.browser.getRecording(recordingId);

console.log(recording.status);      // PENDING | PROCESSING | COMPLETED | ERROR
console.log(recording.video_url);   // Presigned S3 URL (7 days)
console.log(recording.replay_url);  // rrweb replay URL
console.log(recording.network_url); // Network logs
console.log(recording.console_url); // Console logs

// Wait for recording to complete
const recording = await morph.browser.waitForRecording(
  recordingId,
  { timeout: 60000, pollInterval: 2000 }
);

// Get console errors with screenshots
const { errors, total_errors } = await morph.browser.getErrors(recordingId);

console.log(total_errors);
errors.forEach(e => {
  console.log(e.type);           // 'console' | 'network'
  console.log(e.message);
  console.log(e.screenshot_url); // Screenshot captured 500ms after error
  console.log(e.url);            // Page URL where error occurred
});

Framework Adapters

import { createBrowserTool } from '@morphllm/morphsdk/tools/browser/anthropic';

const tool = createBrowserTool(morph.browser);
// OR with config: createBrowserTool({ apiKey: '...' })

Types

interface BrowserTaskInput {
  task: string;
  url?: string;
  max_steps?: number;         // 1-50, default: 10
  record_video?: boolean;     // Default: false
  video_width?: number;       // Default: 1280
  video_height?: number;      // Default: 720
  viewport_width?: number;    // Default: 1280
  viewport_height?: number;   // Default: 720
  repo_id?: string;
  commit_id?: string;
}

interface BrowserTaskResult {
  success: boolean;
  result?: string;
  steps_taken?: number;
  execution_time_ms?: number;
  recording_id?: string;
  recording_status?: string;
  replay_url?: string;
  error?: string;
}

interface RecordingStatus {
  id: string;
  status: 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'ERROR';
  video_url?: string;         // Presigned S3 URL (expires in 7 days)
  replay_url?: string;        // rrweb replay
  file_size?: number;
  total_events?: number;
  created_at: string;
  completed_at?: string;
  error?: string;
}
Video limits: Videos are limited to 3 minutes. Presigned URLs expire after 7 days. Videos auto-delete after 30 days.

Environment Variables

# Required for most tools
MORPH_API_KEY=sk-your-key-here

# Optional overrides (advanced users only)
MORPH_API_URL=https://api.morphllm.com      # Fast Apply API
MORPH_SEARCH_URL=http://embedrerank.morphllm.com:8081  # Search API
MORPH_ENVIRONMENT=DEV                        # Use localhost for browser worker
Get your API key: morphllm.com/dashboard/api-keys

Import Patterns

// Unified client
import { MorphClient } from '@morphllm/morphsdk';

// Individual clients (for advanced use)
import { 
  BrowserClient, 
  FastApplyClient, 
  CodebaseSearchClient,
  MorphGit 
} from '@morphllm/morphsdk';

// All types
import type { 
  BrowserTaskInput,
  EditFileInput,
  CodebaseSearchInput,
  // ... etc
} from '@morphllm/morphsdk';

Framework Adapters

// Anthropic
import { createEditFileTool } from '@morphllm/morphsdk/tools/fastapply/anthropic';
import { createCodebaseSearchTool } from '@morphllm/morphsdk/tools/codebase-search/anthropic';
import { createBrowserTool } from '@morphllm/morphsdk/tools/browser/anthropic';

// OpenAI
import { createEditFileTool } from '@morphllm/morphsdk/tools/fastapply/openai';
import { createCodebaseSearchTool } from '@morphllm/morphsdk/tools/codebase-search/openai';
import { createBrowserTool } from '@morphllm/morphsdk/tools/browser/openai';

// Vercel
import { createEditFileTool } from '@morphllm/morphsdk/tools/fastapply/vercel';
import { createCodebaseSearchTool } from '@morphllm/morphsdk/tools/codebase-search/vercel';
import { createBrowserTool } from '@morphllm/morphsdk/tools/browser/vercel';

Error Handling

All tools return results with success: boolean and optional error: string.
const result = await morph.fastApply.execute({ ... });

if (!result.success) {
  console.error('Edit failed:', result.error);
  // Handle error...
}

const searchResults = await morph.codebaseSearch.search({ ... });
if (!searchResults.success) {
  console.error('Search failed:', searchResults.error);
}
Automatic retries: SDK automatically retries failed requests with exponential backoff for transient errors (rate limits, timeouts).

Next Steps