# Routex

> Lightweight AI agent runtime for Go. Define multi-agent crews in YAML, run them with goroutines and channels, and let the runtime handle scheduling, parallelism, retries, and observability — without leaving the Go ecosystem.

- **Type:** MCP server
- **Install:** `agentstack add mcp-ad3bay0c-routex`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Ad3bay0c](https://agentstack.voostack.com/s/ad3bay0c)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Ad3bay0c](https://github.com/Ad3bay0c)
- **Source:** https://github.com/Ad3bay0c/routex

## Install

```sh
agentstack add mcp-ad3bay0c-routex
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

```
  ██████╗  ██████╗ ██╗   ██╗████████╗███████╗██╗  ██╗
  ██╔══██╗██╔═══██╗██║   ██║╚══██╔══╝██╔════╝╚██╗██╔╝
  ██████╔╝██║   ██║██║   ██║   ██║   █████╗   ╚███╔╝
  ██╔══██╗██║   ██║██║   ██║   ██║   ██╔══╝   ██╔██╗
  ██║  ██║╚██████╔╝╚██████╔╝   ██║   ███████╗██╔╝ ██╗
  ╚═╝  ╚═╝ ╚═════╝  ╚═════╝    ╚═╝   ╚══════╝╚═╝  ╚═╝

  lightweight AI agent runtime for Go
```

[](https://pkg.go.dev/github.com/Ad3bay0c/routex)
[](https://goreportcard.com/report/github.com/Ad3bay0c/routex)
[](https://codecov.io/github/Ad3bay0c/routex)
[](https://github.com/Ad3bay0c/routex/stargazers)
[](https://opensource.org/licenses/MIT)
[](https://github.com/avelino/awesome-go)

**A lightweight AI agent runtime for Go.**

Routex lets you build, run, and supervise multi-agent AI crews using the primitives Go developers already know — goroutines, channels, and interfaces. Define your crew in a YAML file or pure Go code, wire in any LLM provider and tools, and let the runtime handle scheduling, parallelism, retries, memory, and observability.

```bash
go install github.com/Ad3bay0c/routex/cmd/routex@latest
routex init my-crew && cd my-crew
cp .env.example .env   # add your API key
routex run agents.yaml
```

To depend on Routex from your own Go program (not the CLI):

```bash
go get github.com/Ad3bay0c/routex@latest
```

See [Using it as a library](#using-it-as-a-library) for imports and examples.

---

## Why Routex?

The AI agent ecosystem is almost entirely Python. Frameworks like LangGraph and CrewAI are powerful but they carry a Python runtime, slow cold starts, and async complexity that does not belong in production Go services.

Routex is built for Go developers who want agentic capabilities without leaving the Go ecosystem.

|                     | LangGraph  | CrewAI     | **Routex**                |
| ------------------- | ---------- | ---------- | ------------------------- |
| Language            | Python     | Python     | **Go**                    |
| Concurrency model   | asyncio    | asyncio    | **goroutines + channels** |
| Agent supervision   | none       | none       | **Erlang-style OTP tree** |
| Binary size         | heavy      | heavy      | **~10 MB single binary**  |
| Cold start          | ~2–5 s     | ~2–5 s     | **~50 ms**                |
| Multi-LLM per agent | no         | no         | **yes**                   |
| OpenTelemetry       | partial    | partial    | **first-class**           |
| Deploy target       | Python env | Python env | **any OS, Docker, K8s**   |

---

## Table of Contents

- [Concepts](#concepts)
- [Quickstart](#quickstart)
  - [Using the CLI](#using-the-cli)
  - [Using it as a library](#using-it-as-a-library)
- [YAML reference](#yaml-reference)
- [Built-in tools](#built-in-tools)
- [LLM providers](#llm-providers)
- [Multi-LLM crews](#multi-llm-crews)
- [Memory backends](#memory-backends)
- [MCP tool servers](#mcp-tool-servers)
- [Restart policies](#restart-policies)
- [Observability](#observability)
- [Writing a custom tool](#writing-a-custom-tool)
- [CLI reference](#cli-reference)
- [GitHub Actions](#github-actions)
- [Environment variables](#environment-variables)
- [Repo layout](#repo-layout)
- [Roadmap](#roadmap)

---

## Concepts

Routex borrows ideas from operating systems and applies them to AI agents:

**Agent** — a long-lived goroutine with a brain (LLM), a memory scope, and a set of tools. Agents wait on an `Inbox` channel, process one task at a time, and send results back through typed channels. They never share state.

**Crew** — a collection of agents that work together on a task. Agents declare `depends_on` relationships; the scheduler turns these into a DAG and runs independent agents in parallel.

**Scheduler** — performs topological sort (Kahn's algorithm) on the dependency graph. Agents with no dependencies run immediately in parallel. Each subsequent wave starts only when the previous wave completes. Detects cycles at startup, before any agent runs.

**Supervisor** — watches agents via a dedicated `notify` channel. When an agent fails, the scheduler asks the supervisor what to do. The supervisor checks the agent's restart budget and returns a decision: retry or give up. This design means the scheduler never advances a wave past a failed agent — it blocks until the supervisor resolves the failure.

**Tool** — anything an agent can call: web search, file read/write, HTTP request, translation, image generation. Implement one interface, register once, available to any agent.

**Memory** — each agent has a namespaced key-value and message history store. In-memory by default; Redis for persistence across runs.

**Runtime** — the orchestrator. Builds the agent graph, wires in the LLM adapters, starts the supervisor, hands tasks to the scheduler, and collects results.

---

## Quickstart

### Using the CLI

The fastest path — no Go code required.

```bash
# Install
go install github.com/Ad3bay0c/routex/cmd/routex@latest

# Scaffold a new project
routex init my-research-crew
cd my-research-crew

# Set up environment
cp .env.example .env
# Edit .env — add your ANTHROPIC_API_KEY

# Edit the generated agents.yaml file

# Validate the config
routex validate agents.yaml

# See the execution plan before running
routex run agents.yaml --dry-run

# Run it
routex run agents.yaml
```

Override the task inline without editing YAML:

```bash
routex run agents.yaml --task "Compare the latest releases of Go and Rust"
```

Use a different env file (e.g. production secrets injected by your platform):

```bash
routex run agents.yaml --env-file .env.staging --output ./reports/result.md
```

---

### Using it as a library

Import Routex into any Go application. Task input can come from an HTTP request, message queue, database — anywhere.

**Add the module to your project** (this is the library dependency — unlike `go install`, which only builds the `routex` CLI):

```bash
go get github.com/Ad3bay0c/routex@latest
```

Then import the root package and any subpackages you need (see below). If you add imports first, `go mod tidy` will record the module as well.

| Goal                          | Command                                                    |
| ----------------------------- | ---------------------------------------------------------- |
| Use Routex from your Go code  | `go get github.com/Ad3bay0c/routex@version`                |
| Install the `routex` CLI only | `go install github.com/Ad3bay0c/routex/cmd/routex@version` |

**Tool imports — opt in to what you need**

Routex's built-in tools are in separate sub-packages so you only compile what you use. Import `tools/all` for everything, or pick individual packages for a leaner binary:

```go
// All 11 built-in tools (convenience — larger binary)
import _ "github.com/Ad3bay0c/routex/tools/all"

// Or import only what you need (smaller binary, fewer dependencies)
import (
    _ "github.com/Ad3bay0c/routex/tools/file"   // read_file, write_file
    _ "github.com/Ad3bay0c/routex/tools/search" // web_search, brave_search, wikipedia
    _ "github.com/Ad3bay0c/routex/tools/web"    // http_request, read_url, scrape
    // omit tools/ai   → no OpenAI/Anthropic SDK dependency
    // omit tools/comms → no SendGrid/Resend dependency
)
```

**Option 1 — YAML-driven (recommended for most use cases):**

```go
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/Ad3bay0c/routex"
    _ "github.com/Ad3bay0c/routex/tools/all" // register all built-in tools
)

func main() {
    ctx := context.Background()

    // LoadConfig reads agents.yaml, loads .env via env_file:,
    // resolves all env: references, and validates the agent graph.
    rt, err := routex.LoadConfig("agents.yaml")
    if err != nil {
        log.Fatal(err)
    }

    result, err := rt.StartAndRun(ctx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Done in %s — %d tokens\n", result.Duration, result.TokensUsed)
    rt.Stop()
}
```

**Option 2 — override task at runtime:**

```go
rt, _ := routex.LoadConfig("agents.yaml")

// Task comes from an HTTP request, not from YAML
rt.SetTask(routex.Task{
    Input: r.FormValue("topic"),
})

// Or pass directly to LoadConfig
rt, _ := routex.LoadConfig("agents.yaml",
    routex.WithTaskInput(r.FormValue("topic")),
    routex.WithEnvFile(".env.prod"),
)

result, _ := rt.StartAndRun(ctx)
```

**Option 3 — fully programmatic (no YAML):**

```go
import (
    "github.com/Ad3bay0c/routex"
    "github.com/Ad3bay0c/routex/agents"
    "github.com/Ad3bay0c/routex/tools/search"
    "github.com/Ad3bay0c/routex/tools/file"
)

rt, _ := routex.NewRuntime(routex.Config{
    Name: "my-crew",
    LLM: routex.LLMConfig{
        Provider: "anthropic",
        Model:    "claude-sonnet-4-6",
        APIKey:   os.Getenv("ANTHROPIC_API_KEY"),
    },
})

rt.RegisterTool(search.WebSearch())
rt.RegisterTool(file.WriteFileIn("./outputs"))

rt.AddAgent(agents.AgentConfig{
    ID:    "researcher",
    Role:  agents.Researcher,
    Goal:  "Find recent news about Go 1.24",
    Tools: []string{"web_search"},
})

rt.AddAgent(agents.AgentConfig{
    ID:        "writer",
    Role:      agents.Writer,
    Goal:      "Write a summary report and save it",
    Tools:     []string{"write_file"},
    DependsOn: []string{"researcher"},
})

result, _ := rt.StartAndRun(ctx)
```

---

## YAML reference

```yaml
runtime:
  name: "my-crew" # identifies this crew in logs and traces
  llm_provider: "anthropic" # anthropic | openai | ollama | gemini
  model: "claude-sonnet-4-6" # model name for the chosen provider
  api_key: "env:ANTHROPIC_API_KEY" # env:VAR reads from environment
  log_level: "info" # debug | info | warn | error
  env_file: "." # DEVELOPMENT ONLY — load .env next to this file
  # base_url:   "env:CUSTOM_ENDPOINT"  # override LLM API endpoint

task:
  input: "Research the latest Go releases"
  output_file: "env:OUTPUT_FILE" # env: works in any string field
  max_duration: "5m" # Go duration string — 30s, 5m, 1h

tools:
  # Built-in tools — declare here, auto-registered by the runtime
  - name: "web_search" # DuckDuckGo, free, no key

  - name: "brave_search" # Higher quality, 2,000 free/month
    api_key: "env:BRAVE_API_KEY"
    max_results: 5

  - name: "wikipedia" # Free, no key needed
    extra:
      language: "en"

  - name: "http_request" # Call any REST API
    api_key: "env:MY_API_KEY" # sent as X-Api-Key header
    extra:
      bearer_token: "env:MY_TOKEN" # sent as Authorization: Bearer
      query_api_key: "env:OWM_KEY" # sent as query param (e.g. OpenWeatherMap)
      query_api_key_name: "appid" # the param name (?appid=KEY)
      param_units: "metric" # default query param on every request
      header_Accept: "application/json"

  - name: "write_file"
    base_dir: "./outputs" # agents can only write inside this dir

  - name: "read_file"
    base_dir: "./data"

  - name: "read_url" # Fetch and strip HTML from any URL

  - name: "scrape" # JS-rendered pages via ScrapingBee
    api_key: "env:SCRAPINGBEE_API_KEY"

  - name: "summarise" # LLM-powered text compression
    api_key: "env:ANTHROPIC_API_KEY"

  - name: "translate" # DeepL API, 500k chars/month free
    api_key: "env:DEEPL_API_KEY"

  - name: "generate_image" # DALL-E 3 via OpenAI
    api_key: "env:OPENAI_API_KEY"
    base_dir: "./outputs/images"

  - name: "send_email" # SendGrid or Resend
    api_key: "env:SENDGRID_API_KEY"
    extra:
      provider: "sendgrid" # sendgrid | resend
      from_email: "agent@example.com"
      from_name: "Routex Agent"

  - name: "mcp" # MCP server call
    extra:
      server_url: "http://localhost:3000"
      server_name: "github"

  - name: "mcp"
    extra:
      server_url: "http://localhost:3001"
      server_name: "postgres"

agents:
  - id: "researcher" # unique within this crew
    role: "researcher" # planner | researcher | writer | critic | executor
    goal: "Find and summarise recent Go releases"
    tools: ["web_search", "read_url"]
    restart: "one_for_one" # one_for_one | one_for_all | rest_for_one
    max_retries: 2
    timeout: "90s"
    # depends_on: []                   # list of agent IDs that must complete first
    # max_duplicate_tool_calls: 2      # redirect LLM after N identical tool calls (default: 2)
    # max_total_tool_calls: 20         # absolute tool call budget per attempt (default: 20)

    # Per-agent LLM override — uses a different model from the runtime default
    # llm:
    #   provider: "openai"
    #   model:    "gpt-4o"
    #   api_key:  "env:OPENAI_API_KEY"

  - id: "writer"
    role: "writer"
    goal: "Write a markdown report. Save it as report.md"
    tools: ["write_file"]
    depends_on: ["researcher"] # starts only after researcher finishes
    restart: "one_for_one"
    max_retries: 2
    timeout: "120s"

memory:
  backend: "inmem" # inmem | redis
  ttl: "1h"
  # redis_url: "env:REDIS_URL"         # required when backend is "redis"

observability:
  tracing: false
  metrics: false
  # jaeger_endpoint: "env:OTEL_EXPORTER_OTLP_ENDPOINT"  # e.g. http://localhost:4318
  # metrics_addr:    ":9090"           # curl localhost:9090/metrics
```

### The `env:` prefix

Any string value in the YAML can read from the environment:

```yaml
api_key: "env:ANTHROPIC_API_KEY" # reads os.Getenv("ANTHROPIC_API_KEY")
output_file: "env:OUTPUT_PATH" # works in any string field
model: "env:LLM_MODEL" # even model names
```

This means your `agents.yaml` file can be committed to git with zero secrets in it.

---

## Built-in tools

All tools are registered automatically when listed in the `tools:` section of `agents.yaml`. No `RegisterTool()` call needed for built-ins.

### Search & Data

| Tool           | Key needed            | Free tier           | Description                                 |
| -------------- | --------------------- | ------------------- | ------------------------------------------- |
| `web_search`   | none                  | unlimited           | DuckDuckGo Instant Answers                  |
| `brave_search` | `BRAVE_API_KEY`       | 2,000/month         | Structured web results with publication age |
| `wikipedia`    | none                  | unlimited           | Wikipedia article summaries, 300+ languages |
| `scrape`       | `SCRAPINGBEE_API_KEY` | 1,000 credits/month | JS-rendered page content                    |

### Web & HTTP

| Tool           | Key needed   | Description                                       |
| -------------- | ------------ | ------------------------------------------------- |
| `read_url`     | none         | Fetch and strip HTML from any URL                 |
| `http_request` | configurable | Call any REST API — GET, POST, PUT, PATCH, DELETE |

`http_request` supports four authentication patterns:

```yaml
# 1. Query string key (OpenWeatherMap, Google Maps)
extra:
  query_api_key:      "env:OWM_KEY"
  query_api_key_name: "appid"         # → ?appid=KEY on every request

# 2. Bearer token (GitHub, most REST APIs)
extra:
  bearer_token: "env:GITHUB_TOKEN"   # → Authorization: Bearer TOKEN

# 3. API key header
api_key: "env:MY_KEY"                # → X-Api-Key: KEY

# 4. Custom header
extra:
  header_X-Custom-Header: "value"    # → X-Custom-Header: value
  header_API-KEY: "value"    # → API-KEY: value
```

Default query params (sent on every request):

```yaml
extra:
  param_units: "metric" # → ?units=metric on every request
  param_lang: "en"
```

### File

| Tool         | Config     | Description                           |
| ------------ | ---------- | ------------------------------------- |
| `write_file` | `base_dir` | Write files — sandboxed to `base_dir` |
| `read_file`  | `base_dir` | Read files — sandboxed to `base_dir`  |

Path traversal (`../`) is blocked in both tools. Agents can only read or write inside the configured `base_dir`.

### AI & Generation

| Tool             | Key needed          | Description                                                 |
| ---------------- | ------------------- | -----------------------------------------

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Ad3bay0c](https://github.com/Ad3bay0c)
- **Source:** [Ad3bay0c/routex](https://github.com/Ad3bay0c/routex)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-ad3bay0c-routex
- Seller: https://agentstack.voostack.com/s/ad3bay0c
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
