Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 166 additions & 0 deletions examples/cloud/oauth/mcp_tools/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# OAuth MCP Tools Example

This example demonstrates how to integrate OAuth 2.0-enabled MCP servers within mcp-agent. The example exposes a `github_org_search` tool that calls the GitHub MCP server, requiring OAuth authorization.

## What's included

- `main.py` – exposes a `github_org_search` tool that integrates with the GitHub MCP server using OAuth 2.0
- `mcp_agent.config.yaml` – configures OAuth settings including callback URLs, flow timeout, and token store settings
- `mcp_agent.secrets.yaml.example` – template for storing GitHub OAuth credentials (client ID, secret, and access token)

## Features

- **Interactive (lazy) authorization**: When the tool is invoked without a cached token, the server issues an `auth/request` message and the client opens the browser to interactively complete the GitHub sign-in
- **Pre-authorized workflows**: Leverage the workflows-store-credentials mcp-agent tool to cache a token for a specified workflow before the workflow is run. Once the token is saved, the workflow can access the downstream MCP server without further user interaction
- **GitHub repository search**: Search GitHub repositories within organizations using the OAuth-protected GitHub MCP server
- **Token caching**: OAuth tokens are cached and reused across runs using a stable session ID

## Prerequisites

- Python 3.10+
- [UV](https://github.com/astral-sh/uv) package manager
- GitHub OAuth App for authentication

## Configuration

Before running the example, you'll need to create a GitHub OAuth App and configure the credentials.

### GitHub OAuth App Setup

1. Create a GitHub OAuth App (Settings → Developer settings → OAuth Apps)

2. Set the **Authorization callback URL** to `http://127.0.0.1:33418/callback`

> The example pins its loopback listener to that port, so the value must match exactly.

3. For testing in MCP Inspector, add an additional callback URL: `http://localhost:6274/oauth/callback`

> [!NOTE]
> GitHub does not accept the RFC 8707 `resource` parameter, so the example disables it via `include_resource_parameter: false` in the config.

4. Obtain a GitHub personal access token from https://github.com/settings/personal-access-tokens

### Secrets Configuration

1. Copy the example secrets file:

```bash
cp mcp_agent.secrets.yaml.example mcp_agent.secrets.yaml
```

2. Edit `mcp_agent.secrets.yaml` to add your GitHub OAuth credentials:

```yaml
mcp:
servers:
github:
auth:
oauth:
client_id: "your-github-client-id"
client_secret: "your-github-client-secret"
access_token: "your-github-access-token"
```

## Test Locally

1. Install dependencies:

```bash
cd examples/cloud/oauth/mcp_tools
uv pip install -r requirements.txt
```

2. Start the mcp-agent server locally with SSE transport:

```bash
uv run main.py
```

The server uses a stable session ID so the OAuth token is cached and reused across runs. Once the first authorization completes, subsequent invocations should return immediately without reopening the browser.

3. Use [MCP Inspector](https://github.com/modelcontextprotocol/inspector) to test the OAuth-enabled MCP tools:

```bash
npx @modelcontextprotocol/inspector --transport sse --server-url http://127.0.0.1:8000/sse
```

4. In MCP Inspector:
- The server will initiate the OAuth 2.0 authorization flow when you first call the `github_org_search` tool
- Complete the GitHub authorization in your browser
- Once authenticated, test the `github_org_search` tool with an organization name (e.g., "github")
- The tool will search for repositories in the specified organization and return the top 5 results
- To pre-authorize the `github_org_search` workflow, call the `workflows-store-credentials` tool with:

```json
{
"workflow_name": "github_org_search_activity",
"tokens": [
{
"access_token": access_token,
"server_name": "github",
}
]
}
```

## Deploy to mcp-agent Cloud

You can deploy this OAuth-enabled MCP-Agent app as a hosted mcp-agent app in the Cloud.

1. In your terminal, authenticate into mcp-agent cloud by running:

```bash
uv run mcp-agent login
```

2. You will be redirected to the login page, create an mcp-agent cloud account through Google or Github

3. Set up your mcp-agent cloud API Key and copy & paste it into your terminal

```bash
uv run mcp-agent login
INFO: Directing to MCP Agent Cloud API login...
Please enter your API key 🔑:
```

4. In your terminal, deploy the MCP app:

```bash
uv run mcp-agent deploy oauth-mcp-tools
```

5. When prompted, specify the type of secret to save your GitHub OAuth credentials. Select (1) deployment secret so that they are available to the deployed server.

The `deploy` command will bundle the app files and deploy them, producing a server URL of the form:
`https://<server_id>.deployments.mcp-agent.com`.

## MCP Clients

Since the mcp-agent app is exposed as an MCP server, it can be used in any MCP client that supports the MCP protocol.

### MCP Inspector

You can inspect and test the deployed server using [MCP Inspector](https://github.com/modelcontextprotocol/inspector):

```bash
npx @modelcontextprotocol/inspector --transport sse --server-url https://<server_id>.deployments.mcp-agent.com/sse
```

This will launch the MCP Inspector UI where you can:

- Complete the OAuth 2.0 authorization flow for GitHub
- See all available tools (including `github_org_search`)
- Test the `github_org_search` tool with different organization queries

Make sure Inspector is configured with the following settings:

| Setting | Value |
| ---------------- | --------------------------------------------------- |
| _Transport Type_ | _SSE_ |
| _SSE_ | _https://[server_id].deployments.mcp-agent.com/sse_ |
| _Header Name_ | _Authorization_ |
| _Bearer Token_ | _your-mcp-agent-cloud-api-token_ |

## Further Reading

More details on OAuth authorization and the MCP protocol can be found at [https://modelcontextprotocol.io/specification/draft/basic/authorization](https://modelcontextprotocol.io/specification/draft/basic/authorization).
95 changes: 95 additions & 0 deletions examples/cloud/oauth/mcp_tools/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"""
MCP Agent MCP Tools OAuth Example

This example demonstrates how to integrate OAuth 2.0-enabled MCP servers within mcp-agent.

1. Interactive (lazy) authorization: When the tool is invoked without a cached token, the server issues an `auth/request` message and the client opens the browser so you can complete the GitHub sign-in.

2. Pre-authorized workflows: Leverage the workflows-store-credentials mcp-agent tool to cache a token for a specified workflow before the workflow is run. Once the token is saved, the workflow can access the downstream MCP server without further user interaction.

"""

import asyncio
import json
from typing import Optional

from mcp_agent.app import MCPApp
from mcp_agent.core.context import Context as AppContext
from mcp_agent.mcp.gen_client import gen_client
from mcp_agent.server.app_server import create_mcp_server_for_app

app = MCPApp(
name="oauth_mcp_tools",
description="Example of MCP tools with OAuth integration",
)

# You can pre-authorize this workflow by calling workflows-store-credentials tool with:
# {
# "workflow_name": "github_org_search_activity",
# "tokens": [
# {
# "access_token": access_token,
# "server_name": "github",
# }
# ]
# }


@app.workflow_task(name="github_org_search_activity")
async def github_org_search_activity(query: str) -> str:
app.logger.info("github_org_search_activity started")
try:
async with gen_client(
"github", server_registry=app.context.server_registry, context=app.context
) as github_client:
app.logger.info("Obtained GitHub MCP client")
result = await github_client.call_tool(
"search_repositories",
{
"query": f"org:{query}",
"per_page": 5,
"sort": "best-match",
"order": "desc",
},
)

repositories = []
if result.content:
for content_item in result.content:
if hasattr(content_item, "text"):
try:
data = json.loads(content_item.text)
if isinstance(data, dict) and "items" in data:
repositories.extend(data["items"])
elif isinstance(data, list):
repositories.extend(data)
except json.JSONDecodeError:
pass

app.logger.info("Repositories fetched", data={"count": len(repositories)})
return json.dumps(repositories, indent=2)
except Exception as e:
import traceback

traceback.print_exc()
return f"Error: {e}"


@app.tool(name="github_org_search")
async def github_org_search(query: str, app_ctx: Optional[AppContext] = None) -> str:
context = app_ctx or app.context
result = await app.executor.execute(github_org_search_activity, query)
context.logger.info("Workflow result", data={"result": result})

return result


# NOTE: This main function is useful for local testing but will be ignored in the cloud deployment.
async def main():
async with app.run() as agent_app:
mcp_server = create_mcp_server_for_app(agent_app)
await mcp_server.run_sse_async()


if __name__ == "__main__":
asyncio.run(main())
32 changes: 32 additions & 0 deletions examples/cloud/oauth/mcp_tools/mcp_agent.config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
$schema: ../../schema/mcp-agent.config.schema.json

name: my_mcp_agent_oauth_server

execution_engine: asyncio
logger:
transports: [console]
level: debug

oauth:
# Base URL for internal callbacks (used when `use_internal_callback` is true).
callback_base_url: http://127.0.0.1:8000
# Maximum number of seconds to wait for an authorization callback before timing out.
flow_timeout_seconds: 300
# Ports to use for local loopback callbacks when internal callbacks are unavailable.
loopback_ports: [33418, 33419, 33420]
token_store:
# Seconds before expiry when tokens should be refreshed.
refresh_leeway_seconds: 60

mcp:
servers:
github:
transport: streamable_http
url: "https://api.githubcopilot.com/mcp/"
auth:
oauth:
enabled: true
scopes: ["read:org", "public_repo", "user:email"]
authorization_server: "https://github.com/login/oauth"
use_internal_callback: false
include_resource_parameter: false
12 changes: 12 additions & 0 deletions examples/cloud/oauth/mcp_tools/mcp_agent.secrets.yaml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
$schema: ../../../schema/mcp-agent.config.schema.json

# Copy this file to mcp_agent.secrets.yaml and fill in your credentials.

mcp:
servers:
github:
auth:
oauth:
client_id: "your-github-client-id"
client_secret: "your-github-client-secret"
access_token: "your-github-access-token"
1 change: 1 addition & 0 deletions examples/cloud/oauth/mcp_tools/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mcp-agent @ file://../../../../ # Link to the local mcp-agent project root
Loading
Loading