Skip to main content
Coming Soon — The GitHub SDK is currently in development. Connect your GitHub account now in the Morph Dashboard to be ready when it launches.
Access your GitHub repositories, pull requests, and deployments through your Morph API key. Perfect for building PR preview testing workflows, code review automation, and CI/CD integrations.

Quick Start

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

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

// List your GitHub installations
const installations = await morph.github.installations.list();
// [{ id: "12345", accountLogin: "acme", accountType: "Organization" }]

// List repos for an installation
const repos = await morph.github.repos.list({ installationId: "12345" });

// Get PR with full context
const pr = await morph.github.pullRequests.get({
  owner: "acme",
  repo: "app",
  number: 42
});

console.log(pr.title);  // "Add user authentication"
console.log(pr.diff);   // Full unified diff
console.log(pr.files);  // Array of changed files

Why Use the GitHub SDK?

The GitHub SDK is designed for agent workflows — particularly combining GitHub context with browser automation:
  • Get PR context (diff, files, metadata) to understand what changed
  • Find preview deployments (Vercel, Netlify, Cloudflare) for a PR
  • Post test results as comments with videos/screenshots
  • Set CI status with check runs

Setup

1. Connect GitHub

Go to morphllm.com/dashboard/integrations/github and install the Morph GitHub App on your account or organization.

2. Use the SDK

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

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

// Your GitHub installations are now accessible
const installations = await morph.github.installations.list();

Core Operations

List Installations

See which GitHub accounts are connected to your Morph account:
const installations = await morph.github.installations.list();

console.log(installations);
// [
//   { id: "12345", accountLogin: "acme", accountType: "Organization" },
//   { id: "67890", accountLogin: "john-doe", accountType: "User" }
// ]

List Repositories

List repos accessible to a specific installation:
const repos = await morph.github.repos.list({
  installationId: "12345"
});

// [{ id: 123, name: "app", fullName: "acme/app", private: true }]

Get Pull Request Context

The get method returns the full PR context including the unified diff and changed files:
const pr = await morph.github.pullRequests.get({
  owner: "acme",
  repo: "app",
  number: 42
});

console.log(pr.title);      // "Add user authentication"
console.log(pr.body);       // PR description
console.log(pr.author);     // "john-doe"
console.log(pr.headSha);    // "abc123..."
console.log(pr.baseBranch); // "main"
console.log(pr.headBranch); // "feature/auth"

// Full diff for agent context
console.log(pr.diff);
// --- a/src/auth.ts
// +++ b/src/auth.ts
// @@ -1,5 +1,10 @@
// ...

// Per-file changes
pr.files.forEach(file => {
  console.log(`${file.filename}: +${file.additions}/-${file.deletions}`);
  console.log(file.patch); // Per-file diff
});

Find Preview Deployments

Get deployments for a PR’s head SHA to find preview URLs:
const deployments = await morph.github.deployments.list({
  owner: "acme",
  repo: "app",
  sha: pr.headSha
});

const preview = deployments.find(d =>
  d.environment === "preview" && d.state === "success"
);

console.log(preview?.url); // "https://app-pr-42.vercel.app"

Post Comments

Post test results, feedback, or status updates to PRs:
// Create a comment
const comment = await morph.github.comments.create({
  owner: "acme",
  repo: "app",
  pr: 42,
  body: `## 🤖 Preview Test Results

✅ All tests passed!

![Recording](https://...)

---
<sub>Automated testing by [Morph](https://morphllm.com)</sub>`
});

// Update the comment later
await morph.github.comments.update({
  owner: "acme",
  repo: "app",
  commentId: comment.id,
  body: "Updated content..."
});

// Delete if needed
await morph.github.comments.delete({
  owner: "acme",
  repo: "app",
  commentId: comment.id
});

Manage Check Runs

Create and update GitHub check runs for CI status:
// Create a check run (shows as "in progress" on PR)
const checkRun = await morph.github.checkRuns.create({
  owner: "acme",
  repo: "app",
  sha: pr.headSha,
  name: "Preview Test",
  status: "in_progress",
  title: "Testing preview deployment...",
  summary: "Running automated browser tests"
});

// Update with success
await morph.github.checkRuns.update({
  owner: "acme",
  repo: "app",
  checkRunId: checkRun.id,
  conclusion: "success",
  title: "✅ Preview test passed",
  summary: "All tests completed successfully"
});

// Or update with failure
await morph.github.checkRuns.update({
  owner: "acme",
  repo: "app",
  checkRunId: checkRun.id,
  conclusion: "failure",
  title: "❌ Preview test failed",
  summary: "3 tests failed",
  text: "## Failed Tests\n\n- Login flow broken\n- Cart not persisting"
});

Full Example: PR Preview Testing

Combine GitHub SDK with Browser automation for end-to-end PR testing:
import { MorphClient } from '@morphllm/morphsdk';

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

async function testPRPreview(owner: string, repo: string, prNumber: number) {
  // 1. Get PR context
  const pr = await morph.github.pullRequests.get({
    owner, repo, number: prNumber
  });

  // 2. Create check run
  const checkRun = await morph.github.checkRuns.create({
    owner, repo,
    sha: pr.headSha,
    name: "Morph Preview Test",
    status: "in_progress",
    title: "Testing preview...",
    summary: "Running automated browser tests"
  });

  // 3. Find preview deployment
  const deployments = await morph.github.deployments.list({
    owner, repo, sha: pr.headSha
  });

  const preview = deployments.find(d =>
    d.state === "success" && d.url
  );

  if (!preview) {
    await morph.github.checkRuns.update({
      owner, repo,
      checkRunId: checkRun.id,
      conclusion: "skipped",
      title: "No preview found",
      summary: "No preview deployment available for this PR"
    });
    return;
  }

  // 4. Run browser test with PR context
  const task = await morph.browser.createTask({
    url: preview.url,
    diff: pr.diff,  // Agent uses diff to understand what to test
    task: "Test the changes in this PR",
    recordVideo: true
  });

  // 5. Wait for results
  const recording = await morph.browser.waitForRecording(task.recordingId);
  const webp = await recording.getWebp({ maxSizeMb: 5 });

  // 6. Update check run
  const success = !recording.error;
  await morph.github.checkRuns.update({
    owner, repo,
    checkRunId: checkRun.id,
    conclusion: success ? "success" : "failure",
    title: success ? "✅ Preview test passed" : "❌ Preview test failed",
    summary: recording.result || "Test completed"
  });

  // 7. Post comment with video
  await morph.github.comments.create({
    owner, repo, pr: prNumber,
    body: `## 🤖 Morph Preview Test

**Preview URL:** ${preview.url}

### Result
${recording.result || "Test completed"}

### Recording
![Recording](${webp.webpUrl})

[View full session →](https://morphllm.com/dashboard/browser-sessions/${task.recordingId})

---
<sub>Automated testing by [Morph](https://morphllm.com)</sub>`
  });

  return { success, recordingId: task.recordingId };
}

Using with Multiple Installations

If you have multiple GitHub accounts connected, specify the installation ID per-request:
// List all installations
const installations = await morph.github.installations.list();

// Use a specific installation for all requests
const repos = await morph.github.repos.list({
  installationId: installations[0].id
});

const pr = await morph.github.pullRequests.get({
  owner: "acme",
  repo: "app",
  number: 42,
  installationId: installations[0].id  // Optional, uses first if not specified
});
Or set a default installation in the client config:
const morph = new MorphClient({
  apiKey: process.env.MORPH_API_KEY,
  github: { installationId: "12345" }  // Default for all GitHub operations
});

API Reference

Installations

MethodDescription
installations.list()List all GitHub installations for your account
installations.get(id)Get details of a specific installation

Repositories

MethodDescription
repos.list({ installationId })List repositories accessible to an installation

Pull Requests

MethodDescription
pullRequests.list({ owner, repo, state? })List pull requests in a repository
pullRequests.get({ owner, repo, number })Get a PR with full context (diff, files)

Deployments

MethodDescription
deployments.list({ owner, repo, sha?, environment? })List deployments for a repository

Comments

MethodDescription
comments.list({ owner, repo, pr })List comments on a pull request
comments.create({ owner, repo, pr, body })Create a comment
comments.update({ owner, repo, commentId, body })Update an existing comment
comments.delete({ owner, repo, commentId })Delete a comment

Check Runs

MethodDescription
checkRuns.create({ owner, repo, sha, name, status, title?, summary? })Create a check run
checkRuns.update({ owner, repo, checkRunId, conclusion?, title?, summary?, text? })Update a check run

Types

interface Installation {
  id: string;
  accountLogin: string;
  accountType: "User" | "Organization";
  displayName?: string;
}

interface Repo {
  id: number;
  name: string;
  fullName: string;
  private: boolean;
  defaultBranch?: string;
}

interface PullRequestWithContext {
  number: number;
  title: string;
  body: string | null;
  state: "open" | "closed";
  author: string;
  headSha: string;
  baseBranch: string;
  headBranch: string;
  diff: string;
  files: FileChange[];
  createdAt: string;
  updatedAt: string;
}

interface FileChange {
  filename: string;
  status: "added" | "removed" | "modified" | "renamed";
  additions: number;
  deletions: number;
  patch?: string;
}

interface Deployment {
  id: number;
  sha: string;
  environment: string;
  state: "pending" | "success" | "failure" | "error" | "inactive";
  url: string | null;
  createdAt: string;
}

interface Comment {
  id: number;
  body: string;
  author: string;
  createdAt: string;
  updatedAt: string;
}

interface CheckRun {
  id: number;
  name: string;
  status: "queued" | "in_progress" | "completed";
  conclusion?: "success" | "failure" | "neutral" | "cancelled" | "skipped" | "timed_out" | "action_required";
}

Error Handling

import { GitHubError, NoInstallationError, NotFoundError } from '@morphllm/morphsdk';

try {
  const pr = await morph.github.pullRequests.get({
    owner: "acme", repo: "app", number: 9999
  });
} catch (error) {
  if (error instanceof NotFoundError) {
    console.log("PR not found");
  } else if (error instanceof NoInstallationError) {
    console.log("Connect GitHub at morphllm.com/dashboard/integrations/github");
  } else if (error instanceof GitHubError) {
    console.log(`GitHub error: ${error.message} (${error.status})`);
  }
}

FAQ

Go to morphllm.com/dashboard/integrations/github and install the Morph GitHub App on your account or organization. You can select specific repositories or grant access to all repos.
Yes! When you install the GitHub App, you can choose to install it on your personal account or any organization you have admin access to. Each installation appears separately in installations.list().
The SDK never exposes your GitHub installation tokens. All GitHub API calls are proxied through Morph’s servers, where tokens are stored securely. You only use your Morph API key.
The Morph GitHub App requests:
  • Read access to code, metadata, and deployments
  • Write access to issues/PRs (for comments) and checks (for CI status)
We request the minimum permissions needed for PR testing workflows.
The GitHub SDK is designed to work seamlessly with Browser automation:
  1. Get PR context with pullRequests.get()
  2. Pass the diff to browser.createTask() for intelligent testing
  3. Post results with comments.create() and checkRuns.update()
See the Full Example above.