fix: sync MarketApp MCP records to admin MCP Client management
This commit is contained in:
1 parent
c9b5f964be
commit
feb9865b00
3 files changed
+114
-4
No files matched your search
@@ -11,6 +11,7 @@ from datetime import datetime
|
||||
import uuid
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
|
||||
from ...database import get_db
|
||||
from ...models import gen_uuid
|
||||
@@ -99,6 +100,63 @@ def server_to_dict(server) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def _extract_url_from_text(text: str) -> Optional[str]:
|
||||
"""Extract first http/https URL from text."""
|
||||
if not text:
|
||||
return None
|
||||
match = re.search(r"https?://[^\s\"'<>\)\]\}]+", text)
|
||||
return match.group(0) if match else None
|
||||
|
||||
|
||||
def _sync_market_mcp_to_servers(db: Session):
|
||||
"""
|
||||
Synchronize MarketApp MCP records (type='mcp') into mcp_servers table.
|
||||
Uses MarketApp.id as MCPServer.id so detail/edit endpoints can work directly.
|
||||
"""
|
||||
from ...models import MarketApp, MCPServer
|
||||
|
||||
market_mcps = db.query(MarketApp).filter(
|
||||
MarketApp.type == "mcp",
|
||||
MarketApp.status == "published",
|
||||
).all()
|
||||
|
||||
existing_ids = {row[0] for row in db.query(MCPServer.id).all()}
|
||||
|
||||
for ma in market_mcps:
|
||||
if ma.id in existing_ids:
|
||||
continue
|
||||
|
||||
transport = ma.mcp_transport or "streamable_http"
|
||||
code = ma.skill_content or ""
|
||||
url = _extract_url_from_text(code) or _extract_url_from_text(ma.description or "") or ""
|
||||
command = ""
|
||||
if not url and ma.agent_endpoint:
|
||||
url = ma.agent_endpoint
|
||||
|
||||
now = datetime.now()
|
||||
server = MCPServer(
|
||||
id=ma.id,
|
||||
name=ma.name,
|
||||
description=ma.description or "",
|
||||
transport_type=transport,
|
||||
command=command,
|
||||
args="{}",
|
||||
env_vars="{}",
|
||||
url=url,
|
||||
headers="{}",
|
||||
is_enabled=True,
|
||||
status="disconnected",
|
||||
tools_count=0,
|
||||
resources_count=0,
|
||||
created_at=ma.created_at or now,
|
||||
updated_at=ma.updated_at or now,
|
||||
)
|
||||
db.add(server)
|
||||
|
||||
if market_mcps:
|
||||
db.commit()
|
||||
|
||||
|
||||
# ============================================================
|
||||
# API Endpoints
|
||||
# ============================================================
|
||||
@@ -110,6 +168,7 @@ async def get_mcp_stats(
|
||||
):
|
||||
"""Get overview statistics for MCP servers"""
|
||||
from ...models import MCPServer
|
||||
_sync_market_mcp_to_servers(db)
|
||||
total = db.query(func.count(MCPServer.id)).scalar() or 0
|
||||
enabled = db.query(func.count(MCPServer.id)).filter(MCPServer.is_enabled == True).scalar() or 0
|
||||
connected = db.query(func.count(MCPServer.id)).filter(MCPServer.status == "connected").scalar() or 0
|
||||
@@ -152,6 +211,7 @@ async def list_mcp_servers(
|
||||
):
|
||||
"""List all MCP servers with pagination and filters"""
|
||||
from ...models import MCPServer
|
||||
_sync_market_mcp_to_servers(db)
|
||||
query = db.query(MCPServer)
|
||||
|
||||
if search:
|
||||
@@ -188,6 +248,7 @@ async def get_mcp_server(
|
||||
):
|
||||
"""Get a single MCP server by ID"""
|
||||
from ...models import MCPServer
|
||||
_sync_market_mcp_to_servers(db)
|
||||
server = db.query(MCPServer).filter(MCPServer.id == server_id).first()
|
||||
if not server:
|
||||
raise HTTPException(status_code=404, detail="MCP server not found")
|
||||
@@ -447,3 +508,4 @@ async def list_mcp_resources(
|
||||
},
|
||||
"message": "ok",
|
||||
}
|
||||
|
||||
@@ -7,11 +7,12 @@ import uuid
|
||||
from datetime import datetime
|
||||
import httpx
|
||||
from urllib.parse import urlparse
|
||||
import re
|
||||
|
||||
from ..database import get_db
|
||||
from ..models import (
|
||||
User, MarketApp, AppPurchase, AppReview, AppInstallation, Order,
|
||||
AdBanner, gen_uuid
|
||||
AdBanner, MCPServer, gen_uuid
|
||||
)
|
||||
from ..auth import get_current_user, get_current_user_id, get_current_user_id_optional
|
||||
from ..middleware.log_service import LogService
|
||||
@@ -19,6 +20,14 @@ from ..middleware.log_service import LogService
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def _extract_url_from_text(text: Optional[str]) -> Optional[str]:
|
||||
"""Extract first http/https URL from text."""
|
||||
if not text:
|
||||
return None
|
||||
match = re.search(r"https?://[^\s\"'<>\)\]\}]+", text)
|
||||
return match.group(0) if match else None
|
||||
|
||||
|
||||
# ===== Agent 接入前连通性 / 鉴权校验 =====
|
||||
def _normalize_dify_url(endpoint: str) -> str:
|
||||
ep = endpoint.rstrip("/")
|
||||
@@ -375,6 +384,39 @@ def upload_app(
|
||||
db.commit()
|
||||
db.refresh(app)
|
||||
|
||||
# MCP 应用同步注册到后台 MCP Client 管理(mcp_servers 表)
|
||||
if app.type == "mcp":
|
||||
transport = "streamable_http"
|
||||
code = req.download_url or ""
|
||||
if code:
|
||||
app.skill_content = code
|
||||
app.mcp_transport = transport
|
||||
db.commit()
|
||||
db.refresh(app)
|
||||
|
||||
existing_server = db.query(MCPServer).filter(MCPServer.id == app.id).first()
|
||||
if not existing_server:
|
||||
url = _extract_url_from_text(code) or _extract_url_from_text(req.description or "") or ""
|
||||
server = MCPServer(
|
||||
id=app.id,
|
||||
name=app.name,
|
||||
description=app.description or "",
|
||||
transport_type=transport,
|
||||
command="",
|
||||
args="{}",
|
||||
env_vars="{}",
|
||||
url=url,
|
||||
headers="{}",
|
||||
is_enabled=True,
|
||||
status="disconnected",
|
||||
tools_count=0,
|
||||
resources_count=0,
|
||||
created_at=app.created_at or datetime.now(),
|
||||
updated_at=app.updated_at or datetime.now(),
|
||||
)
|
||||
db.add(server)
|
||||
db.commit()
|
||||
|
||||
# 用户创建/导入的应用自动标记为已安装
|
||||
existing_install = db.query(AppInstallation).filter(
|
||||
AppInstallation.user_id == current_user.id,
|
||||
@@ -769,3 +811,4 @@ def get_agent_resources(
|
||||
},
|
||||
"message": "ok",
|
||||
}
|
||||
|
||||
@@ -1491,8 +1491,9 @@ const MarketPage: React.FC = () => {
|
||||
// Handle hub import - 调用后端 API 创建应用
|
||||
const handleHubImport = async (platformName: string, url: string, skillName: string, price?: number, description?: string) => {
|
||||
try {
|
||||
const desc = description || `从 ${platformName} 导入的${showHubImport === 'skill' ? 'Skills工具' : 'MCP应用'} (来源: ${url})`;
|
||||
await marketAPI.createApp({
|
||||
const isMcp = showHubImport === 'mcp';
|
||||
const desc = description || `从 ${platformName} 导入的${isMcp ? 'MCP应用' : 'Skills工具'}${!isMcp ? ` (来源: ${url})` : ''}`;
|
||||
const payload: any = {
|
||||
name: skillName,
|
||||
type: showHubImport || 'skill',
|
||||
description: desc,
|
||||
@@ -1500,7 +1501,11 @@ const MarketPage: React.FC = () => {
|
||||
is_member_free: (price || 0) === 0,
|
||||
join_promotion: false,
|
||||
source: 'user',
|
||||
});
|
||||
};
|
||||
if (isMcp && url) {
|
||||
payload.download_url = url;
|
||||
}
|
||||
await marketAPI.createApp(payload);
|
||||
// 重新加载列表
|
||||
loadApps();
|
||||
} catch (e: any) {
|
||||
|
||||
Reference in new issue
Block a user