Connectors
/
GitHub
Live · 9 tools

GitHub Integration for AI Agents

The GitHub API is well-documented. Getting your agent to act on real users’ repositories — correctly, at scale, without token chaos — is the part that takes longer than it should.
GitHub
Live

Developer Tools

Code Collaboration

Status
Live
Tools
9 pre-built
Auth
OAuth 2.0
Credential storage
Zero
Sandbox support
No

OAuth 2.0

Auto token refresh

Zero credential storage

Revocation detection

Repo + issue + PR tools

The real problem

Why this is harder than it looks

The GitHub API documentation is excellent. The OAuth flow is standard. You can have a working proof-of-concept in an afternoon. The complexity arrives when you try to build this for real users, in a product, at scale — and it’s messier than most connectors.

The first thing most teams discover is that GitHub has two meaningfully different auth systems with confusingly similar names: OAuth Apps and GitHub Apps. For agents acting on behalf of individual users, you’ll use OAuth 2.0 user access tokens. But OAuth Apps carry a hard platform limit of 10 active tokens per user per scope combination. In a multi-tenant product — or even a developer running multiple clients — this means old tokens silently get revoked when new ones are issued. Your agent will start returning 401s for some users with no explanation. The fix (switching from an OAuth App to a GitHub App) is non-trivial to discover, and the error message gives you nothing to go on.

Then there’s the scope problem. GitHub’s OAuth scopes are coarse-grained by default. The repo scope — which you need for any private repository work — grants full read and write access to every repository the user can access. Enterprise security teams notice this. Users notice it. The consent screen asks for a lot, and that creates friction at the authorization step for corporate GitHub accounts.

Beyond that: per-user token isolation in a multi-tenant system, proactive refresh logic before expiry, and revocation detection when users remove your OAuth authorization from their GitHub settings. Each is manageable individually. Together, they’re the kind of work that keeps a sprint busy without advancing your agent’s actual capabilities. Scalekit handles all of it so your code only has to name the tool and pass parameters.

Capabilities

What your agent can do with GitHub

Once connected, your agent has 9 pre-built tools covering the core GitHub developer workflow:

  • Read and write repository files: fetch file contents, create new files, or update existing files with a commit message — all in one call
  • Manage issues end-to-end: create issues with labels, assignees, and milestones; list and filter by state, author, label, or date
  • Open and list pull requests: create PRs from a feature branch to a base, list with state and branch filters, support for draft PRs
  • Inspect repositories: get detailed repo metadata and settings; list public repos for any user or the authenticated user’s full repo set
Setup context

What we’re building

This guide connects a developer assistant agent to GitHub — helping engineers manage issues, review open PRs, and create or update files in their repos without leaving your product.

🤖
Example agent
Developer assistant triaging issues, surfacing open PRs, and committing file changes on behalf of each engineer
🔐
Auth model
B2B SaaS — each engineer connects their own GitHub account. identifier = your user ID
⚙️
Scalekit account
app.scalekit.com — Client ID, Secret, Env URL
🐍
Runtime
Python 3.8+ · Node.js also available
Setup

1 Setup: One SDK, One credential

Install the Scalekit SDK. The only credential your application manages is the Scalekit API key — no GitHub secrets, no user tokens, nothing belonging to your users.

pip install scalekit-sdk-python
import scalekit.client import os from dotenv import load_dotenv load_dotenv() scalekit = scalekit.client.ScalekitClient( client_id=os.getenv("SCALEKIT_CLIENT_ID"), client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), env_url=os.getenv("SCALEKIT_ENV_URL"), ) actions = scalekit.actions
Connected Accounts

2 Per-User Auth: Creating connected accounts

Each engineer gets their own Connected Account, giving them a dedicated auth context. The identifier is any unique string from your system — a UUID, email, whatever you use internally.

response = actions.get_or_create_connected_account( connection_name="github", identifier="user_gh_456" # your internal user ID ) connected_account = response.connected_account print(f"Status: {connected_account.status}") # Status: PENDING — user hasn't authorized yet

This call is idempotent — safe to call on every session start. Returns the existing account if one already exists.

Authorization Flow

3 The authorization flow

The engineer authorizes your agent once. Scalekit generates the OAuth URL with correct scopes, PKCE challenge, and redirect handling pre-configured. After approval, you never see the token.

if connected_account.status != "ACTIVE": link = actions.get_authorization_link( connection_name="github", identifier="user_gh_456" ) # Redirect user → GitHub OAuth consent screen # Scalekit captures the token on callback return redirect(link.link)
Token management is automatic
After the user approves, Scalekit stores encrypted tokens and the connected account moves to ACTIVE. Access tokens refresh before expiry. If an engineer revokes access from their GitHub settings, the account moves to REVOKED — no silent failures. Check account.status before critical write operations.
Bring Your Own Credentials for production
For production deployments, register your own GitHub OAuth App in GitHub Developer Settings and supply your Client ID and Secret in the Scalekit dashboard. Users will see your app name on the consent screen. Token management stays fully handled.
Already have credentials?
Calling GitHub

4 Calling GitHub: What your agent writes

With the connected account active, your agent calls GitHub actions using execute_tool. Name the tool, pass parameters. Scalekit handles token retrieval, request construction, and response parsing.

List a user’s repositories

Fetch all repositories accessible to the authenticated user. Use type and sort to narrow results — useful for letting an agent discover what the user has access to before taking action.

result = actions.execute_tool( identifier="user_gh_456", tool_name="github_user_repos_list", tool_input={ "type": "owner", "sort": "updated", "per_page": 20 } ) # Returns array of repo objects with name, description, visibility, updated_at

List open issues with filters

Pull all open issues from a repo, filtered by label, assignee, or update date. The since parameter is ISO 8601 — useful for “what’s changed since the last standup” queries.

result = actions.execute_tool( identifier="user_gh_456", tool_name="github_issues_list", tool_input={ "owner": "acme-corp", "repo": "backend-api", "state": "open", "labels": "bug,p1", "sort": "updated", "per_page": 50 } )

Create an issue

Open a new issue with title, body, labels, and assignees. Setting assignees or milestones requires the connected user to have push access to the target repo.

result = actions.execute_tool( identifier="user_gh_456", tool_name="github_issue_create", tool_input={ "owner": "acme-corp", "repo": "backend-api", "title": "Auth token refresh silently fails on 429 response", "body": "Reproducible in staging. Steps to reproduce: ...", "labels": ["bug", "p1"], "assignees": ["jdoe"] } ) # Returns: { "number": 142, "html_url": "https://github.com/...", ... }

Read and update a file in a repo

Get a file’s current contents (returned Base64 encoded), then write an updated version back. The sha from the get response is required when updating — GitHub uses it to detect conflicting edits.

import base64 # 1. Read the current file current = actions.execute_tool( identifier="user_gh_456", tool_name="github_file_contents_get", tool_input={ "owner": "acme-corp", "repo": "backend-api", "path": "docs/CHANGELOG.md", "ref": "main" } ) # 2. Decode, modify, re-encode original = base64.b64decode(current["content"]).decode() updated = "## v2.4.1\n- Fixed auth token refresh\n\n" + original encoded = base64.b64encode(updated.encode()).decode() # 3. Write back — sha is required for updates result = actions.execute_tool( identifier="user_gh_456", tool_name="github_file_create_update", tool_input={ "owner": "acme-corp", "repo": "backend-api", "path": "docs/CHANGELOG.md", "message": "docs: add v2.4.1 changelog entry", "content": encoded, "sha": current["sha"], # required for updates "branch": "main" } )
Framework wiring

5 Wiring into your agent framework

Scalekit integrates directly with LangChain. The agent decides what to call; Scalekit handles auth on every invocation. No token plumbing in your agent logic.

from langchain_anthropic import ChatAnthropic from langchain.agents import AgentExecutor, create_tool_calling_agent from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from scalekit.langchain import get_tools gh_tools = get_tools( connection_name="github", identifier="user_gh_456" ) prompt = ChatPromptTemplate.from_messages([ ("system", "You are a developer assistant. Use the available tools to help manage GitHub repositories, issues, and pull requests."), MessagesPlaceholder("chat_history", optional=True), ("human", "{input}"), MessagesPlaceholder("agent_scratchpad"), ]) agent = create_tool_calling_agent(ChatAnthropic(model="claude-sonnet-4-6"), gh_tools, prompt) result = AgentExecutor(agent=agent, tools=gh_tools).invoke({ "input": "Show me all open P1 bugs in the backend-api repo and create a summary issue linking them" })
Other frameworks supported
Tool reference

All 9 GitHub tools

Grouped by capability. Your agent calls tools by name — no API wrappers to write.

Repositories
github_repo_get
Get detailed metadata, settings, and statistics for a specific repository by owner and repo name
github_user_repos_list
List all repositories accessible to the authenticated user, with sort, type, and pagination filters
github_public_repos_list
List public repositories for any specified GitHub username — does not require authentication
Files
github_file_contents_get
Get the contents of a file or directory. Returns Base64 encoded content and SHA for files. Supports branch, tag, and commit ref
github_file_create_update
Create a new file or update an existing one with a commit message. Requires SHA when updating. Supports custom author and committer metadata
Issues
github_issue_create
Create a new issue with title, body, labels, assignees, and milestone. Assignees require push access
github_issues_list
List issues filtered by state, label, assignee, creator, milestone, and date. Pagination supported
Pull Requests
github_pull_request_create
Create a pull request from a head branch into a base branch. Supports draft PRs and maintainer modification flag
github_pull_requests_list
List pull requests filtered by state, head branch, or base branch. Pagination and sort supported
Connector notes

GitHub-specific behavior

File updates require the current file’s SHA
When using github_file_create_update to modify an existing file, you must supply the blob sha from a prior github_file_contents_get call. GitHub uses this to detect and prevent conflicting edits. Omitting it on an update returns a 422 — not a 404 or auth error. Always fetch before writing.
File contents are Base64 encoded
The GitHub Contents API returns file content as Base64. When reading, decode before use. When writing, encode the full file content before passing it as content. This applies to both text and binary files. See the code example in Step 4 for the standard pattern.
Issues API returns pull requests too
GitHub’s REST API returns pull requests as a type of issue. When using github_issues_list, results may include PRs alongside regular issues. If your agent needs to distinguish between them, check for the presence of the pull_request key in each result object.
Infrastructure decision

Why not build this yourself

The GitHub OAuth flow is documented. Token storage isn’t technically hard. But here’s what you’re actually signing up for:

PROBLEM 01
GitHub OAuth Apps have a hard limit of 10 active tokens per user per scope combination — exceeding it silently revokes your oldest tokens with no warning
PROBLEM 02
Access token expiry requires proactive refresh logic running before the token expires, not in response to a failed API call mid-operation
PROBLEM 03
Revocation detection when users remove your OAuth authorization from GitHub Settings — and graceful handling so agents stop making calls immediately
PROBLEM 04
Per-user token isolation across a multi-tenant system — one user’s GitHub credentials must never be accessible to another, even under the same organization

That’s one connector. Your agent product will eventually need Salesforce, Slack, Jira, Notion, and whatever else your customers ask for. Each has its own OAuth quirks and failure modes.

Scalekit maintains every connector. You maintain none of them.

Ready to ship

Connect your agent to Salesforce in minutes

Free to start. No Salesforce Connected App required. Token management fully handled.