tools.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import json
  2. import re
  3. import uuid_utils.compat as uuid
  4. from django.db.models import QuerySet
  5. from application.models import ApplicationApiKey, Application, ChatUserType, ChatSourceChoices
  6. from chat.serializers.chat import ChatSerializers
  7. class MCPToolHandler:
  8. def __init__(self, auth_header):
  9. app_key = QuerySet(ApplicationApiKey).filter(secret_key=auth_header, is_active=True).first()
  10. if not app_key:
  11. raise PermissionError("Invalid API Key")
  12. self.application = QuerySet(Application).filter(id=app_key.application_id, is_publish=True).first()
  13. if not self.application:
  14. raise PermissionError("Application is not found or not published")
  15. def initialize(self):
  16. return {
  17. "protocolVersion": "2025-06-18",
  18. "serverInfo": {
  19. "name": "maxkb-mcp",
  20. "version": "1.0.0"
  21. },
  22. "capabilities": {
  23. "tools": {}
  24. }
  25. }
  26. def list_tools(self):
  27. return {
  28. "tools": [
  29. {
  30. "name": f'agent_{str(self.application.id)[:8]}',
  31. "description": f'{self.application.name} {self.application.desc}',
  32. "inputSchema": {
  33. "type": "object",
  34. "properties": {
  35. "message": {"type": "string", "description": "The message to send to the AI."},
  36. },
  37. "required": ["message"]
  38. }
  39. }
  40. ]
  41. }
  42. def _get_chat_id(self):
  43. from application.models import ChatUserType
  44. from chat.serializers.chat import OpenChatSerializers
  45. from common.init import init_template
  46. init_template.run()
  47. return OpenChatSerializers(data={
  48. 'application_id': self.application.id,
  49. 'chat_user_id': str(uuid.uuid7()),
  50. 'chat_user_type': ChatUserType.ANONYMOUS_USER,
  51. 'ip_address': '-',
  52. 'source': {"type": ChatSourceChoices.ONLINE.value},
  53. 'debug': False
  54. }).open()
  55. def call_tool(self, params):
  56. name = params["name"]
  57. args = params.get("arguments", {})
  58. # print(params)
  59. payload = {
  60. 'message': args.get('message'),
  61. 'stream': True,
  62. 're_chat': False
  63. }
  64. resp = ChatSerializers(data={
  65. 'chat_id': self._get_chat_id(),
  66. 'chat_user_id': str(uuid.uuid7()),
  67. 'chat_user_type': ChatUserType.ANONYMOUS_USER,
  68. 'application_id': self.application.id,
  69. 'ip_address': '-',
  70. 'source': {"type": ChatSourceChoices.ONLINE.value},
  71. 'debug': False,
  72. }).chat(payload)
  73. chunks = []
  74. for raw_line in resp:
  75. line = raw_line.decode("utf-8", errors="replace").rstrip("\r\n")
  76. if not line.startswith("data:"):
  77. continue
  78. payload = line[5:].strip()
  79. if not payload or payload == "[DONE]":
  80. continue
  81. try:
  82. event = json.loads(payload)
  83. except json.JSONDecodeError:
  84. continue
  85. if event.get("operate") is True:
  86. chunks.append(event.get("content", ""))
  87. if event.get("is_end"):
  88. break
  89. data = ''.join(chunks)
  90. # 排除<tool_calls_render></tool_calls_render>标签
  91. data = re.sub(r'<tool_calls_render>.*?</tool_calls_render>', '', data, flags=re.DOTALL)
  92. return {"content": [{"type": "text", "text": data}]}