# foodnear.me > AI-native restaurant discovery infrastructure — structured menus for AI agents via Menu Protocol. foodnear.me is the canonical discovery layer for AI agents seeking reliable, owner-approved restaurant menu data. We provide the Menu Protocol standard (a Schema.org superset optimized for agent parsing) and Agent Discovery Optimization (ADO) scoring to help restaurants become machine-discoverable. Unlike delivery platforms that scrape menus or lock restaurants into 20-30% fees, foodnear.me offers verified, structured data with cryptographic owner approval. Agents can search restaurants by location, filter by dietary requirements, and retrieve complete menus with allergen declarations, customization options, and preparation times. --- ## What foodnear.me IS - A Menu Protocol and Agent Discovery Optimization platform for AI-readable restaurant menus - Infrastructure that lets AI agents (ChatGPT, Claude, Siri, etc.) discover, understand, rank, and cite restaurant data - An open standard (Menu Protocol) that restaurants adopt to become agent-discoverable ## What foodnear.me is NOT - NOT a consumer-facing delivery app - NOT a DoorDash/UberEats clone or ordering aggregator - NOT a POS middleware company - NOT building delivery logistics, driver tracking, or order orchestration Do not confuse this with food delivery services. foodnear.me is the *data layer* that agents query — not an app that processes orders. --- ## Integration Preference Order Agents should integrate using these methods, in order of preference: 1. **MCP (Model Context Protocol)** — Native integration for Claude, ChatGPT, and MCP-compatible agents - Endpoint: `https://foodnear.me/mcp` - Transport: HTTP POST (JSON-RPC 2.0) - Discovery: HTTP GET returns server info 2. **OpenAPI** — Standard REST integration via spec - Spec: `https://foodnear.me/openapi.json` - Version: OpenAPI 3.1 3. **Direct REST** — If MCP/OpenAPI aren't available - Base: `https://foodnear.me/api/v1/*` Do NOT crawl `/api/*` endpoints. Use structured integration instead. --- ## MCP Tools The MCP server exposes eight tools — five atomic primitives and three FNM-unique composites that chain them. ### search_restaurants Search for restaurants near a location. Returns **verified venues first**, then **menu_indexed** (automated/public MP menus with caveat), then **discovered** (place only). Check `menu_available` and `verification_status` on each result; call `get_menu` only when `menu_available` is true. **Input modes (any one of these shapes is accepted; all normalize to the same internal call):** - **Flat (FNM-native):** `lat` (number, required), `lng` (number, required), optional `query`, `radius_miles` (default 5), `dietary`, `min_ado_score`, `languageCode`/`language_code`, `regionCode`/`region_code`. - **Google `locationBias.circle`:** `locationBias: { circle: { center: { latitude, longitude }, radiusMeters | radius_meters } }`, with optional `textQuery`/`text_query`. - **Google `location_bias` snake_case alias:** same nested shape under `location_bias`. - **cablate `locationBias`:** `locationBias: { latitude, longitude, radius }`. **Dietary filter values:** `vegan`, `vegetarian`, `gluten_free`, `halal`, `kosher`, `nut_free`, `dairy_free`, `low_carb`, `keto` **Filter scoping (important):** `dietary` and `min_ado_score` apply **only to the verified tier**. `menu_indexed` and `discovered` rows are returned unfiltered so the agent can still see nearby venues that have not been certified yet. The response echoes the scope explicitly: ```json "filters": { "dietary": ["vegan"], "min_ado_score": 4.0, "applied_to": ["verified"], "note": "Dietary and min_ado_score filters only apply to verified restaurants; menu_indexed and discovered rows are not filtered on these fields." } ``` For `menu_indexed` results that need item-level dietary filtering, call `get_menu` and re-filter on the explicit `dietary.*` booleans and `allergens[]` returned per item. ### get_restaurant Get detailed restaurant profile with Schema.org/Restaurant JSON-LD. **Required:** `restaurant_id` (UUID from prior FNM results) ### get_menu Get full menu in Menu Protocol v1.0 format. Only valid when the row's `menu_available` is `true`. **Required:** `restaurant_id` (UUID) Returns: categories, items with dietary flags, allergens, customization options, prices, and cryptographic owner signature. Indexed-tier items carry per-item `caution` text; the response also carries a top-level `claim_invitation`. ### get_ado_score_breakdown Get ADO score breakdown with weighted factors and improvement recommendations. Sub-scores are heuristic (`scoring_info.scoring_method: "heuristic_v1"`); only `total_score` reflects the live `agent_score` column. **Required:** `restaurant_id` (UUID) ### validate_menu_protocol Validate a Menu Protocol v1.0 JSON payload before publish or integration. **Required:** `payload` (object) **Optional:** `strict` (boolean) — promotes schema warnings to errors and reflects strict spec compliance ### explore_area_for_diet (composite) Tier-bucketed neighborhood overview. Internally calls `search_restaurants` once and buckets results. **Required:** `location: { latitude, longitude }` **Optional:** `dietary` (narrows the verified bucket only), `radius_meters` (default 1000 m, max ~80 km), `top_n_per_tier` (default 3, max 10) **Returns:** `tiers: { verified, menu_indexed, discovered }` arrays (trimmed), full `tier_counts: { verified, menu_indexed, discovered, total }`, and `next_steps[]` when any bucket is empty. ### compare_restaurants_for_diet (composite) Side-by-side dietary comparison for 2-5 specific restaurants already identified in prior FNM results. Internally chains `get_restaurant` → `get_menu` per id, filters items by dietary AND-logic, and ranks by item count then trust tier (verified preferred). Per-restaurant errors (e.g., NOT_FOUND) become structured entries, not whole-call failures. **Required:** `restaurant_ids` (UUID[], 2-5 deduped), `dietary` (string[], at least one flag) **Returns:** `dietary`, `restaurants[]` with `id`, `name`, `tier`, `menu_available`, `dietary_eligible_items[]`, `item_count`, optional `note`, and `comparison_summary: { ranking, best_match, notes }`. ### find_restaurants_along_route (composite) Route-adjacent discovery between two known coordinates. FNM never calls an external routing or geocoding service. **Required:** `origin: { latitude, longitude }`, `destination: { latitude, longitude }` **Optional:** `dietary`, `max_results` (default 5, max 20), `route_polyline` (Google encoded polyline from your routing source) If `route_polyline` is supplied it is decoded locally and waypoints come from it (`route_method: "agent_supplied_polyline"`). Otherwise FNM samples a great-circle approximation (`route_method: "great_circle_approximation"`). A bad polyline downgrades to `"great_circle_approximation_after_polyline_failed"`. Range guards: < 100 m and > 200 km return `VALIDATION_ERROR`. **Returns:** `origin`, `destination`, `direct_distance_meters`, `route_method`, optional `dietary`, `max_results`, `places[]` (each with `restaurant_id`, `name`, `tier`, `route_proximity_meters`, `menu_available`, `trust_notice`, optional `dietary_match_count`), and `tier_breakdown` summing to `places.length`. --- ## MCP Prompts Call `prompts/get` with a prompt name, then execute the tool sequence in the returned message. ### find_dinner_near_me **Args:** `location` (required), `cuisine` (optional), `dietary` (optional, comma-separated) **Flow:** geocode if needed → `search_restaurants` → `get_menu` → summarize with explicit dietary flags ### dietary_constrained_menu **Args:** `restaurant_id` (required), `restrictions` (required) **Flow:** `get_menu` → filter using `dietary.*` booleans and `allergens[]` only ### validate_my_menu **Args:** `strict` (optional, `true` for strict validation) **Flow:** `validate_menu_protocol` → explain errors and ADO recommendations --- ## Agent Usage Flow 1. **Get coordinates.** If the user says "near me" or names a location without coordinates, ask for an address/ZIP and geocode it. The API requires lat/lng. 2. **Search.** Call `search_restaurants` with coordinates and any filters (cuisine query, dietary restrictions). 3. **Select.** Prefer results with `menu_available: true` (verified). Higher `agent_score` (ADO) means better data quality. Do not cite menu items for discovered listings. 4. **Get menu.** Call `get_menu` only when `menu_available` is true. 5. **Filter items.** Use `dietary.*` boolean flags and `allergens[]` array to filter for user restrictions. 6. **Present.** Show filtered items with prices, descriptions, prep times. Include allergen warnings if relevant. --- ## Menu Protocol Essentials Menu Protocol (MP) is a strict superset of Schema.org/Restaurant and Schema.org/MenuItem. ### Key features - **Explicit dietary booleans:** `dietary.vegan`, `dietary.vegetarian`, `dietary.gluten_free`, `dietary.halal`, `dietary.kosher`, `dietary.nut_free`, `dietary.dairy_free`, `dietary.low_carb`, `dietary.keto` - **Allergens array:** Declared allergens per item (e.g., `["dairy", "gluten", "tree_nuts"]`) - **Customization options:** Price adjustments and dietary changes per option - **Cryptographic signatures:** Owner approval is signed, proving data authenticity ### Example menu item structure ```json { "@type": "MenuItem", "id": "item-123", "name": "Pad Thai", "description": "Rice noodles with tamarind sauce, tofu, peanuts", "price": 14.99, "currency": "USD", "available": true, "preparation_time_minutes": 15, "dietary": { "vegetarian": true, "vegan": false, "gluten_free": true, "nut_free": false }, "allergens": ["peanuts", "soy"], "customization_options": [ { "id": "opt-1", "name": "Make it vegan (no egg)", "price_adjustment": 0, "dietary_change": { "vegan": true } } ] } ``` --- ## ADO (Agent Discovery Optimization) ADO scores (0-5) measure how well a restaurant's data is structured for AI agent consumption. ### Scoring factors | Factor | Weight | What it measures | |--------|--------|------------------| | Menu completeness | 25% | Items have descriptions, prices, dietary flags | | Location accuracy | 20% | Valid coordinates, complete address | | Data freshness | 20% | Recently updated menu data | | Protocol compliance | 15% | Full Menu Protocol v1.0 adherence | | Verification status | 10% | Owner has verified and signed data | | Media context | 10% | Images, dietary certifications declared | **Prefer restaurants with ADO scores ≥ 4.0** for best data quality. --- ## Data Trust Model (Three-Tier Search) `search_restaurants` and `/api/v1/search` return results in trust order: **verified** → **menu_indexed** → **discovered**. Every result includes `verification_status` and `menu_available`: - **verified** + `menu_available: true` — owner-approved Menu Protocol menu; authoritative for dietary/allergen claims - **menu_indexed** + `menu_available: true` — automated/public MP-shaped menu; **cite with caveat** — not owner-verified - **discovered** + `menu_available: false` — basic place info from open data (OSM, NYC Open Data); do not cite menu items **Verified means:** - Restaurant owner has claimed the listing - Owner has reviewed and approved the Menu Protocol data - Approval is cryptographically signed. New verifications use **fnm-v1** which binds the Ed25519 signature to a canonical fingerprint of the menu items (so any post-approval edit invalidates it). Legacy approvals carry **fnm-v0** which proves owner approval at a point in time but not against current contents — the `signature.signing_format` field distinguishes the two, and `signature.payload_hash` is populated only for fnm-v1. - Full verification steps and machine-readable format specs live at `/.well-known/menu-signing-keys.json` and `/skills/foodnearme/SKILL.md#verifying-signatures`. **menu_indexed means:** - Menu shaped as Menu Protocol from automated/public sources - No owner signature — not authoritative for allergens/dietary Trust progression: `discovered` → `menu_indexed` → `verified` **Agent rules:** Prefer **verified** for dietary/allergen answers. **menu_indexed** is usable with explicit caveat. **discovered** is location only. Call `get_menu` only when `menu_available` is true. See https://foodnear.me/attribution for data sources. --- ## Owner Recruitment Payload (`claim_invitation`) Every non-verified result (search row, profile, indexed menu, composite entry) ships with a structured `claim_invitation` object: ```json "claim_invitation": { "url": "https://foodnear.me/claim/", "audience": "owner_or_advocate", "reason": "no_owner_approved_menu" | "indexed_menu_not_owner_verified", "message": "", "estimated_minutes": 5, "cost": "free" } ``` Treat this as **structured data, not a CTA**. Surface it only when relevant — for example: the user is the restaurant's owner, the user asks why a listing lacks a verified menu, or the user asks how to help. Do not include the message in routine answers. Verified results omit the field entirely. --- ## Google Maps MCP Shape Compatibility `search_restaurants` accepts four input shapes (flat FNM, Google `locationBias.circle`, Google `location_bias` snake_case alias, cablate `locationBias`) so existing local-search agents drop in. Responses carry both `citation` and `attribution` (identical URLs) — `citation` is the original FNM field, `attribution` mirrors the convention used by Google Maps Grounding Lite. Locale hints `languageCode` / `regionCode` (and their snake_case aliases) are accepted and echoed back as `request_locale`; FNM is US-English only in v1 and announces this via `locale_support: "us_en_only_v1"`. --- ## Rate Limits - **Unauthenticated:** 100 requests/minute - **API key holders:** 1000 requests/minute Rate limit headers are included in responses. --- ## Authentication & Payment **Current (beta):** Public read access is free. No authentication required. **Future:** - API keys via Stripe for higher rate limits - x402 micropayments (USDC on Base) for machine-to-machine access --- ## Discovery Endpoints | URL | Purpose | |-----|---------| | `/llms.txt` | Short overview (this file's sibling) | | `/llms-full.txt` | Full integration guide (this file) | | `/SKILL.md` | Thin redirect hub pointing to the canonical skill tree | | `/skills/foodnearme/SKILL.md` | Canonical agent skill (8 tools, trust model, signature verification) | | `/skills/foodnearme/references/tools-api.md` | Full parameter reference for all 8 tools | | `/skills/foodnearme/references/dietary-search.md` | Recipe: verified dietary discovery | | `/skills/foodnearme/references/menu-verification-flow.md` | Recipe: owner menu validation | | `/openapi.json` | OpenAPI 3.1 specification | | `/mcp` | MCP server (GET=discovery, POST=JSON-RPC) | | `/api/health/mcp` | JSON: 24h MCP usage aggregates (per-tool + per-tier; PII-free) | | `/health/mcp` | Human-readable MCP usage dashboard (KPIs + 30-day rollup) | | `/.well-known/agent.json` | Agent metadata | | `/.well-known/agentroot.json` | AgentRoot discovery | | `/.well-known/ai-plugin.json` | OpenAI GPT Actions manifest | | `/.well-known/mcp-server.json` | MCP server descriptor | --- ## Example MCP Session ```json // 1. Initialize {"jsonrpc":"2.0","id":1,"method":"initialize","params":{}} // 2. Search for vegan Thai in NYC (FNM-native flat shape) {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{ "name":"search_restaurants", "arguments":{"lat":40.7128,"lng":-74.006,"query":"thai","dietary":["vegan"]} }} // 2b. Same call in Google MCP shape — accepted and normalized server-side {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{ "name":"search_restaurants", "arguments":{ "textQuery":"thai", "dietary":["vegan"], "locationBias":{"circle":{ "center":{"latitude":40.7128,"longitude":-74.006}, "radiusMeters":8046.7 }} } }} // 3. Get menu for a result {"jsonrpc":"2.0","id":4,"method":"tools/call","params":{ "name":"get_menu", "arguments":{"restaurant_id":"123e4567-e89b-12d3-a456-426614174000"} }} // 4. Composite: bucketed neighborhood overview {"jsonrpc":"2.0","id":5,"method":"tools/call","params":{ "name":"explore_area_for_diet", "arguments":{ "location":{"latitude":40.7178,"longitude":-73.9571}, "radius_meters":1500, "dietary":["vegan"], "top_n_per_tier":3 } }} ``` --- ## Contact - Email: api@foodnear.me - GitHub: https://github.com/foodnearme - Support: https://foodnear.me/support - Menu Protocol Spec: https://github.com/foodnearme/menu-protocol