Synapse: Enterprise Memory & Action Agent for Slack


Inspiration: The Enterprise Knowledge Crisis

In fast-paced enterprise teams, critical decisions and assigned tasks are constantly buried in rapid-fire Slack channels. When someone asks "Why did we choose AWS?" or "Who owns the database migration?", the answer is almost always lost somewhere in thousands of lines of chat.

We noticed that existing Slack bots make this worse, not better. They dump large, noisy UI cards directly into collaborative channels — causing "ephemeral stacking" that degrades the shared chat interface for everyone. They are also stateless, re-scanning the same messages on every run and burning unnecessary API tokens without retaining anything permanently.

We validated this problem directly against real user testimony. On r/Slack, users describe it identically: "Someone will ask me to review a document in a thread with ten other people and if I do not immediately star the message or set a reminder it completely disappears from my consciousness within ten minutes." Engineering managers on r/EngineeringManagers articulate the deeper trust problem: "The moment a message in a channel could plausibly be from the agent, every message in that channel gets re-read with that doubt attached. You've changed the medium, not just added a feature."

Synapse exists because enterprise teams need an agent that does the heavy lifting of extracting knowledge and triaging tasks, but does so entirely in the background — acting as a silent, stateful organizational memory layer that never speaks publicly without a human's explicit approval.

The core design principle, stated as an invariant and enforced throughout the entire codebase:

Synapse never posts to a public channel on anyone's behalf. Every output is a private Card routed to the specific user's DMs. Nothing becomes permanent until a named human taps Approve.

This is not a safety feature bolted on afterward. It is the architectural constraint that every other design decision was built around.


What It Does

Synapse transforms Slack from a chat stream into a structured knowledge vault and task management layer. All interactions are routed through slash commands or direct @Synapse mentions to guarantee zero channel clutter.

Slash Command Reference

Command What It Does Enterprise Impact
/synapse-scan Triggers a silent semantic scan of the current channel. Users can optionally paste one or two message links to define scan boundaries with millisecond precision. All detected items are routed as interactive approval Cards directly to the user's DMs — invisible to teammates. Surgical, clutter-free triage. No channel spam. Supports all flag for full history scan, link-bounded precision scan, or simple count-based scan.
/synapse-state Renders a two-tiered Block Kit dashboard: the user's global open commitments across all channels, and the current channel's active task list scoped to that user. Strict data isolation. Users see only their own items — matching the privacy model of Jira or Asana — without requiring a separate tool login.
/synapse-stalled Audits all tracked tasks in the current channel by checking the most recent reply timestamp for each thread. Any thread with no reply for more than 12 hours is flagged and surfaced with a one-click Bump Thread action button. Automates follow-up. Eliminates the need for project managers to manually chase overdue threads. Stall risk is computed from real, explainable metadata — not invented percentages.
/synapse-logs Compiles every logged decision for the current channel into a native Slack Canvas with markdown formatting, dated entries, and source thread links. DMs the user a shareable Canvas link. Instantly settles team debates by surfacing the collaborative audit trail of high-level agreements, each linked back to the original conversation. Zero channel pollution — the Canvas is generated silently and delivered privately.
/synapse-ping Validates the Slack app's connection to the MCP vault with a live round-trip call. Infrastructure health check. Confirms both the Slack app and the backend database are reachable before relying on the system in a live session.

The Recall Engine

Users can ask @Synapse any natural language question at any time:

@Synapse why did we choose AWS over GCP?
@Synapse who is responsible for the auth refactor?
@Synapse what did we decide about the pricing model?

Synapse queries the full decision vault across all channels (not just the current channel — a deliberate fix from the initial architecture to ensure cross-channel knowledge is always reachable), displays a typing indicator via assistant.threads.setStatus, and responds in-thread with a sourced summary and a direct link back to the original conversation. If the answer is not in the vault, it says exactly: "I don't have a record of that decision." — it never guesses.

The App Home: Calendar-Style Mission Control

The App Home renders as a professional, date-grouped task calendar. Tasks are grouped by the calendar date they were committed (derived from message_ts, reflecting when the conversation actually happened), each date presented as a distinct visual day-block with a header, compact task cards beneath it, and a divider separator — the closest Slack Block Kit gets to a Linear or Asana-style card grid. Every "View Thread" button opens the source conversation directly without firing a block_action event, eliminating the unhandled-request log errors that plagued earlier iterations.

The Trust Gate

Every single Synapse output — whether a detected task, a logged decision, a stall alert, or a topic split — arrives first as a private Card in the user's DMs. Nothing is written to permanent storage, posted publicly, or sent to an external system until the named human explicitly taps Approve or Save to Vault. Tapping Ignore deletes the card silently with zero storage write.

This means Synapse can be deployed in sensitive enterprise environments without risk of the agent speaking on behalf of employees, hallucinating decisions into the public record, or creating noise in shared channels.


How We Built It

Synapse is architected in Python using the Slack Bolt framework with Socket Mode transport, with deep integration of all three core hackathon technologies.

1. MCP Server (Memory & Persistence Layer)

We built a FastMCP server backed by SQLite. This acts as Synapse's persistent memory vault with distinct tables:

  • tasks — extracted commitments with owner, source thread URL, channel, message_ts, and approval status
  • decisions — finalized team agreements with summary, participants, source thread URL, channel, and ISO timestamp
  • pending_approvals — draft items awaiting human approval, with expiry
  • channel_state — per-channel High-Water Mark timestamps for deduplication (Layer 1)

The MCP server exposes typed tools that the Slack agent calls rather than writing to storage directly:

MCP Tool Purpose
propose_task Writes a draft task to pending_approvals, not yet permanent
approve_task Moves approved task to tasks table, optionally writes to Linear
save_task Direct save path used by interactive approval button handler
propose_decision Writes a draft decision to pending_approvals
approve_decision Moves approved decision to decisions table with full audit fields
save_decision Direct save path used by interactive approval button handler
get_user_tasks Retrieves all open tasks for a given user ID
get_user_channel_tasks Retrieves tasks for a user scoped to a specific channel
get_recent_decisions Retrieves logged decisions for a channel (used for Canvas)
get_all_decisions Retrieves all decisions across all channels (used for Recall)
query_decisions Semantic lookup over the decisions store
is_item_saved Pre-LLM deduplication check against message_ts (Layer 2)
get_last_scanned_ts Retrieves the High-Water Mark for a channel (Layer 1)
update_last_scanned_ts Advances the High-Water Mark after a successful scan
dismiss Removes a pending approval without any permanent write
synapse_ping Returns runtime status — used by /synapse-ping

This separation — agent reasoning on one side, persistence on the other, connected only through typed MCP tools — means the memory layer is independently testable and entirely replaceable without touching any Slack integration code.

2. Real-Time Search API (Detection & Recall Layer)

We use the RTS API in two distinct ways, and we are explicit about the distinction because it matters for judging:

Semantic Recall — when a user asks @Synapse a natural language question, the Recall Engine queries the decision vault using semantic search. This is the capability that conversations.history alone cannot provide: finding relevant past context by meaning across an entire workspace history, not by keyword.

Ambient Detection — for open commitments and concluded decisions in the background scanner and /synapse-scan, we use the RTS API's advanced query operators to semantically scan scoped channels for patterns that exact-keyword matching would miss:

# Open commitment detection
query: "can you OR could you OR please review OR need this by OR your turn"
scope: configured channel list
window: rolling 48 hours

# Concluded decision detection
query: "let's go with OR decided OR final call OR we'll use OR agreed OR landing on"
scope: configured channel list
window: rolling 7 days

For user-initiated precision scans where message link boundaries are provided, we supplement RTS with conversations.history using oldest/latest timestamp parameters derived from the linked message IDs — applying a ±1.0 second buffer to fix the boundary millisecond drop bug discovered during testing. This hybrid approach keeps API costs low while enabling both open-ended semantic discovery and precision-bounded extraction.

3. AI Classification (Intelligence Layer)

Message payloads are routed through Groq (Llama 3.3 70B Versatile) with temperature=0.1 and strict JSON-mode enforcement. We chose Groq for its sub-200ms inference latency during interactive Card flows, where a slow LLM call degrades the UX meaningfully; the RTS API handles semantic workspace-level retrieval, and Groq handles structured extraction from the retrieved payloads.

Prompt engineering enforces two specific output behaviors:

Imperative Voice normalization — converts casual human phrasing into clean, actionable task language suitable for storage and later recall:

Input:  "I guess I'll probably write the slide deck at some point"
Output: "Draft the slide deck"

Slack ID extraction — reliably pulls alphanumeric Slack user IDs (e.g., U0123ABCDE) from message text for accurate task assignment, handling the many informal ways people reference colleagues in chat.

The Recall Engine uses temperature=0.1 with a strict prompt that instructs the model to answer only from the provided Memory Log and always include the Source URL — never guess, never fabricate.

4. Block Kit UI Layer

Synapse uses four of Slack's new agent Block Kit components, each chosen for a specific structural purpose:

Component Where Used Why This Component
Card Task approval DMs, Decision approval DMs, Recall answers Single focused action with a clear approve/dismiss binary choice
Alert Stall notifications, high-priority flags in dashboards Communicates urgency via severity level without requiring additional text
Data Table /synapse-state two-tier dashboard, /synapse-stalled stall report Structured multi-item display with owner, age, and status columns — scannable at a glance
Carousel Daily digest summary, future ContextStream topic splits Horizontal browsing of multiple items without forcing vertical scroll

All Block Kit output is routed to private DMs or App Home. The only exception is the ContextStream Carousel (currently in the roadmap), which would post a structural suggestion into a noisy channel — not a content claim — and still requires a human tap before anything changes.


Challenges We Ran Into

1. The Unhandled Action Error (URL Button Routing)

The most persistent bug during development: Slack dispatches a block_action event even for buttons that have a url field (which should open a link client-side without server involvement). Because we hadn't set explicit action_id values on these buttons, Slack auto-generated IDs like oVRu2 that Bolt had no handler for, producing a stream of 404 unhandled request log errors.

The two-part fix: set action_id: "view_thread_link" on every URL button explicitly, and register a minimal @app.action("view_thread_link") handler that only calls ack(). The error is permanently eliminated.

2. Sandbox Scope & Permission Crisis

We built the background cron scanner first. It immediately crashed with a missing_scope error in the Slack Developer Sandbox. Debugging revealed we needed channels:read and groups:read in addition to the history and message scopes — and that the workspace required a full reinstall to pick up the updated OAuth token. This cost half a day but produced a clean scope checklist we now verify before any deployment.

3. Socket Mode SSL Noise

During sandbox testing, the terminal was flooded with SSLEOFError and [SSL: BAD_LENGTH] exceptions that looked like application failures. Deep-diving into Bolt's internal connection handling revealed these are normal Socket Mode network blips that the framework silently auto-reconnects from. We added proper log-level filtering and moved on.

4. The Boundary Millisecond Bug

Our precision scanner lets users paste Slack message links to define scan boundaries. The API was silently dropping the exact linked messages due to microsecond timestamp rounding — the kind of silent data loss that would be very hard to notice in production. We fixed this by parsing the numeric message ID from the URL, casting it to a float, and applying a ±1.0 second buffer to the oldest/latest parameters — guaranteeing no boundary message is ever excluded from a scan.

5. Ephemeral UI Stacking

Initial builds routed all output Cards to the channel_id, which caused "Only visible to you" messages to stack up and visually degrade the channel interface for the triggering user. We re-architected payload routing to use respond(delete_original=True) for the ephemeral status message and client.chat_postMessage(channel=user_id, ...) for all substantive output — sending everything to App Home DMs rather than the originating channel.

6. Recall Only Searched the Current Channel

The original recall implementation called get_recent_decisions with a channel_id filter. This meant @Synapse why did we choose AWS? returned nothing if asked in a different channel than where the decision was logged — silently breaking cross-channel recall. The fix: the Recall Engine now calls get_all_decisions with a higher limit (50 decisions), and the LLM prompt instructs the model to search the full context. Cross-channel recall now works correctly regardless of where you ask the question.

7. The Deduplication Problem

A continuously-running background scanner will re-process messages it has already analyzed unless the architecture explicitly prevents it at every layer. We solved this with a Triple-Layer Deduplication System that provides defense-in-depth against silent data corruption:

Layer Mechanism Failure Mode Prevented
State (Layer 1) High-Water Mark (last_ts) per channel, stored in MCP DB Prevents already-seen messages from being fetched from the API at all
Logic (Layer 2) Pre-LLM check via is_item_saved(message_ts) before any Groq call Prevents spending API tokens on already-processed messages
Schema (Layer 3) UNIQUE(message_ts) constraint enforced in SQLite Prevents duplicate rows even if both upper layers fail simultaneously

Each layer addresses a different failure mode independently. Together they make the system reliable enough for enterprise use where silent data corruption over weeks of operation would be unacceptable. The High-Water Mark alone eliminates the vast majority of redundant API and LLM calls, making the system cheap to run at scale.


Accomplishments We're Proud Of

The Triple-Layer Deduplication System is the technical accomplishment we're most proud of. It's not a single clever fix — it's a defense-in-depth architecture where each layer handles a different failure mode independently. The system is demonstrably correct: Layer 1 minimizes API cost, Layer 2 minimizes LLM cost, and Layer 3 ensures database integrity even under concurrent write conditions.

The trust-gate architecture — the deliberate constraint that Synapse never acts without explicit human approval — turned out to be both harder to implement correctly and more valuable than we initially expected. Routing every output through a pending_approvals state before any permanent write required careful state management across the MCP server, the Slack event handlers, and the Block Kit interaction callbacks. But it's the constraint that makes enterprise deployment realistic rather than theoretical, and we believe it is the right default design pattern for any agent operating in a shared human workspace.

The hybrid RTS + History API approach gave us the best of both worlds: semantic discovery for open-ended queries (where the user doesn't know what they're looking for), and precision extraction for bounded scans (where the user knows exactly which conversation they want analyzed). Neither API alone would have covered both use cases cleanly.


What We Learned

Building for Slack requires deep empathy for the end user's visual space. An AI agent can be technically impressive but immediately uninstalled if it creates noise in shared channels. The constraint of "never posting publicly without approval" shaped every architectural decision we made, and we believe it is the right default for any enterprise agent, not just Synapse.

We learned that the Model Context Protocol is genuinely valuable not just as an AI connectivity standard but as a clean architectural separation between the agent's reasoning and its persistence — the same separation that makes traditional application architecture maintainable, now applied to agentic systems. The MCP boundary made our agent independently testable in ways a monolithic bot would not be.

We learned that the most important bugs in a live Slack agent are the silent ones: the timestamp rounding that drops messages without error, the duplicate that gets stored without warning, the scope that is missing but doesn't raise an exception until runtime. Building for correctness at the edge cases is what separates a demo from something an enterprise team would trust with their institutional memory.

We also validated our core problem against real user research rather than assumptions — searching Reddit's r/Slack and r/EngineeringManagers for authentic pain descriptions before writing a line of code. Every feature in Synapse maps to a documented, upvoted, real complaint. This kept us from building interesting features that solve no one's actual problem.


What's Next for Synapse

ContextStream Engine

Upgrade the RTS detection layer to identify when 3 or more distinct topic clusters are simultaneously active in a single channel — the same problem Zulip's topic system was built to solve, brought natively into Slack.

Calendar Integration

Connect the FastMCP server to Google Calendar and Outlook. When a user approves a task Card, Synapse automatically blocks 30 minutes on their calendar — closing the loop between "I acknowledged this commitment" and "I have time budgeted to fulfill it."

Multi-Workspace Scaling

Migrate the SQLite MCP backend to a managed PostgreSQL cluster to support Slack Enterprise Grid deployments across thousands of channels and multiple workspaces, with per-workspace data isolation enforced at the database schema level rather than the application level.


Tech Stack

Layer Technology Role
Agent Framework Python + Slack Bolt Event handling, slash commands, Block Kit rendering, Socket Mode transport
Memory & State FastMCP + SQLite Append-only knowledge store with integrity constraints and per-channel High-Water Marks
Real-Time Search Slack RTS API Semantic workspace search for ambient detection and cross-channel recall
History Extraction Slack conversations.history Precision-bounded interval scans with ±1.0s boundary buffer
AI Classification Groq — Llama 3.3 70B Versatile Low-latency structured extraction; temperature=0.1; JSON-mode enforced
UI Components Slack Block Kit Card, Alert, Data Table, Carousel, Work Object, Canvas
Transport Socket Mode Real-time event delivery without requiring a public HTTP endpoint
Background Scheduler APScheduler 15-minute ambient scan interval across all joined channels
External Integration Linear MCP Server Optional task write-back on approval (roadmap: fully wired)
Deployment Local / Railway-ready SQLite for hackathon; PostgreSQL path documented for production

Required Technology Checklist

Hackathon Requirement How Synapse Satisfies It
MCP Server Integration FastMCP server with 14 typed tools; clean separation between agent reasoning and persistence; optional Linear write-back on task approval
Real-Time Search API Used for semantic ambient detection (commitment/decision patterns) and for cross-channel natural language recall via @Synapse — both use cases requiring semantic search that conversations.history cannot provide
Slack AI / Agent Builder Groq/Llama handles all classification; assistant.threads.setStatus for typing indicator; App Home as interactive mission control; Canvas generation for decision logs
Card Block Task approval cards, decision approval cards, recall answer format
Alert Block Stall notifications, high-priority flags in dashboards
Data Table Block /synapse-state two-tier dashboard, /synapse-stalled stall report
Carousel Block Daily digest summary; ContextStream topic-split offer (roadmap)
Share this project:

Updates