cd /news/ai-agents/how-i-connected-an-ai-agent-to-githu… · home › topics › ai-agents › article
[ARTICLE · art-141126] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

How I connected an AI agent to GitHub with Nango and MCP (without touching a single OAuth token) published: false tags: ai, mcp, python, tutorial

A developer built a Python MCP server that exposes GitHub tools to any MCP client while delegating all OAuth handling to Nango, so the server code never sees a GitHub token and only holds a Nango secret key and connection ID. The server exposes list_my_repos, list_open_issues, and create_issue tools, and the author notes that in MCP Python SDK 2.x FastMCP was renamed to MCPServer, breaking older tutorials, and that only errors raised as ToolError pass readable messages through to the agent.

by read3 min views3 publishedSep 28, 2026

Every time you connect an AI agent to an external API, you inherit the boring, risky part: OAuth flows, token storage, token refresh, and making sure nothing leaks.

In this tutorial I built a small MCP server in Python that exposes GitHub to any MCP client, where my code never sees a GitHub token. Nango handles the auth. My server only knows a Nango secret key and a connection ID.

Code: https://github.com/sravya520/nango-github-mcp

MCP client  ->  my MCP server (Python)  ->  Nango proxy (adds GitHub auth)  ->  GitHub API

The server exposes three tools:

Tool What it does
list_my_repos Lists my repos, most recently updated first
list_open_issues Lists open issues in a repo (skips pull requests)
create_issue Creates an issue in a repo

Nango's getting-started flow creates a GitHub integration (github-getting-started) and has you authorize your GitHub account. That gives you a connection ID. From then on, Nango stores and refreshes the GitHub token for that connection.

Put your values in a .env file and add .env to .gitignore:

NANGO_SECRET_KEY=your-secret-key
NANGO_CONNECTION_ID=your-connection-id
NANGO_PROVIDER_CONFIG_KEY=github-getting-started

Instead of calling api.github.com with a token, you call Nango's proxy with three headers. Nango finds the connection, adds the GitHub token and forwards the request.

def nango_request(method, endpoint, params=None, json=None):
    headers = {
        "Authorization": f"Bearer {os.getenv('NANGO_SECRET_KEY')}",
        "Connection-Id": os.getenv("NANGO_CONNECTION_ID"),
        "Provider-Config-Key": os.getenv("NANGO_PROVIDER_CONFIG_KEY"),
    }
    response = httpx.request(
        method, f"https://api.nango.dev/proxy{endpoint}",
        headers=headers, params=params, json=json, timeout=20,
    )
    ...

A one-line smoke test (GET /user) confirms the whole chain works:

I used the official MCP Python SDK. One thing that caught me out: in version 2.x, FastMCP was renamed to MCPServer, so older tutorials fail on import.

from mcp.server.mcpserver import MCPServer

mcp = MCPServer("github-via-nango")

@mcp.tool(annotations=READ_ONLY)
def list_my_repos(limit: int = 10) -> list[dict]:
    """List the user's GitHub repositories, most recently updated first.
    Use this first to find a repo's full name (owner/name)."""
    repos = nango_request("GET", "/user/repos",
                          params={"sort": "updated", "per_page": _clamp(limit)})
    return [{"full_name": r["full_name"], "url": r["html_url"]} for r in repos]

Three small choices make agents behave better:

create_issue is not, so clients can treat writes more carefully. This was my biggest lesson. In the MCP SDK, if a tool raises a normal Python exception, the agent only sees "Error executing tool". It has no idea what went wrong.

Only errors raised as ToolError pass their message through. So my error class extends it:

from mcp.server.mcpserver.exceptions import ToolError

class NangoError(ToolError):
    """A readable error the agent can show to the user."""

Now a bad repo name produces an answer the agent can act on (see the screenshot in the next step). I wrote a test that locks this in, plus seven others covering the Nango headers and URL, filtering out pull requests, input validation and error messages.

VS Code is an MCP host. When I added the server, it started it and showed it as Running, with all three tools:

To test the protocol directly, I wrote a small Python client (client_demo.py in the repo). It launches the server over stdio, lists the tools, and calls them the way an agent would:

And the error case, where the message comes through clearly:

What I tested, and what I didn't: list_my_repos ran live over MCP, through Nango to GitHub, and the bad-repo error came back over MCP too. list_open_issues and create_issue are covered by the unit tests, but I did not run them against my live account.

.env for stray lines: python-dotenv warned me about a parse error on line 4.command is the program that runs the server (Python). The server file goes in args. I had typed the server's name into claude mcp list showed it connected. Rather than keep debugging, I tested with the script above. Because the server speaks standard MCP, it should work with any MCP client. The agent side stays simple: three small tools and clear errors. All the hard auth work (OAuth, storage, refresh) lives in Nango. Adding another API like Notion or Slack would mostly mean a new integration in Nango and a few more tools, not a new auth system.

Code, tests and setup: https://github.com/sravya520/nango-github-mcp

── more in #ai-agents 4 stories · sorted by recency
── more on @nango 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/how-i-connected-an-a…] indexed:0 read:3min 2026-09-28 · —