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

1"""Dining MCP tool.""" 

2 

3from typing import Literal, Optional 

4 

5from data_api.mcp.server import mcp 

6from data_api.mcp.tools.result_types import DiningBuildingsResult 

7from data_api.runtime import get_runtime 

8 

9 

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. 

17 

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. 

25 

26 Returns: 

27 Dictionary with buildings and nested restaurants, at most 10 per building 

28 unless a non-empty building filter is supplied. 

29 

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 ) 

38 

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 ) 

57 

58 return {"buildings": buildings} 

59 

60 

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)