Schema Creation
This guide walks you through creating FlowMCP v3.0.0 schemas. A schema is a .mjs file that describes how to interact with a REST API — what endpoints exist, what parameters they accept, and how responses should be transformed.
Prerequisites
Section titled “Prerequisites”Before creating a schema, you need:
- The API documentation for the service you want to wrap
- An API key if the service requires authentication
- Node.js 18+ installed
- FlowMCP CLI installed (
npm install -g github:FlowMCP/flowmcp-cli)
Creation Process
Section titled “Creation Process”-
Choose namespace and identify endpoints
Pick a unique namespace for your schema and list the API endpoints you want to expose.
The namespace becomes part of the tool name:
namespace_toolName. Keep it short and recognizable (e.g.,coingecko,etherscan,defillama).// Namespace: "myapi"// Endpoints to wrap:// GET /api/v1/status -> ping// GET /api/v1/data/:id -> getData -
Create the main export
Every schema exports a
mainobject with the API definition:export const main = {namespace: 'myapi',name: 'MyAPI',description: 'Access data from MyAPI service',version: '3.0.0',docs: [ 'https://docs.myapi.com' ],tags: [ 'data', 'utility' ],root: 'https://api.myapi.com/v1',requiredServerParams: [ 'MYAPI_KEY' ],requiredLibraries: [],headers: {},tools: {ping: {method: 'GET',path: '/status',description: 'Check if MyAPI is online',parameters: [],output: {mimeType: 'application/json',schema: {type: 'object',properties: {status: { type: 'string', description: 'Server status' }}}}},getData: {method: 'GET',path: '/data/{{id}}',description: 'Get data record by ID',parameters: [{position: { key: 'id', value: '{{USER_PARAM}}', location: 'insert' },z: { primitive: 'string()', options: [ 'min(1)' ] }},{position: { key: 'apikey', value: '{{SERVER_PARAM:MYAPI_KEY}}', location: 'query' },z: { primitive: 'string()', options: [] }}],output: {mimeType: 'application/json',schema: {type: 'object',properties: {id: { type: 'string' },value: { type: 'number' }}}}}}} -
Add output schemas
Each tool can declare its response structure in the
outputfield. This tells AI clients what to expect:output: {mimeType: 'application/json',schema: {type: 'object',properties: {name: { type: 'string', description: 'Protocol name' },tvl: { type: 'number', description: 'Total value locked in USD' }}}} -
Add handlers (optional)
If the raw API response needs transformation, add a
handlersexport. This is a factory function that receives shared lists and libraries:export const handlers = ( { sharedLists, libraries } ) => ( {getData: {postRequest: async ( { response } ) => {const { id, rawValue, metadata } = responseconst simplified = {id,value: rawValue / 100,source: metadata.provider}return { response: simplified }}}} )Handlers support two hooks per tool:
preRequest— modify the request before it is sentpostRequest— transform the response before it reaches the AI client
-
Validate with CLI
Run the schema through the validation pipeline:
Terminal window flowmcp validate ./my-schema.mjsThe validator checks rules covering structure, security, and correctness.
-
Test with CLI
Execute live API calls to verify the schema works:
Terminal window flowmcp test single ./my-schema.mjsflowmcp test single ./my-schema.mjs --route getData
Parameter Patterns
Section titled “Parameter Patterns”Parameters define how user input and server credentials map to API requests. Each parameter has a position that controls where it goes:
Query Parameters
Section titled “Query Parameters”Appended to the URL as ?key=value:
{ position: { key: 'symbol', value: '{{USER_PARAM}}', location: 'query' }, z: { primitive: 'string()', options: [ 'min(1)' ] }}// GET /api/data?symbol=BTCPath Parameters (insert)
Section titled “Path Parameters (insert)”Substituted into the URL path:
{ position: { key: 'userId', value: '{{USER_PARAM}}', location: 'insert' }, z: { primitive: 'string()', options: [ 'min(1)' ] }}// path: '/users/{{userId}}' -> /users/abc123Body Parameters
Section titled “Body Parameters”Sent in the request body for POST/PUT requests:
{ position: { key: 'query', value: '{{USER_PARAM}}', location: 'body' }, z: { primitive: 'string()', options: [] }}Server Parameters
Section titled “Server Parameters”Injected from environment variables. Never exposed to the AI client:
{ position: { key: 'apikey', value: '{{SERVER_PARAM:ETHERSCAN_API_KEY}}', location: 'query' }, z: { primitive: 'string()', options: [] }}Zod Validation
Section titled “Zod Validation”Each parameter includes a z field that defines validation rules:
// String with minimum lengthz: { primitive: 'string()', options: [ 'min(1)' ] }
// Number with minimum valuez: { primitive: 'number()', options: [ 'min(1)' ] }
// Enum from a fixed listz: { primitive: 'enum(["bitcoin","ethereum","solana"])', options: [] }
// Enum from a shared list fieldz: { primitive: 'enum({{evmChains:etherscanAlias}})', options: [] }
// Optional stringz: { primitive: 'string()', options: [ 'optional()' ] }Shared List References
Section titled “Shared List References”Schemas can reference shared lists for reusable value enumerations like chain IDs or token symbols:
// In main:sharedLists: [ { ref: 'evmChains', version: '1.0.0', filter: { key: 'etherscanAlias', exists: true } }],
// In a parameter:z: { primitive: 'enum({{evmChains:etherscanAlias}})', options: [] }The {{evmChains:etherscanAlias}} syntax interpolates the etherscanAlias field from all entries in the evmChains shared list that pass the filter. This generates an enum like enum(["ETH","POLYGON","ARBITRUM","OPTIMISM","BASE","BSC"]).
Handler Patterns
Section titled “Handler Patterns”Response Filtering
Section titled “Response Filtering”Reduce large API responses to the fields the AI client needs:
export const handlers = ( { sharedLists, libraries } ) => ( { getProtocols: { postRequest: async ( { response } ) => { const items = response .filter( ( item ) => item.tvl > 0 ) .map( ( item ) => { const { name, slug, tvl, chain, category } = item
return { name, slug, tvl, chain, category } } )
return { response: items } } }} )Pre-Request Modification
Section titled “Pre-Request Modification”Modify request parameters before the API call:
export const handlers = ( { sharedLists, libraries } ) => ( { getData: { preRequest: async ( { params } ) => { const { symbol } = params const normalized = symbol.toUpperCase()
return { params: { ...params, symbol: normalized } } } }} )