flAPI - MCP Protocol Reference

May 17, 2026 · View on GitHub

Version: 1.0.0 Protocol Version: 2025-11-25 flAPI Version: >= 1.0.0

This document provides a comprehensive reference for flAPI's MCP (Model Context Protocol) implementation, covering protocol details, configuration, and usage.


Table of Contents

  1. Overview
  2. Getting Started
  3. Protocol Reference
  4. MCP Methods
  5. Tools
  6. Resources
  7. Prompts
  8. Authentication
  9. Content Types
  10. Testing & Examples

1. Overview

What is MCP

MCP (Model Context Protocol) is a JSON-RPC 2.0 based protocol designed for AI model integration. It provides a standardized way for AI assistants and agents to interact with data sources, execute tools, and access resources.

flAPI implements MCP as a first-class protocol alongside REST, allowing the same YAML configurations to expose both REST endpoints and MCP tools/resources.

Key Benefits:

BenefitDescription
Unified ConfigurationSame YAML defines both REST and MCP interfaces
AI-OptimizedStructured tool definitions with input schemas for LLM consumption
Session-BasedPersistent sessions with authentication context
Type-SafeJSON Schema validation for tool inputs

Architecture

┌─────────────────────────────────────────────────────────────┐
│                     MCP Client                               │
│              (Claude, GPT, Custom Agent)                     │
└─────────────────────┬───────────────────────────────────────┘
                      │ JSON-RPC 2.0 / HTTP

┌─────────────────────────────────────────────────────────────┐
│                  flAPI Server                                │
├─────────────────────────────────────────────────────────────┤
│  POST /mcp/jsonrpc   ─────────►  MCPRouteHandlers           │
│                                        │                     │
│  ┌─────────────────────────────────────┴──────────────────┐ │
│  │                 MCP Session Manager                     │ │
│  │   - Session lifecycle                                   │ │
│  │   - Auth context tracking                               │ │
│  │   - Protocol version negotiation                        │ │
│  └─────────────────────────────────────────────────────────┘ │
│                            │                                 │
│  ┌─────────────────────────┼─────────────────────────────┐  │
│  │    Tools     │   Resources   │   Prompts              │  │
│  │    ─────     │   ─────────   │   ───────              │  │
│  │  mcp-tool:   │  mcp-resource:│  mcp-prompt:           │  │
│  │  configs     │  configs      │  configs               │  │
│  └──────────────┴───────────────┴────────────────────────┘  │
│                            │                                 │
│                     ┌──────┴──────┐                         │
│                     │   DuckDB    │                         │
│                     │   Engine    │                         │
│                     └─────────────┘                         │
└─────────────────────────────────────────────────────────────┘

Protocol Versions

flAPI supports multiple MCP protocol versions for backward compatibility:

VersionReleaseStatusNotes
2025-11-25LatestDefaultFull feature support
2025-06-18Q2 2025Supported-
2025-03-26Q1 2025Supported-
2024-11-05Q4 2024SupportedInitial MCP specification

The server negotiates the highest mutually supported version during initialize.

Implementation: src/mcp_server.cpp, src/mcp_route_handlers.cpp | Tests: test/cpp/mcp_server_test.cpp, test/integration/test_mcp_methods.py


2. Getting Started

Enabling MCP

MCP is enabled in flapi.yaml under the mcp section:

# flapi.yaml
mcp:
  enabled: true
  port: 8080                              # Same port as REST (shared)
  host: localhost
  allow-list-changed-notifications: true

  # Instructions for LLM clients (optional)
  instructions-file: ./mcp_instructions.md
  # Or inline:
  # instructions: |
  #   # flapi MCP Server
  #   Use tools for customer lookups.
PropertyTypeDefaultDescription
enabledbooleantrueEnable/disable MCP server
portinteger8080HTTP port (shared with REST)
hoststringlocalhostBind address
allow-list-changed-notificationsbooleantrueEnable listChanged capability
instructions-filestring-Path to instructions markdown file
instructionsstring-Inline instructions for LLM clients

Configuration Tools Availability

The MCP server exposes two categories of tools:

  1. Declarative Tools (Always available when MCP enabled)

    • Tools defined in YAML configuration files via mcp-tool sections
    • Endpoint-based tools from REST API definitions
  2. Configuration Management Tools (Only when --config-service flag is used)

    • flapi_* tools for runtime management of endpoints, templates, caches
    • 18 tools organized in 4 categories: Discovery, Template, Endpoint, Cache
    • See MCP Configuration Tools API Reference

To enable configuration tools:

./flapi --config-service
# or with custom token
./flapi --config-service --config-service-token "your-token"

Note: Without the --config-service flag, only declarative tools are available in tools/list responses.

First Connection

MCP Endpoint: POST /mcp/jsonrpc

To establish a session, send an initialize request:

curl -X POST http://localhost:8080/mcp/jsonrpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2025-11-25",
      "clientInfo": {
        "name": "my-client",
        "version": "1.0.0"
      }
    }
  }'

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-11-25",
    "capabilities": {
      "tools": { "listChanged": true },
      "resources": { "subscribe": false, "listChanged": true },
      "prompts": { "listChanged": true },
      "logging": {}
    },
    "serverInfo": {
      "name": "flapi-mcp-server",
      "version": "0.3.0"
    },
    "instructions": "# flapi MCP Server\n..."
  }
}

The response includes an Mcp-Session-Id header that must be included in subsequent requests.

Health Check

Check MCP server status without JSON-RPC:

curl http://localhost:8080/mcp/health

Response:

{
  "status": "healthy",
  "server": "flapi-mcp-server",
  "version": "0.3.0",
  "protocol_version": "2025-11-25",
  "mcp_available": true,
  "tools_available": true,
  "resources_available": true,
  "tools_count": 5,
  "resources_count": 2
}

3. Protocol Reference

3.1 JSON-RPC Format

All MCP requests use JSON-RPC 2.0 over HTTP POST.

Request Format:

{
  "jsonrpc": "2.0",
  "id": "<string|number>",
  "method": "<method_name>",
  "params": { ... }
}
FieldTypeRequiredDescription
jsonrpcstringYesMust be "2.0"
idstring/numberYesRequest identifier (returned in response)
methodstringYesMCP method name
paramsobjectNoMethod-specific parameters

Response Format (Success):

{
  "jsonrpc": "2.0",
  "id": "<matching_id>",
  "result": { ... }
}

Response Format (Error):

{
  "jsonrpc": "2.0",
  "id": "<matching_id>",
  "error": {
    "code": -32600,
    "message": "Invalid Request"
  }
}

3.2 Session Management

MCP uses HTTP headers for session tracking:

HeaderDirectionDescription
Mcp-Session-IdResponse → RequestSession identifier
Content-TypeBothMust be application/json
AuthorizationRequestAuthentication credentials (if required)

Session Lifecycle:

  1. Creation: New session created on first initialize request
  2. Maintenance: Session ID returned in Mcp-Session-Id header
  3. Usage: Include session ID header in all subsequent requests
  4. Cleanup: Send DELETE /mcp/jsonrpc with session ID to close
  5. Timeout: Sessions expire after 30 minutes of inactivity (configurable)

Close Session:

curl -X DELETE http://localhost:8080/mcp/jsonrpc \
  -H "Mcp-Session-Id: <session_id>"

Implementation: src/mcp_session_manager.cpp | Tests: test/integration/test_mcp_sessions.py

3.3 Error Codes

flAPI uses standard JSON-RPC error codes plus MCP-specific codes:

CodeNameDescription
-32700Parse ErrorInvalid JSON
-32600Invalid RequestNot a valid JSON-RPC request
-32601Method Not FoundUnknown method name
-32602Invalid ParamsInvalid method parameters
-32603Internal ErrorInternal server error
-32001Authentication RequiredMethod requires authentication
-32000Session ErrorSession-related errors

3.4 Protocol Negotiation

During initialize, client and server negotiate the protocol version:

Client sends:    "protocolVersion": "2025-11-25"
Server supports: ["2024-11-05", "2025-03-26", "2025-06-18", "2025-11-25"]
Negotiated:      "2025-11-25" (highest mutually supported)

If the client requests an unknown version, the server uses its latest supported version and logs a warning.


4. MCP Methods

4.1 initialize

Establishes a new MCP session and negotiates capabilities.

Request:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-11-25",
    "clientInfo": {
      "name": "my-client",
      "version": "1.0.0"
    },
    "capabilities": {
      "sampling": {},
      "roots": {}
    }
  }
}
ParameterTypeRequiredDescription
protocolVersionstringYesDesired protocol version
clientInfo.namestringNoClient name
clientInfo.versionstringNoClient version
capabilitiesobjectNoClient capabilities

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-11-25",
    "capabilities": {
      "tools": { "listChanged": true },
      "resources": { "subscribe": false, "listChanged": true },
      "prompts": { "listChanged": true },
      "logging": {}
    },
    "serverInfo": {
      "name": "flapi-mcp-server",
      "version": "0.3.0"
    },
    "instructions": "..."
  }
}
FieldTypeDescription
protocolVersionstringNegotiated protocol version
capabilities.toolsobjectTool capabilities
capabilities.resourcesobjectResource capabilities
capabilities.promptsobjectPrompt capabilities
capabilities.loggingobjectLogging capabilities
serverInfoobjectServer identification
instructionsstringUsage instructions for LLM clients

Implementation: src/mcp_route_handlers.cpp | Tests: test/cpp/mcp_server_test.cpp, test/integration/test_mcp_methods.py


4.2 ping

Health check for active sessions.

Request:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "ping",
  "params": {}
}

Response:

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {}
}

Returns an empty object on success.

Implementation: src/mcp_route_handlers.cpp | Tests: None - see TEST_TODO.md


4.3 tools/list

Lists all available MCP tools.

Request:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/list",
  "params": {}
}

Response:

{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "tools": [
      {
        "name": "customer_lookup",
        "description": "Retrieve customer information by ID",
        "inputSchema": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "Customer ID"
            }
          },
          "required": ["id"]
        }
      }
    ]
  }
}
FieldTypeDescription
tools[].namestringUnique tool identifier
tools[].descriptionstringHuman-readable description
tools[].inputSchemaobjectJSON Schema for tool inputs

Implementation: src/mcp_route_handlers.cpp | Tests: test/integration/test_mcp_methods.py, test/integration/test_mcp_integration.py


4.4 tools/call

Executes an MCP tool with provided arguments.

Request:

{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/call",
  "params": {
    "name": "customer_lookup",
    "arguments": {
      "id": "12345"
    }
  }
}
ParameterTypeRequiredDescription
namestringYesTool name to execute
argumentsobjectNoTool input arguments
arguments._dryRunbooleanNoReserved flag. When true, flAPI renders the SQL (with ? placeholders for typed params), validates inputs, and returns the rendered SQL + execution plan as a JSON payload without executing the query. Use for shadow-mode auditing and pre-production batch validation. See § 4.4.1 Dry-run mode

Response:

{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "[{\"id\":12345,\"name\":\"John Doe\",\"email\":\"john@example.com\"}]"
      }
    ]
  }
}

Tool results are returned as content blocks (see Content Types).

Authorization: when mcp.auth.enabled: true, every tool MUST declare mcp-tool.allowed-roles in its YAML. Calls from a JWT/OIDC principal whose roles claim doesn't intersect the allowed list return:

{"jsonrpc":"2.0","id":4,"error":{"code":-32603,"message":"Permission denied: Tool 'X' requires one of [analyst]; caller has [reader]."}}

Tools without allowed-roles are denied — this is the secure-by-default stance.

Per-tool rate limit: if mcp-tool.rate-limit.enabled: true, requests over the configured budget receive a JSON-RPC error similar to the RBAC denial. Rate limits are keyed on the authenticated principal (with an anonymous fallback bucket per tool).

Response shaping: the tool's mcp-tool.response block (see CONFIG_REFERENCE § 2.6) applies redaction, row-count caps, or summary-only sampling to the result before it enters the content block.

4.4.1 Dry-run mode

Set arguments._dryRun: true to run the validate → render → plan pipeline without executing the query. Useful for:

  • Pre-production audit: have every tool definition exercised by a smoke-test harness before promoting the endpoint.
  • Operator debugging: see exactly what SQL would have been built for a given parameter set.

Request:

{
  "jsonrpc":"2.0","id":4,"method":"tools/call",
  "params":{"name":"customer_lookup","arguments":{"id":42,"_dryRun":true}}
}

Response payload (inside result.content[0].text, JSON-encoded):

{
  "dry_run": true,
  "rendered_sql": "SELECT 42 AS customer_id, 'fake' AS name",
  "params": {"id": "42"}
}

The dry-run path does NOT skip validators, role checks, or rate limits — it skips only the SQL execution step. A caller that lacks the role to invoke the tool gets the same Permission denied from a dry-run as from a real call.

Implementation: src/mcp_route_handlers.cpp, src/mcp_authorization_policy.cpp, src/mcp_dry_run.cpp, src/mcp_response_shaper.cpp, src/mcp_tool_rate_limiter.cpp | Tests: test/integration/test_mcp_methods.py, test_mcp_rbac.py, test_mcp_dry_run.py, test_mcp_response_shaping.py, test_mcp_per_tool_rate_limit.py


4.5 resources/list

Lists all available MCP resources.

Request:

{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "resources/list",
  "params": {}
}

Response:

{
  "jsonrpc": "2.0",
  "id": 5,
  "result": {
    "resources": [
      {
        "name": "customer_schema",
        "description": "Customer database schema definition",
        "mimeType": "application/json",
        "uri": "flapi://customer_schema"
      }
    ]
  }
}
FieldTypeDescription
resources[].namestringResource identifier
resources[].descriptionstringHuman-readable description
resources[].mimeTypestringContent MIME type
resources[].uristringResource URI

Implementation: src/mcp_route_handlers.cpp | Tests: test/integration/test_mcp_integration.py


4.6 resources/read

Reads the content of an MCP resource.

Request:

{
  "jsonrpc": "2.0",
  "id": 6,
  "method": "resources/read",
  "params": {
    "uri": "flapi://customer_schema"
  }
}
ParameterTypeRequiredDescription
uristringYesResource URI to read

Response:

{
  "jsonrpc": "2.0",
  "id": 6,
  "result": {
    "contents": [
      {
        "uri": "flapi://customer_schema",
        "mimeType": "application/json",
        "text": "{\"fields\":[{\"name\":\"id\",\"type\":\"INTEGER\"}]}"
      }
    ]
  }
}

Implementation: src/mcp_route_handlers.cpp, src/mcp_content_types.cpp | Tests: test/integration/test_mcp_integration.py


4.7 prompts/list

Lists all available MCP prompts.

Request:

{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "prompts/list",
  "params": {}
}

Response:

{
  "jsonrpc": "2.0",
  "id": 7,
  "result": {
    "prompts": [
      {
        "name": "analyze_customer",
        "description": "Generate customer analysis prompt",
        "arguments": [
          {
            "name": "customer_id",
            "type": "string",
            "description": "Parameter customer_id"
          }
        ]
      }
    ]
  }
}

Implementation: src/mcp_route_handlers.cpp | Tests: None - see TEST_TODO.md


4.8 prompts/get

Retrieves a prompt with template arguments substituted.

Request:

{
  "jsonrpc": "2.0",
  "id": 8,
  "method": "prompts/get",
  "params": {
    "name": "analyze_customer",
    "arguments": {
      "customer_id": "12345"
    }
  }
}
ParameterTypeRequiredDescription
namestringYesPrompt name
argumentsobjectNoTemplate arguments

Response:

{
  "jsonrpc": "2.0",
  "id": 8,
  "result": {
    "description": "Generate customer analysis prompt",
    "messages": [
      {
        "role": "user",
        "content": {
          "type": "text",
          "text": "Analyze customer 12345 and provide insights."
        }
      }
    ]
  }
}

Implementation: src/mcp_route_handlers.cpp | Tests: None - see TEST_TODO.md


4.9 logging/setLevel

Sets the server log level for the current session.

Request:

{
  "jsonrpc": "2.0",
  "id": 9,
  "method": "logging/setLevel",
  "params": {
    "level": "debug"
  }
}
ParameterTypeRequiredDescription
levelstringYesLog level to set

Valid Log Levels:

MCP LevelMaps ToDescription
debugDEBUGDetailed debugging information
infoINFOGeneral operational information
noticeINFONormal but significant events
warningWARNINGWarning conditions
errorERRORError conditions
criticalERRORCritical conditions
alertERRORAction must be taken immediately
emergencyERRORSystem is unusable

Response:

{
  "jsonrpc": "2.0",
  "id": 9,
  "result": {}
}

Implementation: src/mcp_route_handlers.cpp | Tests: test/integration/test_mcp_methods.py


4.10 completion/complete

Provides argument completion suggestions for tools and prompts.

Request:

{
  "jsonrpc": "2.0",
  "id": 10,
  "method": "completion/complete",
  "params": {
    "ref": "customer_lookup",
    "argument": "status",
    "value": "act"
  }
}
ParameterTypeRequiredDescription
refstringYesTool or prompt name
argumentstringYesArgument name to complete
valuestringNoPartial value prefix for filtering

Response:

{
  "jsonrpc": "2.0",
  "id": 10,
  "result": {
    "values": ["active", "inactive"],
    "total": 2,
    "hasMore": false
  }
}

Completions are generated from enum validators defined on the argument.

Implementation: src/mcp_route_handlers.cpp | Tests: test/integration/test_mcp_methods.py


5. Tools

5.1 Tool Configuration

MCP tools are defined in endpoint YAML files using the mcp-tool section:

# sqls/customer-lookup.yaml

mcp-tool:
  name: customer_lookup
  description: Retrieve customer information by ID, segment, or other criteria
  result-mime-type: application/json

request:
  - field-name: id
    field-in: query
    description: Customer ID
    required: false
    validators:
      - type: int
        min: 1
        preventSqlInjection: true

  - field-name: status
    field-in: query
    description: Customer status filter
    required: false
    validators:
      - type: enum
        values: ["active", "inactive", "pending"]

template-source: customer-lookup.sql
connection:
  - customer-database

MCP Tool Properties:

PropertyTypeRequiredDescription
namestringYesUnique tool identifier
descriptionstringYesHuman-readable description for LLM
result-mime-typestringNoExpected result MIME type (default: application/json)

5.2 Input Schema

The inputSchema for tools is automatically generated from request fields:

Request FieldJSON Schema Property
field-nameProperty name
descriptionProperty description
required: trueAdded to required array
validators[].typeInforms type (string by default)

Example Conversion:

# YAML Request Definition
request:
  - field-name: customer_id
    description: Customer identifier
    required: true
    validators:
      - type: int
        min: 1
// Generated Input Schema
{
  "type": "object",
  "properties": {
    "customer_id": {
      "type": "string",
      "description": "Customer identifier"
    }
  },
  "required": ["customer_id"]
}

5.3 Execution Flow

When a tool is called:

tools/call Request


┌─────────────────┐
│ Extract name &  │
│ arguments       │
└────────┬────────┘


┌─────────────────┐
│ Find endpoint   │
│ configuration   │
└────────┬────────┘


┌─────────────────┐
│ Validate inputs │
│ against schema  │
└────────┬────────┘


┌─────────────────┐
│ Render SQL      │
│ template        │
└────────┬────────┘


┌─────────────────┐
│ Execute on      │
│ DuckDB          │
└────────┬────────┘


┌─────────────────┐
│ Format as       │
│ ContentResponse │
└────────┴────────┘

5.4 Write Operations

MCP tools support write operations (INSERT, UPDATE, DELETE) using the same configuration as REST endpoints:

mcp-tool:
  name: create_customer
  description: Create a new customer record

request:
  - field-name: name
    field-in: body
    required: true
    validators:
      - type: string
        min-length: 1
        max-length: 100

  - field-name: email
    field-in: body
    required: true
    validators:
      - type: email

template-source: create-customer.sql
connection:
  - customer-database
-- create-customer.sql
INSERT INTO customers (name, email)
VALUES ('{{{ params.name }}}', '{{{ params.email }}}')
RETURNING id, name, email

Implementation: src/mcp_route_handlers.cpp, src/endpoint_config_parser.cpp | Tests: test/cpp/mcp_tool_handler_test.cpp, test/integration/test_mcp_methods.py


6. Resources

6.1 Resource Configuration

MCP resources provide static or semi-static data to AI clients:

# sqls/customer-schema.yaml

mcp-resource:
  name: customer_schema
  description: Customer database schema definition and field descriptions
  mime-type: application/json

template-source: customer-schema.sql
connection:
  - customer-database

# Optional rate limiting
rate-limit:
  enabled: true
  max: 10
  interval: 60

MCP Resource Properties:

PropertyTypeRequiredDescription
namestringYesUnique resource identifier
descriptionstringYesHuman-readable description
mime-typestringYesContent MIME type

6.2 URI Format

Resources are identified by URIs following this scheme:

flapi://<resource_name>

Examples:

Resource NameURI
customer_schemaflapi://customer_schema
product_catalogflapi://product_catalog
system_configflapi://system_config

6.3 Response Format

Resource content is returned in the contents array:

{
  "contents": [
    {
      "uri": "flapi://customer_schema",
      "mimeType": "application/json",
      "text": "{\"fields\":[...]}"
    }
  ]
}
FieldTypeDescription
uristringResource URI
mimeTypestringContent MIME type
textstringResource content (text-based)
blobstringBase64-encoded binary content

Implementation: src/mcp_route_handlers.cpp | Tests: test/integration/test_mcp_integration.py


7. Prompts

7.1 Prompt Configuration

MCP prompts provide pre-defined prompt templates for AI interactions:

# sqls/analyze-customer.yaml

mcp-prompt:
  name: analyze_customer
  description: Generate customer analysis prompt
  template: |
    Analyze customer {{customer_id}} and provide insights on:
    - Purchase history
    - Engagement patterns
    - Churn risk assessment

    Format the response as a structured report.
  arguments:
    - customer_id

MCP Prompt Properties:

PropertyTypeRequiredDescription
namestringYesUnique prompt identifier
descriptionstringYesHuman-readable description
templatestringYesPrompt template with placeholders
argumentsarrayNoList of argument names

7.2 Template Syntax

Prompts use Mustache-style placeholders:

{{argument_name}}

Example Template:

template: |
  Generate a sales report for {{region}} covering {{time_period}}.

  Include:
  - Total revenue
  - Top 5 products
  - Customer acquisition trends

  {{#include_projections}}
  Also include Q4 projections based on current trends.
  {{/include_projections}}

7.3 Arguments

Arguments are passed in the prompts/get request and substituted into the template:

{
  "method": "prompts/get",
  "params": {
    "name": "sales_report",
    "arguments": {
      "region": "North America",
      "time_period": "Q3 2024",
      "include_projections": true
    }
  }
}

Implementation: src/mcp_route_handlers.cpp | Tests: None - see TEST_TODO.md


8. Authentication

8.1 Basic Auth

Configure Basic authentication for MCP:

# flapi.yaml
mcp:
  enabled: true
  auth:
    enabled: true
    type: basic
    users:
      - username: admin
        password: "$apr1$xyz..."  # MD5 hashed
        roles: [admin, read, write]
      - username: reader
        password: plaintext123    # Plain text (not recommended)
        roles: [read]

Request Header:

Authorization: Basic YWRtaW46cGFzc3dvcmQ=

8.2 Bearer/JWT Auth

Configure JWT authentication:

# flapi.yaml
mcp:
  enabled: true
  auth:
    enabled: true
    type: bearer
    jwt-secret: "${JWT_SECRET}"
    jwt-issuer: "my-auth-server"

Request Header:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Expected JWT Claims:

ClaimDescription
subUsername/subject
rolesArray of role strings
issIssuer (must match jwt-issuer)
expExpiration timestamp

8.3 OIDC Auth

Configure OpenID Connect authentication:

# flapi.yaml
mcp:
  enabled: true
  auth:
    enabled: true
    type: oidc
    oidc:
      provider-type: auth0  # or: okta, google, azure, generic
      issuer-url: "https://your-tenant.auth0.com/"
      client-id: "your-client-id"
      client-secret: "${OIDC_CLIENT_SECRET}"
      allowed-audiences:
        - "your-api-audience"
      username-claim: email
      roles-claim: "https://your-app/roles"

Provider Presets:

Providerprovider-typeAuto-configured
Auth0auth0JWKS URL, claims mapping
OktaoktaJWKS URL, claims mapping
GooglegoogleJWKS URL, claims mapping
Azure ADazureJWKS URL, claims mapping
GenericgenericManual configuration

8.4 Per-Method Auth

Override authentication requirements per method:

# flapi.yaml
mcp:
  enabled: true
  auth:
    enabled: true
    type: bearer
    jwt-secret: "${JWT_SECRET}"
    methods:
      initialize:
        required: false    # Allow unauthenticated initialize
      tools/list:
        required: false    # Allow listing tools without auth
      tools/call:
        required: true     # Require auth for tool execution
      resources/read:
        required: true     # Require auth for resource access

Implementation: src/mcp_auth_handler.cpp, src/oidc_auth_handler.cpp | Tests: test/integration/test_oidc_authentication.py


9. Content Types

9.1 Text Content

Default content type for tool results:

{
  "type": "text",
  "text": "Result data here",
  "mimeType": "application/json"
}
FieldTypeRequiredDescription
typestringYesAlways "text"
textstringYesText content
mimeTypestringNoMIME type hint

9.2 Image Content

For binary image data (base64 encoded):

{
  "type": "image",
  "data": "iVBORw0KGgoAAAANSUhEUgAA...",
  "mimeType": "image/png"
}
FieldTypeRequiredDescription
typestringYesAlways "image"
datastringYesBase64-encoded image data
mimeTypestringYesImage MIME type

9.3 Audio Content

For binary audio data:

{
  "type": "audio",
  "data": "UklGRiQAAABXQVZFZm10...",
  "mimeType": "audio/wav"
}

9.4 Resource Content

References to MCP resources:

{
  "type": "resource",
  "resource": {
    "uri": "flapi://customer_schema",
    "mimeType": "application/json",
    "text": "{...}"
  }
}

9.5 MIME Type Detection

flAPI automatically detects MIME types from file extensions:

ExtensionMIME Type
.pngimage/png
.jpg, .jpegimage/jpeg
.gifimage/gif
.svgimage/svg+xml
.wavaudio/wav
.mp3audio/mpeg
.jsonapplication/json
.csvtext/csv
.txttext/plain
.pdfapplication/pdf
.xmlapplication/xml
(unknown)application/octet-stream

Implementation: src/mcp_content_types.cpp | Tests: Image/audio/binary not tested - see TEST_TODO.md


10. Testing & Examples

10.1 cURL Examples

Initialize Session:

# Initialize and capture session ID
SESSION_ID=$(curl -s -D - -X POST http://localhost:8080/mcp/jsonrpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {"protocolVersion": "2025-11-25"}
  }' | grep -i "Mcp-Session-Id" | cut -d: -f2 | tr -d ' \r')

echo "Session: $SESSION_ID"

List Tools:

curl -X POST http://localhost:8080/mcp/jsonrpc \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: $SESSION_ID" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/list",
    "params": {}
  }'

Call Tool:

curl -X POST http://localhost:8080/mcp/jsonrpc \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: $SESSION_ID" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "customer_lookup",
      "arguments": {"id": "12345"}
    }
  }'

With Authentication:

# Basic Auth
curl -X POST http://localhost:8080/mcp/jsonrpc \
  -H "Content-Type: application/json" \
  -H "Authorization: Basic $(echo -n 'admin:password' | base64)" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {"protocolVersion": "2025-11-25"}
  }'

# Bearer Token
curl -X POST http://localhost:8080/mcp/jsonrpc \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $JWT_TOKEN" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {"name": "customer_lookup", "arguments": {"id": "123"}}
  }'

10.2 Python Client

Simple Client:

import requests
import json

class FlapiMCPClient:
    def __init__(self, base_url: str = "http://localhost:8080"):
        self.base_url = base_url
        self.session_id = None
        self.request_id = 0

    def _call(self, method: str, params: dict = None) -> dict:
        self.request_id += 1
        headers = {"Content-Type": "application/json"}
        if self.session_id:
            headers["Mcp-Session-Id"] = self.session_id

        response = requests.post(
            f"{self.base_url}/mcp/jsonrpc",
            headers=headers,
            json={
                "jsonrpc": "2.0",
                "id": self.request_id,
                "method": method,
                "params": params or {}
            }
        )

        # Capture session ID from response
        if "Mcp-Session-Id" in response.headers:
            self.session_id = response.headers["Mcp-Session-Id"]

        result = response.json()
        if "error" in result:
            raise Exception(f"MCP Error: {result['error']}")
        return result.get("result", {})

    def initialize(self, protocol_version: str = "2025-11-25") -> dict:
        return self._call("initialize", {"protocolVersion": protocol_version})

    def list_tools(self) -> list:
        result = self._call("tools/list")
        return result.get("tools", [])

    def call_tool(self, name: str, arguments: dict = None) -> dict:
        return self._call("tools/call", {"name": name, "arguments": arguments or {}})

    def list_resources(self) -> list:
        result = self._call("resources/list")
        return result.get("resources", [])

    def read_resource(self, uri: str) -> dict:
        return self._call("resources/read", {"uri": uri})

    def ping(self) -> dict:
        return self._call("ping")

# Usage
client = FlapiMCPClient()
client.initialize()

# List available tools
tools = client.list_tools()
print(f"Available tools: {[t['name'] for t in tools]}")

# Call a tool
result = client.call_tool("customer_lookup", {"id": "12345"})
print(f"Result: {result}")

With Authentication:

class AuthenticatedMCPClient(FlapiMCPClient):
    def __init__(self, base_url: str, username: str = None,
                 password: str = None, token: str = None):
        super().__init__(base_url)
        self.auth_header = None

        if username and password:
            import base64
            credentials = base64.b64encode(f"{username}:{password}".encode()).decode()
            self.auth_header = f"Basic {credentials}"
        elif token:
            self.auth_header = f"Bearer {token}"

    def _call(self, method: str, params: dict = None) -> dict:
        self.request_id += 1
        headers = {"Content-Type": "application/json"}
        if self.session_id:
            headers["Mcp-Session-Id"] = self.session_id
        if self.auth_header:
            headers["Authorization"] = self.auth_header

        # ... rest of implementation

Appendix A: Complete Configuration Example

# flapi.yaml - Complete MCP Configuration

project-name: customer-api
project-description: Customer data API with MCP support

# Template configuration
template:
  path: ./sqls
  environment-whitelist:
    - '^DB_.*'
    - '^JWT_SECRET$'

# Database connections
connections:
  customer-db:
    properties:
      path: ./data/customers.parquet

# DuckDB configuration
duckdb:
  access_mode: READ_WRITE
  threads: 4
  max_memory: 2GB

# MCP configuration
mcp:
  enabled: true
  port: 8080
  host: 0.0.0.0
  allow-list-changed-notifications: true

  # Instructions for LLM clients
  instructions: |
    # Customer API MCP Server

    This server provides access to customer data.

    ## Available Tools
    - customer_lookup: Search for customers by ID or criteria
    - create_customer: Create new customer records

    ## Usage Guidelines
    - Always validate customer IDs before lookup
    - Use pagination for large result sets

  # Authentication
  auth:
    enabled: true
    type: bearer
    jwt-secret: "${JWT_SECRET}"
    jwt-issuer: "customer-api"

    # Per-method overrides
    methods:
      initialize:
        required: false
      tools/list:
        required: false
      tools/call:
        required: true
      resources/read:
        required: true
# sqls/customer-lookup.yaml - MCP Tool Definition

mcp-tool:
  name: customer_lookup
  description: |
    Retrieve customer information by ID or search criteria.
    Returns customer details including name, email, and status.
  result-mime-type: application/json

request:
  - field-name: id
    field-in: query
    description: Customer ID (exact match)
    required: false
    validators:
      - type: int
        min: 1
        preventSqlInjection: true

  - field-name: name
    field-in: query
    description: Customer name (partial match)
    required: false
    validators:
      - type: string
        max-length: 100

  - field-name: status
    field-in: query
    description: Customer status filter
    required: false
    validators:
      - type: enum
        values: ["active", "inactive", "pending"]

  - field-name: limit
    field-in: query
    description: Maximum results to return
    required: false
    validators:
      - type: int
        min: 1
        max: 100

template-source: customer-lookup.sql
connection:
  - customer-db

auth:
  enabled: true
  type: basic
  users:
    - username: api
      password: secret
      roles: [read]
-- sqls/customer-lookup.sql

SELECT
    id,
    name,
    email,
    status,
    created_at
FROM read_parquet('{{{ conn.path }}}')
WHERE 1=1
{{#params.id}}
  AND id = {{ params.id }}
{{/params.id}}
{{#params.name}}
  AND name ILIKE '%' || '{{{ params.name }}}' || '%'
{{/params.name}}
{{#params.status}}
  AND status = '{{{ params.status }}}'
{{/params.status}}
ORDER BY created_at DESC
LIMIT {{#params.limit}}{{ params.limit }}{{/params.limit}}{{^params.limit}}25{{/params.limit}}

Appendix B: Error Reference

Error CodeMessageCauseResolution
-32700Parse errorInvalid JSON in request bodyCheck JSON syntax
-32600Invalid RequestMissing required JSON-RPC fieldsInclude jsonrpc, id, method
-32601Method not foundUnknown MCP methodCheck method name spelling
-32602Invalid paramsMissing or invalid parametersCheck required params for method
-32603Internal errorServer-side errorCheck server logs
-32001Authentication requiredMethod requires authProvide Authorization header
-32000Session errorSession-related issueRe-initialize session