Github MCP Proxy for AI Agents, i.e. in mwaeckerlin/openclaw und mwaeckerlin/hermes.
8.5K
Standalone MCP server for secure GitHub access from sandboxed agents. GitHub credentials are stored only on the MCP server side.
This repository is explicitly modeled after mwaeckerlin/openclaw-mcp-gateway:
Sandboxed agents should not hold GitHub tokens. Instead:
GITHUB_TOKEN.The agent does not need a GitHub token. The token is required only on the MCP server side.
Security properties:
per_page, first, last, limit, pageSize are clamped to 1..100)@startuml architecture
node "Sandbox / Agent" {
[MCP Client]
}
node "MCP GitHub Server" {
[mcp-github]
}
node "GitHub API" {
[api.github.com]
}
[MCP Client] --> [mcp-github] : MCP tool calls
[mcp-github] --> [api.github.com] : validated REST + GraphQL
@enduml
github_rest_list_operations: lists allowlisted GitHub OpenAPI operation IDs and their mapped family.All GitHub REST operations from @octokit/openapi are mapped into one of these families:
github_repositories_restgithub_branches_restgithub_commits_restgithub_git_data_restgithub_pull_requests_restgithub_issues_restgithub_labels_milestones_restgithub_releases_tags_restgithub_actions_workflows_restgithub_checks_status_restgithub_discussions_projects_restgithub_users_orgs_teams_restgithub_search_restgithub_notifications_reactions_restgithub_webhooks_deployments_restgithub_codespaces_restgithub_rest_miscEach REST family tool takes:
operationId (required, must belong to that family)parameters (validated object; pagination bounded)github_graphql: validated GraphQL operation execution (operationName, query, optional variables) for gaps where GraphQL is needed.github_copilot_assign_issue: assign GitHub Copilot cloud agent to an existing issue. Copilot researches the issue, creates an implementation plan, and opens a pull request. Accepts optional agent_assignment for target_repo, base_branch, custom_instructions, custom_agent, and model. Requires a Copilot plan with cloud agent enabled (public preview).github_rest_list_operations{ "family": "github_pull_requests_rest", "limit": 50, "offset": 0 }
*_rest family tool{
"operationId": "pulls/list",
"parameters": {
"owner": "mwaeckerlin",
"repo": "mcp-github",
"state": "open",
"per_page": 30
}
}
github_graphql{
"operationName": "Viewer",
"query": "query Viewer { viewer { login } }",
"variables": {}
}
Use this sequence for reliable MCP usage from an agent.
GET /healthz.status is degraded, continue in read-only mode: only public read calls are expected to work.Call tool github_rest_list_operations with:
{
"family": "github_issues_rest",
"limit": 200,
"offset": 0
}
Then choose the operation ID you need from operations[].
For creating issues, this is typically issues/create.
Each operation item also includes method, path, and parameterNames so users can see which route/query/body names are expected before calling the family tool.
For each REST call, parameters are discovered and validated using this chain:
github_rest_list_operations gives the exact operationId plus method, path, and parameterNames.parameterNames are taken from GitHub OpenAPI metadata embedded via @octokit/openapi.parameters in the matching *_rest family tool.operationId must belong to that tool family).Practical guidance:
github_rest_list_operations and filter by family.issues/create, issues/create-comment, pulls/create, and so on).parameterNames as your parameter checklist.To create a new issue in mwaeckerlin/mcp-github, call tool github_issues_rest:
{
"operationId": "issues/create",
"parameters": {
"owner": "mwaeckerlin",
"repo": "mcp-github",
"title": "Example issue via MCP",
"body": "Created through mcp-github using github_issues_rest.",
"labels": ["bug"]
}
}
Expected behavior:
GitHub token is not configured... or authentication or permission error).Call tool github_issues_rest:
{
"operationId": "issues/list-for-repo",
"parameters": {
"owner": "mwaeckerlin",
"repo": "mcp-github",
"state": "open",
"per_page": 30
}
}
Tool: github_users_orgs_teams_rest
{
"operationId": "users/get-authenticated",
"parameters": {}
}
Tool: github_issues_rest
{
"operationId": "issues/create-comment",
"parameters": {
"owner": "mwaeckerlin",
"repo": "mcp-github",
"issue_number": 1,
"body": "Comment added via MCP"
}
}
Tool: github_pull_requests_rest
{
"operationId": "pulls/create",
"parameters": {
"owner": "mwaeckerlin",
"repo": "mcp-github",
"title": "Example PR via MCP",
"head": "feature-branch",
"base": "main",
"body": "Created through mcp-github"
}
}
Production rule: keep
GITHUB_TOKENserver-side only.
| Variable | Required | Description |
|---|---|---|
GITHUB_TOKEN | no | GitHub token used by the server (never passed to sandbox); if missing, server starts in degraded mode: public read calls can work, while private and write operations fail |
MCP_AUTH_TOKEN | no | Shared secret token for MCP endpoint authentication; if set, all MCP requests must supply this token via Authorization: Bearer <token> header or ?token=<token> query parameter; /healthz is exempt |
MCP_GITHUB_HOST | no | Bind host (default 0.0.0.0) |
MCP_GITHUB_PORT | no | Bind port (default 4000) |
DISABLE_TOOLS | no | Comma-separated MCP tool names to disable |
When MCP_AUTH_TOKEN is set on the server, the sandbox must present the same token in every MCP request. The /healthz endpoint is not protected and can always be polled for readiness.
Server side — set the shared secret:
export MCP_AUTH_TOKEN=$(openssl rand -hex 32)
Client side — pass the token via HTTP header (recommended):
Authorization: Bearer <token>
Or via query parameter (alternative):
http://mcp-github:4000/?token=<token>
Client environment variable — expose the token to the sandbox:
| Variable | Required | Description |
|---|---|---|
MCP_AUTH_TOKEN | no | Shared secret that the sandbox must present when MCP_AUTH_TOKEN is also set on the server |
Security considerations:
openssl rand -hex 32).MCP_AUTH_TOKEN set, the server accepts all requests (backward-compatible default).GET /healthz always returns HTTP 200 while the process is running (authentication is not required for this endpoint).{ "ok": true, "status": "ready", "githubTokenConfigured": true }{ "ok": true, "status": "degraded", "githubTokenConfigured": false, "message": "...set GITHUB_TOKEN..." }github_rest_list_operations still works and is filtered to read-only (GET/HEAD) operations; public read calls can work, while write/private operations fail.| Variable | Required | Description |
|---|---|---|
MCP_GITHUB_URL | yes | URL where the sandbox MCP client reaches this server (for example http://mcp-github:4000) |
MCP_AUTH_TOKEN | no | Shared secret to present in MCP requests when the server requires authentication |
MCP_GITHUB_URL must be set by your deployment/startup configuration and exposed in the sandbox user environment, because the MCP client reads this variable to know where to send requests.
arguments and cannot override server auth/config.GITHUB_E2E_TOKEN permissionsRun the E2E test suite with:
export GITHUB_E2E_TOKEN=ghp_...
cd test && docker compose up
The token is set server-side only (GITHUB_TOKEN in the mcp-github container). The test-client container never sees it.
Create a fine-grained PAT at https://github.com/settings/tokens?type=beta. See the GitHub fine-grained PAT permission reference for the full list.
Repository access: Select "All repositories" or "Public Repositories" — all repository tests access public repos (octocat/Hello-World, mwaeckerlin/mcp-github), which the GitHub API allows with no repository permission at all.
No permissions are required for the E2E tests with a "Public repositories" fine-grained token:
users/get-authenticated and GraphQL viewer { login }: "The fine-grained token does not require any permissions" (GitHub docs).octocat/Hello-World, mwaeckerlin/mcp-github) and the public github org — no permissions required per GitHub's API.GET /user/codespaces) gracefully skips (passes) when the token lacks the "Codespaces" repository permission. To fully run it, select "All repositories" access and add the "Codespaces" repository permission (read) — this appears under Repository permissions in the GitHub UI (not Account permissions), and has nothing to do with secrets.Note on "Codespaces user secrets": This is an Account permission about secrets stored inside codespaces. It is a completely different thing from the Repository permission "Codespaces" and is not needed here.
If you use a classic PAT (legacy), no scopes are needed for the basic tests. Add the codespace scope only if you want the codespaces test to run fully.
Note: GitHub's API returns HTTP 401 if you supply any token that is invalid or expired, even for public-data endpoints. Always use a fresh, valid token.
These permissions apply when the agent should perform write actions (branch, commit/push, issues, PRs) through this MCP server.
Repository access:
Repository permissions:
Contents: Read and write (checkout via API, create branch, commit, push)Issues: Read and write (create/update issue)Pull requests: Read and write (create/update PR)Optional (only if needed):
Workflows: Read and write (required when commits modify .github/workflows/*)Minimum for public repositories:
public_repoOptional (only if needed):
workflow (required when pushing/modifying .github/workflows/*)Notes:
All E2E tests are strictly read-only. They only call GET endpoints. No data is created, modified, or deleted. Specifically:
Risk assessment: There is no risk of data loss or unintended side-effects regardless of what permissions your token carries. Even a token with full write permissions will not cause any mutations because the tests only use read operations.
npm install
npm run build
npm start
Dev mode:
npm run dev
Tests:
npm test
github_rest_list_operations to discover valid operation IDs.| Symptom | Cause | Action |
|---|---|---|
GitHub token is not configured on the MCP server ... | Server started without token | Set server-side GITHUB_TOKEN to enable GitHub REST/GraphQL execution tools |
operationId ... is not allowlisted for tool ... | Wrong tool family | Query github_rest_list_operations and use matching family |
GitHub authentication or permission error (401/403) | Invalid token or insufficient scopes | Rotate token or add required scopes |
GitHub resource not found or not accessible | Missing permission or wrong resource | Validate owner/repo/resource access |
Tool disabled by DISABLE_TOOLS | Tool explicitly disabled | Remove from DISABLE_TOOLS or call different tool |
GitHub API error (422) with assignees | Wrong assignee username or missing agent_assignment | Use the dedicated github_copilot_assign_issue tool |
Use the dedicated github_copilot_assign_issue tool to assign GitHub Copilot cloud agent to an existing issue. Copilot will research the issue, create an implementation plan, make code changes on a branch, and open a pull request.
Requires a Copilot plan (Pro, Pro+, Business, or Enterprise) with cloud agent enabled in the repository. This feature is in public preview.
Why not use issues/add-assignees directly? The REST API requires the assignee "copilot-swe-agent[bot]" (including the [bot] suffix) and an agent_assignment body object. The github_copilot_assign_issue tool handles this automatically.
Minimal call:
{
"owner": "octo-org",
"repo": "octo-repo",
"issue_number": 42
}
With optional configuration:
{
"owner": "octo-org",
"repo": "octo-repo",
"issue_number": 42,
"agent_assignment": {
"base_branch": "main",
"custom_instructions": "Add unit tests only. Do not modify existing logic.",
"model": "gpt-4o"
}
}
This repository ships SKILL.md for local OpenClaw skill installation and agent-first operating guidance.
Content type
Image
Digest
sha256:18e8b923c…
Size
72.5 MB
Last updated
1 day ago
docker pull mwaeckerlin/mcp-github