Skip to content
API Reference
Guides
MCP Servers
Delegated Access

GitHub

Build an MCP server with GitHub API tools using Keycard delegated access

Build an MCP server with repository-focused tools that let AI agents interact with GitHub on behalf of users.

  1. Add GitHub from Resource Catalog

    In Keycard Console, navigate to Resource Catalog and add GitHub. This automatically creates the GitHub OAuth provider and GitHub API resource.

  2. Set up the GitHub OAuth App callback

    1. Go to Zone Settings in Keycard Console and copy the OAuth2 Redirect URL
    2. In GitHub Developer Settings, create or edit your OAuth App
    3. Set the Authorization callback URL to the Keycard redirect URL
  3. Register your MCP server resource

    Navigate to ResourcesCreate Resource:

    FieldValue
    Resource NameGitHub MCP Server
    Resource Identifierhttp://localhost:8000/mcp
    Credential ProviderZone Provider
  4. Create an Application

    Navigate to ApplicationsCreate Application:

    FieldValue
    Provided ResourceGitHub MCP Server (your MCP server)
    DependencyGitHub API

    After creating the application, generate client credentials (Client ID + Client Secret) and save them.

The server is split into two files: setup and tools. The setup file configures Keycard authentication; the tools file contains your API wrappers.

Install dependencies:

Terminal window
pip install keycardai-mcp-fastmcp fastmcp httpx

Create a .env file:

Terminal window
KEYCARD_ZONE_ID=your-zone-id
MCP_SERVER_URL=http://localhost:8000
KEYCARD_CLIENT_ID=your-client-id
KEYCARD_CLIENT_SECRET=your-client-secret

server.py — Server setup and entry point:

"""GitHub MCP Server with Keycard Delegated Access."""
import os
from fastmcp import FastMCP
from keycardai.mcp.integrations.fastmcp import AuthProvider, ClientSecret
auth_provider = AuthProvider(
zone_id=os.getenv("KEYCARD_ZONE_ID"),
mcp_server_name="GitHub MCP Server",
mcp_base_url=os.getenv("MCP_SERVER_URL", "http://localhost:8000"),
application_credential=ClientSecret((
os.getenv("KEYCARD_CLIENT_ID"),
os.getenv("KEYCARD_CLIENT_SECRET"),
)),
)
auth = auth_provider.get_remote_auth_provider()
mcp = FastMCP("GitHub MCP Server", auth=auth)
from tools import register_tools # noqa: E402
register_tools(mcp, auth_provider)
def main():
mcp.run(transport="streamable-http")
if __name__ == "__main__":
main()

tools.py — Each tool uses the grant decorator and extracts the exchanged token:

"""GitHub tools — thin wrappers around the GitHub REST API."""
import httpx
from fastmcp import Context, FastMCP
from keycardai.mcp.integrations.fastmcp import AccessContext, AuthProvider
GITHUB_API = "https://api.github.com"
async def github_request(access_context: AccessContext, method: str, path: str, **kwargs) -> dict:
"""Make an authenticated request to the GitHub API."""
token = access_context.access(GITHUB_API).access_token
async with httpx.AsyncClient() as client:
response = await client.request(
method,
f"{GITHUB_API}{path}",
headers={
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github+json",
},
**kwargs,
)
response.raise_for_status()
return response.json()
def register_tools(mcp: FastMCP, auth_provider: AuthProvider):
@mcp.tool()
@auth_provider.grant(GITHUB_API)
async def list_repos(ctx: Context, per_page: int = 30, sort: str = "updated") -> dict:
"""List the authenticated user's repositories."""
access_context: AccessContext = await ctx.get_state("keycardai")
if access_context.has_errors():
return {"error": "Token exchange failed", "details": access_context.get_errors()}
repos = await github_request(
access_context, "GET", "/user/repos", params={"per_page": per_page, "sort": sort}
)
return {
"count": len(repos),
"repositories": [
{"name": r["name"], "full_name": r["full_name"], "html_url": r["html_url"]}
for r in repos
],
}
# Additional tools follow the same pattern: grant → get_state → check errors → call API
# See full examples: https://github.com/keycardai/python-sdk/tree/main/packages/mcp-fastmcp/examples
  1. Start your server

  2. Connect from Cursor — add the MCP server URL to your Cursor MCP settings and complete the OAuth flow

  3. Try these prompts:

    • “List my GitHub repos”
    • “Show open PRs on owner/repo
    • “Create an issue on owner/repo titled ‘Bug: login broken’”
  4. Verify in Audit Logs

    In Keycard Console, navigate to Audit Logs to see the full flow:

    EventDescription
    users:authenticateUser logged in via Keycard
    users:authorizeUser authorized access to the MCP server
    credentials:issueAccess token issued — shows the identity chain (user + application)

    Each credentials:issue event includes an identity chain showing both the user identity (e.g., alice@acme.com) and the application identity (e.g., GitHub), so you can trace exactly which user triggered which API call through which application.