Beta — The GitHub SDK is available in beta. Expect occasional rough edges, and please email founders@MorphLM with any bugs or issues.
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. installationId is required unless you set a default in the client config.
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 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!

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

[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
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
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
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
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
Can I use this with organization 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().
Is my GitHub token stored securely?
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.
What permissions does the GitHub App need?
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.
How do I use this with Browser automation?
The GitHub SDK is designed to work seamlessly with Browser automation:
Get PR context with pullRequests.get()
Pass the diff to browser.createTask() for intelligent testing
Post results with comments.create() and checkRuns.update()
See the Full Example above.