Skip to main content

Self-hosted infrastructure that provisions isolated workspaces, clones GitHub repos, and launches external coding agents (Claude Code CLI) — streaming their stdout as agent.output SSE events. Not an orchestrator: no tool-call parsing, no conversation loop.

Project description

Mad

That's mad!

Multi Agent Develop — a self-hosted infrastructure layer that provisions isolated workspaces, clones a GitHub repository, and launches an external coding agent (Claude Code CLI today) against it. Each agent's stdout is streamed as agent.output Server-Sent Events on a per-session log, and a final session.status_idle (or session.error) event signals completion.

Mad is infrastructure, not an orchestrator. It does NOT parse tool calls, NOT execute tools, and NOT manage a conversation loop — those concerns belong to the external agent's own harness. Multiple sessions can run in parallel, each with its own agent process and its own event stream; what Mad does not do is coordinate them into a single autonomous "team."

The full scope contract lives in CLAUDE.md ("What this project is" + hard rule 1).

Status

Early days — 0.x. Single launcher provider (claude_cli); HTTP + SSE surface stable enough to build clients against; multi-tenancy deferred (ADR-0006).

Requirements

  • Linux host (see Operating System :: POSIX :: Linux classifier)
  • Python ≥ 3.11
  • The claude CLI installed and on PATH (override the binary with MAD_CLAUDE_CLI_BIN; per-run timeout via MAD_CLAUDE_CLI_TIMEOUT_S)
  • Optionally: the opencode CLI for the opencode provider (override the binary with MAD_OPENCODE_BIN; per-run timeout via MAD_OPENCODE_TIMEOUT_S, default 600 s)
  • A GitHub token with repo scope for cloning private repos (passed per-request, never persisted — see hard rule 2)
  • Session workspaces are created under ~/mad by default. Override the base directory with MAD_WORKSPACE_DIR (used verbatim — no ~/$VAR expansion) when you need a larger or persistent disk; resolution is MAD_WORKSPACE_DIR~/mad → the system temp dir (last resort, only if the home directory cannot be resolved). The base is created on first use.

Install

The distribution is published as mad-bros; the import package and console script are both mad:

pip install mad-bros
mad serve            # uvicorn factory on 0.0.0.0:8000 by default

From a checkout (development):

make install   # create venv + `pip install -e '.[dev]'`
make test      # pytest -q
make serve     # uvicorn mad.adapters.inbound.http.app:create_app --factory
make help      # full target list

With Docker (one or more isolated instances on a single host):

cp .env.example .env
docker compose -f compose.example.yml up -d --build

See docs/docker.md for per-instance credential setup, the workspace bind-mount model, and running multiple instances.

Quickstart

A session has two parts: an agent spec (which launcher to run) and a list of resources to mount into the isolated workspace. Resources can be github_repository (cloned into mount_path) or file (literal content written at mount_path). The prompt is sent as a separate message after creation; that's what kicks the agent off.

# 1. Create the session — provisions a workspace and clones the repo.
curl -sS -X POST http://localhost:8000/v1/sessions \
  -H 'Content-Type: application/json' \
  -d '{
        "agent": {
          "name": "my-agent",
          "provider": "claude_cli"
        },
        "resources": [
          {
            "type": "github_repository",
            "url": "https://github.com/octocat/Hello-World.git",
            "mount_path": "/workspace/repo",
            "authorization_token": "ghp_xxx",
            "checkout": {"type": "branch", "name": "main"}
          }
        ]
      }'
# → { "session_id": "sesn_…", "status": "created", "workspace": "…", "resources_mounted": […] }

# 2. Send the first user message — this launches the external agent.
curl -sS -X POST http://localhost:8000/v1/sessions/sesn_XXX/messages \
  -H 'Content-Type: application/json' \
  -d '{"content": "Summarize the README in one sentence."}'

# 3. Stream the cross-session event log (Last-Event-ID resumable per ADR-0005).
curl -N http://localhost:8000/v1/events/stream
# Optional filters: ?session_id=sesn_XXX&kind=agent.output

Each frame on the stream is id: <uuidv7>\ndata: {…}\n\n where the JSON object carries event_id, session_id, type, data, and timestamp. Representative types Mad emits:

Type Emitted when
session.created Session row written and workspace provisioned
agent.output One line of stdout from the external agent
session.status_idle Agent exited 0
session.error Agent exited non-zero or timed out

For private repos, set authorization_token on the github_repository resource. Mad uses it once for git clone and immediately strips it from the remote URL (hard rule 2). For historical replay outside SSE, GET /v1/events?after_event_id=…&limit=… returns the same shape with a next_cursor.

Project structure

The package follows a hexagonal / ports-and-adapters layout — see ADR-0003 for the rationale.

mad/
├── pyproject.toml                     # package metadata, deps, `mad` console script
├── src/mad/
│   ├── core/                          # framework-free domain (no FastAPI, no subprocess)
│   │   ├── sessions/                  # sessions bounded context (domain, ports, use_cases)
│   │   └── events/                    # cross-session events (domain, ports, use_cases, emitter)
│   ├── adapters/
│   │   ├── inbound/http/              # FastAPI app factory + routes (sessions, events stream)
│   │   └── outbound/                  # agents (claude_cli launcher), persistence (JSONL), events
│   └── entry_points/cli.py            # `mad` console script (uvicorn launcher)
└── tests/
    ├── unit/                          # core + adapters in isolation
    ├── integration/                   # HTTP + SSE end-to-end
    └── support/                       # test-only doubles (e.g. ScriptedLauncher)

The architectural boundary (mad.core is framework-free and adapter-free) is enforced by import-linter — see hard rule 4 in CLAUDE.md.

Vision

Today Mad runs one external agent per session. The longer-term direction is to use this same infrastructure as the substrate for multi-agent workflows — multiple coordinated sessions collaborating on a goal, each one an isolated workspace with its own event stream. Mad itself stays an infrastructure layer; orchestration, when it exists, will live in a separate module on top.

The "Multi Agent Develop — takes an idea and ships it end-to-end" framing belongs to that future. The package today is the substrate, not the orchestrator.

Documentation

License

See LICENSE.

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

mad_bros-0.5.15.tar.gz (87.9 kB view details)

Uploaded Source

Built Distribution

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

mad_bros-0.5.15-py3-none-any.whl (126.0 kB view details)

Uploaded Python 3

File details

Details for the file mad_bros-0.5.15.tar.gz.

File metadata

  • Download URL: mad_bros-0.5.15.tar.gz
  • Upload date:
  • Size: 87.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mad_bros-0.5.15.tar.gz
Algorithm Hash digest
SHA256 d67e68c6ae24840aec93f052ba5067bd7ea27e11e5d362fa0bb3c5605a053fc0
MD5 a0d1561ced9e6bd56f5a9b76056c369d
BLAKE2b-256 ffcca315c6205addf71856331da62813a0e36af86d748abfd0a369719d48de45

See more details on using hashes here.

Provenance

The following attestation bundles were made for mad_bros-0.5.15.tar.gz:

Publisher: release.yml on jlsaco/mad

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mad_bros-0.5.15-py3-none-any.whl.

File metadata

  • Download URL: mad_bros-0.5.15-py3-none-any.whl
  • Upload date:
  • Size: 126.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mad_bros-0.5.15-py3-none-any.whl
Algorithm Hash digest
SHA256 08d34325a6533fa9e9606e4a991dc841e229f81e00282d01769e775e54c33017
MD5 f8fa4d688e02aa10a341928651917032
BLAKE2b-256 eec348eeffab031d9864ebecbd7bff05663a3c8245c25e0fc8f53ed36ef82e90

See more details on using hashes here.

Provenance

The following attestation bundles were made for mad_bros-0.5.15-py3-none-any.whl:

Publisher: release.yml on jlsaco/mad

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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