You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Proposal: sharedDatabases — SQLite as Local Data Source
Summary
Extend FlowMCP with a new source: 'sqlite' type that enables schemas to query local SQLite databases instead of HTTP APIs. Databases are registered as shared resources (analogous to sharedLists) and made available to multiple schemas. Query execution happens entirely inside the core executor — handlers never get direct DB access.
Motivation
FlowMCP currently only supports HTTP-based data sources. This limits tools to external APIs with rate limits, API keys, and latency. Many valuable datasets (address directories, token metadata, registry snapshots) could be served locally:
No API keys — Zero cost per query
No rate limits — Instant responses
Offline-capable — Works without internet
Large datasets — 8M+ rows queryable in milliseconds
Historical snapshots — Registry/market data frozen in time
Design Principles
Following the existing spec philosophy:
Principle
Application
Deterministic over clever
SQL query declared in main, same input = same result
Declare over code
Query is static, hashable, no dynamic SQL
Inject over import
DB resolved by core, not by schema
Hash over trust
DB has SHA-256 hash in meta.json
Constrain over permit
Read-only, SELECT only, no DDL/DML
Architecture
flowchart TD
A[Schema loaded] --> B{main.source?}
B -->|http — default| C[HttpExecutor — unchanged]
B -->|sqlite| D[SqliteExecutor — NEW]
D --> D1["Resolve DB path from sharedDatabases"]
D1 --> D2["db.prepare(query).all(bindings)"]
D2 --> D3["Rows → postRequest handler → response"]
Loading
No Mixing Rule
A schema is either source: 'http' or source: 'sqlite' — never both. There is no way to combine HTTP calls and SQLite queries within a single schema.
Database Storage
Databases live as shared resources in a dedicated directory:
~/.flowmcp/
├── schemas/
├── shared-lists/
└── databases/ ← NEW
└── eth-addresses/
├── meta.json ← version, hash, table info
└── eth-addresses.db
1. Read schema file as string
2. Static security scan (existing + new SQL scan)
3. Dynamic import
4. Extract main export
5. Validate main block (source-aware conditional validation)
6. Resolve sharedDatabases (check ~/.flowmcp/databases/, verify hash)
7. If handlers export exists:
- Call handlers({ sharedLists, libraries })
- Register ONLY postRequest handlers (no preRequest for sqlite)
8. On tool call:
a. Validate parameters (z-block, unchanged)
b. Open DB connection (read-only mode)
c. db.prepare(query).all(bindings)
d. Apply LIMIT cap (max 1000 rows)
e. Pass rows to postRequest handler (if exists)
f. Wrap in standard response envelope
g. Close DB connection
Handler Restrictions for SQLite
Handler
HTTP
SQLite
preRequest
Yes — modify struct + payload
No — query is declarative
postRequest
Yes — transform response
Yes — transform rows
The handler receives { response } where response is an array of row objects. No db object is injected. The handler transforms data, nothing more.
# Register a database
flowmcp db add <name><path-to-db># → copies to ~/.flowmcp/databases/<name>/# → generates meta.json with hash + table info# List registered databases
flowmcp db list
# Inspect database
flowmcp db info <name># Remove database
flowmcp db remove <name># Verify integrity
flowmcp db verify <name>
What Does NOT Change
Feature
Unchanged
namespace, name, version, tags, docs
Identical metadata
Parameter z-block validation
Same primitives and options
output schema
Same JSON Schema subset
tests format
Same test declarations
Cherry-Pick Groups
Same tool references
Shared Lists
Can still be used for enum interpolation
Two-export model
main + handlers
Standard response envelope
{ status, messages, data }
Dependencies
better-sqlite3 (MIT) added to flowmcp-core
SQLite itself is Public Domain
Suggested Implementation Order
Spec document — Extend spec v2.1.0 with source: 'sqlite' section
SqliteExecutor — New executor in flowmcp-core (~100 lines)
SQL Security Scan — Static analysis for query strings
CLI: flowmcp db — Database management commands in flowmcp-cli
Proof of Concept — MCP Registry snapshot as first sharedDatabase
Example Use Cases
Database
Rows
Description
ethAddresses
~8M
Ethereum address directory with labels
mcpRegistry
~500
MCP Server Registry snapshot
tokenMetadata
~50K
ERC-20 token directory
contractLabels
~100K
Known contract labels (Etherscan)
duneCache
variable
Cached Dune Analytics query results
This proposal follows the existing FlowMCP v2.0.0 patterns (sharedLists, two-export model, dependency injection, security scanning) and extends them for local data sources.
Proposal:
sharedDatabases— SQLite as Local Data SourceSummary
Extend FlowMCP with a new
source: 'sqlite'type that enables schemas to query local SQLite databases instead of HTTP APIs. Databases are registered as shared resources (analogous tosharedLists) and made available to multiple schemas. Query execution happens entirely inside the core executor — handlers never get direct DB access.Motivation
FlowMCP currently only supports HTTP-based data sources. This limits tools to external APIs with rate limits, API keys, and latency. Many valuable datasets (address directories, token metadata, registry snapshots) could be served locally:
Design Principles
Following the existing spec philosophy:
main, same input = same resultmeta.jsonArchitecture
flowchart TD A[Schema loaded] --> B{main.source?} B -->|http — default| C[HttpExecutor — unchanged] B -->|sqlite| D[SqliteExecutor — NEW] D --> D1["Resolve DB path from sharedDatabases"] D1 --> D2["db.prepare(query).all(bindings)"] D2 --> D3["Rows → postRequest handler → response"]No Mixing Rule
A schema is either
source: 'http'orsource: 'sqlite'— never both. There is no way to combine HTTP calls and SQLite queries within a single schema.Database Storage
Databases live as shared resources in a dedicated directory:
meta.jsonFormat{ "name": "ethAddresses", "version": "1.0.0", "description": "8M Ethereum addresses with labels and balances", "hash": "sha256:a1b2c3...", "tables": [ { "name": "addresses", "rows": 8000000, "columns": ["address", "balance", "label", "firstSeen"] } ], "sizeBytes": 524288000, "updatedAt": "2026-02-20" }Schema Format
New Top-Level Fields
source'http' | 'sqlite''http')databasesArray<{ ref, version }>source: 'sqlite'Conditional Fields by Source
source: 'http'source: 'sqlite'rootdatabasesrequiredServerParamsroutes.methodroutes.pathroutes.databaseroutes.queryparameters.locationinsert | query | bodybindExample Schema
Execution Flow
Handler Restrictions for SQLite
preRequestpostRequestThe handler receives
{ response }whereresponseis an array of row objects. Nodbobject is injected. The handler transforms data, nothing more.Security
SQL Static Scan
Before execution, the query string is scanned. Rejected patterns:
INSERT,UPDATE,DELETEDROP,CREATE,ALTERATTACHPRAGMA(excepttable_info);(semicolons)--(SQL comments)Runtime Constraints
{ readonly: true }New
location: 'bind'Parameters with
location: 'bind'are passed as prepared statement bindings:This is SQL-injection-safe by design — values are never interpolated into the query string.
Hashing
SQLite route hashes follow the same pattern, replacing HTTP-specific fields:
CLI Extensions
What Does NOT Change
namespace,name,version,tags,docsz-block validationoutputschematestsformatmain+handlers{ status, messages, data }Dependencies
better-sqlite3(MIT) added toflowmcp-coreSuggested Implementation Order
source: 'sqlite'sectionflowmcp-core(~100 lines)flowmcp db— Database management commands inflowmcp-clisharedDatabaseExample Use Cases
ethAddressesmcpRegistrytokenMetadatacontractLabelsduneCacheThis proposal follows the existing FlowMCP v2.0.0 patterns (sharedLists, two-export model, dependency injection, security scanning) and extends them for local data sources.