Content
# Jules MCP Server
A **pure-stdlib Python MCP server** that exposes the [Google Jules](https://jules.google) autonomous coding agent as **10 native MCP tools**, so any MCP-capable agent host (Hermes Agent, Claude Desktop, Claude Code, n8n, custom clients) can create Jules coding sessions, gate its plans, track progress, and publish/merge the resulting pull requests — completely hands-free.
**Zero external dependencies.** No `pip install` needed — the server speaks the MCP stdio protocol (Content-Length framed JSON-RPC) using only `json` + `urllib`. This matters because the official `mcp` Python SDK depends on `pydantic-core` (Rust), which fails to build on Python 3.14.x. This server runs on any Python 3.8+.
---
## Table of Contents
- [What is Jules?](#what-is-jules)
- [Architecture](#architecture)
- [The 10 Tools](#the-10-tools)
- [Actual Workflow (the orchestration loop)](#actual-workflow-the-orchestration-loop)
- [Setup](#setup)
- [Step 1 — Prerequisites](#step-1--prerequisites)
- [Step 2 — Get a Jules API key](#step-2--get-a-jules-api-key)
- [Step 3 — Configure credentials](#step-3--configure-credentials)
- [Step 4 — Register the server in your MCP client](#step-4--register-the-server-in-your-mcp-client)
- [Step 5 — Verify](#step-5--verify)
- [HTTP Streamable Bridge (n8n & HTTP clients)](#http-streamable-bridge-n8n--http-clients)
- [The jules-orchestrator Skill](#the-jules-orchestrator-skill)
- [Session States](#session-states)
- [Repo Layout](#repo-layout)
- [Troubleshooting](#troubleshooting)
- [Caveats](#caveats)
- [License](#license)
---
## What is Jules?
[Jules](https://jules.google) is Google's asynchronous coding agent. You point it at a GitHub repo with a task prompt; it clones the repo into a cloud VM, proposes a plan, writes the code, and opens a pull request. Google exposes it via a REST API at `https://jules.googleapis.com/v1alpha` with three resource types:
| Resource | Meaning |
|----------|---------|
| **Source** | A GitHub repo connected to your Jules account, e.g. `sources/github/owner/repo` |
| **Session** | A unit of work (`prompt` + `sourceContext`); can auto-open a PR and auto-approve plans |
| **Activity** | Plan / progress / question / completion events inside a session |
This server wraps that API so your agent can drive Jules with tool calls instead of you clicking through the web UI.
## Architecture
```
┌─────────────────────┐ stdio (JSON-RPC, ┌──────────────────────┐ HTTPS (X-Goog-Api-Key) ┌─────────────────────┐
│ MCP client / agent │ Content-Length │ jules_mcp_server.py │ ─────────────────────────► │ Jules REST API │
│ (Hermes, Claude, │ ◄───framing─────────► │ (pure stdlib) │ │ v1alpha │
│ custom host, ...) │ │ │ HTTPS (Bearer PAT) ├─────────────────────┤
└─────────────────────┘ │ 10 jules_* tools │ ─────────────────────────► │ GitHub REST API │
└──────────────────────┘ (jules_publish_pr only) │ (PR merge) │
┌─────────────────────┐ HTTP Streamable ▲ └─────────────────────┘
│ n8n MCP Client Tool │ POST /mcp ┌─────────┴────────────┐
│ (or any HTTP host) │ ◄──────────────────► │ jules_mcp_http_bridge│
└─────────────────────┘ │ (wraps same handle())│
└──────────────────────┘
```
- **`jules_mcp_server.py`** — the stdio MCP server. Handles `initialize` / `tools/list` / `tools/call`, maps each tool to a Jules REST call. Errors are always returned as `isError: true` tool results — the process never crashes on a bad call.
- **`jules_mcp_http_bridge.py`** — optional HTTP Streamable transport wrapper around the same server, for clients that speak HTTP instead of stdio (n8n's built-in MCP Client Tool, remote hosts).
- **GitHub integration** — `jules_publish_pr` merges Jules's PR via the GitHub REST API directly, so no separate GitHub MCP server is required for the merge step.
## The 10 Tools
| # | Tool | REST mapping | What it does |
|---|------|--------------|--------------|
| 1 | `jules_list_sources` | `GET /sources` | List GitHub repos connected to your Jules account. Returns source names like `sources/github/owner/repo` — needed by `jules_create_session`. |
| 2 | `jules_create_session` | `POST /sessions` | Create **and** assign a coding session: prompt + source (+ optional `starting_branch`, `title`). Defaults: `automationMode=AUTO_CREATE_PR`, `requirePlanApproval=false` (fully autonomous). Set `require_plan_approval=true` to gate the plan. |
| 3 | `jules_list_sessions` | `GET /sessions` | List your sessions, most recent first (`page_size`, default 10). |
| 4 | `jules_get_session` | `GET /sessions/{id}` | Full state of one session — including `outputs[].pullRequest.url` once Jules finishes. |
| 5 | `jules_approve_plan` | `POST /sessions/{id}:approvePlan` | Approve the pending plan (only needed when the session was created with `require_plan_approval=true`). |
| 6 | `jules_send_message` | `POST /sessions/{id}:sendMessage` | Send a follow-up prompt: answer Jules's clarifying questions, send fix-it instructions. |
| 7 | `jules_list_activities` | `GET /sessions/{id}/activities` | The session's event log — plans, progress updates, questions, completion. Poll this to track status. |
| 8 | `jules_get_plan` | *(wrapper)* | Reads activities, finds the latest `planGenerated` event, and returns the plan as **readable text** (Plan ID + numbered steps with titles/descriptions). The Jules API has no plan-read endpoint — this tool synthesizes one. |
| 9 | `jules_update_plan` | *(wrapper)* | Request plan changes. The API has no plan-edit endpoint either — this tool reads the current plan for context, formats your `feedback` into a structured correction message, and sends it via `sendMessage`. Jules regenerates the plan; poll `jules_get_plan` to see the revision. |
| 10 | `jules_publish_pr` | *(wrapper → GitHub API)* | The "accept Jules's PR" action: reads the PR URL from the session's outputs, parses `owner/repo/pull/N`, and merges it via `PUT /repos/{owner}/{repo}/pulls/{n}/merge` (default `squash`; also `merge` / `rebase`, optional `commit_title`). |
### Tool parameter quick reference
```jsonc
jules_create_session {
"prompt": "Fix the login bug and add tests", // required
"source": "sources/github/owner/repo", // required — FULL id incl. "sources/" prefix!
"starting_branch": "main", // optional, default "main"
"automation_mode": "AUTO_CREATE_PR", // optional, default AUTO_CREATE_PR
"require_plan_approval": true, // optional, default false
"title": "Login bugfix" // optional
}
jules_update_plan {
"session_id": "1234567890",
"feedback": "Remove step 3 (CSS changes) — out of scope. Add a step to write tests for the auth handler."
}
jules_publish_pr {
"session_id": "1234567890",
"merge_method": "squash", // "merge" | "squash" | "rebase"
"commit_title": "feat: login fix" // optional
}
```
> ⚠️ **Common mistake:** the `source` parameter must be the *full* ID exactly as returned by `jules_list_sources`, **including the `sources/` prefix** (`sources/github/owner/repo`). Passing `github/owner/repo` causes an `INVALID_ARGUMENT` error.
## Actual Workflow (the orchestration loop)
There are two ways to run Jules through this server:
### Mode A — Fully autonomous ("fire and forget")
```
jules_list_sources
→ jules_create_session { prompt, source } # defaults: auto-approve plan, auto-create PR
→ poll jules_get_session / jules_list_activities # until state = COMPLETED
→ jules_get_session # read outputs[].pullRequest.url
→ review the PR yourself
→ jules_publish_pr { session_id } # merge it
```
Jules plans, codes, and opens the PR with zero interaction. Good for small, well-scoped tasks.
### Mode B — Gated (recommended, what the orchestrator skill uses)
```
1. SCOPE Write a closed spec: goal, files touched, out-of-scope, checkable acceptance criteria.
2. CREATE jules_create_session { prompt: <spec>, source, require_plan_approval: true }
3. GATE Wait for state AWAITING_PLAN_APPROVAL, then:
jules_get_plan { session_id } # read the plan
├─ plan matches spec → jules_approve_plan
└─ plan drifts → jules_update_plan { feedback } → re-read → approve
4. WAIT Poll jules_get_session (state) + jules_list_activities (events).
If Jules asks a clarifying question → jules_send_message with the answer.
5. PR On COMPLETED, the PR exists (AUTO_CREATE_PR). Get its URL from jules_get_session.
6. REVIEW Read the diff yourself (gh pr diff / GitHub MCP). Log concrete findings.
7. DECIDE Clean → step 8. Issues → step 9.
8. SHIP jules_publish_pr { session_id } # squash-merge
9. ITERATE jules_send_message { fix-it prompt with file:line findings } → back to 4.
10. STOP On iteration cap (default 4), recurring issues, or decisions Jules can't make.
```
The full playbook — including the role split (orchestrator reviews, Jules implements), prompt templates, a code-review rubric, and a per-task state file — lives in the bundled [`skills/jules-orchestrator/`](skills/jules-orchestrator/) skill.
## Setup
### Step 1 — Prerequisites
1. **Python 3.8+** (`python3 --version`). No packages to install.
2. **A Jules account** — sign in at [jules.google.com](https://jules.google.com) with your Google account.
3. **Install the Jules GitHub app** and connect your repos in the Jules web UI. *The API cannot use a repo until the GitHub app is installed on it* — this must be done in the browser first.
4. *(Only for `jules_publish_pr`)* A **GitHub personal access token** with `repo` scope, so the server can merge PRs.
### Step 2 — Get a Jules API key
1. Go to **[jules.google.com/settings#api](https://jules.google.com/settings#api)**.
2. Create an API key (maximum 3 keys per account).
3. Copy it — you'll store it in the next step.
### Step 3 — Configure credentials
The server reads credentials from **environment variables first**, then falls back to an env file (default `~/.hermes/.env`, override with `JULES_ENV_FILE`).
```bash
# Option A: env file (default location)
mkdir -p ~/.hermes
cat >> ~/.hermes/.env <<'EOF'
JULES_API_KEY=your_jules_api_key_here
GITHUB_TOKEN=github_pat_xxx # only needed for jules_publish_pr
EOF
chmod 600 ~/.hermes/.env
# Option B: plain environment variables (set in your MCP client config)
export JULES_API_KEY=your_jules_api_key_here
export GITHUB_TOKEN=github_pat_xxx
```
GitHub token resolution order for `jules_publish_pr`:
`GITHUB_TOKEN` env → `GITHUB_PERSONAL_ACCESS_TOKEN` env → env file (either name) → `mcp_servers.github.env.GITHUB_PERSONAL_ACCESS_TOKEN` in `~/.hermes/config.yaml` (so if you already run the GitHub MCP server under Hermes, the PAT is reused automatically).
### Step 4 — Register the server in your MCP client
Clone the repo somewhere stable first:
```bash
git clone https://github.com/Rahul7562/jules-mcp-server.git
cd jules-mcp-server
```
**Hermes Agent** (`~/.hermes/config.yaml`):
```yaml
mcp_servers:
jules:
command: python3
args:
- /absolute/path/to/jules-mcp-server/jules_mcp_server.py
```
Restart Hermes; the `jules_*` tools appear in the agent's toolset.
**Claude Desktop** (`claude_desktop_config.json` — macOS: `~/Library/Application Support/Claude/`, Windows: `%APPDATA%\Claude\`):
```json
{
"mcpServers": {
"jules": {
"command": "python3",
"args": ["/absolute/path/to/jules-mcp-server/jules_mcp_server.py"],
"env": { "JULES_API_KEY": "your_key", "GITHUB_TOKEN": "github_pat_xxx" }
}
}
}
```
**Claude Code**:
```bash
claude mcp add jules -e JULES_API_KEY=your_key -e GITHUB_TOKEN=github_pat_xxx \
-- python3 /absolute/path/to/jules-mcp-server/jules_mcp_server.py
```
**Any other stdio MCP client** — run `python3 jules_mcp_server.py` as the server command; it implements protocol version `2024-11-05` with the `tools` capability.
### Step 5 — Verify
Run the bundled smoke test (needs **no** API key — it verifies the protocol layer and clean error paths):
```bash
python3 test_jules_mcp.py
```
Expected output ends with:
```
TOOLS/LIST -> 10 tools:
- jules_list_sources
- jules_create_session
...
RESULT: PASS — 10 tools incl jules_publish_pr
```
Then do a live end-to-end check from your agent: call `jules_list_sources`. If it prints your connected GitHub repos, the whole chain (key → env → config → server → live API) is proven working.
## HTTP Streamable Bridge (n8n & HTTP clients)
Some hosts (notably **n8n's built-in MCP Client Tool**, which uses the official `@modelcontextprotocol/sdk`) speak MCP over HTTP instead of stdio. `jules_mcp_http_bridge.py` wraps the same server over the **MCP Streamable HTTP** transport:
```bash
python3 jules_mcp_http_bridge.py
# [jules-bridge] Listening on http://0.0.0.0:9673/mcp
```
| Route | Behavior |
|-------|----------|
| `POST /mcp` | JSON-RPC request → JSON response (`Content-Type: application/json`) |
| `GET /mcp` | `405` — tells the SDK "no SSE stream, use POST only" |
| `DELETE /mcp` | `200` — session teardown |
| notifications | `202`, no body |
Configuration via env vars: `JULES_BRIDGE_PORT` (default `9673`), `JULES_BRIDGE_HOST` (default `0.0.0.0`). Point your n8n MCP Client Tool at `http://localhost:9673/mcp`.
> The bridge sets permissive CORS headers and has no auth of its own — bind it to localhost or put it behind a reverse proxy if the machine is shared.
## The jules-orchestrator Skill
This repo bundles the **[`jules-orchestrator`](skills/jules-orchestrator/SKILL.md)** agent skill — a complete playbook for running the gated loop (Mode B) with discipline. It encodes:
- **The role split** — the orchestrator model scopes, gates, reviews, and merges; Jules only implements. Jules never reviews its own work, and the orchestrator never edits code by hand.
- **The 10-step loop** with exact tool calls per step.
- **Stop conditions** — iteration cap (default 4), recurring issues (a spec problem, not an implementation one), and decisions Jules can't make.
Bundled files:
| File | Purpose |
|------|---------|
| [`SKILL.md`](skills/jules-orchestrator/SKILL.md) | The main playbook — role split, the 10-step loop, stop conditions |
| [`references/jules-mcp-tools.md`](skills/jules-orchestrator/references/jules-mcp-tools.md) | Per-tool deep reference: parameters, returns, which loop step uses it, pitfalls |
| [`references/code-review-rubric.md`](skills/jules-orchestrator/references/code-review-rubric.md) | The checklist the review step runs against every diff (correctness, edge cases, error handling, security, integration, tests, performance) |
| [`references/lessons-learned.md`](skills/jules-orchestrator/references/lessons-learned.md) | Real-run notes: provider quirks, a boundary-condition FPGA bug caught in review, polling cadence, branch cleanup |
| [`assets/prompt-templates.md`](skills/jules-orchestrator/assets/prompt-templates.md) | Scoping template, initial session prompt, fix-it prompt |
| [`assets/state-template.md`](skills/jules-orchestrator/assets/state-template.md) | Per-task YAML state file (session ids, iteration count, PR number, merge status, review log) |
**Install** (for skill-aware hosts like Hermes Agent / Claude Code): copy the directory into your skills folder, e.g.
```bash
cp -r skills/jules-orchestrator ~/.hermes/skills/autonomous-ai-agents/ # Hermes
# or
cp -r skills/jules-orchestrator ~/.claude/skills/ # Claude Code
```
Also add a short "Working with the orchestrator" section to your target repo's `AGENTS.md` (see the skill for the exact snippet) so Jules states plans before editing and lists changed files on completion.
## Session States
```
QUEUED → PLANNING → AWAITING_PLAN_APPROVAL → IN_PROGRESS → COMPLETED
→ FAILED
↗ AWAITING_USER_FEEDBACK (can happen mid-session)
↗ PAUSED
```
- `AWAITING_PLAN_APPROVAL` only occurs when `require_plan_approval=true`.
- `jules_get_plan` errors with "No plan found" while state is still `PLANNING` — poll `jules_get_session` until `AWAITING_PLAN_APPROVAL` first.
- Poll every 10–30 s; Jules sessions typically take minutes.
- A full condensed REST reference (endpoints, activity types, artifact schemas) is in [`docs/jules-api-reference.md`](docs/jules-api-reference.md).
## Repo Layout
```
jules-mcp-server/
├── jules_mcp_server.py # The MCP server (stdio transport) — the main artifact
├── jules_mcp_http_bridge.py # Optional HTTP Streamable transport bridge (port 9673)
├── test_jules_mcp.py # Smoke test: helper units + full MCP handshake (no API key needed)
├── docs/
│ └── jules-api-reference.md # Condensed Jules v1alpha REST reference
├── skills/
│ └── jules-orchestrator/ # The bundled orchestration skill (see above)
├── LICENSE # MIT
└── README.md
```
## Troubleshooting
| Symptom | Cause / fix |
|---------|-------------|
| `ERROR: JULES_API_KEY not set` | Key missing from env and env file. Add `JULES_API_KEY=...` to `~/.hermes/.env` (or set `JULES_ENV_FILE` to your file's path, or export the var in the client's `env` block). |
| `Jules API HTTP 400 ... INVALID_ARGUMENT` on create_session | You passed `github/owner/repo` as source. Use the **full** id `sources/github/owner/repo` exactly as returned by `jules_list_sources`. |
| `jules_list_sources` returns empty | The Jules GitHub app isn't installed on any repo. Connect repos in the Jules web UI first — the API can't do this. |
| `No plan found in session ...` from `jules_get_plan` | Session is still `PLANNING`, or was created with auto-approve (no plan gate exists). Poll `jules_get_session` for state first. |
| `Session ... has no pull request yet` from `jules_publish_pr` | Session still running, or `automation_mode` was empty (no auto PR). Confirm `COMPLETED` via `jules_list_activities`. |
| `No GitHub token available` | Set `GITHUB_TOKEN` or `GITHUB_PERSONAL_ACCESS_TOKEN` (env or env file). Needed only for `jules_publish_pr`. |
| `GitHub merge HTTP 405/409` | PR not mergeable — branch conflicts, failing required checks, or branch protection. Resolve on GitHub, then retry. |
| Official `mcp` SDK install fails on Python 3.14 | That's exactly why this server exists — it needs **no** SDK. Just run `jules_mcp_server.py` directly. |
| n8n can't connect | Use the HTTP bridge (`jules_mcp_http_bridge.py`), endpoint `http://localhost:9673/mcp`, not the stdio server. |
## Caveats
- The Jules API is **v1alpha** — endpoints, fields, and keys may change without notice; no stability guarantee.
- Sessions are **repo-scoped**, not person-scoped: "assigning" work = pointing a session at a repo + prompt.
- Auto-created PRs land on **your** GitHub account — always review generated PRs before merging (that's the whole point of the orchestrator loop's review gate).
- Usage may consume Jules/Gemini quota; rate limits apply. Max 3 API keys per account.
- `jules_publish_pr` **merges** the PR (the terminal "accept" action). If you want publish-without-merge semantics, read the PR URL from `jules_get_session` and handle the merge separately.
## License
[MIT](LICENSE)
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available