Skip to main content
Version: Latest

GitHub

Proxy for the GitHub REST API. Each managed resource exposes seven MCP tools mapped onto specific api.github.com routes, with per-route OAuth scopes (gh:repo:read, gh:repo:write, gh:issues:read, gh:issues:write, gh:workflows:write).

Overview

Pick an auth mode at install time:

  • idp_passthrough (recommended) — each caller's own GitHub OAuth token is forwarded upstream. The agent acts as the user; their GitHub permissions apply on top of PBAC policy. Requires a GitHub IdP registered on the AS (one OAuth App per instance).
  • static — a single Personal Access Token (PAT) is forwarded for every caller. Use for unattended automations where the operator owns the GitHub identity and there is no human in the loop. The PAT is stored as a secret reference; it is not typed into a form field directly.

A managed resource is bound to one auth mode. To serve both shapes of caller, create two managed resources (e.g. github-user and github-bot). Policy can key off input.resource.name to apply different rules.

Setting up GitHub as an IdP

The admin portal's "Connect a resource" wizard inlines this step the first time you install GitHub with idp_passthrough. The flow is:

  1. Open GitHub Developer Settings → OAuth Apps → New OAuth App.
  2. Set Homepage URL to your AS issuer (e.g. http://localhost:8080).
  3. Set Authorization callback URL to <issuer>/oauth2/callback — the wizard shows the exact value with a copy button.
  4. Generate a client secret. Copy both the Client ID and Client secret into the wizard.

GitHub OAuth Apps don't have you pre-pick scopes at registration — scopes are requested at authorize time and approved by the user. The connector requests read:user, repo, and read:org so callers can read user metadata, work with both public and private repos they own, and resolve org memberships for ABAC decisions. If your tenants only need read access, you can drop repo to its narrower equivalent (public_repo) by editing the upstream_scopes on the managed resource after install.

Policy patterns

Unlike Gmail and Google Drive, the GitHub connector does not ship its own Rego bundle today. The reference policy patterns below live in the global evaluations_ext.rego (in the AS's bundled OPA policies) and are wired up by the managed-gateway demo's seed scripts. Treat them as a recipe, not a turnkey install:

1. Repository owner allowlist (RBAC by namespace)

deny contains msg if {
_is_github(context)
owner := tool_arguments.owner
attrs := data.github_subject_attributes[subject.sub]
not owner in attrs.allowed_orgs
msg := sprintf("github: %v is not in your allowed orgs", [owner])
}

Every connector route includes a {owner} path variable, which the gateway extracts as tool_arguments.owner. Subject attributes seed each developer with the orgs (or personal namespaces) they are allowed to touch. The same rule covers all seven tools — the operator only needs to maintain the per-subject allowed_orgs list.

2. Sensitive file path block

deny contains msg if {
_is_github(context)
path := tool_arguments.path
_github_sensitive_path(path)
msg := sprintf("github: path %v is blocked", [path])
}

_github_sensitive_path(p) if contains(p, ".env")
_github_sensitive_path(p) if endswith(p, ".key")
_github_sensitive_path(p) if endswith(p, ".pem")
_github_sensitive_path(p) if contains(p, "secrets/")

Fires for get_repo_file and upsert_repo_file. No subject attributes needed — the rule depends only on the URL path. Customize the predicates to match your secret-scanning conventions.

3. Inactive-user kill switch

deny contains msg if {
_is_github(context)
attrs := data.github_subject_attributes[subject.sub]
attrs.employment_status != "active"
msg := "github: inactive employee"
}

Flipping employment_status for a user blocks every GitHub tool call within the OPA bundle poll interval (~2 s). No token revocation is needed — the deny fires at introspect time, so already-issued tokens are immediately blocked.

Subject-attribute schema

The reference rules read from data.github_subject_attributes[<sub>]. The shape used by the demo:

{
"allowed_orgs": ["acme", "acme-research"],
"teams": ["eng", "platform"],
"employment_status": "active",
"clearance": "high"
}

The sub claim on a token is a numeric GitHub user ID (not the login). Find it via https://api.github.com/users/<login> and store the id field as the key.

Scope model

ScopeUsed for
PBAC scopes (internal)gh:repo:read, gh:repo:write, gh:issues:read, gh:issues:write, gh:workflows:writePer-route OAuth gating on the managed gateway. A token without gh:repo:write cannot reach upsert_repo_file regardless of ABAC outcome.
Upstream OAuth scopesread:user, repo, read:orgThe grants the user is asked to approve when they sign in to GitHub via the AS.

Splitting reads from writes at the OAuth scope layer keeps the ABAC policy small — most operator concerns (who can write where, what paths are sensitive) compose with the scope gate rather than reimplementing it.

Static (PAT) mode

When the connector is installed with upstream_auth.type = "static", the wizard asks for a secret reference (pat_env) instead of running the IdP setup. Create the secret first via the Secrets page or inline via the form's + Add secret button. The PAT must carry whichever GitHub OAuth scopes the routes will exercise — typically repo and workflow.

PAT mode forwards the same token for every caller. Subject attributes still apply (the PBAC sub and scopes come from the token PBAC issued, not the upstream PAT) but you lose the per-user GitHub permission layer. Use this mode for automations where the operator owns the identity and the permission model is enforced entirely in PBAC.

Verifying the install

A direct introspect probe is the fastest way to confirm policy reaches OPA correctly:

curl -s -X POST "$PBAC_AS_URL/oauth/introspect" \
-H "Authorization: Bearer $TOKEN" \
--data-urlencode "token=$TOKEN" \
--data-urlencode 'context={
"resource_type": "urn:connector:identos:github",
"resource_name": "github",
"scopes": ["gh:repo:read"],
"tool_arguments": {"owner": "acme", "repo": "demo", "path": ".env"}
}' | jq '{active, allow}'

{ "active": true, "allow": false } for a .env path confirms the sensitive-path rule fires. Switch path to README.md to confirm the allow path. The full validation runbook lives at docs/superpowers/runbooks/github-mcp-connector-validation.md.

Troubleshooting

SymptomLikely cause
Wizard shows "Step 1 · Set up GitHub sign-in" but you want PAT modePick static from the Authentication dropdown in Step 2; the wizard now treats Step 1 as advisory and lets you submit.
First-time PKCE login fails with a JSON parse errorOlder AS versions sent no Accept header to GitHub's token endpoint. Upgrade — the fix is in IdpFederationService.
idp_passthrough returns 401 from api.github.com after PBAC allowsThe user's GitHub token does not carry the requested scope (e.g. repo for a private repo). Have them re-authorize the OAuth App and accept the missing scope.
All tools return 403 even for an authenticated userIf you seeded subject attributes, confirm sub is the user's numeric GitHub ID. The login string will silently miss every rule body and fall through to deny.

Manifest reference

  • ID: identos.github
  • Version: 1.0.0
  • Resource type: urn:connector:identos:github

Supported auth modes

TypeDetails
idp_passthroughrequires IdP github
staticscheme bearer; setup fields: pat_env

Setup fields

IDLabelDefaultSecret?Notes
upstream_auth.typeAuthenticationidp_passthroughnoidp_passthrough forwards each user's own GitHub OAuth token (recommended). static uses a single shared Personal Access Token (machine identity).
pat_envPersonal Access TokenyesPick a secret containing the GitHub PAT. Required only for the static auth mode. / shown when upstream_auth.type == 'static'

Scopes

Scope
gh:repo:read
gh:repo:write
gh:issues:read
gh:issues:write
gh:workflows:write

Routes

MethodPatternScopeResource template
GET/repos/{owner}/{repo}gh:repo:readgithub://{{owner}}/{{repo}}
GET/repos/{owner}/{repo}/contents/{path:.*}gh:repo:readgithub://{{owner}}/{{repo}}
PUT/repos/{owner}/{repo}/contents/{path:.*}gh:repo:writegithub://{{owner}}/{{repo}}
GET/repos/{owner}/{repo}/issuesgh:issues:readgithub://{{owner}}/{{repo}}
POST/repos/{owner}/{repo}/issuesgh:issues:writegithub://{{owner}}/{{repo}}
PATCH/repos/{owner}/{repo}/issues/{issue_number}gh:issues:writegithub://{{owner}}/{{repo}}/issues/{{issue_number}}
POST/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatchesgh:workflows:writegithub://{{owner}}/{{repo}}/workflows/{{workflow_id}}

MCP tools

NameScopeDescription
get_repogh:repo:readGet metadata for a GitHub repository.
get_repo_filegh:repo:readRead a file or directory listing from a GitHub repository at a given path.
upsert_repo_filegh:repo:writeCreate or update a file in a GitHub repository.
list_issuesgh:issues:readList issues in a GitHub repository.
create_issuegh:issues:writeOpen a new issue in a GitHub repository.
update_issuegh:issues:writeUpdate fields on an existing GitHub issue.
dispatch_workflowgh:workflows:writeTrigger a manual run of a GitHub Actions workflow on a given ref, with optional inputs.