Skip to main content
Glama
eerihyppanen

LUT Lunch MCP

by eerihyppanen

LUT Lunch MCP

An MCP server that lets AI assistants answer questions about lunch at LUT University's Skinnarila campus in Lappeenranta: what's on today, where to find something vegan or gluten-free, whether a dish shows up this week, and how often it has been served before.

It reads the public menus of LUT Buffet, Bistro Skinnarila and Yolo, normalizes their diet and allergen labels, and explains why a restaurant has no menu (weekend, public holiday, not published yet, source unavailable) instead of returning an empty list.

You:    Any vegan options on campus tomorrow?
Claude: Tomorrow is Saturday, so LUT Buffet and Bistro Skinnarila are closed. Yolo is open and
        has one vegan dish: Vegetable tikka masala (gluten-free; contains celery).

Remote server (no install)

The same server runs on Cloudflare Workers. Add it as a connector, with no authentication:

https://lut-lunch-mcp.eerihyppanen-lut-lunch-mcp.workers.dev/mcp
  • Claude (claude.ai or the desktop app): Settings → Connectors → Add custom connector, paste the URL.

  • ChatGPT: Settings → Apps & Connectors → Advanced settings → enable developer mode, then create a connector with the URL and no authentication.

It allows 600 requests per minute per client address. Claude and ChatGPT connectors share their vendors' servers, so that budget is shared by everyone using the same assistant.

Related MCP server: LounasMCP

Install

To run the server locally instead, you need Node.js 22 or newer. It runs over stdio; nothing to configure, no API keys.

Claude Desktop

Add this to claude_desktop_config.json (Settings → Developer → Edit Config), then restart Claude Desktop:

{
  "mcpServers": {
    "lut-lunch": {
      "command": "npx",
      "args": ["-y", "@eerihyppanen/lut-lunch-mcp"]
    }
  }
}

On Windows, if the server doesn't start, use "command": "cmd" with "args": ["/c", "npx", "-y", "@eerihyppanen/lut-lunch-mcp"].

Claude Code

claude mcp add lut-lunch -- npx -y @eerihyppanen/lut-lunch-mcp

Cursor

Add the same block as for Claude Desktop to .cursor/mcp.json in your project, or to ~/.cursor/mcp.json for all projects.

ChatGPT only supports remote servers; use the remote server.

Example questions

  • "What's for lunch at LUT today?"

  • "Any vegan options on campus this week?"

  • "Which restaurant has something gluten-free tomorrow?"

  • "Is there salmon anywhere this week?"

  • "I'm allergic to egg. What can I eat at LUT Buffet today?"

  • "Where's Yolo and when is it open?"

  • "How often has salmon been served?"

  • "What was on at LUT Buffet last Tuesday?"

  • "What's the most common soup on campus?"

Tools

Tool

Use it for

Main inputs

get_menu

Full menus for a day or up to 7 consecutive days, past days included

date (today, tomorrow or YYYY-MM-DD), days, restaurants, diet, exclude_allergens, language (en/fi)

find_dishes

Searching dishes across days and restaurants, past days included

query (English or Finnish), diet, exclude_allergens, from, to (≤ 14 days), restaurants, limit

menu_stats

How often and when dishes were served, trends, most common dishes

query, diet, restaurants, from, to, group_by (restaurant/month/weekday/none), top, language

list_restaurants

Ids, addresses, usual lunch hours, data coverage

none

Every result has structured content (validated against an output schema) and a compact text version for clients that only show text.

  • Restaurant ids: lut-buffet, bistro-skinnarila, yolo.

  • Diet tags: vegan, vegetarian, gluten_free, milk_free, lactose_free, low_lactose.

  • Allergens: the EU's 14 (gluten, milk, egg, fish, crustaceans, molluscs, peanuts, nuts, soy, celery, mustard, sesame, sulphites, lupin).

Each restaurant in a get_menu result has a status (open, no_menu, error) and, when it isn't open, a reason: weekend, public_holiday (with its name), none_listed, not_published_yet, history_not_available, source_unavailable (temporary outage; try again later), source_error (the source answered with data the server couldn't read, e.g. after a site redesign) or history_unavailable (the recorded history needed for a past day could not be loaded). find_dishes lists those days in days_without_data, so "no matches" and "no data" are never confused.

Data sources and credits

Restaurant

Operator

Source

LUT Buffet

Kampusravintolat

Poweresta public menu API

Bistro Skinnarila

Compass Group / Food & Co

Compass menu API

Yolo

Aalef

The restaurant's web page

All menus belong to their operators; every result links back to the source.

Allergen disclaimer. Diet and allergen labels come from the restaurants and can change. People with allergies should confirm at the restaurant. The server repeats this whenever diet or allergen filters are used.

History

The restaurants' sites only show current and upcoming menus, so history has to be recorded as it happens. A GitHub Actions workflow fetches every restaurant's menu each morning (with two retries later in the morning) and commits it to the data branch: one JSON file per day, a combined history.json for anyone who wants the whole dataset, and stats.json, a compact index of every dish served. GitHub sometimes skips scheduled runs, so each run also fills in menus missing from earlier days of the same week. The server reads the day files it needs and stats.json when it is first asked about the past (at most every 6 hours) and answers past-day, "how often" and "when last" questions from them.

History starts on 2026-10-24, the first day the collector ran; every menu_stats result states the period it covers. Only what the server needs is stored (names, prices, diet labels, allergens), with attribution to each restaurant. Set LUT_LUNCH_HISTORY_URL to the base URL of another data branch, e.g. a fork's (https://raw.githubusercontent.com/<owner>/lut-lunch-mcp/data/); a URL ending in history.json, as used before 0.2.2, still works.

Design decisions

Tools designed for an AI caller, not mirrored from the sources. Four tools match the questions people ask: browse (get_menu), search (find_dishes), count (menu_stats) and look up (list_restaurants). Which source a restaurant uses is an implementation detail the model never needs. Tool descriptions say when to use which tool and when to try the Finnish word.

Every empty answer has a reason. Sources return empty lists for weekends, holidays, unpublished weeks and outages alike. The server works out which one it is (checked in order: dishes → error → not covered by the source → Finnish public holiday → weekend → nothing listed) so the model can say "closed for All Saints' Day" instead of guessing.

The server resolves dates. Models often don't know today's date. Tools accept today and tomorrow, resolve them in Europe/Helsinki time, and always echo the resolved date and weekday. Invalid dates get an error that says which forms are accepted.

Unknown is never safe. Allergen data differs by source: LUT Buffet publishes full lists for most dishes, Yolo marks five allergens on some dishes, Bistro only labels gluten-free and milk-free. Each dish records which allergens its source actually assessed. A Yolo marker proves an allergen is present, but a missing marker proves nothing (its "Tuna and eggs" salad carries no egg marker), and an allergen name the server doesn't recognise makes that dish's list count as incomplete. When excluding, say, egg, a dish without reliable egg data is left out and counted as unverifiable, never shown as egg-free. Gluten and milk are decided by the gluten-free/milk-free labels every source has (so gluten-free oats pass even though oats appear in the allergen list). That also means a dish labelled gluten-free passes a gluten filter when its list says it may contain wheat or other gluten cereals; the "may contain" warning is still shown with the dish, so people with coeliac disease should look for it.

History is collected once, centrally. A local server could only record the days its own user happened to ask about, so one scheduled collector writes a shared, public dataset instead. It reuses the server's own fetching code, never overwrites a recorded menu with a later failure, and only commits when something changed. Plain JSON on a branch was enough: no database, no hosting, no keys.

Polite to the sources. Each source is fetched a whole week per request, cached for 30 minutes, and concurrent requests share one fetch. If a source fails, the last good copy is served (marked stale) for up to a day, and the source isn't retried for a minute. Dates far in the future are answered without any request, and past dates come from the recorded history. Requests identify the project in their User-Agent.

Ported, not imported, from Campus Food. The menu logic started in my Campus Food web app, but it was tied to Next.js caching and a UI-shaped data model. Here it lives in a framework-free src/core/ that only uses fetch (ready for Cloudflare Workers) and switches Bistro from HTML scraping to the JSON API the restaurant page itself uses.

TypeScript. The official MCP SDK is first-class in TypeScript, and the same core can run as an npm package now and on Cloudflare Workers later.

Development

npm install
npm test            # unit and end-to-end tests against captured menus (offline)
npm run inspect     # open the server in MCP Inspector
npm run smoke       # check the live sources still parse (about 8 requests)
npm run collect -- --data-dir <folder>   # record today's menus into a local history folder
npm run worker:dev  # run the Worker locally at http://127.0.0.1:8787/mcp
npm run smoke:remote -- <url of /mcp>    # check a running remote server like a client would
npm run lint && npm run typecheck && npm run build && npm run worker:check

Tests run against real menus captured on 2026-10-23 (test/fixtures/), so they are deterministic and make no network requests. The smoke check runs weekly in GitHub Actions to catch upstream changes.

To try a local build in a client, point it at node /absolute/path/to/dist/bin/stdio.js instead of npx.

Layout: src/core/ (sources, normalization, calendar, cache, menu service; no Node or MCP imports), src/mcp/ (tools, schemas, formatting), src/worker/ (remote server: HTTP, rate limit, collection trigger; no Node imports), src/bin/stdio.ts and src/bin/worker.ts (entry points). Design notes are in docs/specs/; changes per release are in CHANGELOG.md.

Releasing (from an up-to-date main):

npm version patch         # bumps package.json and src/version.ts, commits and tags
git push --follow-tags
npm publish               # runs lint, typecheck, tests and build first; asks for npm 2FA

The tag also deploys the Worker (.github/workflows/deploy.yml) and smoke-tests the deployed server.

Maintenance

  • Collection trigger token. The Worker starts the daily collection with a fine-grained GitHub token (this repository only, Actions: read and write). GitHub emails before it expires; create a new one and store it with npx wrangler secret put GITHUB_DISPATCH_TOKEN. Until then GitHub's own schedule keeps collecting, hours late.

  • Deploy secrets. deploy.yml needs the repository secrets CLOUDFLARE_API_TOKEN (template "Edit Cloudflare Workers") and CLOUDFLARE_ACCOUNT_ID, and the variable WORKER_URL (the deployed /mcp URL).

  • Rebuilding the history files. After editing day files on the data branch by hand, delete history.json there (only that one); the next collection rebuilds it and stats.json from the day files. Don't delete stats.json: servers read it, so past days and menu_stats would fail until the next collection.

  • Logs. The Worker logs one line per request (method, tool, duration, status; no IP addresses or arguments) and each collection trigger. They are in the Cloudflare dashboard under the Worker's Observability tab.

Limitations

  • Skinnarila campus only.

  • History starts when collection started; earlier menus can't be recovered from the sources.

  • menu_stats treats dishes with the same name as the same dish; slightly different names count separately (searching a word like "lohi" still finds all of them).

  • Yolo is scraped from its web page; a redesign breaks it until the parser is updated.

  • Yolo publishes about four serving days ahead and no per-course prices.

  • "Vegetarian" is inferred from course names (no source has a vegetarian label).

  • Lunch hours in list_restaurants are the usual hours and can change; check the menu link.

  • The remote server runs on Cloudflare's free plan. The first request a new Worker instance serves takes slightly more CPU than the plan's 10 ms budget (later ones about 2 ms); Cloudflare has served them all so far, and the logs are watched for limit errors.

  • Gluten filtering trusts the restaurant's gluten-free label, even over a "may contain" note (see Design decisions).

Roadmap

  1. Phase 1: local stdio server on npm.

  2. Phase 2: daily history collection and menu_stats for "how often" and trend questions.

  3. Phase 3 (this release): remote MCP server on Cloudflare Workers, so it can be added as a connector without installing anything (including in ChatGPT), and a collection trigger that starts on time.

License

MIT

Available Tools

4 tools
find_dishesFind dishesA
Read-onlyIdempotent

Search LUT Skinnarila campus lunch menus for dishes matching a word, diet or allergen restriction, across several days and restaurants. Use it for questions like "any vegan options this week?", "where is something gluten-free tomorrow?" or "is there salmon this week?". Returns a flat list of matching dishes with date and restaurant. Searches Finnish and English dish names; if an English word finds nothing, try the Finnish one (e.g. "lohi" for salmon). Past dates are searched in the recorded history, up to 14 days per search; for "how often" or "when last" over longer periods use menu_stats.

NameRequiredDescriptionDefault
toNoLast day to search (at most 14 days). Default: from + 6 days.
dietNoOnly dishes labelled with every one of these diets.
fromNoFirst day to search. Default: today.today
limitNoMaximum matches to return (1–100). Default: 30.
queryNoWord or part of a dish name, in English or Finnish (e.g. "salmon" or "lohi").
languageNoLanguage for dish and course names. Default: en.en
restaurantsNoRestaurant ids to include. Default: all.
exclude_allergensNoLeave out dishes that contain or may contain these allergens. Dishes whose allergens are not published are left out too and counted as "unverifiable".

Output Schema

NameRequiredDescription
todayYes
noticeYes
matchesYes
searchedYes
timezoneYes
truncatedYes
unverifiableYes
total_matchesYes
days_without_dataYes

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (readOnly, idempotent, openWorld), yet the description adds real substance beyond them: it discloses the return shape ('a flat list of matching dishes with date and restaurant'), states that past dates come from recorded history and are capped at 14 days per search, and warns that dishes with unpublished allergens are dropped and counted as 'unverifiable'. That last point is a non-obvious behavioral quirk that would otherwise cause an agent to misread an empty list as 'no such dishes exist'.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Roughly four sentences, front-loaded with scope, then examples, then return behavior, then the sibling hand-off and the language fallback. No sentence is filler: the examples, the 14-day cap, and the Finnish retry rule each change agent behavior. Structure mirrors the order an agent needs them.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Even with 8 parameters and an output schema present, the description covers the pieces structured fields can't express: the 14-day history window, the language fallback, the allergen 'unverifiable' drop behavior, and the boundary against menu_stats. An agent has everything needed to call this correctly and interpret an empty result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline would be 3, but the description adds meaning the schema cannot: it links `query` and `language` by explaining that names exist in both Finnish and English and that a miss in one should be retried in the other, which changes how those two parameters should be sequenced. It also frames `exclude_allergens` as a semantic exclusion (contain *or may contain*) rather than a plain field filter, matching the schema's 'unverifiable' note.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource ('Search LUT Skinnarila campus lunch menus for dishes') and immediately names the dimensions it filters on: word, diet, allergen, across days and restaurants. The closing sentence explicitly distinguishes it from menu_stats ('for "how often" or "when last" over longer periods use menu_stats'), so an agent can separate it from the three siblings without reading a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives concrete trigger questions ('any vegan options this week?', 'where is something gluten-free tomorrow?') that map directly onto parameter combinations, plus an explicit exclusion routing aggregate questions to menu_stats. It also supplies an operational fallback rule — if an English word finds nothing, retry in Finnish (e.g. 'lohi') — which is exactly the kind of usage guidance that prevents empty-result retries.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_menuGet lunch menusA
Read-onlyIdempotent

Get lunch menus for LUT University's Skinnarila campus restaurants (Lappeenranta, Finland) for one day or several consecutive days, including past days from the recorded history. Use it to show what is or was served, e.g. "what's for lunch today?" or "what was on last Tuesday?". To look for a particular dish, ingredient, diet or allergen across days and restaurants, use find_dishes; for "how often" and "when last", use menu_stats. Pass "today" or "tomorrow" rather than computing dates yourself; dates are in Finland time. Restaurants without a menu are still listed with a reason (weekend, public holiday, not published yet, no history for that day, source unavailable).

NameRequiredDescriptionDefault
dateNo"today", "tomorrow" or a date as YYYY-MM-DD (Finland time).today
daysNoNumber of consecutive days starting from `date` (1–7). Default: 1.
dietNoOnly dishes labelled with every one of these diets.
languageNoLanguage for dish and course names. Default: en.en
restaurantsNoRestaurant ids to include. Default: all.
exclude_allergensNoLeave out dishes that contain or may contain these allergens. Dishes whose allergens are not published are left out too and counted as "unverifiable".

Output Schema

NameRequiredDescription
daysYes
todayYes
noticeYes
timezoneYes

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the read-only/idempotent/open-world annotations, the description discloses important behavior: menus can cover past days from recorded history, dates use Finland time, and restaurants without a menu are still returned with a reason such as weekend, public holiday, not published yet, no history, or source unavailable.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose, then adds usage examples, sibling routing, date guidance, and missing-menu behavior in a compact sequence. Each sentence carries useful information for selecting or invoking the tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only, six-parameter tool with full schema description coverage and an output schema, the description supplies everything needed: scope, historical coverage, timezone handling, alternative tools, and fallback behavior when a restaurant has no menu.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already documents all six parameters at 100% coverage, so the baseline is 3. The description adds useful semantic context by clarifying that "date" can cover past days from recorded history and by advising "today"/"tomorrow" instead of computed dates because dates are in Finland time.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: getting lunch menus for LUT University's Skinnarila campus restaurants for one or several consecutive days, including past days. It clearly distinguishes this lookup tool from find_dishes and menu_stats by naming the specific questions it answers.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit usage conditions with examples such as "what's for lunch today?" and "what was on last Tuesday?", and names alternatives for related needs: find_dishes for searching a particular dish/ingredient/diet/allergen, and menu_stats for frequency or last-served questions. It also tells the caller to pass "today" or "tomorrow" rather than computing dates.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_restaurantsList restaurantsA
Read-onlyIdempotent

List the LUT Skinnarila campus lunch restaurants this server covers, with ids for the other tools, address, operator, menu link and how complete their diet and allergen data is. No menus; use get_menu or find_dishes for those.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
restaurantsYes

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint and openWorldHint, so the safety profile is covered. The description still adds real value by disclosing exactly what each entry contains — address, operator, menu link, and a data-completeness signal for diet/allergen data — which tells the agent how far this list can be trusted for allergen filtering.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, both earning their place: the first builds the value proposition and returned fields, the second handles the routing exclusion. Nothing is buried in the middle.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With zero params, full schema coverage, annotations, and an output schema, the description only needs to cover purpose, scope and routing — all present. No behavioral detail an agent needs in order to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes no parameters, so there is nothing for the description to disambiguate and the baseline of 4 applies. It adds no misleading param-like language.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

A specific verb (List) plus a precisely bounded resource (LUT Skinnarila campus lunch restaurants covered by this server). It also enumerates the returned fields, including 'ids for the other tools', which immediately distinguishes it from its siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

States an explicit exclusion ('No menus') and names the exact alternatives (get_menu, find_dishes) that cover that case. It also implies the intended role of the call, discovery of restaurant ids for the other tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.3.1
    • Changedfind_dishes1 field changed
      • changedOutput schema / properties / days_without_data / items / properties / reason / enum
        Previous value: -[
        -  "weekend",
        -  "public_holiday",
        -  "none_listed",
        -  "not_published_yet",
        -  "history_not_available",
        -  "source_unavailable",
        -  "source_error"
        -]New value: +[
        +  "weekend",
        +  "public_holiday",
        +  "none_listed",
        +  "not_published_yet",
        +  "history_not_available",
        +  "source_unavailable",
        +  "source_error",
        +  "history_unavailable"
        +]
    • Changedget_menu1 field changed
      • changedOutput schema / properties / days / items / properties / restaurants / items / properties / reason / anyOf
        Previous value: -[
        -  {
        -    "enum": [
        -      "weekend",
        -      "public_holiday",
        -      "none_listed",
        -      "not_published_yet",
        -      "history_not_available",
        -      "source_unavailable",
        -      "source_error"
        -    ],
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "enum": [
        +      "weekend",
        +      "public_holiday",
        +      "none_listed",
        +      "not_published_yet",
        +      "history_not_available",
        +      "source_unavailable",
        +      "source_error",
        +      "history_unavailable"
        +    ],
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
  2. 3 tool updatesv0.2.0
    • Changedfind_dishes3 fields changed
      • addedOutput schema / properties / days_without_data / items / properties / holiday_name
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / days_without_data / items / properties / reason / enum
        Previous value: -[
        -  "weekend",
        -  "public_holiday",
        -  "none_listed",
        -  "not_published_yet",
        -  "history_not_available",
        -  "source_unavailable"
        -]New value: +[
        +  "weekend",
        +  "public_holiday",
        +  "none_listed",
        +  "not_published_yet",
        +  "history_not_available",
        +  "source_unavailable",
        +  "source_error"
        +]
      • changedOutput schema / properties / days_without_data / items / required
        Previous value: -[
        -  "date",
        -  "restaurant_id",
        -  "status",
        -  "reason"
        -]New value: +[
        +  "date",
        +  "restaurant_id",
        +  "status",
        +  "reason",
        +  "holiday_name"
        +]
    • Changedget_menu1 field changed
      • changedOutput schema / properties / days / items / properties / restaurants / items / properties / reason / anyOf
        Previous value: -[
        -  {
        -    "enum": [
        -      "weekend",
        -      "public_holiday",
        -      "none_listed",
        -      "not_published_yet",
        -      "history_not_available",
        -      "source_unavailable"
        -    ],
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "enum": [
        +      "weekend",
        +      "public_holiday",
        +      "none_listed",
        +      "not_published_yet",
        +      "history_not_available",
        +      "source_unavailable",
        +      "source_error"
        +    ],
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
    • Addedmenu_stats
  3. 3 tool updatesv0.1.0
    • First observedfind_dishes
    • First observedget_menu
    • First observedlist_restaurants

TDQS

A4.4/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: listing restaurants, fetching menus, searching dishes, and computing statistics. The descriptions explicitly guide users between overlapping-sounding needs, such as find_dishes for search versus menu_stats for frequency and last-served questions.

Naming Consistency4/5

Three tools follow a clear verb_noun pattern (list_restaurants, get_menu, find_dishes), while menu_stats is noun-only. The deviation is minor and the name is still descriptive and predictable.

Tool Count5/5

Four tools are well-scoped for a campus lunch menu service, with no redundant or filler tools. Each tool covers a meaningful query type without bloating the surface.

Completeness4/5

The set covers restaurant listing, daily/range menus, dish/diet/allergen search, and historical statistics, which is strong lifecycle coverage for this domain. A dedicated diet/allergen metadata lookup is absent, but find_dishes largely compensates.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides real-time access to menus from all six MIT Bon Appétit dining halls. It enables users to query daily or weekly schedules and filter options by specific dietary requirements such as vegan, halal, or gluten-free.
    25 npm
    1
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables retrieval of daily lunch menus for restaurants in Sello shopping center (Espoo) from lounaat.info.
    -
  • A
    license
    A
    quality
    A
    maintenance
    Enables AI assistants to interact with the Paxaver school lunch platform, allowing users to check menus, order lunch, manage wallet funds, register for events, donate, and perform administrative tasks like managing restaurants and menu items.
    26
    0
    Apache 2.0
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables querying Yale Hospitality dining menus, listing residential colleges, fetching meal offerings, and searching dishes across colleges via MCP tools.
    MIT