An extensible AI agent framework with event-sourced architecture. Supports multiple LLM providers (OpenAI, Anthropic, Google, local via Ollama), MCP server integration, custom Python tools, parallel sub-agents, persistent memory, and both terminal and web interfaces.
In Greek mythology, an eidolon is an ideal form -- a spirit that acts on your behalf. That's the idea here.
The AI agent landscape is fragmented. LangChain is a dependency maze. CrewAI is opinionated about multi-agent patterns you may not want. AutoGen assumes you're building chatrooms. Most frameworks lock you into one provider, one way of doing things, one interface. And almost none of them speak MCP.
Eidolon was built on a few beliefs:
Your agent framework shouldn't be smarter than you. No hidden chains, no magic graphs, no 47-layer abstractions between your prompt and the LLM. The agent loop is a while True that calls the model, executes tools, and loops. You can read it in five minutes. When something breaks, the stack trace makes sense.
Provider lock-in is a trap. Today you want Claude for reasoning. Tomorrow you want Gemini for its context window. Next week you're running Llama locally for privacy. Eidolon's provider protocol is a Python Protocol -- structural typing, no inheritance. Write a class with complete(), stream_complete(), and count_tokens(), and it works. Swap providers by changing one line in a YAML file.
Tools should be functions, not frameworks. Decorate a Python function with @tool. Its type hints become JSON Schema. Its docstring becomes the description. That's it. No tool classes, no schema builders, no registration ceremony. If you can write a function, you can write a tool.
MCP is the future of tool interoperability. Instead of building a GitHub integration, a Playwright integration, and a database integration from scratch, Eidolon connects to MCP servers. Drop a .mcp.json in your project and the agent gains access to every tool those servers provide. The same MCP servers you use with Claude Code or Cursor work with Eidolon unchanged.
Events are the natural language of agents. An agent thinks, calls tools, receives results, and responds. That's a stream of events. By making events first-class -- typed, published, subscribable -- everything else falls out naturally. The WebSocket handler? Subscribe to the bus. The terminal UI? Subscribe to the bus. Audit logging? Subscribe to the bus. A new interface you haven't thought of yet? Subscribe to the bus.
You should own your agent. Eidolon runs on your machine, with your models, with your data. It works with a $0 Ollama setup or a $100/month API key. The code is MIT-licensed, 100% open source, and has zero telemetry.
- Multi-provider -- OpenAI, Anthropic (Claude), Google (Gemini), and local models via Ollama. Swap providers with one config change.
- Event-sourced kernel -- Every action produces typed events flowing through an async bus. Built-in audit trail and replay.
- MCP integration -- Connect to any MCP server (stdio, SSE, HTTP, WebSocket). Auto-discovers tools from
.mcp.json. - Tool system -- Define tools as decorated Python functions. Type hints become JSON Schema automatically.
- Sub-agents -- Spawn parallel child agents with filtered tool access and supervision policies.
- Persistent memory -- SQLite-backed conversation history with session management.
- Terminal UI -- Rich Textual TUI with streaming, tool call panels, and syntax highlighting.
- Web UI -- React + TypeScript three-panel interface with real-time WebSocket streaming.
- FastAPI backend -- REST API + WebSocket for building custom integrations.
git clone https://github.com/Parasaran-Python/eidolon.git
cd eidolon
python -m venv .venv
source .venv/bin/activate
pip install -e ".[all,dev]"Create eidolon.yaml (or copy the included one):
agent:
provider: anthropic # or openai, google, ollama
model: claude-sonnet-4-20250514
system_prompt: "You are a helpful assistant."
providers:
anthropic:
api_key: ${ANTHROPIC_API_KEY}
# openai:
# api_key: ${OPENAI_API_KEY}
# ollama:
# base_url: http://localhost:11434
# model: gemma3:4b# One-shot
eidolon run "What is the capital of France?"
# Interactive TUI
eidolon chat
# Web UI
eidolon serve # API on http://localhost:8000
cd web && npm install && npm run dev # UI on http://localhost:5173 EVENT BUS
(async, topic-based)
/ | \
v v v
AGENT LOOP TOOL MCP
(think/act) EXECUTOR MANAGER
| | |
PROVIDER @tool MCP Servers
REGISTRY funcs (stdio/SSE/HTTP)
/ | | \
OpenAI Anthropic Google Ollama
Every action produces a typed Pydantic event. The TUI, Web UI, and API are all event subscribers -- adding a new interface means subscribing to the bus.
UserMessage -> CompletionRequested -> CompletionStarted -> StreamChunk (x N)
-> CompletionFinished -> ToolCallRequested -> ToolCallStarted
-> ToolCallCompleted -> (loop back to CompletionRequested)
-> TurnCompleted
Define tools as decorated Python functions:
from eidolon.tools import tool
@tool
async def web_search(query: str, max_results: int = 10) -> list[dict]:
"""Search the web for information.
Args:
query: The search query
max_results: Maximum results to return
"""
# Your implementation
return resultsType hints become JSON Schema automatically via Pydantic. Sync functions run in a thread pool. Optional sandbox=True runs in a subprocess.
Eidolon discovers MCP servers from two sources:
eidolon.yaml-- explicit configuration.mcp.json-- auto-discovered (Claude Desktop / Cursor format)
{
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest"]
}
}MCP tools are merged into the agent's tool registry transparently -- the agent loop doesn't distinguish between local and MCP tools.
| Provider | SDK | Tool Calling | Key Features |
|---|---|---|---|
| OpenAI | openai |
Yes | GPT-4o, o-series. tiktoken token counting. |
| Anthropic | anthropic |
Yes | Claude series. Extended thinking, prompt caching. |
google-genai |
Yes | Gemini series. 1M context window. | |
| Ollama | openai (compat) |
Model-dependent | Local models. Auto-detects tool support per model family. |
Install only what you need:
pip install eidolon[openai] # OpenAI only
pip install eidolon[anthropic] # Anthropic only
pip install eidolon[google] # Google only
pip install eidolon[all] # EverythingSpawn parallel child agents with independent context:
from eidolon.agents import SubAgentSpawner, SubAgentTask
spawner = SubAgentSpawner(bus=bus, provider=provider, ...)
results = await spawner.spawn([
SubAgentTask(prompt="Research X", tool_names=["web_search"]),
SubAgentTask(prompt="Analyze Y", tool_names=["read_file"]),
])
# Both run in parallel via asyncio.TaskGroup| Command | Description |
|---|---|
eidolon chat |
Interactive TUI (or REPL fallback) |
eidolon serve |
Start FastAPI server |
eidolon run "<prompt>" |
One-shot mode |
eidolon tools list |
List available tools |
eidolon config |
Show resolved configuration |
eidolon mcp status |
MCP server connection status |
Three-panel layout: session sidebar, chat area with tool call cards, and an event inspector.
cd web
npm install
npm run dev # http://localhost:5173Requires the API server running (eidolon serve). The Vite dev server proxies /api and /ws to localhost:8000.
cd docker
docker compose up -dServices: Ollama (model server), Eidolon (Python backend), Web (nginx frontend).
eidolon.yaml is loaded from: ./eidolon.yaml -> ~/.eidolon/config.yaml. Supports ${ENV_VAR} expansion.
agent:
name: my-assistant
provider: anthropic
model: claude-sonnet-4-20250514
system_prompt: "You are a helpful assistant."
temperature: 0.7
max_tokens: 4096
providers:
anthropic:
api_key: ${ANTHROPIC_API_KEY}
ollama:
base_url: http://localhost:11434
tools:
directories: [./tools]
mcp_servers: []
# - name: github
# transport: stdio
# command: ["npx", "-y", "@modelcontextprotocol/server-github"]
memory:
backend: sqlite
path: ~/.eidolon/memory.db
server:
host: 0.0.0.0
port: 8000
cors_origins: ["http://localhost:5173"]src/eidolon/
core/ # Agent loop, event bus, event types, core types
providers/ # LLMProvider protocol + OpenAI, Anthropic, Google, Ollama
tools/ # @tool decorator, registry, executor
mcp/ # MCP client, manager, bridge, lifecycle
memory/ # SQLite persistence, event store, context window
agents/ # Sub-agent spawner, supervisor
api/ # FastAPI routes, WebSocket streaming
tui/ # Textual terminal UI
config/ # Pydantic settings, YAML loading
plugins/ # Entry-point plugin discovery
cli/ # Click CLI entry point
web/ # React + TypeScript frontend
docker/ # Docker Compose, Dockerfiles
tests/ # 482 tests (unit, integration, e2e)
pip install -e ".[all,dev]"
pytest tests/ -v # Run all tests
ruff check src/ # Lint
mypy src/ # Type checkMIT