apollo-mcp
Enables interacting with Apollo through a Telegram bot, including sending captures/check-ins, receiving notifications, approvals, polls, reactions, and rich HTML messages.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@apollo-mcpshow my current goals and upcoming tasks"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Apollo
A single-user, local-first personal agent platform.
Declare goals. Apollo decomposes them into practices, projects, habits and metrics, a scheduler drives check-ins and reviews, and agents use skills and MCP tools to do real work — while keeping you accountable.
Features & use cases · Architecture · Quick start · Configuration · CLI
Why Apollo
Most productivity tools give you a place to put tasks. Apollo gives you a loop: evidence feeds reviews, reviews rewrite practice, and agents do the upkeep.
Ontology, not tags. A
Goalis what you want; aPracticeis what you run to get it. Projects, tasks, habits and metrics hang off those two — so the system knows why something matters, not just that it exists.Agents with a job. A cheap supervisor routes to specialists (triage, planner, coach, researcher) that capture, decompose, review and research. Every run is typed, logged and replayable.
A scheduler that acts. Time, event and condition rules drive briefings, reviews, drift detection and overdue sweeps.
Durable memory. Your journal, check-ins and reviews are embedded and recalled semantically, behind a swappable
Memoryfacade.Local-first. Your database, vault and vector store stay on your machine. The only outbound traffic is your model/embeddings endpoint, Telegram, and MCP servers you choose to add.
Bidirectional MCP. Apollo consumes external MCP servers as tools, and exposes itself to Claude Desktop, Kilo and Cursor as an MCP server.
Related MCP server: Frontispice
Features
Area | What you get |
Domain | Areas, Goals, Practices, Projects, Tasks, Habits, Metrics, Check-ins, Reviews, Journal — with a classification test that keeps the schema honest |
Agents | Supervisor → one specialist (triage / planner / coach / researcher); no fan-out; capture and planning are tool-free single calls with slim typed schemas; per-run |
Tools |
|
Skills | Markdown-first ( |
Memory | Mem0 over a local Chroma server, API embeddings with a configurable fallback chain, worker-only writes, lazily constructed (no cost unless used) |
Scheduling | Cron, event subscriptions and safe registered condition predicates; cooldowns, timezone-aware, quiet hours |
Telegram | Native Bot API 10.3: streaming drafts, rich HTML, inline keyboards, approvals, polls, reactions, deep links, inline mode |
MCP | Config-driven client registry (default-deny allowlists, namespacing) and a curated stdio server with approval-aware writes |
Safety | Read free · mutate auto+audited · irreversible requires approval; single-user allowlist; path-sandboxed vault; no shell |
Ops |
|
Quality | Ruff, Pyright, 135 tests, |
Architecture
Split processes, one SQLite database as the source of truth, and a transactional outbox for every side effect.
┌──────────────────────────────────────────────┐
│ SQLite (WAL) data/apollo.db │
│ domain · events(outbox) · jobs │
│ pending_actions · notifications · audit │
│ vault_fts (FTS5 index of markdown vault) │
└──────────────────────────────────────────────┘
▲ ▲ ▲ ▲ ▲
writes │ │ │ │ │
┌───────────────┴──┐ ┌────┴─────┐ ┌──┴───────┐ ┌┴─────────┴───┐
│ apollo telegram │ │apollo- │ │apollo- │ │ apollo-mcp │
│ long polling │ │worker │ │dispatcher│ │ stdio (per │
│ in→events/jobs │ │agents │ │tick → │ │ MCP client) │
│ out←notifications│ │skills │ │jobs │ │ read tools +│
└──────────────────┘ │memory │ │retries │ │ approval- │
└────┬─────┘ └──────────┘ │ aware writes│
│ └─────────────┘
┌───────┴────────┐ ┌──────────────────┐
│ Chroma (server)│ │ MCP client │
│ localhost:8000 │ │ registry → │
└────────────────┘ │ calendar/web/… │
└──────────────────┘Process | Command | Responsibility |
Telegram bot |
| Inbound messages → events/jobs; drains notifications; renders approvals |
Worker |
| Claims jobs, runs agents/skills, owns Mem0 writes |
Dispatcher |
| Scheduler tick, event drain, condition predicates, retries |
MCP server |
| stdio server for Claude Desktop / Kilo / Cursor |
CLI |
| Admin, migrations, inspection, one-off runs |
Concurrency discipline. WAL + busy_timeout, every write in a short
BEGIN IMMEDIATE transaction, atomic job claim (UPDATE … RETURNING), leases with
heartbeat, exponential backoff to dead-letter, and a DB advisory lock enforcing a
single dispatcher. Side effects are never performed inside a transaction — domain
mutation and its outbox event commit together.
Request flow
One message becomes at most two model calls. There is no parallel fan-out: the supervisor picks a specialist and only that specialist runs.
Telegram text
│
├─ capture phrasing ("remind me…", "add…", "log…") ──► triage (1 call)
│ tool-free · slim CaptureResult · parent ids injected
│
└─ anything else ──► supervisor (1 call) ──► one of:
triage tool-free · slim CaptureResult
planner tool-free · slim nested PlanResult
coach tools + memory + skills
researcher tools + memory + skills + MCP toolsets
│
▼ typed output
apply (capture/plan) or tools (coach/researcher) ← writes happen here, with events
│
├─ events → outbox reactions (memory.add, notify.send, approval.resume)
└─ notifications → Telegram drainer → sendMessage (Markdown → safe HTML)Specialist | Tools | Skills | Output |
supervisor | none | – |
|
triage | none (ids + current time injected) | – |
|
planner | none (existing goal/task ids injected) | – |
|
coach | read, write, memory.recall, notify | summaries + on-demand |
|
researcher | read, memory.recall, notify, approval + MCP toolsets | summaries + |
|
Tools are real and still central — db_tools is what the MCP server exposes to
Claude Desktop/Kilo/Cursor, and coach/researcher use it as agent tools. Triage and
planner are deliberately tool-free fast paths: a tool-call loop cost 120 s timeouts
and duplicated writes, so they emit one typed object instead. Skills remain fully
functional for coach/researcher (progressive disclosure: only summaries are in the
prompt until load_skill is called).
Performance
Measured on the maintainer's setup (OpenRouter deepseek/deepseek-v4-flash-0731,
local Chroma), message → final Telegram reply:
Path | Avg | Range | Notes |
Capture (“remind me to …”) | 3.4 s | 2.5–4.9 s | 1 model call, ~2.2 s run, rest is claim + drain + send |
Question / chat | ~+1 call | — | supervisor routing adds one model call (~1.5–6 s, provider-bound) |
Goal / replan | ~55 s | — | one larger call (~1.2k output tokens) + one retry |
Iterations that got there: 14 s → 7.9 s (tool-free agents + lazy memory + faster poll/drain) → 3.4 s (slim capture schema). Reproduce with:
scripts/run.sh start
uv run python scripts/bench_capture.py --count 4The big levers, in order: number of tool-call round trips, output schema size, eager service construction (Mem0), and poll/drain intervals. A single provider trip measures ~1.3 s and varies; provider latency is the floor.
Quick start
# 1. Install (Python 3.11+; 3.12 recommended)
uv sync --extra memory # add --extra voice, --extra local-embeddings as needed
# 2. Configure
cp .env.example .env && chmod 600 .env # provider key, model ids, Telegram token
$EDITOR apollo.toml # timezone, tiers, autonomy, MCP servers
# 3. Verify everything is reachable
uv run apollo doctor
# 4. Start the whole stack (Chroma + dispatcher + worker + Telegram)
scripts/run.sh startThat's it — scripts/run.sh is the single entrypoint. It installs dependencies if
needed, creates the database, starts Chroma and waits for it to be healthy, then
brings up the dispatcher, worker and Telegram bot, and reports status.
scripts/run.sh # start everything (idempotent)
scripts/run.sh status # ●/○ for each component
scripts/run.sh logs worker # tail a component log
scripts/run.sh stop # stop everything
scripts/run.sh restartSend the bot a message and talk normally: "remind me to call the bank tomorrow", "log a 6 for sleep", "what should I focus on today?"
Why Chroma runs as a server (not embedded)
Chroma's embedded mode (PersistentClient(path=…)) takes an exclusive file lock
and is single-process only. Apollo is deliberately split across processes that all
touch memory — worker (writes), dispatcher, and mcp/agents (reads) — so an
embedded store can't be shared. Chroma therefore runs as a local server on loopback,
and Mem0 connects over HTTP.
That is still local-first: the vector store never leaves your machine. scripts/run.sh
starts it for you with uv run chroma run --path ./data/chroma --port 8000
(version-matched to uv.lock); no container runtime is required. See
docs/FEATURES.md for details.
Configuration
Non-secret settings live in apollo.toml; secrets live in .env (0600, git-ignored).
[app]
timezone = "America/New_York"
[provider]
tiers = { triage = "…", plan = "…", coach = "…", research = "…" }
[autonomy]
default_tier = "mutate"
irreversible = ["vault.delete", "web.send", "calendar.delete_event"]
[telegram]
topic_routing = false # single chat by default; topics are optional
quiet_hours = { start = "22:00", end = "07:00" }
[memory]
enabled = true
chroma_url = "http://localhost:8000"APOLLO_PROVIDER__BASE_URL=https://openrouter.ai/api/v1
APOLLO_PROVIDER__API_KEY=…
APOLLO_PROVIDER__CHAT_MODEL_ID=deepseek/deepseek-v4-flash-0731
APOLLO_PROVIDER__LIGHT_MODEL_ID=openai/gpt-oss-20b
APOLLO_PROVIDER__EMBEDDING_MODEL_ID=openai/text-embedding-3-small
APOLLO_TELEGRAM__BOT_TOKEN=…
APOLLO_TELEGRAM__ALLOWED_USER_IDS=[123456789]Any OpenAI-compatible endpoint works. CHAT_MODEL_ID overrides all tiers;
LIGHT_MODEL_ID handles the simple schemas only (supervisor routing, memory
extraction) — capture and planning stay on the chat model because the large command
union is unreliable on tiny models. EMBEDDING_MODEL_ID overrides
[embeddings].model. If a provider serves no embeddings, Apollo falls back to a
second provider or local fastembed.
A typical capture (message → final Telegram reply) lands in ~2–5 s end to end;
planning (goal decomposition) is heavier at ~30–60 s. Both are tool-free
single-call agents with slim schemas, and the supervisor is skipped for obvious
captures. Measure with uv run python scripts/bench_capture.py --count 4.
Telegram setup
Apollo ships in single-chat mode: natural language is routed by the supervisor,
so you just talk. Private-chat topics are optional (nine-topic information
architecture) — enable them in @BotFather and set topic_routing = true.
In @BotFather:
/mybots→ your bot → Bot Settings → Topics in Private Chats (optional)/setinline— enable inline mode (share your day from any chat)/setdescription,/setabouttext,/setuserpic— profile polish
Commands and the menu button are configured automatically at startup. allowed_updates
is set to message, edited_message, callback_query, poll_answer, inline_query, stopped_message_generation.
MCP
Consume external servers (default-deny — only allowlisted tools reach an agent, namespaced):
[mcp.servers.calendar]
transport = "stdio"
command = ["npx", "-y", "@modelcontextprotocol/server-google-calendar"]
allowlist = ["list_events", "create_event"]
namespace = "calendar"Expose Apollo to any MCP client:
{ "mcpServers": { "apollo": { "command": "uv", "args": ["run", "apollo", "mcp"] } } }Reads are free; writes return applied or pending_approval, and skills are exposed
as MCP prompts. See docs/FEATURES.md for the full tool list.
Skills
A skill is one markdown file with YAML frontmatter:
---
name: weekly-review
description: Summarize practices, surface drift, propose adjustments.
triggers: ["schedule:weekly_review", "user:review", "event:review.due"]
allowed-tools: ["db.read", "db.write", "memory.recall", "notify.telegram"]
model-tier: coach
---
<instructions / methodology / output format>The registry loads frontmatter only, keeping prompts small; full instructions load
on demand. Coach and researcher see the skill summaries and can call load_skill;
the tool-free capture/plan paths skip skills for speed. Optional
skills/<slug>/tools.py adds Python tools gated by allowed-tools. Built-ins:
capture, daily-briefing, evening-checkin, weekly-review, goal-decompose,
drift-detect, deep-research.
Scheduling
Built-in routines: morning briefing 07:00 · evening check-in 21:00 · weekly review
Sun 18:00 · drift check daily · overdue sweep hourly · vault sync every 5 minutes.
Add your own with cron, event, or safe registered condition predicates
(no_checkin_days, metric_below_target, habit_streak_broken,
goal_no_progress_days, overdue_tasks).
One-off reminders are supported: “remind me to drink water in 30 minutes”
creates a reminder.fire job scheduled at that time, which sends an urgent
notification when it fires.
Quiet hours buffer proactive notifications until the window ends, but never a reply: an answer to a message you just sent is transactional and goes out immediately, even at 2am.
Privacy & security
Everything local: SQLite, markdown vault and Chroma on loopback; single-user Telegram allowlist rejects everyone else.
Autonomy tiers:
readfree ·mutateauto-applied, audited and notified ·irreversiblerequires approval and auto-denies on expiry.Secrets are never logged;
.env,data/and editor configs that may embed keys are git-ignored.MCP and web output is treated as untrusted data; the trust boundary is stated to every agent. No arbitrary shell execution in v1.
See SECURITY.md for the threat model and reporting process.
CLI reference
apollo version | doctor | init-db | upgrade | schema
apollo pause | resume | status
apollo worker [--once] | dispatcher [--once] | telegram | mcp
apollo run --agent <triage|planner|coach|researcher> "text"
apollo queue list|retry|replay|purge
apollo schedule list|toggle
apollo vault sync|search
apollo memory recall|reindex
apollo mcp-servers list|check
apollo topics list|resetDevelopment
uv run ruff check . # lint
uv run pyright # types
uv run pytest # unit + integration (98 tests)
uv run pytest -m eval # evaluations (live model)CI runs lint, types and tests on every push. Contributions are welcome — see
CONTRIBUTING.md.
Project layout
apollo/
apollo.toml .env.example pyproject.toml alembic.ini
assets/ logo & brand assets
docs/ features & use cases
scripts/ run.sh (one-command stack), launchd/systemd templates
skills/ built-in skills (markdown + optional tools.py)
src/apollo/
domain/ db/ queue/ agents/ skills/ tools/
mcp/ memory/ scheduler/ telegram/ observability/
tests/ unit · integration · evals · fakesLicense
MIT — see LICENSE.
This server cannot be deployed
Related MCP Connectors
Private-by-default, local-first memory/context/task orchestrator for MCP apps and agents.
Shared memory and actions for Claude, Kiro, OpenAI, Cursor, and other MCP-compatible AI clients.
Agent-native notes, tasks, dev-docs, vaults, sync & handoffs. MCP + OpenAPI dual surface.
Remote MCP server to read and manage your Atako AI agents, messages, files, and integrations.
Related MCP Servers
- FlicenseAqualityBmaintenanceMCP server for Personal OS API that enables managing tasks, notes, projects, collections, reviews, and more through typed tools over stdio.66-
- AlicenseAqualityBmaintenanceMCP server for local-first agent handoff: enables AI sessions to publish curated context, list/read updates, route handoffs to specialists, and advance per-consumer cursors, with SQLite persistence and no shell or network access.8Apache 2.0
- AlicenseAqualityCmaintenanceProvides local persistent working memory for AI agents, allowing them to load rules, preferences, and project state before tasks and save validated experiences after successful completion. Works with stdio MCP clients via a local JSON file.5MIT
- FlicenseNot gradedqualityBmaintenanceEnables MCP clients to read Obsidian vault content and interact with agents, goals, runs, and projects stored in a SQLite database.-