Two features: 1. SQL query action for agents via execute MCP tool — read-only, curated views, LIMIT/timeout, SELECT-only validation 2. Split read/write SQLite connection pools — writeDB (1 conn) + readDB (8 conns) to eliminate SQLITE_BUSY Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6.4 KiB
Feature Specification: SQL Query Interface + Split Connection Pools
Feature Branch: 015-sql-query-split-pools
Created: 2026-03-26
Status: Draft
Input: Architecture research from reactive agent triggering session
Assumptions
- SQL query interface is exposed as a
queryaction via the existingexecuteMCP tool, not a new top-level MCP tool - Queries are read-only (enforced via
PRAGMA query_only=ONon a dedicated connection) - Agents query curated SQL views (not raw tables) that bake in per-agent access control
- Views:
my_messages,my_channels,channel_messages— parameterized by the authenticated agent's name - Results are automatically limited to 100 rows; agent can specify lower limit
- Query timeout: 5 seconds max
- Only SELECT statements allowed (validated before execution); WITH (CTEs) permitted
- Split connection pools: writeDB (MaxOpenConns=1) for all INSERT/UPDATE/DELETE, readDB (MaxOpenConns=8) for all SELECT
- Both pools share the same SQLite file with WAL mode
- The read pool uses
PRAGMA query_only=ONfor safety - No schema changes needed — this is a runtime architecture change
- Agent SQL queries use the read pool
User Scenarios & Testing (mandatory)
User Story 1 - Agent Queries Messages via SQL (Priority: P1)
An agent connected via MCP uses the execute tool to run a SQL query against its accessible messages. For example: "Show me all messages in #news-mcpproxy from the last 3 days with priority >= 7".
Why this priority: Removes the expressiveness ceiling — agents can compose arbitrary queries instead of being limited to fixed API endpoints.
Independent Test: Agent calls execute with call('query', {sql: "SELECT * FROM my_messages WHERE channel_name = 'news-mcpproxy' AND priority >= 7 ORDER BY created_at DESC LIMIT 5"}) and gets results.
Acceptance Scenarios:
- Given an authenticated agent, When it calls
querywith a valid SELECT, Then it receives JSON results with column names and rows. - Given an agent, When it runs a query referencing
my_messages, Then it only sees messages it has access to (own DMs + joined channels). - Given an agent, When it runs
INSERT INTO messages ..., Then the query is rejected with "only SELECT statements allowed". - Given an agent, When it runs a query without LIMIT, Then results are automatically capped at 100 rows.
- Given an agent, When it runs a slow query (> 5s), Then the query is cancelled and an error is returned.
User Story 2 - Split Read/Write Connection Pools (Priority: P1)
SynapBus uses separate connection pools for reads and writes to eliminate SQLITE_BUSY errors under concurrent agent load.
Why this priority: Directly fixes the SQLITE_BUSY errors observed during reactive agent runs.
Independent Test: Run concurrent read and write operations; verify no SQLITE_BUSY errors and writes serialize correctly.
Acceptance Scenarios:
- Given concurrent agents sending messages, When writes happen simultaneously, Then they serialize through the single-writer pool without SQLITE_BUSY.
- Given a write in progress, When a read query arrives, Then the read executes immediately on the read pool (WAL mode).
- Given the read pool, When any write operation is attempted, Then it fails (query_only=ON enforcement).
User Story 3 - Agent Queries Channel Messages (Priority: P2)
An agent queries messages from a specific channel with rich filtering — date ranges, keywords, reactions, workflow states.
Why this priority: Enables the social-commenter to query #opportunities channel structured data via SQL.
Acceptance Scenarios:
- Given an agent that has joined #opportunities, When it queries
SELECT * FROM channel_messages WHERE channel_name = 'opportunities' AND created_at > datetime('now', '-3 days'), Then it sees messages from that channel. - Given an agent that has NOT joined a private channel, When it queries that channel's messages, Then no results are returned.
Edge Cases
- Query with syntax error returns a clear error message, not a crash
- Query referencing non-existent view returns "no such table" error
- Empty result set returns empty array, not null
- Very large result (>100 rows) is truncated with a warning
- Concurrent SQL queries from multiple agents don't interfere
Requirements (mandatory)
Functional Requirements
- FR-001: System MUST provide a
queryaction callable via theexecuteMCP tool that accepts a SQL string and returns results as JSON. - FR-002: System MUST enforce read-only execution — no INSERT, UPDATE, DELETE, DROP, ALTER, or PRAGMA statements allowed.
- FR-003: System MUST expose curated views (
my_messages,my_channels,channel_messages) that enforce per-agent access control. - FR-004: System MUST automatically limit query results to 100 rows (or fewer if agent specifies).
- FR-005: System MUST cancel queries that exceed 5 seconds.
- FR-006: System MUST use a separate read-only connection pool (MaxOpenConns=8) for all SELECT operations.
- FR-007: System MUST use a single-writer connection pool (MaxOpenConns=1) for all write operations.
- FR-008: System MUST configure
PRAGMA query_only=ONon the read pool connections. - FR-009: System MUST validate SQL statements before execution — only SELECT and WITH (CTE) prefixes allowed.
- FR-010: System MUST return query results as
{columns: [...], rows: [[...], ...], row_count: N, truncated: bool}.
Key Entities
- Read Pool: SQLite connection pool with MaxOpenConns=8, query_only=ON, for all SELECT operations including agent SQL queries.
- Write Pool: SQLite connection pool with MaxOpenConns=1, for all INSERT/UPDATE/DELETE operations.
- Agent Views: SQL views parameterized by agent name that enforce access control.
Success Criteria (mandatory)
Measurable Outcomes
- SC-001: Agents can execute arbitrary SELECT queries against curated views and receive structured JSON results within 5 seconds.
- SC-002: No SQLITE_BUSY errors under concurrent 4-agent workload (verified by running all 4 reactive agents simultaneously).
- SC-003: Write operations on the read pool are rejected at the SQLite engine level.
- SC-004: Query results are limited to 100 rows maximum.
- SC-005: All 28+ existing test packages continue to pass with the split pool architecture.