DietlyAPI developer guide. Last reviewed: July 2026

Build an MCP server for nutrition data

An MCP server gives an AI client a narrow, inspectable nutrition lookup tool instead of asking the model to recall product labels from memory. Wrapping DietlyAPI in the Model Context Protocol lets Claude, Cursor and other MCP hosts fetch real macros on demand, then explain exactly what they found. This guide builds a small but complete server with two tools, in Python.

Why a tool beats a guess

Language models are fluent about nutrition and frequently wrong about specific numbers. A tool call changes that: the model asks for "greek yogurt", gets back a real record with a source and a per-100 g basis, and reports the value rather than inventing one. Keep the surface small so the host can reason about when to call it.

1. Define two tools with clear intent

Start with search_foods (a query plus an optional small limit) and get_food (a Dietly food ID). Resist a single vague tool that tries to parse a meal, pick a diet and build a plan at once; narrow tools are easier for a model to use correctly.

{"name":"search_foods",
 "description":"Search Dietly's food catalog by product or food name.",
 "inputSchema":{"type":"object",
   "properties":{"query":{"type":"string"},
                 "limit":{"type":"integer","maximum":10}},
   "required":["query"]}}

2. A complete server with the Python SDK

The official mcp package exposes a FastMCP helper. Each tool is a normal async function; return plain dictionaries and the SDK handles the protocol. This server calls GET /search and GET /food/{id} and preserves the API's meaning.

import httpx
from mcp.server.fastmcp import FastMCP

API = "https://api.getdietly.com"
mcp = FastMCP("dietly-nutrition")

@mcp.tool()
async def search_foods(query: str, limit: int = 5) -> list[dict]:
    """Search Dietly's catalog. Returns [] when nothing matches."""
    async with httpx.AsyncClient(timeout=10) as client:
        r = await client.get(f"{API}/search",
            params={"q": query, "limit": min(limit, 10)})
        r.raise_for_status()
        return r.json()

@mcp.tool()
async def get_food(food_id: int) -> dict | None:
    """Fetch one food by Dietly ID. Returns None if it does not exist."""
    async with httpx.AsyncClient(timeout=10) as client:
        r = await client.get(f"{API}/food/{food_id}")
        if r.status_code == 404:
            return None
        r.raise_for_status()
        return r.json()

if __name__ == "__main__":
    mcp.run()

3. Preserve the API's meaning

Do not invent a match when the search is empty, and keep nullable values nullable rather than defaulting them to zero. Pass source and confidence through to the output so a person can see where a number came from and how sure Dietly is. The value of a data tool is that it does not smooth over uncertainty.

4. Register the server with a host

An MCP host launches your server over stdio. For Claude Desktop, add it to the config file; the same shape works for Cursor and other hosts.

{
  "mcpServers": {
    "dietly-nutrition": {
      "command": "python",
      "args": ["/absolute/path/to/server.py"]
    }
  }
}

Install and run it locally

The server needs two dependencies: the MCP SDK and an HTTP client. Install them into a virtual environment, save the code above as server.py, and run it. MCP servers speak over stdio by default, so there is no port to open and nothing to expose to the internet for local use.

python -m venv .venv && source .venv/bin/activate
pip install "mcp[cli]" httpx
python server.py

While developing, the MCP command-line inspector lets you call each tool by hand and see the raw JSON a host would receive, which is the fastest way to confirm your field passthrough is intact before you wire it into a real client. Run mcp dev server.py to open it. Because the server holds no state between calls, you can restart it freely; the host reconnects and re-lists the tools automatically.

5. Tell the model how to use the results

Suggested host instruction: Call search_foods for a named food. State the per-100 g basis, name the selected product and brand, and say when a nutrient is unavailable. Nutrition data can support an answer, but do not turn it into a diagnosis or an individual medical recommendation.

6. Protect keys and limits

For a local read-only tool, anonymous access is fine. If you add a key for higher limits, load it from the host environment, never from tool arguments or a prompt, so it cannot leak into a transcript. Honor 429 responses by waiting for Retry-After and cap retries. When output derived from Open Food Facts is shown publicly, include the attribution required by the API terms.

Test before you trust it

Exercise a precise product, a generic food and a deliberate typo, and confirm the model calls the tool rather than guessing and can explain an empty result. A tool that is silently skipped is worse than no tool, because the answer still sounds authoritative. If the host has a way to log tool calls, watch a few real sessions early on: you want to see the model reaching for search_foods on specific-product questions and answering from its own knowledge only on general ones. That balance, not the raw code, is what makes the integration trustworthy in daily use.

Next: inspect the OpenAPI schema for every field, read the API guide, and use the Python quickstart for a non-MCP integration.