Coverage for src/data_api/mcp/tools/dining.py: 100%
16 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-11-13 19:28 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-11-13 19:28 +0000
1"""Dining MCP tool."""
3from typing import Literal, Optional
5from data_api.mcp.server import mcp
6from data_api.mcp.tools.result_types import DiningBuildingsResult
7from data_api.runtime import get_runtime
10async def _find_dining(
11 restaurant_name: Optional[str] = None,
12 building: Optional[str] = None,
13 check_open: Optional[Literal["today", "weekday", "saturday", "sunday"]] = None,
14) -> DiningBuildingsResult:
15 """
16 Find dining options on campus.
18 Args:
19 restaurant_name: Search for restaurant by name.
20 building: Filter by building name (e.g., '小吃部', '水木').
21 A non-empty filter returns all matching restaurants per matched building.
22 check_open: Filter potentially open restaurants by schedule notes: 'today',
23 'weekday', 'saturday', or 'sunday'. 'today' uses the Asia/Taipei date,
24 not the current opening hours.
26 Returns:
27 Dictionary with buildings and nested restaurants, at most 10 per building
28 unless a non-empty building filter is supplied.
30 Building, restaurant name, and open-status filters apply together.
31 """
32 service = get_runtime().dining
33 _, data = await service.fuzzy_search_dining_data(
34 building_name=building,
35 restaurant_name=restaurant_name,
36 schedule=check_open,
37 )
39 buildings = []
40 for b in data:
41 restaurants = b.get("restaurants", [])
42 if restaurants:
43 buildings.append(
44 {
45 "building": b.get("building"),
46 "restaurants": [
47 {
48 "name": r.get("name"),
49 "phone": r.get("phone"),
50 "schedule": r.get("schedule"),
51 "note": r.get("note") if r.get("note") else None,
52 }
53 for r in (restaurants if building else restaurants[:10])
54 ],
55 }
56 )
58 return {"buildings": buildings}
61@mcp.tool(
62 name="find_dining",
63 title="搜尋餐廳列表",
64 description="Find restaurants and dining options on campus. "
65 "Use this to find where to eat, check which restaurants are open, or search for specific food. "
66 "Open-status uses schedule notes, not live opening hours; today uses the Asia/Taipei date. "
67 "Building, restaurant name, and open-status filters apply together. "
68 "Always returns buildings with nested restaurants, at most 10 per building. "
69 "Supply a non-empty building filter to list all matches in each matched building.",
70)
71async def find_dining(
72 restaurant_name: Optional[str] = None,
73 building: Optional[str] = None,
74 check_open: Optional[Literal["today", "weekday", "saturday", "sunday"]] = None,
75) -> dict:
76 """Find dining options on campus."""
77 return await _find_dining(restaurant_name, building, check_open)