Skip to main content
These examples demonstrate the four most common FlowMCP schema patterns, from the simplest possible schema to multi-step async workflows. All examples use the v2.0.0 format.

1. Minimal Schema

The simplest possible schema: a single route with no parameters, no handlers, and no shared lists. This is the CoinGecko API ping endpoint.
// coingecko-ping.mjs
export const main = {
    namespace: 'coingecko',
    name: 'Ping',
    description: 'Check CoinGecko API server status',
    version: '2.0.0',
    docs: [ 'https://docs.coingecko.com/reference/simple-ping' ],
    tags: [ 'utility', 'health' ],
    root: 'https://api.coingecko.com/api/v3',
    requiredServerParams: [],
    requiredLibraries: [],
    headers: {},
    routes: {
        ping: {
            method: 'GET',
            path: '/ping',
            description: 'Check if CoinGecko API is online',
            parameters: [],
            output: {
                mimeType: 'application/json',
                schema: {
                    type: 'object',
                    properties: {
                        gecko_says: { type: 'string', description: 'Response message from CoinGecko' }
                    }
                }
            }
        }
    }
}
Key takeaways:
  • No requiredServerParams — this API needs no authentication
  • No handlers export — the raw API response is returned as-is
  • The output.schema describes what the AI client receives
  • parameters: [] means no user input is needed

2. Multi-Route Schema with Handlers

Multiple routes in one schema, with postRequest handlers that filter and reshape API responses. This wraps the DeFi Llama protocol analytics API.
// defillama-protocols.mjs
export const main = {
    namespace: 'defillama',
    name: 'ProtocolAnalytics',
    description: 'DeFi Llama protocol TVL and analytics data',
    version: '2.0.0',
    docs: [ 'https://defillama.com/docs/api' ],
    tags: [ 'defi', 'tvl', 'analytics' ],
    root: 'https://api.llama.fi',
    requiredServerParams: [],
    requiredLibraries: [],
    headers: {},
    routes: {
        getProtocols: {
            method: 'GET',
            path: '/protocols',
            description: 'List all DeFi protocols with TVL data',
            parameters: [],
            output: {
                mimeType: 'application/json',
                schema: {
                    type: 'array',
                    items: {
                        type: 'object',
                        properties: {
                            name: { type: 'string', description: 'Protocol name' },
                            slug: { type: 'string', description: 'URL-safe identifier' },
                            tvl: { type: 'number', description: 'Total value locked in USD' },
                            chain: { type: 'string', description: 'Primary chain' },
                            category: { type: 'string', description: 'Protocol category' }
                        }
                    }
                }
            }
        },
        getTvl: {
            method: 'GET',
            path: '/tvl/{{protocolSlug}}',
            description: 'Get current TVL for a specific protocol',
            parameters: [
                {
                    position: { key: 'protocolSlug', value: '{{USER_PARAM}}', location: 'insert' },
                    z: { primitive: 'string()', options: [ 'min(1)' ] }
                }
            ],
            output: {
                mimeType: 'application/json',
                schema: { type: 'number' }
            }
        },
        getProtocolTvl: {
            method: 'GET',
            path: '/protocol/{{protocolSlug}}',
            description: 'Get detailed TVL history for a protocol',
            parameters: [
                {
                    position: { key: 'protocolSlug', value: '{{USER_PARAM}}', location: 'insert' },
                    z: { primitive: 'string()', options: [ 'min(1)' ] }
                }
            ],
            output: {
                mimeType: 'application/json',
                schema: {
                    type: 'object',
                    properties: {
                        name: { type: 'string', description: 'Protocol name' },
                        tvl: { type: 'array', items: { type: 'object' } },
                        currentChainTvls: { type: 'object', description: 'TVL per chain' }
                    }
                }
            }
        },
        getChainTvl: {
            method: 'GET',
            path: '/v2/historicalChainTvl/{{chainName}}',
            description: 'Get historical TVL for a specific chain',
            parameters: [
                {
                    position: { key: 'chainName', value: '{{USER_PARAM}}', location: 'insert' },
                    z: { primitive: 'string()', options: [ 'min(1)' ] }
                }
            ],
            output: {
                mimeType: 'application/json',
                schema: {
                    type: 'array',
                    items: {
                        type: 'object',
                        properties: {
                            date: { type: 'number', description: 'Unix timestamp' },
                            tvl: { type: 'number', description: 'TVL in USD' }
                        }
                    }
                }
            }
        }
    }
}
Key takeaways:
  • Four routes in one schema covering related endpoints
  • location: 'insert' substitutes parameters into the URL path
  • The handlers export transforms responses for two of the four routes
  • Routes without handlers return the raw API response
  • The handler factory receives { sharedLists, libraries } even when unused

3. Shared List Schema

Demonstrates shared list references and {{listName:fieldName}} interpolation. This Etherscan gas tracker schema uses the evmChains shared list to generate a chain selector enum.
// etherscan-gas.mjs
export const main = {
    namespace: 'etherscan',
    name: 'GasTracker',
    description: 'EVM gas price tracking via Etherscan API',
    version: '2.0.0',
    docs: [ 'https://docs.etherscan.io/api-endpoints/gas-tracker' ],
    tags: [ 'evm', 'gas', 'transactions' ],
    root: 'https://api.etherscan.io/v2/api',
    requiredServerParams: [ 'ETHERSCAN_API_KEY' ],
    requiredLibraries: [],
    headers: {},
    sharedLists: [
        {
            ref: 'evmChains',
            version: '1.0.0',
            filter: { key: 'etherscanAlias', exists: true }
        }
    ],
    routes: {
        getGasOracle: {
            method: 'GET',
            path: '/api',
            description: 'Get current gas prices for an EVM chain',
            parameters: [
                {
                    position: { key: 'chainName', value: '{{USER_PARAM}}', location: 'query' },
                    z: { primitive: 'enum({{evmChains:etherscanAlias}})', options: [] }
                },
                {
                    position: { key: 'module', value: 'gastracker', location: 'query' },
                    z: { primitive: 'string()', options: [] }
                },
                {
                    position: { key: 'action', value: 'gasoracle', location: 'query' },
                    z: { primitive: 'string()', options: [] }
                },
                {
                    position: { key: 'apikey', value: '{{SERVER_PARAM:ETHERSCAN_API_KEY}}', location: 'query' },
                    z: { primitive: 'string()', options: [] }
                }
            ],
            output: {
                mimeType: 'application/json',
                schema: {
                    type: 'object',
                    properties: {
                        LastBlock: { type: 'string', description: 'Latest block number' },
                        SafeGasPrice: { type: 'string', description: 'Safe gas price in Gwei' },
                        ProposeGasPrice: { type: 'string', description: 'Proposed gas price in Gwei' },
                        FastGasPrice: { type: 'string', description: 'Fast gas price in Gwei' }
                    }
                }
            }
        }
    }
}
Key takeaways:
  • sharedLists declares which shared lists this schema needs
  • filter: { key: 'etherscanAlias', exists: true } excludes chains without an Etherscan alias (Avalanche is filtered out)
  • enum({{evmChains:etherscanAlias}}) generates enum(["ETH","POLYGON","ARBITRUM","BASE","BSC"]) at load time
  • {{SERVER_PARAM:ETHERSCAN_API_KEY}} injects the API key from the environment
  • Fixed parameters like module and action have hardcoded values (not {{USER_PARAM}})

4. Async Workflow Schema

A multi-step API workflow with execute, poll status, and retrieve results. This wraps the Dune Analytics query execution pipeline.
// dune-query-engine.mjs
export const main = {
    namespace: 'dune',
    name: 'QueryEngine',
    description: 'Execute and retrieve Dune Analytics SQL queries',
    version: '2.0.0',
    docs: [ 'https://docs.dune.com/api-reference/executions' ],
    tags: [ 'analytics', 'sql', 'blockchain' ],
    root: 'https://api.dune.com',
    requiredServerParams: [ 'DUNE_API_KEY' ],
    requiredLibraries: [],
    headers: {},
    routes: {
        executeQuery: {
            method: 'POST',
            path: '/api/v1/query/{{queryId}}/execute',
            description: 'Execute a saved Dune query',
            parameters: [
                {
                    position: { key: 'queryId', value: '{{USER_PARAM}}', location: 'insert' },
                    z: { primitive: 'number()', options: [ 'min(1)' ] }
                },
                {
                    position: { key: 'x-dune-api-key', value: '{{SERVER_PARAM:DUNE_API_KEY}}', location: 'body' },
                    z: { primitive: 'string()', options: [] }
                }
            ],
            output: {
                mimeType: 'application/json',
                schema: {
                    type: 'object',
                    properties: {
                        execution_id: { type: 'string', description: 'Unique execution identifier' },
                        state: { type: 'string', description: 'Current execution state' }
                    }
                }
            }
        },
        getExecutionStatus: {
            method: 'GET',
            path: '/api/v1/execution/{{executionId}}/status',
            description: 'Check the status of a query execution',
            parameters: [
                {
                    position: { key: 'executionId', value: '{{USER_PARAM}}', location: 'insert' },
                    z: { primitive: 'string()', options: [] }
                },
                {
                    position: { key: 'x-dune-api-key', value: '{{SERVER_PARAM:DUNE_API_KEY}}', location: 'body' },
                    z: { primitive: 'string()', options: [] }
                }
            ],
            output: {
                mimeType: 'application/json',
                schema: {
                    type: 'object',
                    properties: {
                        execution_id: { type: 'string' },
                        state: { type: 'string', description: 'Current state' },
                        queue_position: { type: 'number', description: 'Position in execution queue', nullable: true }
                    }
                }
            }
        },
        getExecutionResults: {
            method: 'GET',
            path: '/api/v1/execution/{{executionId}}/results',
            description: 'Get the results of a completed query execution',
            parameters: [
                {
                    position: { key: 'executionId', value: '{{USER_PARAM}}', location: 'insert' },
                    z: { primitive: 'string()', options: [] }
                },
                {
                    position: { key: 'x-dune-api-key', value: '{{SERVER_PARAM:DUNE_API_KEY}}', location: 'body' },
                    z: { primitive: 'string()', options: [] }
                }
            ],
            output: {
                mimeType: 'application/json',
                schema: {
                    type: 'object',
                    properties: {
                        execution_id: { type: 'string' },
                        state: { type: 'string' },
                        result: {
                            type: 'object',
                            properties: {
                                rows: { type: 'array', items: { type: 'object' } },
                                metadata: {
                                    type: 'object',
                                    properties: {
                                        column_names: { type: 'array', items: { type: 'string' } },
                                        total_row_count: { type: 'number' }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
Key takeaways:
  • Three routes form an async workflow: execute, poll, retrieve
  • The AI agent calls executeQuery first to get an execution_id
  • Then polls getExecutionStatus until state is "QUERY_STATE_COMPLETED"
  • Finally retrieves results with getExecutionResults
  • Each route uses {{SERVER_PARAM:DUNE_API_KEY}} for authentication
  • The AI client orchestrates the multi-step flow using the tool descriptions
Async workflow schemas work naturally with AI agents. The agent reads the tool descriptions, understands the execute-poll-retrieve pattern, and orchestrates the calls in sequence.

Pattern Summary

PatternWhen to useExample
MinimalSimple endpoints with no authHealth checks, public APIs
Multi-RouteRelated endpoints from one APIProtocol analytics, user management
Shared ListMulti-chain or multi-provider schemasEVM chain selection, exchange lists
Async WorkflowAPIs with execute/poll/retrieve patternsQuery engines, batch processing