Mcp Jules — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Mcp Jules (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
MCP (Model Context Protocol) server for Google Jules - the AI coding agent. This server enables LLMs like Claude to interact with Jules API, creating and managing coding sessions programmatically.
Unofficial project. This is an independent, community-built client for the Jules API. It is not affiliated with, sponsored by, or endorsed by Google LLC. "Google" and "Jules" are trademarks of Google LLC. The Jules API is an alpha release and its specifications may change without notice.
The Jules MCP Server acts as a bridge between MCP-compatible clients and the Jules REST API.
┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ MCP Client │ │ Jules MCP Server │ │ Jules REST API │
│ (Claude, etc)│ ◄──► │ (stdio/HTTP) │ ◄──► │ (googleapis.com) │
└──────────────┘ └──────────────────┘ └──────────────────┘mcp-jules is published on npm as mcp-jules and installs a mcp-jules command.
# Install globally
npm install -g mcp-jules
# Or run it without installing (recommended for MCP clients)
npx -y mcp-jules --helpThe package requires Node.js 18 or newer and declares no post-install scripts. npx -y is the most common way to run it, because MCP clients start the server as a child process.
git clone https://github.com/cipansor/mcp-jules
cd mcp-jules
npm install
npm run build
node dist/index.js --helpThree steps from zero to a working server:
jules.google.com/settings and make sure at least one GitHub repository is connected to Jules in the Jules web UI.
export JULES_API_KEY="your-api-key-here"mcpServers entry shown in Usage.
Then ask your client to call jules_list_sources - if it returns your repositories, the wiring works.
| Variable | Required | Default | Description |
|---|---|---|---|
JULES_API_KEY | Yes | - | Jules API key. The server exits with an error when it is missing. |
TRANSPORT | No | stdio | Set to http to use the HTTP transport instead of stdio (same as --http). |
HOST | No | 127.0.0.1 | HTTP bind address. Only used by the HTTP transport. |
PORT | No | 3000 | HTTP port. Only used by the HTTP transport. Invalid values are rejected. |
Set the key as an environment variable:
export JULES_API_KEY="your-api-key-here"The key is sent to the Jules API in the x-goog-api-key header and is never written to disk by this server.
These apply only to the HTTP transport. See HTTP transport hardening for how they interact.
| Variable | Default | Description |
|---|---|---|
MCP_AUTH_TOKEN | unset | When set, /mcp requires Authorization: Bearer <token>. Mandatory for any non-loopback bind. |
ALLOWED_HOSTS | unset | Comma-separated Host header allowlist. Required when binding 0.0.0.0 or ::. |
ALLOWED_ORIGINS | loopback origins | Comma-separated Origin allowlist. Requests from unlisted origins get 403. |
ALLOW_INSECURE_HTTP | unset | Set to true to permit an unauthenticated non-loopback bind. Not recommended. |
Manage your `JULES_API_KEY` with care:
.env files containing keys to version control. The .gitignore file is configured to ignore .env files.The HTTP transport can drive mutating Jules tools with your API key, so it is locked down by default:
127.0.0.1. It refuses to bind anon-loopback address unless MCP_AUTH_TOKEN is set and ALLOWED_HOSTS lists the hostnames clients will use. ALLOW_INSECURE_HTTP=true overrides this refusal and is not recommended.
Host header is validated against theallowed hostnames, and the MCP transport rejects requests from origins outside ALLOWED_ORIGINS.
MCP_AUTH_TOKEN to requireAuthorization: Bearer <token> on /mcp. /health stays unauthenticated.
For example, to expose the server on a trusted LAN:
JULES_API_KEY=your-key \
HOST=0.0.0.0 \
MCP_AUTH_TOKEN=a-long-random-token \
ALLOWED_HOSTS=mcp.internal \
ALLOWED_ORIGINS=https://mcp.internal \
npx mcp-jules --httpALLOWED_ORIGINS must include the origin each browser client sends. If your clients run on https://app.example.com, add it to the comma-separated list; requests from origins that are not listed receive 403.
If you deploy behind a reverse proxy, terminate TLS at the proxy and keep ALLOWED_ORIGINS restricted to the exact origins of your MCP clients.
Do not set ALLOWED_HOSTS to a name that resolves elsewhere while binding loopback without a token: a tokenless listener only accepts hostnames that resolve to the local machine.
See GHSA-rgh8-8cv7-4rj9 for the advisory that prompted these controls.
Add to your Claude Desktop configuration (claude_desktop_config.json):
{
"mcpServers": {
"jules": {
"command": "npx",
"args": ["-y", "mcp-jules"],
"env": {
"JULES_API_KEY": "your-api-key-here"
}
}
}
}You can add this server to Claude Code by running:
claude mcp add jules --env JULES_API_KEY=your-api-key-here -- npx -y mcp-jules# Run with stdio transport
JULES_API_KEY=your-key npx mcp-jules
# Run with HTTP transport (binds 127.0.0.1 by default)
JULES_API_KEY=your-key npx mcp-jules --http
# Custom port for HTTP
JULES_API_KEY=your-key PORT=8080 npx mcp-jules --http
# Expose on a trusted LAN (token + explicit host allowlist required)
JULES_API_KEY=your-key HOST=0.0.0.0 MCP_AUTH_TOKEN=your-token \
ALLOWED_HOSTS=mcp.internal ALLOWED_ORIGINS=https://mcp.internal \
npx mcp-jules --http
# Show help
npx mcp-jules --helpSee HTTP transport hardening for the full list of security-related environment variables.
The HTTP transport exposes exactly two routes:
| Route | Method | Auth | Description |
|---|---|---|---|
/mcp | POST | Bearer token if MCP_AUTH_TOKEN is set | MCP streamable-HTTP endpoint. This is what MCP clients connect to. |
/health | GET | none | Liveness probe. Returns {"status":"ok","server":"mcp-jules","version":"..."}. |
The version in /health is read from package.json at runtime, so it always matches the published npm version. /health is intentionally unauthenticated so a container orchestrator can probe it without a token; it exposes no session data.
# Start the HTTP transport
JULES_API_KEY=your-key npx mcp-jules --http
# Probe liveness
curl http://127.0.0.1:3000/healthThe server exposes ten tools across three groups. Inputs are validated with a strict schema, so an unknown argument is rejected rather than ignored.
| Tool | Group | response_format | Paginated |
|---|---|---|---|
jules_create_session | Sessions | yes | no |
jules_list_sessions | Sessions | yes | yes |
jules_get_session | Sessions | yes | no |
jules_delete_session | Sessions | no | no |
jules_send_message | Sessions | no | no |
jules_approve_plan | Sessions | no | no |
jules_list_activities | Activities | yes | yes |
jules_get_activity | Activities | yes | no |
jules_list_sources | Sources | yes | yes |
jules_get_source | Sources | yes | no |
Every tool that accepts response_format defaults to markdown and also accepts json. Paginated tools accept pageSize (1-100, default 20) and pageToken, and report whether more results are available. In markdown output, jules_get_activity truncates long artifact bodies — git patches at 5,000 characters and bash output at 2,000 characters — appending ... [truncated]. JSON output is never truncated.
#### jules_create_session
Creates a new coding session where Jules executes a task on a repository.
prompt (string, required): Task description. Max 10,000 characters.sourceContext (object, required): Repository info (e.g., { "source": "sources/github-owner-repo", "githubRepoContext": { "startingBranch": "main" } }). githubRepoContext is optional.title (string, optional): Session title. Max 200 characters.requirePlanApproval (boolean, optional): If Jules should wait for plan approval.automationMode (string, optional): Use AUTO_CREATE_PR to automatically create a PR on completion.response_format (string, optional): Response format, either markdown (default) or json. ## Session: Add auth tests
**ID:** 1234567
**State:** ⏳ QUEUED
**Prompt:** Add unit tests for the authentication module
**URL:** https://jules.google/session/1234567#### jules_list_sessions
Lists all your coding sessions.
pageSize (number, optional): Max results per page (1-100, default: 20).pageToken (string, optional): Token for the next page.response_format (string, optional): Format for the response output. {
"sessions": [
{
"id": "1234567",
"title": "Add auth tests",
"state": "IN_PROGRESS",
"createTime": "2023-10-27T10:00:00Z"
}
],
"total": 1,
"hasMore": false
}#### jules_get_session
Retrieves full details for a specific session, including current state, URL, and outputs (like PR links) if completed.
sessionId (string, required): The ID of the session.response_format (string, optional): 'markdown' (default) or 'json'. ## Session: Add auth tests
**ID:** 1234567
**State:** ✅ COMPLETED
**Prompt:** Add unit tests for the authentication module
**URL:** https://jules.google/session/1234567
**Created:** Oct 27, 2023, 10:00 AM
**Updated:** Oct 27, 2023, 10:45 AM
### Outputs
**Pull Request:** [Add auth unit tests](https://github.com/myorg/myrepo/pull/42)
> This PR adds comprehensive unit tests for the auth module. {
"id": "1234567",
"title": "Add auth tests",
"prompt": "Add unit tests for the authentication module",
"state": "COMPLETED",
"url": "https://jules.google/session/1234567",
"outputs": [
{
"pullRequest": {
"title": "Add auth unit tests",
"url": "https://github.com/myorg/myrepo/pull/42",
"description": "This PR adds comprehensive unit tests for the auth module."
}
}
],
"createTime": "2023-10-27T10:00:00Z",
"updateTime": "2023-10-27T10:45:00Z"
}#### jules_delete_session
Deletes a coding session. This cannot be undone.
sessionId (string, required): The ID of the session.#### jules_send_message
Sends a message to an active session. Use this to provide feedback, answer Jules's questions, or change instructions mid-session.
sessionId (string, required): The ID of the session.prompt (string, required): Your message.#### jules_approve_plan
Approves a pending plan. Required if requirePlanApproval was set to true during session creation. Once approved, Jules begins execution.
sessionId (string, required): The ID of the session.#### jules_list_activities
Lists all activities (events, plan generation, code changes, messages) for a session.
sessionId (string, required): The ID of the session.pageSize (number, optional): Results per page. Must be between 1 and 100. Defaults to the schema-defined default when omitted.pageToken (string, optional): Pagination token.response_format (string, optional): Response format for the returned activities.#### jules_get_activity
Gets detailed information about a specific activity. This is how you view code diffs (changeSet), bash outputs, or full plan details.
sessionId (string, required): The ID of the session.activityId (string, required): The activity ID.response_format (string, optional): Controls the format of the returned activity details.and bash output to 2,000 characters, each ending with ... [truncated]. Use response_format: "json" to receive the full, untruncated payload.
#### jules_list_sources
Lists GitHub repositories connected to your Jules account via the Jules web UI.
filter (string, optional): Filter by name (e.g., name=sources/github-owner-repo).pageSize (number, optional): Results per page.pageToken (string, optional): Pagination token.response_format (string, optional): Response format for the returned data.#### jules_get_source
Gets details about a specific connected repository, including all available branches.
sourceId (string, required): The source ID (e.g., github-owner-repo). This is not the sources/... resource name used by sourceContext.source.response_format (string, optional): Controls the response format returned by the tool.jules_list_sources() -> returns sources/github-acme-webapp.
jules_create_session(prompt="Add a search bar to the header", sourceContext={"source": "sources/github-acme-webapp"}, requirePlanApproval=true) -> returns sessionId 7890.
jules_list_activities(sessionId="7890"). Look for an activity with "Plan Generated".
jules_get_activity(sessionId="7890", activityId="plan_activity_id"). If the plan looks good: jules_approve_plan(sessionId="7890").
Periodically call jules_get_session(sessionId="7890") to check state or jules_list_activities to see recent actions.
Once state is COMPLETED, check jules_get_session for the Pull Request URL.
| State | Description |
|---|---|
QUEUED | Session created, waiting for processing. |
PLANNING | Jules is analyzing the codebase and creating a plan. |
AWAITING_PLAN_APPROVAL | Plan is ready and requires your approval to proceed. |
AWAITING_USER_FEEDBACK | Jules has a question or needs more information. |
IN_PROGRESS | Jules is executing the task/plan. |
PAUSED | Execution has been temporarily halted. |
COMPLETED | Task finished successfully (check for PR link). |
FAILED | Task failed. Check activities for error details. |
| Error / Symptom | Meaning | Resolution |
|---|---|---|
Error: JULES_API_KEY environment variable is required. | Missing API key | Export JULES_API_KEY before starting the server. The process exits immediately without it. |
| 401 Unauthorized | Invalid API Key | Check your JULES_API_KEY environment variable and ensure it's valid. |
| 403 Forbidden | Permission Denied | Ensure your API key has access to the requested resource or repository. |
| 404 Not Found | Resource Missing | Verify the sessionId, activityId, or sourceId is correct. |
| 429 Too Many Requests | Rate Limited | You've exceeded the API rate limit. Wait a few minutes before retrying. |
| 500 / 503 | Jules API Error | The Jules service is experiencing issues. Try again later or check Jules Status. |
Network error - unable to reach Jules API | DNS / connectivity | Check your internet connection, DNS, or proxy settings. |
Network error: ... | Transport failure | Usually a timeout (ECONNABORTED) or a refused connection. Retry, then check firewall rules. |
... [truncated] at the end of output | A markdown artifact body exceeded its limit (5,000 chars for a git patch, 2,000 for bash output) | Re-run jules_get_activity with response_format: "json" for the untruncated payload. |
403 from the HTTP transport | Origin not allowed | Add the client origin to ALLOWED_ORIGINS. |
Refuses to bind a non-loopback HOST | Hardening guardrail | Set both MCP_AUTH_TOKEN and ALLOWED_HOSTS before binding 0.0.0.0. |
Server exits with an invalid PORT | Bad configuration | PORT must be an integer between 1 and 65535. |
Errors are returned to the MCP client as tool errors; the original Jules API status code is preserved in the message as Jules API Error (<status>): ....
# Install dependencies
npm install
# Build (emits dist/ from src/)
npm run build
# Type-check without emitting files
npm run check
# Lint source files
npm run lint
# Check formatting
npm run format:check
# Run the test suite (Node's built-in test runner)
npm test
# Development mode (watch)
npm run devThe build compiles TypeScript to dist/, which is what the mcp-jules bin and the npm tarball ship. dist/ is generated, so edit files under src/ only. The CI workflow runs the format check, lint, type-check and tests on every push and pull request.
We welcome contributions! See CONTRIBUTING.md for more details.
git checkout -b feature/my-feature).npm run check and npm run lint.Project hygiene and community documents:
Releases are driven by a git tag. The tag, the package.json version and the npm version must all agree, so the version is bumped first and the tag is pushed at the merge commit:
npm version <major.minor.patch> --no-git-tag-version.main, then tag the merge commit v<version> and push the tag.Pushing a v*.*.* tag runs the release workflow, which verifies the tag matches package.json, builds, attaches the tarball to a GitHub release, and publishes to npm with provenance via Trusted Publishing.
Publishing uses Trusted Publishing, so the release workflow authenticates with a short-lived OIDC token and no `NPM_TOKEN` secret is stored in the repository. Trusted publishing can only be attached to a package that already exists in the registry, so the package was created once with a staged publish before the first tag-driven release.
1. Bootstrap the package (already done for `mcp-jules`). A brand-new package is created with staged publishing, which needs npm CLI >= 11.15.0 and Node >= 22.14.0:
npm stage publish --access public # uploads the tarball to the stage queue
npm stage list # wait for status: staged, then copy the id
npm stage approve <stage-id> # needs 2FAStaging a package that does not exist yet creates a 0.0.0-stage placeholder so the name is reserved and the trusted publisher can be attached; the real version goes live only after npm stage approve. Staging itself does not need 2FA, which is why this path works from a token that cannot bypass 2FA - the proof-of-presence is deferred to the interactive approve step. A direct npm publish instead fails with 403 ... Two-factor authentication or granular access token with bypass 2fa enabled is required to publish packages.
Bootstrap with a throwaway version (for example 0.0.1) and leave the release you want CI to ship unpublished: a published version cannot be published again, so bootstrapping 0.2.0 would make the v0.2.0 tag fail in the publish job.
2. Add the trusted publisher. On npmjs.com, open the mcp-jules package, then Settings → Trusted Publisher → GitHub Actions and enter:
cipansormcp-julesrelease.yml (the filename only, not the full path)npm publish, not stage-only. The releaseworkflow runs npm publish, so a stage-only connection makes the publish job fail.
The identity fields are exact-match, so a wrong repository or workflow filename is the usual cause of an ENEEDAUTH or 404 failure in the publish job. These fields cannot be edited after the connection is created; delete and recreate the connection to change them.
While you are there, set Publishing access to _Require two-factor authentication and disallow bypass 2fa tokens_. Trusted publishers keep working under that setting, because CI authenticates with OIDC rather than a long-lived token.
Based on Jules REST API:
https://jules.googleapis.com/v1alphax-goog-api-key headerApache-2.0. See LICENSE and NOTICE.
Copyright 2026 Yayasan Pesantren Cipansor and the mcp-jules contributors.
This is an independent project, not affiliated with, sponsored by, or endorsed by Google LLC. "Google" and "Jules" are trademarks of Google LLC, used here only to describe what this software interoperates with.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.