Skip to main content
Resources provide fast, local read-only data access through SQLite databases. Unlike tools that make HTTP requests to external APIs, resources query local .db files using sql.js (a pure JavaScript/WASM SQLite implementation). Resources are the second MCP primitive supported by FlowMCP v3.0.0.
Resources are optional. Most schemas only need tools. Add resources when your schema benefits from fast local data lookups that do not require network calls.

When to Use Resources

Use CaseTool or Resource?
Fetch live data from an APITool
Look up static reference data (chain IDs, token lists)Resource
Query historical data that changes infrequentlyResource
Perform calculations on cached dataResource
Call external servicesTool
Resources are ideal for data that is bundled with the schema and updated only when the schema version changes.

Schema Format

Resources are declared in the resources key of the main export:
export const main = {
    namespace: 'etherscan',
    name: 'ContractExplorer',
    version: '3.0.0',
    root: 'https://api.etherscan.io',
    tools: { /* ... */ },
    resources: {
        verifiedContracts: {
            description: 'Lookup verified contracts by address or name',
            source: 'sqlite',
            database: 'contracts.db',
            queries: {
                byAddress: {
                    description: 'Find a verified contract by its address',
                    sql: 'SELECT name, compiler, optimization, source_code FROM contracts WHERE address = ?',
                    parameters: [
                        { key: 'address', type: 'string', description: 'Contract address (0x...)', required: true }
                    ]
                },
                byName: {
                    description: 'Search contracts by name pattern',
                    sql: 'SELECT address, name, compiler FROM contracts WHERE name LIKE ? LIMIT 20',
                    parameters: [
                        { key: 'pattern', type: 'string', description: 'Name search pattern (use % as wildcard)', required: true }
                    ]
                }
            }
        }
    }
}

Resource Fields

FieldTypeRequiredDescription
descriptionstringYesWhat data this resource provides. Visible to AI clients.
sourcestringYesMust be 'sqlite'. Only SQLite is supported in v3.0.0.
databasestringYesPath to the .db file, relative to the schema file. Must end in .db.
queriesobjectYesNamed queries with SQL and parameters. Max 4 queries per resource.

Query Fields

Each query is a named entry in the queries object:
FieldTypeRequiredDescription
descriptionstringYesWhat this query returns. Used by AI clients for query selection.
sqlstringYesSQL query string with ? placeholders. Must start with SELECT.
parametersarrayNoQuery parameters corresponding to ? placeholders. Can be empty [].
testsarrayNoTest cases with example parameter values.

Query Parameters

Resource query parameters use a simplified flat format (no position/z blocks):
parameters: [
    { key: 'address', type: 'string', description: 'Contract address', required: true },
    { key: 'limit', type: 'number', description: 'Max results', required: false }
]
FieldTypeRequiredDescription
keystringYesParameter name (camelCase). Maps to a ? placeholder in order.
typestringYesMust be string, number, or boolean.
descriptionstringYesWhat this parameter controls.
requiredbooleanYesWhether the parameter must be provided.
The number of parameters must match the number of ? placeholders in the SQL query.

SQL Security Enforcement

Resources enforce strict read-only access. The following patterns are blocked at validation time:
Blocked PatternReason
INSERT, UPDATE, DELETE, DROPWrite operations not allowed
CREATE TABLE, ALTER TABLESchema modifications not allowed
ATTACH DATABASECross-database access not allowed
LOAD_EXTENSIONExtension loading not allowed
PRAGMA (most)Configuration changes not allowed
String interpolationAll values must use ? placeholders
Subqueries with writesNested write operations not allowed
// Valid — SELECT with prepared statement
sql: 'SELECT * FROM contracts WHERE address = ?'

// Valid — JOIN query
sql: 'SELECT c.name, t.symbol FROM contracts c JOIN tokens t ON c.token_id = t.id WHERE c.chain_id = ?'

// Invalid — not a SELECT statement
sql: 'INSERT INTO contracts VALUES (?)'

// Invalid — uses string interpolation instead of ?
sql: `SELECT * FROM contracts WHERE address = '${address}'`

// Invalid — blocked pattern
sql: 'ATTACH DATABASE "other.db" AS other'
All SQL queries must start with SELECT and use ? placeholders for all dynamic values. String interpolation, template literals, and concatenation in SQL strings are forbidden.

Runtime

Resources use sql.js, a pure JavaScript/WASM implementation of SQLite. This means:
  • No native dependencies — works on any platform that supports WASM
  • No SQLite installation required — everything is bundled
  • Read-only mode — databases are opened in read-only mode by default
  • Memory-safe — each query runs in an isolated context

Constraints

ConstraintValueRationale
Max resources per schema2Resources are supplementary, not primary output
Max queries per resource4Keeps resource scope focused
Source typesqlite onlyFuture versions may add other sources
Database file extension.dbStandard SQLite extension
SQL must start withSELECTRead-only enforcement
Parameter placeholders? onlyPrevents SQL injection
Parameter typesstring, number, booleanSimple types only

Complete Example

A schema with both tools and resources:
export const main = {
    namespace: 'etherscan',
    name: 'ContractExplorer',
    description: 'Explore Ethereum contracts via API and local database',
    version: '3.0.0',
    root: 'https://api.etherscan.io',
    requiredServerParams: [ 'ETHERSCAN_API_KEY' ],
    requiredLibraries: [],
    headers: {},
    tools: {
        getContractAbi: {
            method: 'GET',
            path: '/api',
            description: 'Fetch the ABI of a verified contract from the Etherscan API',
            parameters: [
                {
                    position: { key: 'module', value: 'contract', location: 'query' },
                    z: { primitive: 'string()', options: [] }
                },
                {
                    position: { key: 'action', value: 'getabi', location: 'query' },
                    z: { primitive: 'string()', options: [] }
                },
                {
                    position: { key: 'address', value: '{{USER_PARAM}}', location: 'query' },
                    z: { primitive: 'string()', options: [ 'min(42)', 'max(42)' ] }
                },
                {
                    position: { key: 'apikey', value: '{{SERVER_PARAM:ETHERSCAN_API_KEY}}', location: 'query' },
                    z: { primitive: 'string()', options: [] }
                }
            ]
        }
    },
    resources: {
        verifiedContracts: {
            description: 'Local database of verified contract metadata',
            source: 'sqlite',
            database: 'contracts.db',
            queries: {
                byAddress: {
                    description: 'Find contract by Ethereum address',
                    sql: 'SELECT name, compiler_version, optimization, license FROM contracts WHERE address = ?',
                    parameters: [
                        { key: 'address', type: 'string', description: 'Contract address (0x...)', required: true }
                    ],
                    tests: [
                        {
                            _description: 'USDC proxy contract',
                            address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'
                        }
                    ]
                },
                recentlyVerified: {
                    description: 'List recently verified contracts',
                    sql: 'SELECT address, name, verified_at FROM contracts ORDER BY verified_at DESC LIMIT ?',
                    parameters: [
                        { key: 'limit', type: 'number', description: 'Number of contracts to return', required: true }
                    ]
                }
            }
        }
    }
}
This schema provides two ways to access contract data:
  • Tool (getContractAbi) — fetches live ABI data from the Etherscan API (requires network and API key)
  • Resource (verifiedContracts) — queries a local SQLite database of contract metadata (instant, no network, no API key)
The AI agent can choose the appropriate approach based on what data is needed.

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 4 queries per resource
RES012SQL must start with SELECT
RES013SQL must not contain blocked patterns
RES014SQL must use ? placeholders
RES015Placeholder count must match parameter count
See Validation Rules for the complete list.