|
| 1 | +# GitHub API Integration with Keycard Delegated Access |
| 2 | + |
| 3 | +A complete example demonstrating how to use the `@grant` decorator for token exchange, enabling your MCP server to access external APIs (GitHub) on behalf of authenticated users. |
| 4 | + |
| 5 | +## Why Keycard? |
| 6 | + |
| 7 | +Keycard lets you securely connect your AI IDE or agent to external resources. With delegated access, your MCP server can: |
| 8 | + |
| 9 | +- **Exchange user tokens** for API-specific access tokens via OAuth 2.0 Token Exchange |
| 10 | +- **Access external APIs** on behalf of authenticated users with proper scopes |
| 11 | +- **Maintain audit trails** of all delegated operations |
| 12 | + |
| 13 | +## Prerequisites |
| 14 | + |
| 15 | +Before running this example, set up Keycard for delegated access: |
| 16 | + |
| 17 | +### 1. Sign up at [keycard.ai](https://keycard.ai) |
| 18 | + |
| 19 | +### 2. Create a Zone |
| 20 | + |
| 21 | +A zone is your authentication boundary. Create one in the Keycard console. |
| 22 | + |
| 23 | +### 3. Configure an Identity Provider |
| 24 | + |
| 25 | +Set up an identity provider (Google, Microsoft, etc.) for user authentication. |
| 26 | + |
| 27 | +### 4. Create a GitHub App |
| 28 | + |
| 29 | +Create a GitHub App in your organization (or personal account) to enable delegated access: |
| 30 | + |
| 31 | +1. Go to your GitHub organization → **Settings** → **Developer settings** → **GitHub Apps** |
| 32 | +2. Click **New GitHub App** |
| 33 | +3. Configure the app with the permissions your MCP server needs (e.g., **Repository** access, **User** profile read) |
| 34 | +4. Generate a **Client Secret** |
| 35 | +5. Note down the **Client ID** and **Client Secret** — you'll use these to configure the credential provider in Keycard |
| 36 | + |
| 37 | +### 5. Configure a Credential Provider in Keycard |
| 38 | + |
| 39 | +Set up the GitHub credential provider so Keycard can obtain tokens on behalf of users: |
| 40 | + |
| 41 | +1. In your Keycard zone, go to **Providers** |
| 42 | +2. Add a new GitHub credential provider using the **Client ID** and **Client Secret** from the GitHub App you created in step 4 |
| 43 | +3. This credential provider is what Keycard uses to issue GitHub API tokens on behalf of authenticated users |
| 44 | + |
| 45 | +### 6. Configure GitHub as an API Resource |
| 46 | + |
| 47 | +Add GitHub as an API resource in your zone: |
| 48 | + |
| 49 | +1. In your Keycard zone, go to **Resources** |
| 50 | +2. Add a new API resource for GitHub: |
| 51 | + - **Resource URL**: `https://api.github.com` |
| 52 | + - **Credential Provider**: Select the GitHub credential provider you created in step 5 |
| 53 | + - **Scopes**: `read:user`, `repo` (adjust based on your needs) |
| 54 | + |
| 55 | +### 7. Create an Application |
| 56 | + |
| 57 | +Create an application that will represent your MCP server: |
| 58 | + |
| 59 | +1. Go to **Applications** in your zone |
| 60 | +2. Create a new application |
| 61 | + - **Identifier**: Set this to match your `MCP_SERVER_URL` (e.g., `http://localhost:8000/`) |
| 62 | +3. Add the **GitHub API** resource as a dependency of this application |
| 63 | +4. Generate **Application Credentials** (Client ID and Client Secret) |
| 64 | + - These are what you'll use for `KEYCARD_CLIENT_ID` and `KEYCARD_CLIENT_SECRET` |
| 65 | + |
| 66 | +### 8. Create an MCP Server Resource |
| 67 | + |
| 68 | +Register your MCP server with Keycard: |
| 69 | + |
| 70 | +1. Go to **Resources** and add a new MCP Server resource |
| 71 | +2. Set the URL to your server's MCP endpoint: `http://localhost:8000/mcp` |
| 72 | +3. Configure the resource: |
| 73 | + - **Provided by**: Select the application you created in step 7 |
| 74 | + - **Credential Provider**: Keycard STS Zone Provider |
| 75 | + |
| 76 | +> **Note:** Delegated token exchange requires Keycard to reach your MCP server. For local development, use a tunneling service (e.g., ngrok, Cloudflare Tunnel) or host the server on a publicly accessible URL. |
| 77 | +
|
| 78 | +See [Delegated Access Setup](https://docs.keycard.ai/build-with-keycard/delegated-access) for detailed instructions. |
| 79 | + |
| 80 | +## Quick Start |
| 81 | + |
| 82 | +### 1. Set Up Tunneling (for local development) |
| 83 | + |
| 84 | +Delegated access requires Keycard to reach your server. For local development, set up a tunnel: |
| 85 | + |
| 86 | +```bash |
| 87 | +# Using ngrok |
| 88 | +ngrok http 8000 |
| 89 | + |
| 90 | +# Or using Cloudflare Tunnel |
| 91 | +cloudflared tunnel --url http://localhost:8000 |
| 92 | +``` |
| 93 | + |
| 94 | +Use the public URL from your tunnel as `MCP_SERVER_URL`. |
| 95 | + |
| 96 | +### 2. Set Environment Variables |
| 97 | + |
| 98 | +```bash |
| 99 | +export KEYCARD_ZONE_ID="your-zone-id" |
| 100 | +export KEYCARD_CLIENT_ID="your-client-id" |
| 101 | +export KEYCARD_CLIENT_SECRET="your-client-secret" |
| 102 | +export MCP_SERVER_URL="https://your-tunnel-url.ngrok.io/" # Must be publicly reachable |
| 103 | +``` |
| 104 | + |
| 105 | +### 3. Install Dependencies |
| 106 | + |
| 107 | +```bash |
| 108 | +cd packages/mcp-fastmcp/examples/delegated_access |
| 109 | +uv sync |
| 110 | +``` |
| 111 | + |
| 112 | +### 4. Run the Server |
| 113 | + |
| 114 | +```bash |
| 115 | +uv run python main.py |
| 116 | +``` |
| 117 | + |
| 118 | +The server will start on `http://localhost:8000`. |
| 119 | + |
| 120 | +### 5. Verify the Server |
| 121 | + |
| 122 | +Check that OAuth metadata is being served: |
| 123 | + |
| 124 | +```bash |
| 125 | +curl http://localhost:8000/.well-known/oauth-authorization-server |
| 126 | +``` |
| 127 | + |
| 128 | +You should see JSON with `issuer`, `authorization_endpoint`, and other OAuth metadata. |
| 129 | + |
| 130 | +## Testing with MCP Client |
| 131 | + |
| 132 | +1. Connect to your server using an MCP-compatible client (e.g., Cursor, Claude Desktop) |
| 133 | +2. Authenticate through your configured identity provider |
| 134 | +3. When prompted by Keycard, authorize GitHub access |
| 135 | +4. Call the `get_github_user` or `list_github_repos` tools |
| 136 | +5. Verify GitHub user data is returned |
| 137 | + |
| 138 | +## How It Works |
| 139 | + |
| 140 | +### Token Exchange Flow |
| 141 | + |
| 142 | +``` |
| 143 | +User MCP Server Keycard GitHub |
| 144 | + │ │ │ │ |
| 145 | + │──── Authenticate ──────►│ │ │ |
| 146 | + │ │◄── User Token ───────│ │ |
| 147 | + │ │ │ │ |
| 148 | + │──── Call Tool ─────────►│ │ │ |
| 149 | + │ │── Exchange Token ───►│ │ |
| 150 | + │ │◄─ GitHub Token ──────│ │ |
| 151 | + │ │ │ │ |
| 152 | + │ │──────────────────────┼── API Request ───────►│ |
| 153 | + │ │◄─────────────────────┼── API Response ───────│ |
| 154 | + │◄─── Tool Result ────────│ │ │ |
| 155 | +``` |
| 156 | + |
| 157 | +1. User authenticates to your MCP server via Keycard |
| 158 | +2. When a tool with `@grant` is called, Keycard exchanges the user's token |
| 159 | +3. The exchanged token has the scopes configured for the external resource |
| 160 | +4. Your server uses this token to call GitHub API on behalf of the user |
| 161 | + |
| 162 | +## Error Handling |
| 163 | + |
| 164 | +The example demonstrates comprehensive error handling patterns: |
| 165 | + |
| 166 | +| Method | Description | |
| 167 | +|--------|-------------| |
| 168 | +| `has_errors()` | Check for any errors (global or resource-specific) | |
| 169 | +| `get_errors()` | Get all error details as a dictionary | |
| 170 | +| `has_resource_error(url)` | Check for errors on a specific resource | |
| 171 | +| `get_resource_errors(url)` | Get errors for a specific resource | |
| 172 | +| `has_error()` | Check for global errors only | |
| 173 | +| `get_error()` | Get global error details | |
| 174 | + |
| 175 | +## Environment Variables Reference |
| 176 | + |
| 177 | +| Variable | Required | Description | |
| 178 | +|----------|----------|-------------| |
| 179 | +| `KEYCARD_ZONE_ID` | Yes | Your Keycard zone ID | |
| 180 | +| `KEYCARD_CLIENT_ID` | Yes | Client ID from application credentials | |
| 181 | +| `KEYCARD_CLIENT_SECRET` | Yes | Client secret from application credentials | |
| 182 | +| `MCP_SERVER_URL` | Yes | Server URL (must be publicly reachable for delegated access) | |
| 183 | + |
| 184 | +## Learn More |
| 185 | + |
| 186 | +- [Keycard Documentation](https://docs.keycard.ai) |
| 187 | +- [Delegated Access Guide](https://docs.keycard.ai/build-with-keycard/delegated-access) |
| 188 | +- [FastMCP Documentation](https://docs.fastmcp.com) |
| 189 | +- [GitHub API Documentation](https://docs.github.com/rest) |
0 commit comments