Labsco
vercel-labs logo

github

1,500

by vercel · part of vercel-labs/emulate

Emulated GitHub REST API for local development and testing. Use when the user needs to interact with GitHub API endpoints locally, test GitHub integrations,…

🔥🔥🔥✓ VerifiedFreeAdvanced setup
🧰 Not standalone. This skill ships with vercel-labs/emulate and only works together with that tool — install the tool first, then add this skill.

Emulated GitHub REST API for local development and testing. Use when the user needs to interact with GitHub API endpoints locally, test GitHub integrations,…

Inspect the full instructions your agent will receiveExpand

This is the exact playbook injected into your agent when the skill activates — shown here so you can audit it before installing. You don't need to read it to use the skill.

by vercel

Emulated GitHub REST API for local development and testing. Use when the user needs to interact with GitHub API endpoints locally, test GitHub integrations,… npx skills add https://github.com/vercel-labs/emulate --skill github Download ZIPGitHub1.5k

GitHub API Emulator

Fully stateful GitHub REST API emulation. Creates, updates, and deletes persist in memory and affect related entities.

Start

Copy & paste — that's it
# GitHub only
npx emulate --service github

# Default port
# http://localhost:4001

Or programmatically:

Copy & paste — that's it
import { createEmulator } from 'emulate'

const github = await createEmulator({ service: 'github', port: 4001 })
// github.url === 'http://localhost:4001'

Auth

Pass tokens as Authorization: Bearer <token> or Authorization: token <token>.

Copy & paste — that's it
curl http://localhost:4001/user \
 -H "Authorization: Bearer test_token_admin"

Public repo endpoints work without auth. Private repos and write operations require a valid token. When no token is provided, requests fall back to the first seeded user.

GitHub App JWT

Configure apps in the seed config with a private key. Sign a JWT with { iss: "<app_id>" } using RS256. The emulator verifies the signature and resolves the app.

Copy & paste — that's it
github:
 apps:
 - app_id: 12345
 slug: my-github-app
 name: My GitHub App
 private_key: |
 -----BEGIN RSA PRIVATE KEY-----
 ...
 -----END RSA PRIVATE KEY-----
 permissions:
 contents: read
 issues: write
 events: [push, pull_request]
 webhook_url: http://localhost:8080/github/webhook
 webhook_secret: my-webhook-secret
 description: My CI/CD bot
 installations:
 - installation_id: 100
 account: my-org
 repository_selection: all
 permissions:
 contents: read
 events: [push]
 repositories: [my-org/org-repo]

Pointing Your App at the Emulator

Environment Variable

Copy & paste — that's it
GITHUB_EMULATOR_URL=http://localhost:4001

Octokit

Copy & paste — that's it
import { Octokit } from '@octokit/rest'

const octokit = new Octokit({
 baseUrl: process.env.GITHUB_EMULATOR_URL ?? 'https://api.github.com',
 auth: 'test_token_admin',
})

OAuth URL Mapping

Real GitHub URL Emulator URL https://github.com/login/oauth/authorize $GITHUB_EMULATOR_URL/login/oauth/authorize https://github.com/login/oauth/access_token $GITHUB_EMULATOR_URL/login/oauth/access_token https://api.github.com/user $GITHUB_EMULATOR_URL/user

Auth.js / NextAuth.js

Copy & paste — that's it
import GitHub from '@auth/core/providers/github'

GitHub({
 clientId: process.env.GITHUB_CLIENT_ID,
 clientSecret: process.env.GITHUB_CLIENT_SECRET,
 authorization: {
 url: `${process.env.GITHUB_EMULATOR_URL}/login/oauth/authorize`,
 },
 token: {
 url: `${process.env.GITHUB_EMULATOR_URL}/login/oauth/access_token`,
 },
 userinfo: {
 url: `${process.env.GITHUB_EMULATOR_URL}/user`,
 },
})

Seed Config

Copy & paste — that's it
tokens:
 test_token_admin:
 login: admin
 scopes: [repo, user, admin:org, admin:repo_hook]

github:
 users:
 - login: octocat
 name: The Octocat
 email: [email protected]
 bio: I am the Octocat
 company: GitHub
 location: San Francisco
 blog: https://github.blog
 twitter_username: github
 site_admin: false
 orgs:
 - login: my-org
 name: My Organization
 description: A test organization
 email: [email protected]
 repos:
 - owner: octocat
 name: hello-world
 description: My first repository
 language: JavaScript
 topics: [hello, world]
 default_branch: main
 private: false
 - owner: my-org
 name: org-repo
 description: An organization repository
 language: TypeScript
 oauth_apps:
 - client_id: Iv1.abc123
 client_secret: secret_abc123
 name: My Web App
 redirect_uris:
 - http://localhost:3000/api/auth/callback/github

Repos are auto-initialized with a commit, branch, and README unless auto_init: false is set.

Pagination

All list endpoints support page and per_page query params with Link headers:

Copy & paste — that's it
curl "http://localhost:4001/repos/octocat/hello-world/issues?page=1&per_page=10" \
 -H "Authorization: Bearer $TOKEN"

API Endpoints

Users

Copy & paste — that's it
# Authenticated user
curl http://localhost:4001/user -H "Authorization: Bearer $TOKEN"

# Update profile
curl -X PATCH http://localhost:4001/user \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"bio": "Hello!"}'

# Get user by username
curl http://localhost:4001/users/octocat

# List users
curl http://localhost:4001/users

# User repos / orgs / followers / following
curl http://localhost:4001/users/octocat/repos
curl http://localhost:4001/users/octocat/orgs
curl http://localhost:4001/users/octocat/followers
curl http://localhost:4001/users/octocat/following

# User hovercard
curl http://localhost:4001/users/octocat/hovercard

# User emails
curl http://localhost:4001/user/emails -H "Authorization: Bearer $TOKEN"

Repositories

Copy & paste — that's it
# Get repo
curl http://localhost:4001/repos/octocat/hello-world

# Create user repo
curl -X POST http://localhost:4001/user/repos \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"name": "new-repo", "private": false}'

# Create org repo
curl -X POST http://localhost:4001/orgs/my-org/repos \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"name": "org-project"}'

# Update repo
curl -X PATCH http://localhost:4001/repos/octocat/hello-world \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"description": "Updated description"}'

# Delete repo (cascades issues, PRs, etc.)
curl -X DELETE http://localhost:4001/repos/octocat/hello-world \
 -H "Authorization: Bearer $TOKEN"

# Topics, languages, contributors, forks, collaborators, tags, transfer

Issues

Copy & paste — that's it
# List issues (filter by state, labels, assignee, milestone, creator, since)
curl "http://localhost:4001/repos/octocat/hello-world/issues?state=open&labels=bug"

# Create issue
curl -X POST http://localhost:4001/repos/octocat/hello-world/issues \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"title": "Bug report", "body": "Details here", "labels": ["bug"]}'

# Get / update / lock / unlock / timeline / events / assignees

Pull Requests

Copy & paste — that's it
# List PRs
curl "http://localhost:4001/repos/octocat/hello-world/pulls?state=open"

# Create PR
curl -X POST http://localhost:4001/repos/octocat/hello-world/pulls \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"title": "Feature", "head": "feature-branch", "base": "main"}'

# Merge PR (enforces branch protection)
curl -X PUT http://localhost:4001/repos/octocat/hello-world/pulls/1/merge \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"merge_method": "squash"}'

# Commits, files, requested reviewers, update branch

Comments

Copy & paste — that's it
# Issue comments: full CRUD
curl http://localhost:4001/repos/octocat/hello-world/issues/1/comments
curl -X POST http://localhost:4001/repos/octocat/hello-world/issues/1/comments \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"body": "Looks good!"}'

# Comment by ID (cross-resource)
curl http://localhost:4001/repos/octocat/hello-world/issues/comments/1

# PR review comments
curl http://localhost:4001/repos/octocat/hello-world/pulls/1/comments

# Commit comments
curl http://localhost:4001/repos/octocat/hello-world/commits/abc123/comments

# Repo-wide comment listings
curl http://localhost:4001/repos/octocat/hello-world/issues/comments
curl http://localhost:4001/repos/octocat/hello-world/pulls/comments
curl http://localhost:4001/repos/octocat/hello-world/comments

Reviews

Copy & paste — that's it
# List / create / get / update / submit / dismiss reviews
curl http://localhost:4001/repos/octocat/hello-world/pulls/1/reviews
curl -X POST http://localhost:4001/repos/octocat/hello-world/pulls/1/reviews \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"event": "APPROVE", "body": "LGTM"}'

Labels & Milestones

Full CRUD for labels and milestones. Add/remove labels from issues, replace all labels. List labels for a milestone.

Branches & Git Data

Copy & paste — that's it
# List branches
curl http://localhost:4001/repos/octocat/hello-world/branches

# Branch protection CRUD (status checks, PR reviews, enforce admins)
curl -X PUT http://localhost:4001/repos/octocat/hello-world/branches/main/protection \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"required_status_checks": {"strict": true, "contexts": ["ci"]}}'

# Refs, commits, trees (recursive), blobs, tags, matching-refs

Organizations & Teams

Copy & paste — that's it
# List all orgs / user's orgs / get org / update org
curl http://localhost:4001/organizations
curl http://localhost:4001/user/orgs -H "Authorization: Bearer $TOKEN"
curl http://localhost:4001/orgs/my-org
curl -X PATCH http://localhost:4001/orgs/my-org \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"description": "Updated org"}'

# Org members: list, get, remove
curl http://localhost:4001/orgs/my-org/members
curl http://localhost:4001/orgs/my-org/members/octocat
curl -X DELETE http://localhost:4001/orgs/my-org/members/octocat \
 -H "Authorization: Bearer $TOKEN"

# Org memberships: get, set (invite/update role)
curl http://localhost:4001/orgs/my-org/memberships/octocat -H "Authorization: Bearer $TOKEN"
curl -X PUT http://localhost:4001/orgs/my-org/memberships/octocat \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"role": "admin"}'

# Teams: CRUD
curl http://localhost:4001/orgs/my-org/teams
curl -X POST http://localhost:4001/orgs/my-org/teams \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"name": "engineering", "privacy": "closed"}'

# Team members and memberships
curl http://localhost:4001/orgs/my-org/teams/engineering/members
curl -X PUT http://localhost:4001/orgs/my-org/teams/engineering/memberships/octocat \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"role": "maintainer"}'

# Team repos: list, add, remove
curl http://localhost:4001/orgs/my-org/teams/engineering/repos
curl -X PUT http://localhost:4001/orgs/my-org/teams/engineering/repos/my-org/org-repo \
 -H "Authorization: Bearer $TOKEN"

# Legacy team endpoints by ID
curl http://localhost:4001/teams/1
curl http://localhost:4001/teams/1/members

GitHub Apps

Copy & paste — that's it
# Get authenticated app (requires JWT auth)
curl http://localhost:4001/app \
 -H "Authorization: Bearer "

# List app installations
curl http://localhost:4001/app/installations \
 -H "Authorization: Bearer "

# Get installation
curl http://localhost:4001/app/installations/100 \
 -H "Authorization: Bearer "

# Create installation access token (mints ghs_... token)
curl -X POST http://localhost:4001/app/installations/100/access_tokens \
 -H "Authorization: Bearer " \
 -H "Content-Type: application/json" \
 -d '{"permissions": {"contents": "read"}}'

# Find installation for repo / org / user
curl http://localhost:4001/repos/my-org/org-repo/installation
curl http://localhost:4001/orgs/my-org/installation
curl http://localhost:4001/users/octocat/installation

App webhook delivery: when events occur, the emulator POSTs event_callback payloads to configured webhook_url with X-GitHub-Event and X-Hub-Signature-256 headers.

Releases

Copy & paste — that's it
# Create release
curl -X POST http://localhost:4001/repos/octocat/hello-world/releases \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"tag_name": "v1.0.0", "name": "v1.0.0"}'

# List, get, latest, by tag, generate notes

# Release assets: list, upload
curl http://localhost:4001/repos/octocat/hello-world/releases/1/assets
curl -X POST http://localhost:4001/repos/octocat/hello-world/releases/1/assets \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/octet-stream" \
 -H "name: binary.zip" \
 --data-binary @binary.zip

Webhooks

Copy & paste — that's it
# Create webhook (real HTTP delivery on state changes)
curl -X POST http://localhost:4001/repos/octocat/hello-world/hooks \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"config": {"url": "http://localhost:8080/webhook"}, "events": ["push", "pull_request"]}'

# Full CRUD, ping, test, deliveries
# Org webhooks also supported

Search

Copy & paste — that's it
# Search repositories
curl "http://localhost:4001/search/repositories?q=language:JavaScript+user:octocat"

# Search issues and PRs
curl "http://localhost:4001/search/issues?q=repo:octocat/hello-world+is:open"

# Search users, code, commits, topics, labels

Actions

Copy & paste — that's it
# Workflows: list, get, enable/disable, dispatch
# Workflow runs: list, get, cancel, rerun, delete, logs
# Jobs: list, get, logs
# Artifacts: list, get, delete
# Secrets: repo + org CRUD

Checks

Copy & paste — that's it
# Create check run
curl -X POST http://localhost:4001/repos/octocat/hello-world/check-runs \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"name": "CI", "head_sha": "abc123", "status": "completed", "conclusion": "success"}'

# Check suites: create, get, rerequest, preferences, list by ref
# Check runs: list for suite, annotations
# Automatic suite status rollup from check run results

OAuth

Copy & paste — that's it
# Authorize (browser flow, shows user picker)
# GET /login/oauth/authorize?client_id=...&redirect_uri=...&scope=...&state=...

# Token exchange
curl -X POST http://localhost:4001/login/oauth/access_token \
 -H "Content-Type: application/json" \
 -H "Accept: application/json" \
 -d '{"client_id": "Iv1.abc123", "client_secret": "secret_abc123", "code": "`"}'

# User emails
curl http://localhost:4001/user/emails -H "Authorization: Bearer $TOKEN"

# OAuth app management (settings)
curl http://localhost:4001/settings/applications -H "Authorization: Bearer $TOKEN"
curl http://localhost:4001/settings/connections/applications/Iv1.abc123 -H "Authorization: Bearer $TOKEN"

# Revoke OAuth app
curl -X POST http://localhost:4001/settings/connections/applications/Iv1.abc123/revoke \
 -H "Authorization: Bearer $TOKEN"

Misc

Copy & paste — that's it
curl http://localhost:4001/rate_limit
curl http://localhost:4001/meta
curl http://localhost:4001/emojis
curl http://localhost:4001/versions
curl http://localhost:4001/octocat
curl http://localhost:4001/zen

Common Patterns

Create Repo, Issue, and PR

Copy & paste — that's it
TOKEN="test_token_admin"
BASE="http://localhost:4001"

# Create repo
curl -X POST $BASE/user/repos \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"name": "my-project"}'

# Create issue
curl -X POST $BASE/repos/admin/my-project/issues \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"title": "First issue"}'

# Create PR
curl -X POST $BASE/repos/admin/my-project/pulls \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{"title": "First PR", "head": "feature", "base": "main"}'

GitHub App Installation Token Flow

Copy & paste — that's it
# 1. Sign a JWT with { iss: "12345" } using the app's private key (RS256)
# 2. Create an installation access token
curl -X POST $BASE/app/installations/100/access_tokens \
 -H "Authorization: Bearer " \
 -H "Content-Type: application/json" \
 -d '{"permissions": {"contents": "read", "issues": "write"}}'
# Returns { "token": "ghs_...", ... }

# 3. Use the installation token to call API endpoints
curl $BASE/repos/my-org/org-repo \
 -H "Authorization: Bearer ghs_..."

OAuth Flow

  • Redirect user to $GITHUB_EMULATOR_URL/login/oauth/authorize?client_id=...&redirect_uri=...&scope=user+repo&state=...`

  • User picks a seeded user on the emulator's UI

  • Emulator redirects back with ?code=...&state=...

  • Exchange code for token via POST /login/oauth/access_token

  • Use token to call API endpoints