Skip to content

Commit ede8480

Browse files
Fix unused variable warning in catalog_handlers and expand MCP roadmap
Prefix `e` with underscore in match guard to silence CI unused-variable error. Also expands the MCP server roadmap section with architecture details and adds multi-datasource type support section.
1 parent 52d8f02 commit ede8480

2 files changed

Lines changed: 45 additions & 13 deletions

File tree

docs/roadmap.md

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -272,22 +272,38 @@ This works but creates policy explosion when combining multiple conditions.
272272
273273
## AI Integration & Model Context Protocol (MCP)
274274

275-
### Model Context Protocol (MCP) Server Integration
275+
### Model Context Protocol (MCP) Server
276276

277-
Integrate an MCP server into the SQL proxy to turn it into a secure gateway for AI agents (Claude, ChatGPT, Gemini). This allows LLMs to interact with databases through a standardized protocol while inheriting all fine-grained access control (FGAC) and security policies.
277+
Expose BetweenRows as an MCP server so that AI agents (Claude Desktop, VS Code Copilot, Cursor, etc.) can interact with it natively — both for querying data and for managing the system — without needing a PostgreSQL driver or direct UI access.
278278

279-
- **Schema Discovery**: LLMs can safely discover allowed tables/columns via MCP "Resources" or tools, constrained by user-specific policies.
280-
- **Secure Query Execution**: Implement a `run_secure_query` tool that intercepts AI-generated SQL and applies FGAC before execution.
281-
- **Shadow Mode for AI**: Visualize and audit SQL generated by LLMs to identify potential policy violations or "sneaky" access attempts.
282-
- **Ecosystem Connectivity**: Enable direct connection from Claude Desktop, VS Code, and other AI agents to the secure proxy.
279+
**Goal**: An AI agent should be able to handle the full BetweenRows workflow end-to-end — onboard a datasource, discover its schema, create and assign policies, query data, and review audit logs — entirely through tool calls, without ever touching the UI.
283280

284-
**Product Vision**: "BetweenRows: The Firewall for AI-to-SQL Interactions. Policies you can trust, for queries you didn't write."
281+
**Marketing line**: "BetweenRows: The Firewall for AI-to-SQL Interactions. Policies you can trust, for queries you didn't write."
285282

286-
#### Implementation Steps:
287-
1. **Define MCP Tools**: Create tools like `execute_query` or `run_secure_query`.
288-
2. **Contextual Resources**: Share safe schema metadata snippets via MCP Resources.
289-
3. **Identity Mapping**: Pass user identity through MCP headers to apply correct fine-grained rules.
290-
4. **SDK Selection**: Use official TypeScript SDK or `fastmcp` for implementation.
283+
#### Two categories of MCP tools
284+
285+
**1. Data Access** — AI agents querying databases through BetweenRows:
286+
- `execute_query`: Accepts a datasource name and raw SQL; runs it through the policy engine (row filters, column masks, deny rules); returns JSON results. All existing enforcement and audit logging applies automatically.
287+
- `describe_schema`: Returns the tables and columns the calling user is permitted to see (policy-filtered catalog), giving the LLM the right context to write correct SQL without exposing hidden schema.
288+
- Shadow Mode integration: flag AI-generated queries in audit logs for visibility into what LLMs are accessing.
289+
290+
**2. Admin/Management** — AI agents managing BetweenRows itself:
291+
- Policy management: create, update, delete, and assign policies via tool calls. An agent could interpret a natural-language request ("mask SSN for all non-finance users") and produce the correct policy.
292+
- User management: create users, update credentials, assign datasource access.
293+
- Datasource management: register new datasources, trigger catalog discovery.
294+
- Audit log querying: fetch and summarize recent query activity.
295+
296+
#### Architecture
297+
298+
The MCP server is a thin wrapper over the existing admin API:
299+
1. Add a `POST /query` endpoint to the admin API that accepts `{ datasource, sql }` and executes through the policy engine — same enforcement path as the PostgreSQL wire protocol.
300+
2. Build an MCP server (separate sidecar or standalone process) that maps MCP tool calls to admin API HTTP requests. Policy logic stays in the Rust proxy; the MCP layer is stateless and thin.
301+
3. Authenticate MCP clients via API key mapped to a BetweenRows user identity, so user-specific policies apply correctly.
302+
303+
#### Open questions
304+
- MCP server implementation: separate Node/Python sidecar (e.g., `fastmcp`) vs. embedded in the Rust binary?
305+
- Streaming: large query results may need pagination rather than a single JSON response.
306+
- Scope of admin tools in v1: start with policy CRUD only, expand to users/datasources later?
291307

292308
## UI/UX Improvements
293309

@@ -425,6 +441,22 @@ Given complexity of new policy system (interaction with DataFusion and PostgreSQ
425441
- SQL injection
426442
- SSL
427443

444+
## Data Sources
445+
446+
### Multi Data Source Type Support
447+
448+
- Expand beyond PostgreSQL to support additional data source types:
449+
- SQLite
450+
- MySQL
451+
- Amazon Athena
452+
- Amazon Redshift
453+
- Snowflake
454+
- DuckDB
455+
- S3 files (Parquet, CSV, JSON)
456+
- Abstract data source connection layer to support multiple backends
457+
- Preserve existing policy enforcement (row filter, column mask, deny) across all source types
458+
- Catalog discovery and schema introspection per source type
459+
428460
## Infrastructure & Deployment
429461

430462
### Password & Authentication

proxy/src/admin/catalog_handlers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ async fn run_discovery_job(
141141
let mut store = state.job_store.lock().await;
142142
store.complete(&job_id, data);
143143
}
144-
Err(e) if cancel.is_cancelled() => {
144+
Err(_e) if cancel.is_cancelled() => {
145145
send(DiscoveryEvent::Cancelled);
146146
let mut store = state.job_store.lock().await;
147147
store.fail(&job_id, "cancelled".to_string());

0 commit comments

Comments
 (0)