public_gateway.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import logging
  2. import uuid
  3. from constants import DEVICE_INVALID_MESSAGE
  4. from tools.list_order_filter_options import ListOrderFilterOptionsTool
  5. from tools.query_order import QueryOrderTool
  6. from tools.query_order_exact import QueryOrderExactTool
  7. from tools.query_track import QueryTrackTool
  8. from utils.security import hash_gateway_session_id
  9. logger = logging.getLogger(__name__)
  10. class PublicGatewayApp:
  11. def __init__(self, session_store, api_client, auth_client=None):
  12. self.session_store = session_store
  13. self.api_client = api_client
  14. self._tools = {
  15. 'query_order': QueryOrderTool(api_client=None),
  16. 'query_track': QueryTrackTool(api_client=None),
  17. 'query_order_exact': QueryOrderExactTool(api_client=None),
  18. 'list_order_filter_options': ListOrderFilterOptionsTool(
  19. api_client=None
  20. ),
  21. }
  22. def registered_tool_names(self):
  23. return tuple(self._tools.keys())
  24. def _require_session(self, gateway_session_id):
  25. session = self.session_store.get(gateway_session_id)
  26. if not session or not session.get('mcp_token'):
  27. raise RuntimeError(DEVICE_INVALID_MESSAGE)
  28. return session
  29. def _enabled_tool_names(self, response):
  30. if not isinstance(response, dict):
  31. raise RuntimeError('invalid enabled tool response')
  32. if response.get('code') != 'MCP_0000':
  33. raise RuntimeError(response.get('msg') or 'list enabled tools failed')
  34. data = response.get('data')
  35. codes = data.get('tool_codes') if isinstance(data, dict) else None
  36. if not isinstance(codes, list):
  37. raise RuntimeError('invalid enabled tool response')
  38. return {
  39. code.strip().lower()
  40. for code in codes
  41. if isinstance(code, str) and code.strip()
  42. }
  43. def _load_enabled_tool_names(self, token):
  44. response = self.api_client.list_enabled_tools(token)
  45. return self._enabled_tool_names(response)
  46. def list_tools(self, gateway_session_id):
  47. session = self._require_session(gateway_session_id)
  48. enabled = self._load_enabled_tool_names(session['mcp_token'])
  49. return [
  50. tool.metadata()
  51. for name, tool in self._tools.items()
  52. if name in enabled
  53. ]
  54. def build_request_id(self, request_id=''):
  55. request_id = str(request_id or '').strip()
  56. return request_id or 'rq_{0}'.format(uuid.uuid4().hex[:16])
  57. def call_tool(self, gateway_session_id, name, arguments=None, request_id='', client_ip=''):
  58. if name not in self._tools:
  59. raise KeyError('tool not registered: {0}'.format(name))
  60. session = self._require_session(gateway_session_id)
  61. if name not in self._load_enabled_tool_names(session['mcp_token']):
  62. raise RuntimeError('tool disabled: {0}'.format(name))
  63. tool = self._tools[name]
  64. request_id = self.build_request_id(request_id)
  65. session_hash = hash_gateway_session_id(gateway_session_id)[:12]
  66. admin_id = session.get('admin_id')
  67. company_id = session.get('company_id')
  68. logger.info(f"[AUDIT] tool_call: session_hash={session_hash}, admin_id={admin_id}, company_id={company_id}, tool={name}, request_id={request_id}")
  69. try:
  70. result = self.api_client.call_tool(
  71. token=session['mcp_token'],
  72. tool_code=tool.name,
  73. route_path=tool.route_path,
  74. payload=arguments or {},
  75. request_id=request_id,
  76. client_ip=client_ip,
  77. )
  78. if hasattr(self.session_store, 'touch_session'):
  79. self.session_store.touch_session(gateway_session_id)
  80. logger.info(f"[AUDIT] tool_success: session_hash={session_hash}, tool={name}, request_id={request_id}, code={result.get('code')}")
  81. return result
  82. except Exception as e:
  83. logger.error(f"[AUDIT] tool_error: session_hash={session_hash}, tool={name}, request_id={request_id}, error={str(e)}")
  84. raise