Skip to content
API Reference

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. Copy your Keycard redirect URL

    In Keycard Console, navigate to Zone Settings and copy the OAuth2 Redirect URL. You will need this when creating the GitHub OAuth App.

  2. Create a GitHub OAuth App

    In GitHub Developer Settings, create a new OAuth App and set the Authorization callback URL to the redirect URL you copied from Keycard. Once created, note the Client ID and generate a Client Secret.

  3. Add GitHub from Resource Catalog

    In Keycard Console, navigate to Resource Catalog and add GitHub. Enter the Client ID and Client Secret from step 2. This creates the GitHub OAuth provider and API resource.

  4. Register your MCP server Resource

    Navigate to ResourcesCreate Resource:

    FieldValue
    Resource NameGitHub MCP Server
    Resource Identifierhttp://localhost:8000/mcp
    Credential ProviderZone Provider
  5. 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-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.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.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

    Terminal window
    python server.py
  2. Configure your agent

    Add the MCP server to your agent configuration:

    Cursor: add to .cursor/mcp.json:

    {
    "mcpServers": {
    "github-mcp": {
    "url": "http://localhost:8000/mcp"
    }
    }
    }

    Claude Code: run in your terminal:

    Terminal window
    claude mcp add --transport http github-mcp http://localhost:8000/mcp
  3. Authenticate

    Restart your coding agent to detect the server. When prompted, complete the OAuth flow. Keycard will prompt you to sign in to your zone. This is a separate account from your Keycard Console login. If it’s your first time, click Sign up to create one. You will then be prompted to authorize GitHub access.

  4. Try these prompts:

    • “List my GitHub repos”
    • “Show open PRs on owner/repo
    • “Create an issue on owner/repo titled ‘Bug: login broken’”
  5. 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, showing 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.