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

# GitHub SDK

> Access PR context, post comments, and manage check runs through your connected GitHub account

<Note>
  **Beta** — The GitHub SDK is available in beta. Expect occasional rough edges, and please email founders\@MorphLM with any bugs or issues.
</Note>

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

```typescript theme={null}
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](https://morphllm.com/dashboard/integrations/github) and install the Morph GitHub App on your account or organization.

### 2. Use the SDK

```typescript theme={null}
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:

```typescript theme={null}
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. `installationId` is required unless you set a default in the client config.

```typescript theme={null}
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:

```typescript theme={null}
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:

```typescript theme={null}
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:

```typescript theme={null}
// 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:

```typescript theme={null}
// 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:

```typescript theme={null}
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:

```typescript theme={null}
// 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:

```typescript theme={null}
const morph = new MorphClient({
  apiKey: process.env.MORPH_API_KEY,
  github: { installationId: "12345" }  // Default for all GitHub operations
});
```

## API Reference

### Installations

| Method                  | Description                                    |
| ----------------------- | ---------------------------------------------- |
| `installations.list()`  | List all GitHub installations for your account |
| `installations.get(id)` | Get details of a specific installation         |

### Repositories

| Method                           | Description                                     |
| -------------------------------- | ----------------------------------------------- |
| `repos.list({ installationId })` | List repositories accessible to an installation |

### Pull Requests

| Method                                                        | Description                              |
| ------------------------------------------------------------- | ---------------------------------------- |
| `pullRequests.list({ owner, repo, state?, installationId? })` | List pull requests in a repository       |
| `pullRequests.get({ owner, repo, number, installationId? })`  | Get a PR with full context (diff, files) |

### Deployments

| Method                                                                   | Description                       |
| ------------------------------------------------------------------------ | --------------------------------- |
| `deployments.list({ owner, repo, sha?, environment?, installationId? })` | List deployments for a repository |

### Comments

| Method                                                               | Description                     |
| -------------------------------------------------------------------- | ------------------------------- |
| `comments.list({ owner, repo, pr, installationId? })`                | List comments on a pull request |
| `comments.create({ owner, repo, pr, body, installationId? })`        | Create a comment                |
| `comments.update({ owner, repo, commentId, body, installationId? })` | Update an existing comment      |
| `comments.delete({ owner, repo, commentId, installationId? })`       | Delete a comment                |

### Check Runs

| Method                                                                                                          | Description        |
| --------------------------------------------------------------------------------------------------------------- | ------------------ |
| `checkRuns.create({ owner, repo, sha, name, status, title?, summary?, installationId? })`                       | Create a check run |
| `checkRuns.update({ owner, repo, checkRunId, status?, conclusion?, title?, summary?, text?, installationId? })` | Update a check run |

## Types

```typescript theme={null}
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" | "in_progress" | "queued";
  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";
  startedAt?: string;
  completedAt?: string;
}
```

## Error Handling

```typescript theme={null}
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

<AccordionGroup>
  <Accordion title="How do I connect GitHub?" icon="plug">
    Go to [morphllm.com/dashboard/integrations/github](https://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.
  </Accordion>

  <Accordion title="Can I use this with organization repos?" icon="building">
    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()`.
  </Accordion>

  <Accordion title="Is my GitHub token stored securely?" icon="lock">
    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.
  </Accordion>

  <Accordion title="What permissions does the GitHub App need?" icon="shield">
    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.
  </Accordion>

  <Accordion title="How do I use this with Browser automation?" icon="browser">
    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](#full-example-pr-preview-testing) above.
  </Accordion>
</AccordionGroup>
