Skip to main content
Resources provide local, deterministic data via SQLite. Unlike tools (which call remote REST APIs), resources query local databases. They are perfect for bulk-downloaded open data such as company registers, transit schedules, and sanctions lists.

What are Resources?

A resource is a SQLite database bundled with a schema. The FlowMCP runtime loads the .db file via sql.js (a pure JavaScript/WASM SQLite implementation) and exposes each defined query as an MCP resource. No network calls, no API keys, no rate limits. Resources are ideal when:
  • Data is large and rarely changes (company registers, geographic data)
  • Offline access is required
  • Latency must be near-zero
  • The dataset is publicly available as a bulk download

When to Use Resources vs Tools

AspectToolsResources
Data sourceRemote REST APILocal SQLite database
LatencyNetwork-dependentInstant
AvailabilityRequires internetAlways available
Data freshnessReal-timeSnapshot (periodic refresh)
API key requiredUsually yesNo
Use caseLive prices, on-chain dataCompany registers, transit data
Many schemas benefit from combining both. Use tools for live data and resources for reference lookups. The AI agent chooses the right approach based on the query.

Resource Definition

Resources are declared inside main.resources. Each resource points to a SQLite database and defines named queries with SQL and parameters:
export const main = {
    namespace: 'offeneregister',
    name: 'OffeneRegister',
    version: '3.0.0',
    root: '',
    tools: {},
    resources: {
        companiesDb: {
            source: 'sqlite',
            database: 'companies.db',
            origin: 'global',
            description: 'German company register (OffeneRegister)',
            queries: {
                searchCompanies: {
                    sql: "SELECT * FROM companies WHERE name LIKE ? LIMIT ?",
                    description: 'Search companies by name',
                    parameters: {
                        searchTerm: { type: 'string', required: true },
                        limit: { type: 'number', required: false, default: 10 }
                    },
                    output: { columns: ['company_number', 'name', 'registered_address', 'status'] }
                }
            }
        }
    }
}

Database Paths — Three Levels

The origin field determines where the runtime looks for the .db file:
OriginPath ResolutionBest For
global~/.flowmcp/data/{database}Shared datasets used across projects
project.flowmcp/data/{database}Project-specific data
inlineRelative to the schema fileSelf-contained schemas with small databases
The origin field is required. The runtime does not guess where the database lives. If the file is not found at the resolved path, the resource fails to load with a clear error message.

CTE Support

Complex queries can use Common Table Expressions (CTEs) for multi-step filtering:
WITH recent AS (
    SELECT * FROM companies WHERE registered_date > ?
)
SELECT * FROM recent WHERE status = 'active' LIMIT ?
CTEs must still start with a read-only statement. The same SQL security rules apply: no INSERT, UPDATE, DELETE, or other write operations anywhere in the CTE chain.

Constraints

These limits keep resources focused and predictable. If you need more queries, split into multiple schemas.
ConstraintValueRationale
Max resources per schema2Resources are supplementary, not primary output
Max queries per resource87 defined + 1 auto-injected freeQuery
getSchema queryRequiredMust return the database table structure
SQL operationsSELECT onlyRead-only enforcement — no INSERT/UPDATE/DELETE
Parameter placeholders? onlyPrevents SQL injection
Source typesqlite onlyFuture versions may add other sources
Database file extension.dbStandard SQLite extension

Auto-Injected Queries

Two queries are handled automatically by the runtime:
  • getSchema — You must define this query. It returns the database structure so AI agents can understand available tables and columns.
  • freeQuery — Auto-injected by the runtime. Allows AI agents to run arbitrary SELECT queries within the read-only sandbox. This counts toward the 8-query limit.

Complete Example

An OffeneRegister schema with a SQLite resource for querying the German company register:
export const main = {
    namespace: 'offeneregister',
    name: 'OffeneRegister',
    description: 'German company register — local SQLite database',
    version: '3.0.0',
    tags: ['open-data', 'germany', 'companies'],
    root: '',
    tools: {},
    resources: {
        companiesDb: {
            source: 'sqlite',
            database: 'openregister.db',
            origin: 'global',
            description: 'OffeneRegister company database (2.5 GB)',
            queries: {
                getSchema: {
                    sql: "SELECT sql FROM sqlite_master WHERE type='table'",
                    description: 'Get database schema',
                    parameters: {},
                    output: { columns: ['sql'] }
                },
                searchCompanies: {
                    sql: "SELECT company_number, name, registered_address, status FROM companies WHERE name LIKE ? LIMIT ?",
                    description: 'Full-text search for companies by name',
                    parameters: {
                        searchTerm: { type: 'string', required: true, description: 'Company name (use % for wildcards)' },
                        limit: { type: 'number', required: false, default: 10, description: 'Max results' }
                    },
                    output: { columns: ['company_number', 'name', 'registered_address', 'status'] }
                },
                getByNumber: {
                    sql: "SELECT * FROM companies WHERE company_number = ?",
                    description: 'Look up a company by its registration number',
                    parameters: {
                        companyNumber: { type: 'string', required: true, description: 'Company registration number' }
                    },
                    output: { columns: ['company_number', 'name', 'registered_address', 'status', 'registered_date'] }
                },
                recentRegistrations: {
                    sql: "SELECT company_number, name, registered_date FROM companies ORDER BY registered_date DESC LIMIT ?",
                    description: 'List the most recently registered companies',
                    parameters: {
                        limit: { type: 'number', required: false, default: 20, description: 'Max results' }
                    },
                    output: { columns: ['company_number', 'name', 'registered_date'] }
                }
            }
        }
    }
}
This schema has no tools and no root URL — it operates entirely on local data. The AI agent can search companies, look up by number, or browse recent registrations without any network access.
Resource-only schemas set root: '' and tools: {}. They are valid FlowMCP schemas that expose only MCP resources, no MCP tools.

Validation Rules

Resources are validated by rules RES001-RES023. Key rules include:
CodeRule
RES003Maximum 2 resources per schema
RES005Source must be 'sqlite'
RES006Database path must end in .db
RES008Maximum 8 queries per resource (7 + freeQuery)
RES012SQL must start with SELECT (or WITH for CTEs)
RES013SQL must not contain blocked patterns
RES014SQL must use ? placeholders
RES015Placeholder count must match parameter count
See Validation Rules for the complete list.