Skip to content

Latest commit

 

History

History
1760 lines (1408 loc) · 44.2 KB

File metadata and controls

1760 lines (1408 loc) · 44.2 KB

flAPI - MCP Protocol Reference

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:

Benefit Description
Unified Configuration Same YAML defines both REST and MCP interfaces
AI-Optimized Structured tool definitions with input schemas for LLM consumption
Session-Based Persistent sessions with authentication context
Type-Safe JSON 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:

Version Release Status Notes
2025-11-25 Latest Default Full feature support
2025-06-18 Q2 2025 Supported -
2025-03-26 Q1 2025 Supported -
2024-11-05 Q4 2024 Supported Initial 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.
Property Type Default Description
enabled boolean true Enable/disable MCP server
port integer 8080 HTTP port (shared with REST)
host string localhost Bind address
allow-list-changed-notifications boolean true Enable listChanged capability
instructions-file string - Path to instructions markdown file
instructions string - 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": { ... }
}
Field Type Required Description
jsonrpc string Yes Must be "2.0"
id string/number Yes Request identifier (returned in response)
method string Yes MCP method name
params object No Method-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:

Header Direction Description
Mcp-Session-Id Response → Request Session identifier
Content-Type Both Must be application/json
Authorization Request Authentication 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:

Code Name Description
-32700 Parse Error Invalid JSON
-32600 Invalid Request Not a valid JSON-RPC request
-32601 Method Not Found Unknown method name
-32602 Invalid Params Invalid method parameters
-32603 Internal Error Internal server error
-32001 Authentication Required Method requires authentication
-32000 Session Error Session-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": {}
    }
  }
}
Parameter Type Required Description
protocolVersion string Yes Desired protocol version
clientInfo.name string No Client name
clientInfo.version string No Client version
capabilities object No Client 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": "..."
  }
}
Field Type Description
protocolVersion string Negotiated protocol version
capabilities.tools object Tool capabilities
capabilities.resources object Resource capabilities
capabilities.prompts object Prompt capabilities
capabilities.logging object Logging capabilities
serverInfo object Server identification
instructions string Usage 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"]
        }
      }
    ]
  }
}
Field Type Description
tools[].name string Unique tool identifier
tools[].description string Human-readable description
tools[].inputSchema object JSON 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"
    }
  }
}
Parameter Type Required Description
name string Yes Tool name to execute
arguments object No Tool input arguments
arguments._dryRun boolean No Reserved 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"
      }
    ]
  }
}
Field Type Description
resources[].name string Resource identifier
resources[].description string Human-readable description
resources[].mimeType string Content MIME type
resources[].uri string Resource 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"
  }
}
Parameter Type Required Description
uri string Yes Resource 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"
    }
  }
}
Parameter Type Required Description
name string Yes Prompt name
arguments object No Template 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"
  }
}
Parameter Type Required Description
level string Yes Log level to set

Valid Log Levels:

MCP Level Maps To Description
debug DEBUG Detailed debugging information
info INFO General operational information
notice INFO Normal but significant events
warning WARNING Warning conditions
error ERROR Error conditions
critical ERROR Critical conditions
alert ERROR Action must be taken immediately
emergency ERROR System 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"
  }
}
Parameter Type Required Description
ref string Yes Tool or prompt name
argument string Yes Argument name to complete
value string No Partial 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:

Property Type Required Description
name string Yes Unique tool identifier
description string Yes Human-readable description for LLM
result-mime-type string No Expected result MIME type (default: application/json)

5.2 Input Schema

The inputSchema for tools is automatically generated from request fields:

Request Field JSON Schema Property
field-name Property name
description Property description
required: true Added to required array
validators[].type Informs 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:

Property Type Required Description
name string Yes Unique resource identifier
description string Yes Human-readable description
mime-type string Yes Content MIME type

6.2 URI Format

Resources are identified by URIs following this scheme:

flapi://<resource_name>

Examples:

Resource Name URI
customer_schema flapi://customer_schema
product_catalog flapi://product_catalog
system_config flapi://system_config

6.3 Response Format

Resource content is returned in the contents array:

{
  "contents": [
    {
      "uri": "flapi://customer_schema",
      "mimeType": "application/json",
      "text": "{\"fields\":[...]}"
    }
  ]
}
Field Type Description
uri string Resource URI
mimeType string Content MIME type
text string Resource content (text-based)
blob string Base64-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:

Property Type Required Description
name string Yes Unique prompt identifier
description string Yes Human-readable description
template string Yes Prompt template with placeholders
arguments array No List 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:

Claim Description
sub Username/subject
roles Array of role strings
iss Issuer (must match jwt-issuer)
exp Expiration 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:

Provider provider-type Auto-configured
Auth0 auth0 JWKS URL, claims mapping
Okta okta JWKS URL, claims mapping
Google google JWKS URL, claims mapping
Azure AD azure JWKS URL, claims mapping
Generic generic Manual 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"
}
Field Type Required Description
type string Yes Always "text"
text string Yes Text content
mimeType string No MIME type hint

9.2 Image Content

For binary image data (base64 encoded):

{
  "type": "image",
  "data": "iVBORw0KGgoAAAANSUhEUgAA...",
  "mimeType": "image/png"
}
Field Type Required Description
type string Yes Always "image"
data string Yes Base64-encoded image data
mimeType string Yes Image 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:

Extension MIME Type
.png image/png
.jpg, .jpeg image/jpeg
.gif image/gif
.svg image/svg+xml
.wav audio/wav
.mp3 audio/mpeg
.json application/json
.csv text/csv
.txt text/plain
.pdf application/pdf
.xml application/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 Code Message Cause Resolution
-32700 Parse error Invalid JSON in request body Check JSON syntax
-32600 Invalid Request Missing required JSON-RPC fields Include jsonrpc, id, method
-32601 Method not found Unknown MCP method Check method name spelling
-32602 Invalid params Missing or invalid parameters Check required params for method
-32603 Internal error Server-side error Check server logs
-32001 Authentication required Method requires auth Provide Authorization header
-32000 Session error Session-related issue Re-initialize session

Related Documentation