Skip to main content

MCP server exposing a remote Claude Code agent over HTTP

Project description

sk8 🛹

PyPI

A minimal MCP server that exposes a remote Claude Code agent over HTTP. Claude Code on your laptop can call its one tool, run_task, to delegate a complete, self-contained task to a Claude Code instance running on this machine.

The tool runs claude headless here and returns the final text answer. By default it is synchronous and blocking; for long tasks, pass detach=true to get a task id back immediately and poll get_task for the result (see "Detached tasks" below).

server_sdk.py — drives the same agent loop through the Claude Agent SDK (claude-agent-sdk), giving a typed async message stream and structured permission control. This is what the container image runs, because ClaudeAgentOptions applies per-agent profile customization (tools, MCP, system prompt) natively.

Agents can be customized per profile — extra Python packages, bundled Claude Code skills, and a tool/MCP/system-prompt spec baked into the image at build time.

GCP project setup (one-time)

The sk8 CLI drives gcloud to provision agents on Cloud Run, so a GCP project has to be prepared once before sk8 create will work. Run these once per project (not per agent):

# 1. Install the gcloud SDK, then authenticate.
gcloud auth login

# 2. Select the project sk8 should deploy into (must have billing enabled).
gcloud config set project YOUR_PROJECT_ID

# 3. Enable the APIs sk8 uses (Cloud Run, Secret Manager, Artifact Registry, Cloud Build).
gcloud services enable \
  run.googleapis.com \
  secretmanager.googleapis.com \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com

# 4. Create the Docker repo sk8 pushes the agent image to.
#    The name "agents" and location must match sk8's defaults
#    (--repo agents, --region us-central1); override both flags if you change them.
gcloud artifacts repositories create agents \
  --repository-format=docker --location=us-central1

# 5. Store the shared Claude credential the agents run under, as the
#    "anthropic-api-key" secret (sk8 mounts it into every agent).
printf '%s' "$ANTHROPIC_API_KEY" | \
  gcloud secrets create anthropic-api-key --data-file=-

With that in place, sk8 create <id> --build builds the image (first time) and deploys the agent. You can preview every gcloud command without running it via sk8 create <id> --dry-run.

🛹 CLI

sk8 is the command-line tool for managing agents — scriptable for both humans and agents alike. It exposes the full agent lifecycle and emits JSON so a running agent can spawn and register sub-agents. Install it as a console script to run sk8 <cmd> from anywhere, or run it in place with python sk8.py <cmd>:

uv tool install .                  # install the `sk8` command from this repo
uv tool install --reinstall .      # reinstall after pulling/editing the code
uv tool uninstall sk8              # remove it
# (or use pipx/pip: `pipx install .` / `pipx reinstall sk8`)

Then drive the agent lifecycle:

sk8 create iris --build     # build image (first time) + provision
sk8 create iris             # subsequent agents reuse the image
sk8 create iris --profile ./profiles/data-analyst --build  # custom deps/skills/tools
sk8 create iris --json      # for agents: parse back {url, token, mcp_add_command}
sk8 create iris --dry-run   # preview the gcloud commands offline
sk8 list                    # list deployed agents in the region
sk8 delete iris --yes       # tear down the service + its token secret
sk8 suggest 5               # propose adjective-noun names

create prints the end-user's claude mcp add line (with --json, in the mcp_add_command field). Re-running create with an existing id rotates its token and redeploys.

Verify

claude mcp list        # sk8 should show as connected

Then in a Claude Code session on your laptop:

Use the sk8 run_task tool with prompt: "list the files in the current directory and summarize them"

The remote agent runs the task in its cwd and returns the final answer.

Sessions (follow-up tasks)

Every run_task answer ends with a [session_id: <uuid>] line (detached tasks report it via get_task once finished). Pass it back to continue the same conversation instead of restating the brief:

Use the sk8 run_task tool with session_id "" and prompt: "now also add unit tests for that script"

A resumed task remembers the prior exchange and runs in the session's original working directory (the cwd argument is ignored), so files from earlier turns are still in place. Session transcripts are mirrored to GCS on --bucket agents, so follow-ups work even after the instance was recycled; without a bucket, sessions last only as long as the instance. An unknown or expired id fails fast with AGENT_ERROR: unknown session_id ... — start fresh with a full brief.

Detached tasks (long-running work)

A synchronous run_task result lives and dies with its HTTP connection: if the client's MCP timeout fires (Claude Code's default is well under sk8's 600s task budget) or the connection drops, the work completes on the server and evaporates. For anything long, detach instead:

Use the sk8 run_task tool with detach=true and prompt: "..."

The call returns TASK_STARTED: <task_id> immediately; the task runs in the background in its own workspace. Poll:

Use the sk8 get_task tool with task_id "<task_id>"

which returns {status: running|done|error|lost|not_found, result, ...}result carries the final answer (including any Artifacts block) once the status is done. Poll every 30–60s; on a scale-to-zero deployment the polls also keep the instance alive.

Task records are written to local disk and, when the agent has a GCS bucket (--bucket), mirrored to <agent>/tasks/<task_id>.json — so a finished result survives the instance being recycled between the run and the poll. Without a bucket, detach still works but a recycled instance loses the record (get_task then reports not_found).

Detached tasks are supported by the SDK backend (server_sdk.py, what the container image runs). Deploys via sk8 create set --no-cpu-throttling on the Cloud Run service so background work keeps its CPU after the HTTP response returns.

Cloud deployment (Cloud Run, App Runner, Fly, …)

The GCP services (Cloud Build, Artifact Registry, Secret Manager, IAM, Cloud Run) and the agent lifecycle — image build → token mint → a run_task call triggering Cloud Run:

GCP service graph

File transfer (optional, GCS-backed)

Pass files in and out of run_task without bloating the prompt or hitting Cloud Run's 32 MB request cap. Provision an agent with a bucket:

sk8 create iris --bucket my-agents-bucket --build   # or: BUCKET=my-agents-bucket ./deploy.sh iris

This wires up GCS signed URLs and a --ttl-days lifecycle rule (default 7 days) so inputs/outputs auto-expire. The agent then exposes two extra tools:

  • request_upload_url(filename, content_type) → a signed PUT URL; upload an input straight to the bucket, then pass its object key to run_task(inputs=[...]).
  • fetch_result(object) → re-mint a signed GET URL for an artifact.

run_task also accepts small files inline (files=[{"name", "content_base64"}], no bucket needed). Anything the agent writes under cwd/outputs/ comes back in a trailing Artifacts: block — signed download URLs when GCS-backed, else inline base64. Without a bucket the feature stays dark and run_task is text-only. See docs/file-transfer.md for the full design.

Limitations

  • No streaming or progress — synchronous run_task blocks until the remote agent finishes (up to a 600s timeout); detached tasks report only running/done/error via get_task, not intermediate output.
  • No queue — detached tasks run concurrently on the single instance and share its CPU/memory; there's no scheduling or backpressure beyond that.
  • Arbitrary code executionclaude can do anything the host user can. Treat reaching this endpoint as equivalent to a shell on the box.
  • Fresh context unless resumed — each run_task without a session_id is a fresh headless claude invocation with no memory of previous tasks; put all needed context in the prompt, or resume a session for follow-ups.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sk8-0.8.0.tar.gz (526.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sk8-0.8.0-py3-none-any.whl (35.6 kB view details)

Uploaded Python 3

File details

Details for the file sk8-0.8.0.tar.gz.

File metadata

  • Download URL: sk8-0.8.0.tar.gz
  • Upload date:
  • Size: 526.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for sk8-0.8.0.tar.gz
Algorithm Hash digest
SHA256 81471ec8c5f2d3fbd4ff6186c524fb92726fa0dfc79f08a4e593091c6ef2e2dd
MD5 1461b245174e9dc7cef90fa78e7e4a83
BLAKE2b-256 53435bffe19fea57fb7d05ddf0987f8185d20a910316e147bd2d9e0cafb0ae13

See more details on using hashes here.

File details

Details for the file sk8-0.8.0-py3-none-any.whl.

File metadata

  • Download URL: sk8-0.8.0-py3-none-any.whl
  • Upload date:
  • Size: 35.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for sk8-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0c96b7fee33e12edab85d4d6cd6ca4f2c0cc6ae6e1766237c989d10131c64e0b
MD5 da129809af293be0742b43d414454739
BLAKE2b-256 6cdb7dc6f4cb3204b59a69897ded8da73f639c86ea31396e981efffe5d4fb874

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page