Why Raven
Built for control
Your code, your keys, your infrastructure. No Raven-run service in the middle, no telemetry by default, no vendor lock-in.
Self-hosted
One Python process on your own server. Nothing of yours runs on anyone else's hardware: the calls it makes go to the LLM provider, the messengers and the tools you configure, and every one of those destinations is listed in the egress map.
66 tools
Files, shell, git, web search, code analysis, testing, memory, routines, monitors — all over MCP.
Two transports
stdio for local clients that spawn the server as a subprocess, JSON-RPC over HTTP for remote access.
Workspace confinement
Every file operation is bounded to your project. Symlinks resolved, escape attempts blocked.
Keyed auth
HTTP transport requires a secret token. Fail-closed: unconfigured means rejected, not accepted.
MIT licensed
Use it, modify it, distribute it. No copyleft, no strings attached.
Registry
Tool registry
Every tool the server exposes, with the parameters it accepts. Select one to build its request frame in the console.
Loading registry…
Protocol
Send a request
Build a real JSON-RPC 2.0 frame in your browser: start from an example, edit it, or select a tool in the registry and the frame comes pre-filled.
Enter sends the frame, Shift+Enter adds a line. The eight exchanges behind these buttons
were captured from a live raven-mcp process, including its
-32602 and -32601 replies. Anything outside that recording is
answered by a running server, not by this page.
Run it
One registry, three ways in
The same tools are reachable as an MCP server, as a terminal coding agent with its own session store, and behind a gateway that holds the channel adapters.
MCP server
Stdio for a client that spawns the process, JSON-RPC over HTTP for one that reaches it across a network.
raven-mcp --workspace <dir>
ravencode-mcp --workspace <dir>
Coding agent
RavenCode runs the registry in an agent loop, keeps sessions on disk, exports them with credentials stripped and answers as an OpenAI-compatible endpoint.
ravencode tui <dir>
ravencode serve --port 8080
ravencode session export <id>
Gateway
One process holds the registry, the DM policy and the messengers, so a chat message reaches the same tools a client frame does.
raven start
raven start --daemon
ravenflow
Gateway
15 channel adapters, one admission policy
One process holds every messenger on one event loop. Each adapter reads its own variables and stays silent when they are empty, so starting the server without credentials opens no connections and answers no chats.
| Adapter | Inbound path | Variables it reads |
|---|---|---|
discord |
Gateway websocket, launched with the bot token | DISCORD_BOT_TOKEN |
email |
IMAP poll of the inbox, SMTP for the reply |
EMAIL_IMAP_HOST EMAIL_IMAP_USER
EMAIL_IMAP_PASS EMAIL_POLL_INTERVAL
|
feishu |
POST /api/webhooks/feishu, SHA-256 over
timestamp, nonce, key and body |
FEISHU_ENCRYPT_KEY |
github |
POST /api/webhooks/github, HMAC of the raw body
in X-Hub-Signature-256 |
GITHUB_WEBHOOK_SECRET |
gitlab |
POST /api/webhooks/gitlab, shared token in
X-Gitlab-Token |
GITLAB_WEBHOOK_SECRET |
googlechat |
POST /api/webhooks/googlechat |
WEB_SECRET_KEY |
irc |
Socket, TLS by port; channels joined after 001 |
IRC_SERVER IRC_NICK IRC_CHANNELS
|
line |
POST /api/webhooks/line, base64 HMAC in
X-Line-Signature |
LINE_CHANNEL_SECRET |
matrix |
GET /_matrix/client/v3/sync long-poll, 30 s |
MATRIX_HOMESERVER MATRIX_ACCESS_TOKEN |
signal |
POST /api/webhooks/signal from a signal-cli receiver |
WEB_SECRET_KEY |
slack |
POST /api/webhooks/slack/events, v0 HMAC of
timestamp and body |
SLACK_SIGNING_SECRET |
teams |
POST /api/webhooks/teams |
WEB_SECRET_KEY |
telegram |
getUpdates long-polling through the Bot API |
TELEGRAM_BOT_TOKEN |
webchat |
WebSocket /ws, /ws/stream,
/ws/canvas |
WEB_SECRET_KEY |
whatsapp |
POST /api/webhooks/whatsapp plus the
GET handshake |
WHATSAPP_APP_SECRET WHATSAPP_VERIFY_TOKEN
|
A webhook route that has no secret of its own accepts the shared HMAC of the raw body in
X-Webhook-Signature instead. With neither key configured it answers 403 to
everything: an unverified payload is refused, not parked in a low-priority queue.
Outbound-only settings — TEAMS_WEBHOOK_URL, GOOGLECHAT_WEBHOOK_URL,
FEISHU_WEBHOOK_URL — carry replies, they do not admit anyone.
Who may talk to it
An unknown address opens a pairing, not a session
Welcome! Your pairing code is: `H7KQ2M`
Please send `/pair H7KQ2M` to authorize.
Six characters from A–Z0–9, minted per address while DM_POLICY=pairing
The owner approves it on the machine, not in the chat
raven pairing list
raven pairing approve H7KQ2M
raven doctor
raven doctor lists the adapters that have credentials, so an inert one is visible
Policy is a switch you throw on purpose
DM_POLICY=open admits anyone who finds the bot;
closed admits only addresses already in the store.
CHANNEL_ALLOW_FROM takes a JSON map of channel to user ids and
answers a refusal on the spot. Every approval is written to the audit log and
counted in the metrics.
The console the same process serves
raven start mounts the built React console on the API it already runs —
no separate web server, no CDN, no second port to firewall.
cd web && npm install && npm run build
raven start
# http://127.0.0.1:18888 — WEB_HOST, WEB_PORT, RAVEN_WEB_DIST
Bound to loopback by default; the gateway warns when you point WEB_HOST at
0.0.0.0. The bundle is looked up in RAVEN_WEB_DIST, then the wheel,
then web/dist, and if none of them carry an index.html the API still
starts and logs the build command instead.
| Group | Routes under the SPA shell |
|---|---|
| Agent | agent agents chat
chat/history collab contexts
dream tasks |
| Code | code code-quality git
github ide scaffold
tests cicd |
| Automation | workflows routines monitors
plugins chaos abtesting |
| Knowledge | knowledge rag finetune
providers cost |
| Insight | analytics insights
project-insights |
| Content | media voice email
browser web-search |
| Operations | admin settings components |
/assets and a catch-all are mounted last, after every router, so
/api/* keeps winning and an unknown path under /api/ answers 404
instead of the shell. The console speaks to those same routes: it is a client of the
gateway, not a service beside it.
Path
How a request reaches a tool
One protocol, three stages: a client frame, a confined server, a registered tool.
Clients
raven-mcp
Tools (66)
Numbers from the repository
Counted in the code on this branch, not estimated.
- Registered tools
- 66
- Channel adapters
- 15
- Transports
- stdio · HTTP
- Protocol version
- 2025-03-26
- Runtime
- Python 3.11+
- License
- MIT
Each tool carries its own JSON Schema, so tools/list returns the
parameter names and types a client needs before it calls anything.
The two stdio servers answer the same registry and differ only in the name they
report at initialize: raven-mcp and ravencode.
| Surface | Detail |
|---|---|
| stdio, Raven server | raven-mcp --workspace /path/to/project |
| stdio, RavenCode server | ravencode-mcp --workspace /path/to/project |
| module entry points | python -m raven.core.mcp / python -m ravencode.mcp |
| JSON-RPC over HTTP | POST /mcp/rpc |
| plain tool list | GET /mcp/tools |
| direct tool call | POST /mcp/tools/{name} |
| events and liveness | GET /mcp/events, GET /mcp/health |
| auth | X-Raven-Key or Authorization: Bearer |
| path confinement | --workspace, rejects outside paths and symlink escapes |
Refusals
What the server refuses to do
Self-hosting is only worth the name if the process says no by itself, with no policy service to consult and nothing to phone home to.
-
Step outside the workspace
Every file tool resolves its target against the
--workspaceroot with symlinks followed before the check. A path that lands outside is rejected, not clamped to the boundary. -
Answer an unauthenticated call
The HTTP transport requires
X-Raven-KeyorAuthorization: Bearer. With no key configured it fails closed: the request is refused rather than waved through. -
Trust an unknown sender
The gateway's DM policy defaults to
pairing, so a first message from an address it has not paired with gets a code instead of a session.openhas to be chosen on purpose. -
Let an identifier name a path
Session ids are validated against
^[A-Za-z0-9_-]{1,128}$at the HTTP, WebSocket, store and CLI boundaries, so one cannot address a directory outside the session root. -
Hand a key to an export
session export --sanitizemasks entries whose names look like credentials and values that match provider key shapes, recursing through nested lists and objects. -
Report a number it did not measure
Nothing here is fetched from a third-party host: the fonts, the tool data and the recorded frames all ship from this directory, and the build fails if a request escapes it.
Setup
Install and configure
Three steps from a clone to a client calling tools.
Install the server
git clone https://github.com/ssrjkk/raven
cd raven
pip install .
Python 3.11+ · installs the raven-mcp and ravencode-mcp commands
Add it to your client
{
"mcpServers": {
"raven": {
"command": "raven-mcp",
"args": ["--workspace", "/path/to/project"]
}
}
}
Same shape for every MCP client that starts a stdio server
Call the tools
Ask the assistant to read a file, run a command or search the web. It calls the
MCP tool and Raven runs it inside the workspace you allowed. Over HTTP, the same
tools answer POST /mcp/rpc with a bearer key.