server.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. from __future__ import annotations
  2. import os
  3. from typing import Any
  4. from fastmcp import FastMCP
  5. from .auth import load_projects_config
  6. from .config_api import (
  7. list_device_types as api_list_device_types,
  8. list_locations as api_list_locations,
  9. list_meter_types as api_list_meter_types,
  10. list_system_tree as api_list_system_tree,
  11. list_systems as api_list_systems,
  12. search_devices as api_search_devices,
  13. search_meters as api_search_meters,
  14. )
  15. mcp = FastMCP("instrument-config")
  16. @mcp.tool(
  17. name="project.list",
  18. title="Project List",
  19. description="List enabled projects configured in sys_config for instrument-config tools.",
  20. tags={"project", "list"},
  21. )
  22. def project_list() -> dict[str, Any]:
  23. projects = load_projects_config()
  24. result: list[dict[str, Any]] = []
  25. for item in projects:
  26. if not item["enabled"]:
  27. continue
  28. result.append(
  29. {
  30. "project_key": item["project_key"],
  31. "project_name": item["project_name"],
  32. }
  33. )
  34. result.sort(key=lambda item: item["project_key"])
  35. return {
  36. "projects": result,
  37. "total": len(result),
  38. }
  39. def _append_next_page_hint(payload: Any, page_num: int) -> Any:
  40. if not isinstance(payload, dict):
  41. return payload
  42. data = payload.get("data")
  43. if not isinstance(data, dict):
  44. return payload
  45. total_page = data.get("total_page")
  46. if isinstance(total_page, int) and total_page > page_num:
  47. payload.setdefault(
  48. "mcp_note",
  49. f"Current result is page {page_num}. If the target was not found, continue with page_num={page_num + 1}.",
  50. )
  51. return payload
  52. @mcp.tool()
  53. def list_locations(project_key: str, keyword: str = "", page_size: int = 100, page_num: int = 1) -> Any:
  54. """List location data from the config API."""
  55. payload = api_list_locations(project_key, keyword=keyword, page_size=page_size, page_num=page_num)
  56. return _append_next_page_hint(payload, page_num)
  57. @mcp.tool()
  58. def list_system_tree(project_key: str) -> Any:
  59. """Get the full system tree from the config API."""
  60. return api_list_system_tree(project_key)
  61. @mcp.tool()
  62. def list_systems(
  63. project_key: str,
  64. page_size: int = 100,
  65. page_num: int = 1,
  66. system_type_id: int = 0,
  67. show_below: bool = True,
  68. ) -> Any:
  69. """List systems by system type."""
  70. payload = api_list_systems(
  71. project_key,
  72. page_size=page_size,
  73. page_num=page_num,
  74. system_type_id=system_type_id,
  75. show_below=show_below,
  76. )
  77. return _append_next_page_hint(payload, page_num)
  78. @mcp.tool()
  79. def list_device_types(project_key: str) -> Any:
  80. """List all device types."""
  81. return api_list_device_types(project_key)
  82. @mcp.tool()
  83. def list_meter_types(project_key: str) -> Any:
  84. """List all meter types."""
  85. return api_list_meter_types(project_key)
  86. @mcp.tool()
  87. def search_devices(
  88. project_key: str,
  89. page_size: int = 100,
  90. page_num: int = 1,
  91. keyword: str = "",
  92. location_id: int = 0,
  93. show_below: bool = True,
  94. system_ids: list[int] | None = None,
  95. device_type_ids: list[int] | None = None,
  96. ) -> Any:
  97. """Search devices with id-based filters."""
  98. payload = api_search_devices(
  99. project_key,
  100. page_size=page_size,
  101. page_num=page_num,
  102. keyword=keyword,
  103. location_id=location_id,
  104. show_below=show_below,
  105. system_ids=system_ids,
  106. device_type_ids=device_type_ids,
  107. )
  108. return _append_next_page_hint(payload, page_num)
  109. @mcp.tool()
  110. def search_meters(
  111. project_key: str,
  112. page_size: int = 100,
  113. page_num: int = 1,
  114. keyword: str = "",
  115. location_id: int = 0,
  116. show_below: bool = True,
  117. meter_type_id: int = 0,
  118. measurement_location_ids: list[int] | None = None,
  119. measurement_system_ids: list[int] | None = None,
  120. measurement_device_type_ids: list[int] | None = None,
  121. status: int | None = None,
  122. ) -> Any:
  123. """Search meters with id-based filters."""
  124. payload = api_search_meters(
  125. project_key,
  126. page_size=page_size,
  127. page_num=page_num,
  128. keyword=keyword,
  129. location_id=location_id,
  130. show_below=show_below,
  131. meter_type_id=meter_type_id,
  132. measurement_location_ids=measurement_location_ids,
  133. measurement_system_ids=measurement_system_ids,
  134. measurement_device_type_ids=measurement_device_type_ids,
  135. status=status,
  136. )
  137. return _append_next_page_hint(payload, page_num)
  138. def main() -> None:
  139. host = os.getenv("MCP_HOST", "127.0.0.1").strip() or "127.0.0.1"
  140. port = int(os.getenv("MCP_PORT", "8500"))
  141. path = os.getenv("MCP_PATH", "/mcp").strip() or "/mcp"
  142. mcp.run(transport="http", host=host, port=port, path=path)
  143. if __name__ == "__main__":
  144. main()