Skip to content

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.

If your Zone already has a GitHub Provider (the Quickstart creates one), skip to step 3.

  1. Copy your Keycard redirect URL

    In Keycard Console, open Settings → Connection and copy the 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 Redirect URI (older forms call it 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 API to Keycard

    In Keycard Console, open Resources → Add Resource → Explore Resources, search for GitHub, and choose GitHub API. The setup screen offers two ways to connect GitHub: a personal access token or an OAuth App. Paste the Client ID and Client Secret from step 2, leave Personal Access Token empty, and click Protect GitHub API. This creates the GitHub Provider and the GitHub API Resource.

    Leave the token empty for per-user access. With an OAuth App, each User connects their own GitHub account and your tools call GitHub as them; with a token, every call acts as the GitHub user who created it, no matter who signed in, and if you fill in both, the token wins. Act as the user who asked covers why that matters. If your Zone already has a GitHub Provider, the screen shows only the Personal Access Token field; leave it empty and click Protect GitHub API. Keycard reuses that Provider, and if GitHub API is already in Resources, opens it instead of adding a second one.

  4. Register your MCP server Resource

    Navigate to Resources → Add Resource → Add Manually:

    Field Value
    Name GitHub MCP Server
    Identifier http://localhost:8000/mcp
    Credential Provider Zone Provider
  5. Create an Application

    Navigate to Applications → Add Application, set Name and Identifier (for example github-mcp-server), and click Create application.

    Then, on the application details page:

    • On the Provides tab, click Add provided resource and select your GitHub MCP Server.
    • On the Dependencies tab, click Add dependency and select GitHub 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

Set the environment variables:

Terminal window
export KEYCARD_ISSUER=<keycard-issuer-url>
export MCP_SERVER_URL=http://localhost:8000
export KEYCARD_CLIENT_ID=<your-client-id>
export 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_url=os.environ["KEYCARD_ISSUER"],
mcp_server_name="GitHub MCP Server",
mcp_server_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/fastmcp/examples/delegated_access
  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, then connect to it. In Claude Code, run /mcp, select the server and choose Authenticate (or run claude mcp login github-mcp). A browser window opens on Keycard; if it asks you to sign in, use the account you use for Keycard Console. If you set this up in a custom Zone instead, you sign in through the identity provider chosen for that Zone. GitHub asks you to authorize only the first time; if you did the Quickstart, you’ve already granted it.

  4. Try these prompts:

    • “List my GitHub repos”
    • “Which of my GitHub repos did I update most recently?”
  5. Verify in Audit Logs

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

    Event Description
    credentials:issue One per hop (your agent’s token for the MCP server, then your Application’s token for GitHub API), each with an identity chain: your agent or Application, then you
    users:authenticate Only if you signed in during this flow
    users:authorize Only when GitHub asked you to authorize; its Entity is GitHub API

    Each GitHub API credentials:issue event includes an identity chain showing both the user identity (e.g., troy.barnes@acme.com) and the application identity (your Application, such as GitHub MCP Server), so you can trace exactly which user triggered which API call through which application.