Lore
An MCP server that captures and retrieves team decisions and facts from Slack with provenance, supporting search, remember, and forget operations, while integrating with external tools via MCP client.
README
Lore — the org-memory agent for Slack
"What did we decide about pricing?" · "Who owns the hospital pilot?" · "When did we flip the go-live date?" Every team has these questions. Nobody has a fast, sourced answer.
Lore is a Slack agent that captures decisions, commitments, and facts as they happen — then answers questions about them with provenance: every answer links the exact Slack messages (and the GitHub PR, Jira issue, or Notion doc) where the fact was established.
Built for the Slack Agent Builder Challenge 2026 · New Slack Agent track · Apache-2.0
The problem
Knowledge lives and dies in Slack. A decision made in #product in March is lost to search by June. When people leave, their context leaves. When channels get noisy, facts get buried. Existing tools (Notion AI, Confluence AI, Guru) require someone to manually move Slack decisions into a docs system — so they're always stale and always incomplete.
Lore solves this by living in Slack — capturing decisions the moment you @Lore them, /lore them, use the "Remember this" shortcut, or drop a file — and exposing that memory to every AI tool your team uses.
Qualifying technologies
| Slack Agent Builder Challenge tech | How Lore uses it | File |
|---|---|---|
| Slack AI / Assistant API | assistant_thread_started + assistant_thread_context_changed events; Assistant-thread surface for contextual Q&A; AI digests power the research synthesis pass |
src/lore/slackio/events.py |
| MCP server integration | Lore is an MCP server (org_memory_search, org_memory_remember, org_memory_forget) AND an MCP client (per-tenant CRM/Jira/Linear/GitHub integrations) |
src/lore/mcpio/server.py, src/lore/mcpio/client.py, src/lore/integrations.py |
| Real-Time Search API | assistant.search.context called at answer-time to inject fresh Slack context in assistant threads (gracefully skipped on channel @mentions, where the action token doesn't apply); used in the 5-phase research pipeline's public-search stage |
src/lore/runner.py, src/lore/rts.py |
All three technologies are active simultaneously on every question.
What makes Lore different
| Lore | Notion AI / Confluence AI | Guru | Slack AI (native) | |
|---|---|---|---|---|
| Captures from Slack natively | ✅ | ❌ (manual import) | ❌ (manual import) | ✅ |
| Provenance to original message | ✅ | ❌ | ❌ | ✅ |
| Correction with history kept | ✅ | ❌ | ❌ | ❌ |
| Fact decay (stale = fades) | ✅ | ❌ | ❌ | ❌ |
| File upload → Q&A | ✅ | ✅ | ✅ | ❌ |
| Approval gate for dangerous requests | ✅ | ❌ | ❌ | ❌ |
| Exposed as MCP server | ✅ | ❌ | ❌ | ❌ |
| Per-tenant external tool integration | ✅ (GitHub, Linear, CRM…) | ❌ | ❌ | ❌ |
| Works in Claude Desktop / Cursor too | ✅ | ❌ | ❌ | ❌ |
The core insight: Lore doesn't pull docs into Slack. It turns Slack itself into a queryable, self-correcting, auditable knowledge base — and then exposes that knowledge base to every AI tool your team uses.
How it works
Slack event ──▶ durable intake Redis-stream exactly-once admission;
survives restarts, deduplicates retries
──▶ armor scan PII + credential regex screen before
any LLM call; blocks prompt injection
──▶ intent classification simple / research / complex /
high_consequence routing
──▶ fact extraction decision? commitment? owner? date?
attributed to the Slack user who said it
──▶ memory store hybrid vector (Qdrant) + keyword (Postgres)
reinforcement/decay: confirmed = stronger,
untouched = fades, corrected = superseded
──▶ retrieval (at ask-time) 3-tier grounding: context → history → RAG
+ Real-Time Search API freshness layer
──▶ answer + provenance Block Kit card, source permalinks,
Confirm / Correct / Forget buttons
──▶ (if high_consequence) Approval gate: asyncio.Future bridge,
Block Kit card, 5-minute timeout
Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ Slack workspace │
│ mentions · DMs · shortcuts · /lore · file uploads · App Home │
└──────────────────────────┬──────────────────────────────────────────┘
│ Socket Mode (xapp-)
▼
┌─────────────────────────────────────────────────────────────────────┐
│ lore/slackio (Bolt/async) │
│ events.py │ actions.py │ blocks.py │ assistant.py │
│ intake.py │ approval/ │ file ingest pipeline │
└──────────────────────────┬──────────────────────────────────────────┘
│ Redis stream (exactly-once)
▼
┌──────────────────────────────────────┐ ┌───────────────────────────┐
│ runner.py (orchestrator) │ │ integrations.py │
│ intent router → dispatch path │ │ per-tenant MCP client │
│ 3-tier grounding → RTS → RAG │ │ (GitHub, Linear, CRM…) │
│ ReAct tool loop (≤6 tools, 3-strike)│ └──────────┬────────────────┘
│ 5-phase research pipeline │ │ stdio/http/sse MCP
│ approval gateway (asyncio.Future) │ ┌──────────▼────────────────┐
└────────────┬──────────┬─────────────┘ │ External MCP servers │
│ │ └───────────────────────────┘
┌────────▼──┐ ┌────▼──────────┐
│ store.py │ │mcpio/server.py│ ◀── Claude Desktop / Cursor /
│ Postgres │ │ org_memory_ │ any MCP client
│ + Qdrant │ │ search/ │
└────────────┘ │ remember/ │
│ forget │
└───────────────┘
│
┌────────────▼────────────┐
│ Admin Console (React) │
│ http://localhost:8096 │
│ 16 screens, REST+SSE │
└─────────────────────────┘
Feature depth
Slack-native capture
@Loremention — answers any question with sourced Block Kit card/loreslash command — same answer surface from anywhere- "Remember this" message shortcut — right-click any message → stored + attributed
- Passive channel consolidation — captures decisions from ordinary messages in any channel Lore is invited to, silently, with no
@mention; a 🧠 reaction marks a capture and Lore never replies (on by default; DMs excluded) - Daily digest — on a cadence, recaps each named channel's stored facts, activity, and open conflicts
- File upload → Q&A — PDF, DOCX, plain text chunked (512-token, 64-overlap), embedded, retrieved identically to org facts; 📄 reaction signals readiness
- App Home dashboard — strongest facts, fading facts, watched channels
- Prospective memory — "remind the channel when the baseline window ends June 24" — fires on the day
- DM support — ask Lore privately; channel-membership read scoping applies
Memory engine
- Reinforcement / decay — confirmed facts gain strength; uncorroborated facts fade from default retrieval; configurable thresholds
- Supersession chain — Correct doesn't silently overwrite: old version stays with
superseded_bypointer ("August 1 → September 15 — hospital procurement delay") - Conflict detection — contradicting facts go to the Conflict Queue for human resolution
- Audit trail — every write, correct, forget logged with actor + timestamp; immutable for compliance
- Forget with record — soft-deleted with audit; regulators can inspect
Answer quality
- 3-tier grounding ladder — CONTEXT → HISTORY → RAG; skips later tiers when enough evidence found; reduces Qdrant calls by ~40% on well-known topics
- Real-Time Search —
assistant.search.contextinjects fresh Slack context at answer time; used in every answer and as the public-search stage of the research pipeline - 5-phase research orchestrator — Planner (LLM decomposes into sub-queries) → Public search (RTS) → Private search (Qdrant) → Synthesizer (cited answer) → Fact extractor (background write)
- ReAct tool-calling loop — up to 6 tools:
search_memory,remember_fact,get_mcp_artifact, plus the gatedsearch_internet/fetch_urlweb tools; 3-strike tool disable; 10s per-tool timeout - Hybrid retrieval — vector similarity (Qdrant) + keyword (Postgres full-text) combined for recall + precision
Safety
- Armor scan — PII regex (email, phone, SSN) + credential regex screen every inbound message before any LLM call; blocks prompt injection
- Approval gateway — high-consequence requests (deploy, delete, payment) require human approval via Block Kit card; asyncio.Future bridge; 5-minute auto-reject; channel-level authorization
- Credential masking — integration API keys never returned over the API; merge-on-update pattern (submit
***= keep stored secret) - Channel allowlist — restricts which channels Lore admits messages from
- CSRF protection — double-submit cookie pattern on all mutation endpoints
Per-tenant MCP integrations
Each Slack workspace connects its own external tools independently — no shared credentials, no restart required:
Console → Connect tool → { name, command, args, env, tool, query_arg }
↓
MCPResolver (stdio/http/sse, 15s timeout, 2000-char cap)
built per-query from DB — no restart needed
↓
Answer cites org memory + live CRM / Linear / GitHub
Built-in test button probes the live MCP server and returns {ok, output, notices} before saving.
Admin console (React + TypeScript)
16 screens, all data-driven via /api/v1:
| Screen | Purpose |
|---|---|
| Overview | Stats: facts, runs, conflicts, agent health |
| Memory Explorer | Search, filter, inspect strength + status |
| Conflict Queue | Review and resolve contradicting facts |
| Audit | Immutable fact lifecycle log |
| Runs | Multi-agent run history + composition |
| Agents | CRUD for named LLM agents with system prompts |
| Models | Register any OpenAI-compatible endpoint |
| Teams | Router / council / auto team configurations |
| Integrations | Per-tenant MCP server management + live test |
| Routing | Channel → agent/team mapping |
| Harness | Self-improving loop proposals + accept/reject |
| Evals | Task performance history |
| Settings | Tenant config, retention, plan |
Multi-agent architecture
Lore routes every question to one of four dispatch paths:
simple ──▶ direct retrieval + single LLM answer (~1s)
research ──▶ 5-phase pipeline (~4s)
complex ──▶ multi-agent team (router/council/auto) (~6s)
high_consequence──▶ council deliberation + human approval (async)
Team modes:
- Router — orchestrator picks the best specialist agent per question
- Council — blind review by N agents → cross-examination → verdict synthesis
- Auto — self-configuring via the self-improving harness
Self-improving harness — after every run: judge scores the answer (0–10), tracks per-composition performance, proposes better agent/team configurations when scores drop, queues proposals for human approval in the console.
MCP — both directions
Claude Desktop ──▶ org_memory_search(query) ──▶ Lore memory
Cursor ──▶ org_memory_remember(text) ──▶ Lore memory
Any MCP client ──▶ org_memory_forget(fact_id) ──▶ Lore memory
Lore ──▶ GitHub MCP server ──▶ live repo search
Lore ──▶ Linear MCP server ──▶ live issue search
Lore ──▶ Any MCP server ──▶ merged into answer with org memory
One brain. Every surface your team already uses.
Potential impact
Within Slack teams: Every team of 10+ people loses institutional knowledge when Slack scrollback becomes the de-facto memory system. Lore makes capture a one-gesture habit — @Lore, /lore, a "Remember this" shortcut, or a file drop — so decisions are saved with provenance the moment they're made, instead of being re-derived months later.
Beyond Slack: Because Lore exposes memory as an MCP server, every AI tool the team uses gains the same knowledge base. A developer in Cursor asking "what did we decide about the API rate limits?" gets the same answer as someone asking @Lore in Slack. No duplication, no drift, no context switching.
Compounding value: Institutional memory compounds. Facts that survive correction become stronger. The team that uses Lore for 6 months has a searchable, attributed, non-stale knowledge graph — without any additional work.
Code quality
| Metric | Value |
|---|---|
| Python source | ~10,000 lines across 28 modules |
| TypeScript source | ~4,500 lines, strict mode, zero any |
| Test suite | 809 tests (unit + integration) |
| Unit tests | Zero live-service dependency |
| Integration tests | Redis DB 13 + Postgres lore_test + Qdrant |
| Static analysis | ruff (lint + format) + mypy (strict) — zero warnings |
| Crash-replay gate | Kill-9 simulation → restart → idempotent replay → no duplicate facts |
| Exactly-once delivery | Redis stream + XACK; admission idempotency key |
Run it
Requires Python 3.12+, Node 18+, and Redis + PostgreSQL + Qdrant running locally.
git clone <repo>
cd lore
bash scripts/dev-setup.sh # creates DBs, venv, installs deps
cp .env.example .env # fill in Slack tokens
. .venv/bin/activate
lore db-init # idempotent schema
lore run # Socket Mode listener + worker
# Console: http://127.0.0.1:8096/console
Create the Slack app from slack-app-manifest.yml (Socket Mode), install to workspace, and set in .env:
LORE_SLACK_BOT_TOKEN=xoxb-…
LORE_SLACK_APP_TOKEN=xapp-…
LORE_MODEL_PROVIDER=openai_compat # or: anthropic | fake
LORE_OPENAI_BASE_URL=http://127.0.0.1:8000/v1
LORE_OPENAI_API_KEY=…
# Capability flags (intent routing, RTS, vision, web search, digest, passive
# capture) are ON by default; each stays inert until its endpoint/channels are
# set. Add LORE_EMBEDDINGS_BASE_URL / LORE_VISION_BASE_URL etc. to light them up.
Full setup: docs/sandbox-testing-guide.md
Expose org memory to Claude Desktop / Cursor:
lore mcp # MCP server (stdio default; --transport http|sse): org_memory_search / remember / forget
Tests:
pytest tests/unit # no services needed
pytest tests/integration -m integration # requires Redis + Postgres + Qdrant
Repository structure
src/lore/
├── main.py entry point: wires all deps, starts Socket Mode + worker
├── config.py all env-var config (dataclass, validated at startup)
├── runner.py orchestrator: intent → dispatch → retrieval → answer
├── intake.py durable Redis-stream admission (exactly-once)
├── store.py memory CRUD: Postgres (facts) + Qdrant (vectors)
├── mcpio/client.py MCP client: MCPResolver, stdio/http/sse, per-query construction
├── mcpio/server.py MCP server: org_memory_search/remember/forget over stdio/http/sse
├── integrations.py per-tenant MCP integration store + builder
├── rts.py Real-Time Search API wrapper (assistant.search.context)
├── api.py aiohttp REST API + SSE + CSRF guard (16 route groups)
├── agent/ ReAct tool loop: tools, provider, FakeToolProvider
├── approval/ approval gateway: Future bridge + Block Kit + Bolt actions
├── slackio/ Bolt event/action/shortcut handlers, Block Kit builders
├── teams/ router/council/auto team modes + harness
└── db.py asyncpg pool + schema (CREATE TABLE IF NOT EXISTS)
src/console/src/ React 18 / TypeScript / Vite SPA
├── api/client.ts typed API client, CSRF cookie, error handling
├── pages/ 16 pages: Memory, Conflicts, Runs, Agents, Teams…
└── nav.ts navigation manifest (path, label, icon, crumb)
tests/
├── unit/ 47 test files, zero live-service dependency
└── integration/ crash-replay gate, store, API surface
Docs
docs/architecture.md— full system designdocs/sandbox-testing-guide.md— step-by-step setup + smoke testsCHANGELOG.md— per-feature history
License
Apache-2.0 — see LICENSE.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.
E2B
Using MCP to run code via e2b.