Skip to main content
Glama
illustraton916

restaurant-agent

restaurant-agent

A permission-aware MCP server for a restaurant backend, with the process artifacts that built it: the specs it was built from and the eval harness that measures how agents behave against it.

The interesting part is not that an agent can read a menu — it is what happens when an agent asks for something it is not allowed to see, and how you find out whether your agent + tool design actually holds up.

What this demonstrates

  • Permission-aware tool design — every tool declares a minimum role (viewer < staff < manager); an unauthorized call returns a structured refusal the agent can reason about, never an exception, never silent failure, and never data.

  • Agent proposes, human approves — the single write-path tool (draft_daily_special) cannot touch production; it creates a draft that a human approves out of band.

  • Spec-driven processspecs/ holds the contract this server was built from (001), the reusable template (000), and a sanitized real-world case study (002). When agent output is wrong, the first suspect is spec quality — the fix goes into the spec, not just the code.

  • Evals, not vibesevals/ runs a real model against the server and mechanically checks tool choices, refusal handling and prompt-injection resistance. See evals/README.md for why these are not unit tests.

Related MCP server: Ordio MCP

Architecture

flowchart LR
    agent["Agent<br/>(tool-use loop,<br/>Anthropic API)"]
    subgraph mcp["MCP server (stdio)"]
        registry["Tool registry"]
        gate["Permission gate<br/>role rank vs minRole"]
        validate["Zod validation"]
        tools["7 tools"]
    end
    subgraph backend["Data source"]
        mock["Mock API<br/>(fictional fixtures, default)"]
        real["Real API<br/>(Bearer token, opt-in)"]
    end

    agent -- "tools/call" --> registry
    registry --> gate
    gate -- "insufficient role" --> refusal["Structured refusal<br/>{allowed:false, reason,<br/>required_role, current_role}"]
    refusal --> agent
    gate -- ok --> validate --> tools
    tools --> mock
    tools -.-> real

Audit trail: every call is logged as JSONL to stderr (timestamp, tool, role, decision, args hash — raw arguments are never logged).

Quickstart

npm install
npm test              # 57 unit tests: full role x tool gate matrix
npm run agent         # demo agent against the mock server (needs ANTHROPIC_API_KEY)

The server runs in mock mode by default — a fictional restaurant ("Ravintola Kotilounas") with invented menu, orders and sales. No backend, no credentials needed. Try different roles:

AGENT_TOKEN=demo-staff-token   npm run agent -- "What orders are waiting right now?"
AGENT_TOKEN=demo-manager-token npm run agent -- "Revenue for the first week of June?"
AGENT_TOKEN=demo-viewer-token  npm run agent -- "Revenue for the first week of June?"   # watch the refusal

Permission design

Roles are hierarchical: viewer (0) < staff (1) < manager (2). The registry dispatches every call through the same path: gate → validate → handler.

  • The gate runs before argument validation, so an under-privileged caller gets permission_denied — not schema feedback it could use to probe tools.

  • Refusals are template-built from registry data only. Tool arguments are data, not instructions: a unit test injects instruction-like text through arguments and asserts the refusal text is unchanged.

  • Unknown or missing tokens resolve to viewer — least privilege by default.

  • Argument schemas are .strict() — unexpected keys are rejected.

Example refusal (what the agent actually receives):

{
  "allowed": false,
  "error": "permission_denied",
  "reason": "get_sales_summary requires role 'manager'; this session has role 'viewer'.",
  "required_role": "manager",
  "current_role": "viewer"
}

Tools

Tool

Min role

Purpose

get_menu

viewer

full menu grouped by category, with allergen codes

search_menu_items

viewer

filter by text, category, excluded allergens

get_opening_hours

viewer

weekly hours + holiday exceptions

get_todays_lunch

viewer

today's lunch menu and serving window

get_active_orders

staff

live order queue with statuses

get_sales_summary

manager

per-day revenue and top items (mock-only)

draft_daily_special

staff

write-demo: draft → human approval, never prod

The surface mirrors a real production restaurant API (the same one behind vanhamylly-api); real mode forwards a Bearer token and is opt-in via env — no real endpoints ship in this repo.

Evals

ANTHROPIC_API_KEY=... npm run evals          # 14 tasks, prints pass rate
npm run evals -- --task prompt-injection-menu-note

14 tasks: happy paths for every tool at sufficient role, refusal cases (viewer asking for sales, staff asking for manager data), and adversarial cases — prompt injection embedded in user content, requests for nonexistent capabilities, and an attempt to publish straight to production (expected outcome: a draft plus an answer that mentions human approval).

Latest local run: 14/14 (100%) on claude-sonnet-5. The first run scored 12/14 — and both failures were eval-quality defects, not agent defects:

  • the allergen task expected the literal argument "gluten", while the agent did something smarter — looked up the menu's allergen legend first and used the documented code "G";

  • the publish-bypass task demanded an immediate draft_daily_special call, while the agent refused the "skip any approval" instruction and asked before drafting — safer than the expectation.

Both tasks were recalibrated to test the actual property (documented codes; the no-bypass safety guarantee) and the fix went into the tasks, not the code. That loop — failing eval → inspect → fix the spec — is the point of this repo. Rates vary between runs and models; treat single runs as measurements, not verdicts.

CI runs lint + typecheck + unit tests on every push; evals are a manual workflow (they need an API key and cost tokens).

License

MIT © 2026 Oleksii Halushka

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • F
    license
    -
    quality
    C
    maintenance
    An MCP server that implements a multi-stage employee access request and approval workflow, enabling employees to request system access and managers/IT admins to approve or reject requests through an interactive UI.
  • F
    license
    -
    quality
    B
    maintenance
    MCP server for the Ordio restaurant operations platform, enabling AI assistants to manage inventory, orders, recipes, team, shifts, and reports.
    1
  • A
    license
    -
    quality
    B
    maintenance
    MCP server for AI-native restaurant discovery with three-tier search (verified, menu_indexed, discovered), Menu Protocol menus, and structured menu validation.
    2
    MIT
  • F
    license
    -
    quality
    B
    maintenance
    An MCP server that enables non-technical operations users to resolve common commerce-ops tickets, such as orders charged but failed, through an investigate, recommend, approve, and execute workflow with read-only and write tools.

View all related MCP servers

Related MCP Connectors

  • MCP server for Meitre restaurant reservations.

  • A paid remote MCP for CLI tool MCP, built to return verdicts, receipts, usage logs, and audit-ready

  • A paid remote MCP for hosted MCP server, built to return verdicts, receipts, usage logs, and audit-r

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/illustraton916/restaurant-agent'

If you have feedback or need assistance with the MCP directory API, please join our Discord server