Skip to main content
Glama
README.md
# Mediator Agent

An impartial AI mediator for disputes between product managers and architects.
It talks to several parties at once in a shared session — on **Slack**, in
**Microsoft Teams**, on the **CLI**, through a **generic webhook**, or from inside
**any agentic CLI** (Claude Code, Codex, Cursor, …) over **MCP**. It follows a
structured protocol to separate positions from interests and makes an **auditable
recommendation** weighed against **your organization's recorded criteria** — with
human approval.

It is **vendor-independent** (Claude, OpenAI, or any OpenAI-compatible endpoint),
**OS-independent** (Windows, macOS, Linux), and needs **no Azure Bot** for Teams.

## How it works

1. **Exploration** – separate positions from the underlying interests, ask targeted questions
2. **Facts** – request missing/disputed facts (effort, deadline, tech debt, A-SPICE impact)
3. **Steelman** – restate both sides in their strongest form, with the parties' confirmation
4. **Decision** – fixed format: DECISION / RATIONALE / CONDITIONS / DISPUTED FACTS / ESCALATION

There is a code-level guard too: the engine won't allow a final decision until every
party has spoken at least twice (position + reaction) — unless the parties explicitly
ask (the `decide` command), in which case the uncertainty stays flagged.

## Install

Works on Windows, macOS, and Linux (Python 3.10+).

```bash
pip install -e .            # base engine + CLI + Claude provider
pip install -e ".[openai]"  # + OpenAI / OpenAI-compatible provider
pip install -e ".[slack]"   # + Slack adapter
pip install -e ".[teams]"   # + Microsoft Teams (Bot Framework) adapter
pip install -e ".[webhook]" # + generic HTTP webhook adapter (no Azure Bot)
pip install -e ".[mcp]"     # + MCP server for agentic CLIs
pip install -e ".[all]"     # everything
```

Copy `.env.example` to `.env` and fill in the keys you need. On PowerShell:
`Copy-Item .env.example .env`. On bash: `cp .env.example .env`.

> **In a hurry?** See the [Quick Start guide](QUICKSTART.md) — the fastest path to a
> running mediation on each surface, including a fully offline local-model setup.

## Choosing an LLM provider (vendor-independent)

The mediator's reasoning is decoupled from any single vendor. Pick a backend in
`config.yaml` (`provider:`) or with the `MEDIATOR_PROVIDER` env var:

| Provider | Set | Needs | Notes |
|---|---|---|---|
| `anthropic` | default | `ANTHROPIC_API_KEY` | Claude via the Anthropic SDK |
| `openai` | `provider: openai` | `OPENAI_API_KEY` | OpenAI **or** any OpenAI-compatible API |
| `ollama` | `provider: ollama` | Ollama running locally | Local models, **no cloud, no key** |

**Local models with Ollama (fully offline):**

```bash
ollama pull llama3.1                     # once
export MEDIATOR_PROVIDER=ollama          # PowerShell: $env:MEDIATOR_PROVIDER="ollama"
# set  model: llama3.1  in config.yaml, then run any surface (CLI, webhook, MCP…)
```

Ollama defaults to `http://localhost:11434/v1`; override with `OLLAMA_BASE_URL`.
For **Azure OpenAI / LM Studio / OpenRouter / vLLM**, use `provider: openai` with
`OPENAI_BASE_URL` set to that endpoint.

## 1. Try it on the CLI (~2 min)

```bash
# bash:        export ANTHROPIC_API_KEY=sk-ant-...
# PowerShell:  $env:ANTHROPIC_API_KEY = "sk-ant-..."
python -m mediator.cli
```

```
Dispute topic: Ship Feature X in Q3 vs. refactor the payment module
> kata: Feature X has to ship in Q3, it's a contractual OEM commitment.
> gabor: In its current state the payment module can't handle it — refactor first.
...
> /decide
```

Commands: `/decide`, `/reset`, `/exit`.

## 2. Use it from any agentic CLI (MCP)

The mediator ships as an **MCP server**, so Claude Code, Codex, Cursor, Windsurf and
other MCP-speaking CLIs can drive it directly.

```bash
claude mcp add mediator -- python -m mediator.mcp_server
```

Or in an MCP client config (Codex, Cursor, …):

```json
{
  "mcpServers": {
    "mediator": {
      "command": "python",
      "args": ["-m", "mediator.mcp_server"],
      "env": { "ANTHROPIC_API_KEY": "sk-ant-...", "MEDIATOR_CONFIG": "config.yaml" }
    }
  }
}
```

Exposed tools: `start_mediation`, `add_statement`, `request_decision`,
`mediation_status`, `reset_mediation` (each takes an optional `session_id`).

## 3. Generic webhook (no Azure Bot, any platform)

The webhook adapter is the platform-neutral surface: a tiny HTTP server that takes a
message as JSON and returns the mediator's replies as JSON. **This is how you run the
mediator in Microsoft Teams without an Azure Bot resource** — build a Teams flow in
Power Automate / Workflows (trigger on a new channel message → HTTP POST here → post
the replies back). The same endpoint works for Slack/Discord/Mattermost bridges, cron
jobs, or your own app.

```bash
python -m mediator.adapters.webhook_app     # listens on $PORT (default 3979)
```

```bash
curl -X POST http://localhost:3979/message \
  -H "Content-Type: application/json" \
  -d '{"channel_id":"team-42","user_id":"u1","name":"Kata","text":"start Ship X vs refactor"}'
```

Request fields: `channel_id`, `user_id`, `text` (required); `name`, `mentioned`
(optional). Response: `{"replies": [...]}`. Set `MEDIATOR_WEBHOOK_TOKEN` to require an
`X-Mediator-Token` header.

## 4. Slack bot

1. https://api.slack.com/apps → **Create New App** → From scratch
2. **Socket Mode** → enable, generate an App-Level Token with `connections:write` → `SLACK_APP_TOKEN` (xapp-)
3. **OAuth & Permissions** → Bot Token Scopes: `chat:write`, `channels:history`, `groups:history`, `users:read` → Install → `SLACK_BOT_TOKEN` (xoxb-)
4. **Event Subscriptions** → bot events: `message.channels`, `message.groups`
5. Fill `participants` in `config.yaml` with Slack user IDs (profile → ⋮ → Copy member ID)
6. `python -m mediator.adapters.slack_app`
7. Invite the bot: `/invite @Mediator`

## 5. Microsoft Teams

Two options:

- **Webhook (recommended, no Azure Bot)** — use the generic webhook adapter above
  with a Teams Workflow/Power Automate flow. Nothing to register in Azure.
- **Bot Framework adapter** (`mediator/adapters/teams_app.py`, the `[teams]` extra) —
  if you *do* run an Azure Bot resource: set the messaging endpoint to
  `https://<host>/api/messages`, fill `MICROSOFT_APP_ID` / `MICROSOFT_APP_PASSWORD`,
  then `python -m mediator.adapters.teams_app`.

### Chat commands (Slack, Teams, webhook)

| Command | Effect |
|---|---|
| `@Mediator start <topic>` | Start a new dispute |
| `@Mediator decide` | Request a decision (approve with ✅/❌ reactions) |
| `@Mediator status` | Who has spoken, where the dispute stands |
| `@Mediator reset` | Clear the session |

The mediator doesn't reply to every message: it speaks when addressed, or once both
parties have responded since its last turn — so it doesn't talk over anyone.

## Customization

Everything is in `config.yaml`:

- **provider / model / max_tokens** – which LLM backend and model to use
- **criteria** – the decision criteria and their weights (the auditable governance core)
- **participants** – platform user ID → name + role

The mediator's behaviour (protocol, tone, decision format) lives in `mediator/prompts.py`.

## Architecture

```
Slack ───────────┐
Microsoft Teams ─┤
Webhook (any) ───┤─► commands.py (shared protocol) ─► session.py (state, guards, JSON)
Agentic CLI (MCP)┤                                          │
CLI ─────────────┘                                          ▼
                                                     engine.py (protocol enforcement)
                                                            │
                                            ┌───────────────┴───────────────┐
                                            ▼                               ▼
                                     prompts.py (protocol,          llm.py (provider layer:
                                     criteria)                      Anthropic | OpenAI-compatible)
```

- `mediator/llm.py` — vendor-independent provider layer (Claude, OpenAI-compatible)
- `mediator/commands.py` — platform-agnostic command + turn logic shared by every adapter
- `mediator/adapters/` — `slack_app.py`, `teams_app.py`, `webhook_app.py`
- `mediator/mcp_server.py` — MCP server for agentic CLIs
- `mediator/cli.py` — interactive local CLI

Session state persists in `state/` across restarts (filenames are slugified, so any
platform's channel/conversation IDs are safe on any OS).

## Possible next steps

- **Jira/Confluence fact-checking** via MCP (effort, capacity, roadmap)
- **Advocate sub-agents** – separate agents reinforcing the PM and architect perspectives
- **Decision log** exported to Confluence (audit trail for A-SPICE)
```

Maintenance

ActivityStale
ResponsivenessNo issues