mirror of
https://github.com/langgenius/dify.git
synced 2026-05-30 05:37:48 +08:00
Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: EvanYao826 <155432245+EvanYao826@users.noreply.github.com> Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com> Co-authored-by: 盐粒 Yanli <yanli@dify.ai> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Tianle <40735546+Tianlel@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Yunlu Wen <yunlu.wen@dify.ai> Co-authored-by: zyssyz123 <916125788@qq.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: chariri <w@chariri.moe> Co-authored-by: Asuka Minato <i@asukaminato.eu.org> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Nian <11332799+Lillian68@users.noreply.github.com> Co-authored-by: 非法操作 <hjlarry@163.com> Co-authored-by: Carmen Fernández Ruiz <279459669+zeus1959@users.noreply.github.com> Co-authored-by: wangxiaolei <fatelei@gmail.com> Co-authored-by: QuantumGhost <obelisk.reg+git@gmail.com> Co-authored-by: L1nSn0w <l1nsn0w@qq.com> Co-authored-by: Evan <2869018789@qq.com> Co-authored-by: Escape0707 <tothesong@gmail.com> Co-authored-by: Jingyi <jingyi.qi@dify.ai> Co-authored-by: Amr Sherif <140330826+amr-sheriff@users.noreply.github.com> Co-authored-by: ZHOU ZHICHEN <118870511+zhuiguangzhe2003@users.noreply.github.com> Co-authored-by: unknown <EI05187@apwx.com> Co-authored-by: JzoNg <jzongcode@gmail.com> Co-authored-by: Xiyuan Chen <52963600+GareArc@users.noreply.github.com>
178 lines
7.0 KiB
Python
178 lines
7.0 KiB
Python
import json
|
|
from datetime import datetime
|
|
from typing import Any
|
|
from uuid import UUID
|
|
|
|
from flask_restx import Resource
|
|
from pydantic import BaseModel, Field, field_validator
|
|
from sqlalchemy import select
|
|
from werkzeug.exceptions import NotFound
|
|
|
|
from controllers.common.schema import register_schema_models
|
|
from controllers.console import console_ns
|
|
from controllers.console.app.wraps import get_app_model
|
|
from controllers.console.wraps import account_initialization_required, edit_permission_required, setup_required
|
|
from extensions.ext_database import db
|
|
from fields.base import ResponseModel
|
|
from libs.helper import to_timestamp
|
|
from libs.login import current_account_with_tenant, login_required
|
|
from models.enums import AppMCPServerStatus
|
|
from models.model import App, AppMCPServer
|
|
|
|
|
|
class MCPServerCreatePayload(BaseModel):
|
|
description: str | None = Field(default=None, description="Server description")
|
|
parameters: dict[str, Any] = Field(..., description="Server parameters configuration")
|
|
|
|
|
|
class MCPServerUpdatePayload(BaseModel):
|
|
id: str = Field(..., description="Server ID")
|
|
description: str | None = Field(default=None, description="Server description")
|
|
parameters: dict[str, Any] = Field(..., description="Server parameters configuration")
|
|
status: str | None = Field(default=None, description="Server status")
|
|
|
|
|
|
class AppMCPServerResponse(ResponseModel):
|
|
id: str
|
|
name: str
|
|
server_code: str
|
|
description: str
|
|
status: AppMCPServerStatus
|
|
parameters: dict[str, Any] | list[Any] | str
|
|
created_at: int | None = None
|
|
updated_at: int | None = None
|
|
|
|
@field_validator("parameters", mode="before")
|
|
@classmethod
|
|
def _normalize_parameters(cls, value: Any) -> Any:
|
|
if isinstance(value, str):
|
|
try:
|
|
return json.loads(value)
|
|
except (json.JSONDecodeError, TypeError):
|
|
return value
|
|
return value
|
|
|
|
@field_validator("created_at", "updated_at", mode="before")
|
|
@classmethod
|
|
def _normalize_timestamp(cls, value: datetime | int | None) -> int | None:
|
|
return to_timestamp(value)
|
|
|
|
|
|
register_schema_models(console_ns, MCPServerCreatePayload, MCPServerUpdatePayload, AppMCPServerResponse)
|
|
|
|
|
|
@console_ns.route("/apps/<uuid:app_id>/server")
|
|
class AppMCPServerController(Resource):
|
|
@console_ns.doc("get_app_mcp_server")
|
|
@console_ns.doc(description="Get MCP server configuration for an application")
|
|
@console_ns.doc(params={"app_id": "Application ID"})
|
|
@console_ns.response(
|
|
200, "MCP server configuration retrieved successfully", console_ns.models[AppMCPServerResponse.__name__]
|
|
)
|
|
@login_required
|
|
@account_initialization_required
|
|
@setup_required
|
|
@get_app_model
|
|
def get(self, app_model: App):
|
|
server = db.session.scalar(select(AppMCPServer).where(AppMCPServer.app_id == app_model.id).limit(1))
|
|
if server is None:
|
|
return {}
|
|
return AppMCPServerResponse.model_validate(server, from_attributes=True).model_dump(mode="json")
|
|
|
|
@console_ns.doc("create_app_mcp_server")
|
|
@console_ns.doc(description="Create MCP server configuration for an application")
|
|
@console_ns.doc(params={"app_id": "Application ID"})
|
|
@console_ns.expect(console_ns.models[MCPServerCreatePayload.__name__])
|
|
@console_ns.response(
|
|
201, "MCP server configuration created successfully", console_ns.models[AppMCPServerResponse.__name__]
|
|
)
|
|
@console_ns.response(403, "Insufficient permissions")
|
|
@account_initialization_required
|
|
@get_app_model
|
|
@login_required
|
|
@setup_required
|
|
@edit_permission_required
|
|
def post(self, app_model: App):
|
|
_, current_tenant_id = current_account_with_tenant()
|
|
payload = MCPServerCreatePayload.model_validate(console_ns.payload or {})
|
|
|
|
description = payload.description
|
|
if not description:
|
|
description = app_model.description or ""
|
|
|
|
server = AppMCPServer(
|
|
name=app_model.name,
|
|
description=description,
|
|
parameters=json.dumps(payload.parameters, ensure_ascii=False),
|
|
status=AppMCPServerStatus.ACTIVE,
|
|
app_id=app_model.id,
|
|
tenant_id=current_tenant_id,
|
|
server_code=AppMCPServer.generate_server_code(16),
|
|
)
|
|
db.session.add(server)
|
|
db.session.commit()
|
|
return AppMCPServerResponse.model_validate(server, from_attributes=True).model_dump(mode="json"), 201
|
|
|
|
@console_ns.doc("update_app_mcp_server")
|
|
@console_ns.doc(description="Update MCP server configuration for an application")
|
|
@console_ns.doc(params={"app_id": "Application ID"})
|
|
@console_ns.expect(console_ns.models[MCPServerUpdatePayload.__name__])
|
|
@console_ns.response(
|
|
200, "MCP server configuration updated successfully", console_ns.models[AppMCPServerResponse.__name__]
|
|
)
|
|
@console_ns.response(403, "Insufficient permissions")
|
|
@console_ns.response(404, "Server not found")
|
|
@get_app_model
|
|
@login_required
|
|
@setup_required
|
|
@account_initialization_required
|
|
@edit_permission_required
|
|
def put(self, app_model: App):
|
|
payload = MCPServerUpdatePayload.model_validate(console_ns.payload or {})
|
|
server = db.session.get(AppMCPServer, payload.id)
|
|
if not server:
|
|
raise NotFound()
|
|
|
|
description = payload.description
|
|
if description is None or not description:
|
|
server.description = app_model.description or ""
|
|
else:
|
|
server.description = description
|
|
|
|
server.name = app_model.name
|
|
|
|
server.parameters = json.dumps(payload.parameters, ensure_ascii=False)
|
|
if payload.status:
|
|
try:
|
|
server.status = AppMCPServerStatus(payload.status)
|
|
except ValueError:
|
|
raise ValueError("Invalid status")
|
|
db.session.commit()
|
|
return AppMCPServerResponse.model_validate(server, from_attributes=True).model_dump(mode="json")
|
|
|
|
|
|
@console_ns.route("/apps/<uuid:server_id>/server/refresh")
|
|
class AppMCPServerRefreshController(Resource):
|
|
@console_ns.doc("refresh_app_mcp_server")
|
|
@console_ns.doc(description="Refresh MCP server configuration and regenerate server code")
|
|
@console_ns.doc(params={"server_id": "Server ID"})
|
|
@console_ns.response(200, "MCP server refreshed successfully", console_ns.models[AppMCPServerResponse.__name__])
|
|
@console_ns.response(403, "Insufficient permissions")
|
|
@console_ns.response(404, "Server not found")
|
|
@setup_required
|
|
@login_required
|
|
@account_initialization_required
|
|
@edit_permission_required
|
|
def get(self, server_id: UUID):
|
|
_, current_tenant_id = current_account_with_tenant()
|
|
server = db.session.scalar(
|
|
select(AppMCPServer)
|
|
.where(AppMCPServer.id == server_id, AppMCPServer.tenant_id == current_tenant_id)
|
|
.limit(1)
|
|
)
|
|
if not server:
|
|
raise NotFound()
|
|
server.server_code = AppMCPServer.generate_server_code(16)
|
|
db.session.commit()
|
|
return AppMCPServerResponse.model_validate(server, from_attributes=True).model_dump(mode="json")
|