Skip to content

Implement MCP Server for fdm-api #622

Description

@SvenVw

Summary

Expose the Farm Data Model REST API as a Model Context Protocol (MCP) server, enabling AI assistants (Claude Desktop, VS Code Copilot, LangChain clients) to programmatically interact with farm data through the standardized MCP protocol.

The MCP server will be embedded in the existing fdm-api package using @hono/mcp for native Hono integration, sharing the same service layer (FdmApiServices) used by the REST routes.

Motivation

  • AI-native access: LLMs can directly read farm data and perform operations without custom HTTP client code
  • Standardized protocol: MCP is becoming the universal standard for LLM ↔ tool communication
  • Broad client support: Works with Claude Desktop, VS Code Copilot, Cursor, LangChain, and any MCP-compatible client
  • Complements fdm-agents: Provides atomic MCP tools that fdm-agents (LangGraph orchestrator) can consume as a tool provider

Architecture

┌─────────────────────────────────────────────────────────┐
│                     fdm-api (Hono)                       │
│                                                         │
│  ┌──────────────┐     ┌───────────────────────────┐    │
│  │ REST routes  │     │ @hono/mcp middleware      │    │
│  │ /api/*       │     │ /mcp (StreamableHTTP)     │    │
│  └──────┬───────┘     └────────────┬──────────────┘    │
│         │                          │                    │
│         └──────────┬───────────────┘                    │
│                    ▼                                     │
│          FdmApiServices (shared)                        │
│          + fdm-core / fdm-calculator                    │
└─────────────────────────────────────────────────────────┘
                    │
                    ▼
         ┌──────────────────┐
         │  stdio transport  │  (for local CLI/Desktop)
         │  (@modelcontextprotocol/sdk/server/stdio)     │
         └──────────────────┘

Technical Design

Transport

Transport Use Case Implementation
Streamable HTTP Remote clients, web-based AI tools @hono/mcp StreamableHTTPTransport mounted on /mcp
stdio Local Claude Desktop, VS Code Copilot Standalone entry point (mcp-server.ts) using StdioServerTransport

Authentication

API key passthrough — same mechanism as the REST API:

  • HTTP transport: Existing createApiKeyAuth middleware validates X-API-Key / Authorization: Bearer before the MCP handler
  • stdio transport: API key configured via FDM_API_KEY environment variable
  • Audit channel: Must use channel: "mcp" (not "api") in the principal context and withAuditContext call so that MCP-originated mutations are distinguishable from REST API calls in audit logs

Dependencies

{
  "@modelcontextprotocol/sdk": "^1.25.1",
  "@hono/mcp": "^0.3.0"
}

Peer dependencies already satisfied by fdm-api: hono ^4.12.18, zod ^4.4.3.

Integration Pattern

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StreamableHTTPTransport } from '@hono/mcp'

// Create MCP server — name derived from PUBLIC_FDM_NAME env var (same as REST API)
const mcpServer = new McpServer({
  name: `${appName}-mcp-server`, // e.g. "FDM-mcp-server" or custom name
  version: '0.1.0',
}, {
  instructions: `You are connected to ${appName}. You can read farm data 
  (farms, fields, cultivations, soil analyses, etc.) and perform write operations 
  (create, update, delete). Use resources to browse available data before making changes.
  All dates use YYYY-MM-DD format.`,
})

// Mount on Hono app
app.all('/mcp', async (c) => {
  const transport = new StreamableHTTPTransport({
    enableDnsRebindingProtection: true,
    sessionIdGenerator: () => crypto.randomUUID(),
  })
  await mcpServer.connect(transport)
  return transport.handleRequest(c)
})

Security

  • DNS rebinding protection via @hono/mcp (enableDnsRebindingProtection, allowedOrigins from FDM_API_ALLOWED_ORIGINS)
  • Session management via mcp-session-id headers
  • Same rate limiting as REST API applied to MCP endpoint
  • API key validation before MCP handler processes requests
  • MCP server name derived from PUBLIC_FDM_NAME env var (consistent with REST API naming)

MCP Resources (read-only, browsable context)

Resources provide LLMs with structured read access to farm data:

URI Template Description
fdm://farms List all farms for the authenticated user
fdm://farms/{b_id_farm} Single farm detail
fdm://farms/{b_id_farm}/fields Fields belonging to a farm
fdm://fields/{b_id_field} Single field detail (with GeoJSON geometry)
fdm://fields/{b_id_field}/cultivations Cultivations on a field
fdm://fields/{b_id_field}/harvests Harvests for a field
fdm://fields/{b_id_field}/soil-analyses Soil analyses for a field
fdm://fields/{b_id_field}/fertilizer-applications Fertilizer applications on a field
fdm://fields/{b_id_field}/measures Measures on a field
fdm://farms/{b_id_farm}/fertilizers Fertilizers available to a farm
fdm://farms/{b_id_farm}/organic-certifications Organic certifications for a farm
fdm://farms/{b_id_farm}/derogations Derogations for a farm
fdm://farms/{b_id_farm}/grazing-intentions Grazing intentions for a farm

MCP Tools (actions)

Tools enable LLMs to perform write operations and run calculations:

Farm Management

Tool Description
create_farm Create a new farm
update_farm Update farm metadata (name, business ID, address)
delete_farm Permanently delete a farm

Field Management

Tool Description
create_field Create a field with GeoJSON geometry
update_field Update field metadata
delete_field Delete a field

Cultivation Management

Tool Description
create_cultivation Add a cultivation to a field
update_cultivation Update cultivation details
delete_cultivation Remove a cultivation

Harvest Management

Tool Description
create_harvest Record a harvest
update_harvest Update harvest data
delete_harvest Remove a harvest record

Fertilizer Management

Tool Description
create_fertilizer Add a custom fertilizer to a farm
delete_fertilizer Remove a fertilizer
search_fertilizer_catalogue Search the fertilizer catalogue

Fertilizer Application Management

Tool Description
create_fertilizer_application Record a fertilizer application
update_fertilizer_application Update application data
delete_fertilizer_application Remove an application

Soil Analysis Management

Tool Description
create_soil_analysis Upload soil analysis results
update_soil_analysis Update soil analysis data
delete_soil_analysis Remove a soil analysis

Measure Management

Tool Description
create_measure Record an agronomic measure
update_measure Update a measure
delete_measure Remove a measure

Organic Certification Management

Tool Description
create_organic_certification Register an organic certification
delete_organic_certification Remove an organic certification

Derogation Management

Tool Description
create_derogation Add a regulatory derogation
delete_derogation Remove a derogation

Grazing Intention Management

Tool Description
set_grazing_intention Set a yearly grazing intention
delete_grazing_intention Remove a grazing intention

Agronomic Calculations

Tool Description
calculate_nitrogen_balance Compute nitrogen balance for a farm
get_nitrogen_balance_field Get field-level nitrogen balance
calculate_organic_matter_balance Compute organic matter balance for a farm
get_organic_matter_balance_field Get field-level organic matter balance
get_dose_for_field Calculate NPK dose for a field
get_fertilization_norms Get applicable fertilization norms

File Structure

fdm-api/src/
├── mcp/
│   ├── index.ts                        # McpServer factory + registration
│   ├── transport.ts                    # StreamableHTTPTransport + stdio setup
│   ├── auth.ts                         # API key verification for MCP context
│   ├── resources/
│   │   ├── farms.ts
│   │   ├── fields.ts
│   │   ├── cultivations.ts
│   │   ├── harvests.ts
│   │   ├── fertilizers.ts
│   │   ├── fertilizer-applications.ts
│   │   ├── soil-analyses.ts
│   │   ├── measures.ts
│   │   ├── organic-certifications.ts
│   │   ├── derogations.ts
│   │   └── grazing-intentions.ts
│   └── tools/
│       ├── farms.ts
│       ├── fields.ts
│       ├── cultivations.ts
│       ├── harvests.ts
│       ├── fertilizers.ts
│       ├── fertilizer-applications.ts
│       ├── soil-analyses.ts
│       ├── measures.ts
│       ├── organic-certifications.ts
│       ├── derogations.ts
│       ├── grazing-intentions.ts
│       └── calculations.ts
├── mcp-server.ts                       # Standalone stdio entry point

Client Configuration Examples

Claude Desktop (claude_desktop_config.json)

{
  "mcpServers": {
    "your-app-name": {
      "command": "node",
      "args": ["path/to/fdm-api/dist/mcp-server.js"],
      "env": {
        "FDM_API_KEY": "your_api_key_here",
        "PUBLIC_FDM_NAME": "Your App Name",
        "POSTGRES_HOST": "localhost",
        "POSTGRES_PORT": "5432",
        "POSTGRES_DB": "fdm",
        "POSTGRES_USER": "...",
        "POSTGRES_PASSWORD": "...",
        "BETTER_AUTH_SECRET": "..."
      }
    }
  }
}

VS Code Copilot (.vscode/mcp.json)

{
  "servers": {
    "your-app-name": {
      "type": "http",
      "url": "https://api.example.com/mcp",
      "headers": {
        "X-API-Key": "your_api_key_here"
      }
    }
  }
}

LangChain / Custom Client

import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'

const client = new Client({ name: 'my-app', version: '1.0.0' })
await client.connect(new StreamableHTTPClientTransport(
  new URL('https://api.example.com/mcp'),
  { requestInit: { headers: { 'X-API-Key': 'your_api_key_here' } } }
))

Implementation Tasks

  • Install @modelcontextprotocol/sdk and @hono/mcp dependencies
  • Create mcp/index.ts — McpServer factory with instructions and capability registration
  • Create mcp/auth.ts — API key verification adapter for MCP context
  • Create mcp/transport.ts — Mount StreamableHTTPTransport on /mcp in Hono app
  • Implement MCP Resources for all 12 domains (farms, fields, cultivations, harvests, fertilizers, fertilizer applications, soil analyses, measures, organic certifications, derogations, grazing intentions)
  • Implement MCP Tools for all CRUD operations (~30 tools)
  • Implement calculation tools (N-balance, OM-balance, norms, dose)
  • Create standalone mcp-server.ts stdio entry point
  • Add unit tests for tools, resources, and auth
  • Update README with MCP usage documentation and client configuration examples

Design Considerations

  • Shared Zod schemas: Reuse existing route schemas from fdm-api for tool input/output validation where possible
  • Rate limiting: Apply same rate limits as REST API to MCP tool calls
  • Error handling: Map fdm-core errors to MCP error responses with meaningful messages for LLMs
  • Pagination: Resources returning lists should support cursor-based pagination
  • Future: fdm-agents integration: fdm-agents can be refactored to consume this MCP server via @modelcontextprotocol/client, unifying the tool layer

Acceptance Criteria

  • MCP server accessible via Streamable HTTP at /mcp endpoint
  • MCP server accessible via stdio for local clients
  • All 12 API domains exposed as MCP Resources
  • All CRUD operations + calculations exposed as MCP Tools
  • API key authentication works for both transports
  • DNS rebinding protection enabled for HTTP transport
  • Session management working (mcp-session-id)
  • Existing REST API functionality unaffected
  • Tests passing for tools, resources, and auth
  • Documentation with client configuration examples for Claude Desktop and VS Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions