Skip to content

Commit 10957d8

Browse files
committed
chore: refactor MCP configuration retrieval and add MCP view
1 parent c506a23 commit 10957d8

File tree

7 files changed

+152
-79
lines changed

7 files changed

+152
-79
lines changed

apps/application/chat_pipeline/step/chat_step/impl/base_chat_step.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,8 @@ def _handle_mcp_request(self, mcp_enable, tool_enable, mcp_source, mcp_servers,
276276
else:
277277
continue
278278
executor = ToolExecutor()
279-
app_config = executor.get_app_mcp_config(api_key, app.name, app.desc)
280-
mcp_servers_config[str(app.id)] = app_config
279+
app_config = executor.get_app_mcp_config(api_key)
280+
mcp_servers_config[app.name] = app_config
281281

282282
if len(mcp_servers_config) > 0:
283283
return mcp_response_generator(chat_model, message_list, json.dumps(mcp_servers_config), mcp_output_enable)

apps/application/flow/step_node/ai_chat_step_node/impl/base_chat_node.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ def _handle_mcp_request(self, mcp_enable, tool_enable, mcp_source, mcp_servers,
263263
else:
264264
continue
265265
executor = ToolExecutor()
266-
app_config = executor.get_app_mcp_config(api_key, app.name, app.desc)
266+
app_config = executor.get_app_mcp_config(api_key)
267267
mcp_servers_config[str(app.id)] = app_config
268268

269269
if len(mcp_servers_config) > 0:

apps/chat/mcp/__init__.py

Whitespace-only changes.

apps/chat/mcp/tools.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import json
2+
3+
import uuid_utils.compat as uuid
4+
from django.db.models import QuerySet
5+
6+
from application.models import ApplicationApiKey, Application, ChatUserType
7+
from chat.serializers.chat import ChatSerializers
8+
9+
10+
class MCPToolHandler:
11+
def __init__(self, auth_header):
12+
app_key = QuerySet(ApplicationApiKey).filter(secret_key=auth_header, is_active=True).first()
13+
if not app_key:
14+
raise PermissionError("Invalid API Key")
15+
16+
self.application = QuerySet(Application).filter(id=app_key.application_id, is_publish=True).first()
17+
if not self.application:
18+
raise PermissionError("Application is not found or not published")
19+
20+
def initialize(self):
21+
return {
22+
"protocolVersion": "2025-06-18",
23+
"serverInfo": {
24+
"name": "maxkb-mcp",
25+
"version": "1.0.0"
26+
},
27+
"capabilities": {
28+
"tools": {}
29+
}
30+
}
31+
32+
def list_tools(self):
33+
return {
34+
"tools": [
35+
{
36+
"name": 'ai_chat',
37+
"description": f'{self.application.name} {self.application.desc}',
38+
"inputSchema": {
39+
"type": "object",
40+
"properties": {
41+
"message": {"type": "string", "description": "The message to send to the AI."},
42+
},
43+
"required": ["message"]
44+
}
45+
}
46+
]
47+
}
48+
49+
def _get_chat_id(self):
50+
from application.models import ChatUserType
51+
from chat.serializers.chat import OpenChatSerializers
52+
from common.init import init_template
53+
54+
init_template.run()
55+
56+
return OpenChatSerializers(data={
57+
'application_id': self.application.id,
58+
'chat_user_id': str(uuid.uuid7()),
59+
'chat_user_type': ChatUserType.ANONYMOUS_USER,
60+
'debug': False
61+
}).open()
62+
63+
def call_tool(self, params):
64+
name = params["name"]
65+
args = params.get("arguments", {})
66+
# print(params)
67+
68+
payload = {
69+
'message': args.get('message'),
70+
'stream': False,
71+
're_chat': False
72+
}
73+
resp = ChatSerializers(data={
74+
'chat_id': self._get_chat_id(),
75+
'chat_user_id': str(uuid.uuid7()),
76+
'chat_user_type': ChatUserType.ANONYMOUS_USER,
77+
'application_id': self.application.id,
78+
'debug': False,
79+
}).chat(payload)
80+
data = json.loads(str(resp.text))
81+
82+
return {"content": [{"type": "text", "text": data.get('data', {}).get('content')}]}

apps/chat/urls.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
from django.urls import path
22

3+
from chat.views.mcp import mcp_view
34
from . import views
45

56
app_name = 'chat'
67
# @formatter:off
78
urlpatterns = [
89
path('embed', views.ChatEmbedView.as_view()),
10+
path('mcp', mcp_view),
911
path('auth/anonymous', views.AnonymousAuthentication.as_view()),
1012
path('profile', views.AuthProfile.as_view()),
1113
path('resource_proxy',views.ResourceProxy.as_view()),
@@ -15,8 +17,7 @@
1517
path('text_to_speech', views.TextToSpeech.as_view()),
1618
path('speech_to_text', views.SpeechToText.as_view()),
1719
path('captcha', views.CaptchaView.as_view(), name='captcha'),
18-
path('<str:application_id>/chat/completions', views.OpenAIView.as_view(),
19-
name='application/chat_completions'),
20+
path('<str:application_id>/chat/completions', views.OpenAIView.as_view(), name='application/chat_completions'),
2021
path('vote/chat/<str:chat_id>/chat_record/<str:chat_record_id>', views.VoteView.as_view(), name='vote'),
2122
path('historical_conversation', views.HistoricalConversationView.as_view(), name='historical_conversation'),
2223
path('historical_conversation/<str:chat_id>/record/<str:chat_record_id>',views.ChatRecordView.as_view(),name='conversation_details'),

apps/chat/views/mcp.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import json
2+
3+
from django.http import JsonResponse, HttpResponse
4+
from django.views.decorators.csrf import csrf_exempt
5+
6+
from chat.mcp.tools import MCPToolHandler
7+
8+
9+
@csrf_exempt
10+
def mcp_view(request):
11+
request_id = None
12+
try:
13+
data = json.loads(request.body)
14+
method = data.get("method")
15+
params = data.get("params", {})
16+
request_id = data.get("id")
17+
18+
if request_id is None:
19+
return HttpResponse(status=204)
20+
21+
auth_header = request.headers.get("Authorization", "").replace("Bearer ", "")
22+
handler = MCPToolHandler(auth_header)
23+
24+
# 路由方法
25+
if method == "initialize":
26+
result = handler.initialize()
27+
28+
elif method == "tools/list":
29+
result = handler.list_tools()
30+
31+
elif method == "tools/call":
32+
result = handler.call_tool(params)
33+
34+
else:
35+
return JsonResponse({
36+
"jsonrpc": "2.0",
37+
"id": request_id,
38+
"error": {
39+
"code": -32601,
40+
"message": f"Method not found: {method}"
41+
}
42+
})
43+
44+
# 成功响应
45+
return JsonResponse({
46+
"jsonrpc": "2.0",
47+
"id": request_id,
48+
"result": result
49+
})
50+
51+
except Exception as e:
52+
return JsonResponse({
53+
"jsonrpc": "2.0",
54+
"id": request_id,
55+
"error": {
56+
"code": -32603,
57+
"message": f"Internal error: {str(e)}"
58+
}
59+
})

apps/common/utils/tool_code.py

Lines changed: 5 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -235,82 +235,13 @@ def get_tool_mcp_config(self, code, params, name, description):
235235
}
236236
return tool_config
237237

238-
def get_app_mcp_config(self, api_key, name, description):
239-
chat_path = CONFIG.get_chat_path()
240-
# 生成内部令牌(基于时间戳+密钥+api_key)
241-
timestamp = int(time.time())
242-
secret = CONFIG.get('MCP_INTERNAL_SECRET', 'your-secret-key')
243-
token_data = f"{api_key}:{timestamp}"
244-
internal_token = hmac.new(
245-
secret.encode(),
246-
token_data.encode(),
247-
hashlib.sha256
248-
).hexdigest()
249-
_code = f'''
250-
from typing import Optional
251-
252-
def _get_chat_id() -> Optional[str]:
253-
import requests
254-
255-
url = f"http://127.0.0.1:8080{chat_path}/api/open"
256-
headers = {{
257-
'accept': '*/*',
258-
'Authorization': f'Bearer {api_key}',
259-
'X-MCP-Token': '{internal_token}', # 添加内部令牌
260-
'X-MCP-Timestamp': '{timestamp}'
261-
}}
262-
try:
263-
resp = requests.get(url, headers=headers, timeout=10)
264-
resp.raise_for_status()
265-
return resp.json().get("data")
266-
except Exception as e:
267-
raise e
268-
269-
270-
def _chat_with_ai(chat_id: str, message: str) -> Optional[str]:
271-
import requests
272-
273-
url = f"http://127.0.0.1:8080{chat_path}/api/chat_message/{{chat_id}}"
274-
headers = {{
275-
'Content-Type': 'application/json',
276-
'Authorization': f'Bearer {api_key}',
277-
'X-MCP-Token': '{internal_token}', # 添加内部令牌
278-
'X-MCP-Timestamp': '{timestamp}'
279-
}}
280-
payload = {{
281-
"message": message,
282-
"re_chat": False,
283-
"stream": False
284-
}}
285-
try:
286-
resp = requests.post(url, json=payload, headers=headers, timeout=600)
287-
resp.raise_for_status()
288-
data = resp.json()
289-
return str(data.get("data", {{}}).get("content") or data.get("response"))
290-
except Exception as e:
291-
raise e
292-
293-
def ai_chat(message: str) -> str:
294-
chat_id = _get_chat_id()
295-
reply = _chat_with_ai(chat_id, message)
296-
return reply or "AI 未能生成回复"
297-
298-
'''
299-
_code = self.generate_mcp_server_code(_code, {}, name, description)
300-
# print(_code)
301-
maxkb_logger.debug(f"Python code of mcp app: {_code}")
302-
compressed_and_base64_encoded_code_str = base64.b64encode(gzip.compress(_code.encode())).decode()
238+
def get_app_mcp_config(self, api_key):
303239
app_config = {
304-
'command': sys.executable,
305-
'args': [
306-
'-c',
307-
f'import base64,gzip; exec(gzip.decompress(base64.b64decode(\'{compressed_and_base64_encoded_code_str}\')).decode())',
308-
],
309-
'cwd': _sandbox_path,
310-
'env': {
311-
'LD_PRELOAD': f'{_sandbox_path}/lib/sandbox.so',
240+
'url': f'http://127.0.0.1:8080{CONFIG.get_chat_path()}/api/mcp',
241+
'transport': 'streamable_http',
242+
'headers': {
243+
'Authorization': f'Bearer {api_key}',
312244
},
313-
'transport': 'stdio',
314245
}
315246
return app_config
316247

0 commit comments

Comments
 (0)