Skip to content

Commit c3fe151

Browse files
committed
cp dines
1 parent bae2ba4 commit c3fe151

2 files changed

Lines changed: 49 additions & 47 deletions

File tree

EXAMPLES.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ python examples/<example>.py
1010

1111
### [MCP Hub + Claude Code + GitHub](./examples/mcp_github_claude_code.py)
1212

13-
Launches a devbox with GitHub's MCP server attached via **MCP Hub**, installs **Claude Code**, and asks Claude to list repositories — all without the devbox seeing your real GitHub credentials.
13+
Launches a devbox with GitHub's MCP server attached via **MCP Hub**, installs **Claude Code**, and asks Claude to describe your latest PR — all without the devbox seeing your real GitHub credentials.
1414

1515
**What it does:**
1616

@@ -19,11 +19,12 @@ Launches a devbox with GitHub's MCP server attached via **MCP Hub**, installs **
1919
3. Launches a devbox with MCP Hub enabled — the devbox receives `$RL_MCP_URL` and `$RL_MCP_TOKEN`
2020
4. Installs Claude Code (`@anthropic-ai/claude-code`)
2121
5. Registers the MCP Hub endpoint with Claude Code via `claude mcp add`
22-
6. Runs `claude --print` to ask Claude to list repos using the GitHub MCP tools
22+
6. Runs `claude --print` to ask Claude to describe your latest PR using the GitHub MCP tools
2323
7. Cleans up all resources
2424

2525
```sh
26-
GITHUB_TOKEN=ghp_xxx ANTHROPIC_API_KEY=sk-ant-xxx python examples/mcp_github_claude_code.py
26+
GITHUB_TOKEN=ghp_xxx ANTHROPIC_API_KEY=sk-ant-xxx \
27+
python examples/mcp_github_claude_code.py
2728
```
2829

2930
---

examples/mcp_github_claude_code.py

Lines changed: 45 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@
33
44
Launches a devbox with GitHub's MCP server attached via MCP Hub,
55
installs Claude Code, registers the MCP endpoint, and asks Claude
6-
to list repositories — all without the devbox seeing your real
7-
GitHub credentials.
6+
to list repositories in a GitHub org — all without the devbox ever
7+
seeing your real GitHub credentials.
88
99
Prerequisites:
1010
RUNLOOP_API_KEY — your Runloop API key
1111
GITHUB_TOKEN — a GitHub PAT with repo scope
1212
ANTHROPIC_API_KEY — your Anthropic API key (for Claude Code)
1313
1414
Usage:
15-
GITHUB_TOKEN=ghp_xxx ANTHROPIC_API_KEY=sk-ant-xxx python examples/mcp_github_claude_code.py
15+
GITHUB_TOKEN=ghp_xxx ANTHROPIC_API_KEY=sk-ant-xxx \
16+
python examples/mcp_github_claude_code.py
1617
"""
1718

1819
from __future__ import annotations
@@ -24,6 +25,7 @@
2425
from runloop_api_client import RunloopSDK
2526

2627
GITHUB_MCP_ENDPOINT = "https://api.githubcopilot.com/mcp/"
28+
SECRET_NAME = f"example-github-mcp-{int(time.time())}"
2729

2830

2931
def main() -> None:
@@ -38,84 +40,83 @@ def main() -> None:
3840
sys.exit(1)
3941

4042
sdk = RunloopSDK()
41-
secret_name = f"example-github-mcp-{int(time.time())}"
4243

43-
# 1. Create an MCP config for the GitHub MCP server
44-
print("Creating MCP config…")
44+
# ── 1. Register GitHub's MCP server with Runloop ───────────────────
45+
print("[1/6] Creating MCP config…")
4546
mcp_config = sdk.mcp_config.create(
4647
name=f"github-example-{int(time.time())}",
4748
endpoint=GITHUB_MCP_ENDPOINT,
48-
allowed_tools=["*"],
49+
allowed_tools=[
50+
"get_me",
51+
"search_pull_requests",
52+
"get_pull_request",
53+
"get_repository",
54+
"get_file_contents",
55+
],
4956
description="GitHub MCP server — example",
5057
)
51-
print(f" MCP config: {mcp_config.id}")
58+
print(f" Config: {mcp_config.id}")
5259

53-
# 2. Store the GitHub PAT as a Runloop secret
54-
print("Storing GitHub token as a secret…")
55-
sdk.api.secrets.create(name=secret_name, value=github_token)
60+
# ── 2. Store the GitHub PAT as a Runloop secret ────────────────────
61+
# Runloop holds the token server-side; the devbox never sees it.
62+
print("[2/6] Storing GitHub token as secret…")
63+
sdk.api.secrets.create(name=SECRET_NAME, value=github_token)
64+
print(f" Secret: {SECRET_NAME}")
5665

5766
devbox = None
5867
try:
59-
# 3. Launch a devbox with MCP Hub enabled
60-
print("Creating devbox with MCP Hub…")
68+
# ── 3. Launch a devbox with MCP Hub ──────────────────────────────
69+
# The devbox gets $RL_MCP_URL and $RL_MCP_TOKEN — a proxy
70+
# endpoint, not the raw GitHub token.
71+
print("[3/6] Creating devbox…")
6172
devbox = sdk.devbox.create(
6273
name=f"mcp-claude-code-{int(time.time())}",
6374
launch_parameters={
6475
"resource_size_request": "SMALL",
6576
"keep_alive_time_seconds": 300,
6677
},
67-
mcp=[{"mcp_config": mcp_config.id, "secret": secret_name}],
78+
mcp=[{"mcp_config": mcp_config.id, "secret": SECRET_NAME}],
6879
)
69-
print(f" Devbox ready: {devbox.id}")
80+
print(f" Devbox: {devbox.id}")
7081

71-
# 4. Install Claude Code
72-
print("\nInstalling Claude Code…")
82+
# ── 4. Install Claude Code ───────────────────────────────────────
83+
print("[4/6] Installing Claude Code…")
7384
install_result = devbox.cmd.exec("npm install -g @anthropic-ai/claude-code")
7485
if install_result.exit_code != 0:
7586
print("Failed to install Claude Code:", install_result.stderr(), file=sys.stderr)
7687
return
77-
print(" Installed.")
88+
print(" Installed.")
7889

79-
# 5. Register MCP Hub with Claude Code
80-
print("Registering MCP Hub endpoint with Claude Code…")
90+
# ── 5. Point Claude Code at MCP Hub ──────────────────────────────
91+
# Claude Code ──> MCP Hub (Runloop) ──> GitHub MCP Server
92+
# injects secret
93+
print("[5/6] Registering MCP Hub with Claude Code…")
8194
add_result = devbox.cmd.exec(
8295
'claude mcp add runloop-mcp --transport http "$RL_MCP_URL" '
8396
'--header "Authorization: Bearer $RL_MCP_TOKEN"'
8497
)
8598
if add_result.exit_code != 0:
8699
print("Failed to add MCP server:", add_result.stderr(), file=sys.stderr)
87100
return
88-
print(" MCP server registered.")
101+
print(" Registered.")
89102

90-
# 6. Ask Claude Code to list repos
91-
print("\nAsking Claude Code to list runloopai repos…\n")
103+
prompt = (
104+
"Use the MCP tools to get my last pr and describe what it does "
105+
"in 2-3 sentences. Also detail how you collected this information"
106+
)
107+
# ── 6. Ask Claude Code to list repos via MCP ─────────────────────
108+
print(f"[6/6] Asking Claude Code to: \n{prompt}\n")
92109
claude_result = devbox.cmd.exec(
93-
f"ANTHROPIC_API_KEY={anthropic_key} claude -p "
94-
'"Use the MCP tools to list all repositories in the runloopai GitHub org. '
95-
'Just output the repo names, one per line." '
110+
f'ANTHROPIC_API_KEY={anthropic_key} claude -p "{prompt}" '
96111
"--dangerously-skip-permissions"
97112
)
98-
99-
output = claude_result.stdout().strip()
100-
print("── Claude Code output ──────────────────────────────")
101-
print(output)
102-
print("────────────────────────────────────────────────────")
113+
print(claude_result.stdout().strip())
103114

104115
finally:
105-
print("\nCleaning up…")
106116
if devbox:
107-
try:
108-
devbox.shutdown()
109-
except Exception:
110-
pass
111-
try:
112-
mcp_config.delete()
113-
except Exception:
114-
pass
115-
try:
116-
sdk.api.secrets.delete(secret_name)
117-
except Exception:
118-
pass
117+
devbox.shutdown()
118+
mcp_config.delete()
119+
sdk.api.secrets.delete(SECRET_NAME)
119120
print("Done.")
120121

121122

0 commit comments

Comments
 (0)