Skip to main content
Glama

Magic-API MCP Server

by Dwsy
query.py11.5 kB
"""Magic-API 查询相关 MCP 工具。 此模块提供高效的资源查询和检索功能,包括: - 路径到ID的快速转换 - API详细信息查询 - 批量资源查询 - 资源路径查找和匹配 重要提示: - 调用API时请优先使用 get_api_details_by_id 获取接口的完整信息 - 返回的 full_path 字段包含完整的路径(如 "/db/advance/other/number/convert"),建议在调用时与method结合使用 - call_magic_api 工具支持传入接口ID,会自动转换为完整路径 主要工具: - get_api_details_by_path: 根据API路径直接获取接口的详细信息,支持模糊匹配 - get_api_details_by_id: 根据接口ID获取详细信息,包含full_path字段(推荐使用) - search_api_endpoints: 搜索和过滤Magic-API接口端点,返回包含ID的完整信息列表 """ from __future__ import annotations from typing import TYPE_CHECKING, Annotated, Any, Dict, List, Optional from pydantic import Field from magicapi_tools.utils.extractor import extract_api_endpoints, load_resource_tree from magicapi_tools.utils.extractor import filter_endpoints, _clean_path from magicapi_tools.logging_config import get_logger from magicapi_tools.tools.common import ( error_response, path_to_id_impl, ) # 获取查询工具的logger logger = get_logger('tools.query') if TYPE_CHECKING: from fastmcp import FastMCP from magicapi_mcp.tool_registry import ToolContext def _collect_all_endpoints(node: Dict[str, Any], parent_path: str, results: List[Dict[str, Any]]) -> None: """收集所有端点的详细信息(包含ID和display字符串),与 _traverse_api_tree 逻辑完全一致。""" node_info = node.get("node", {}) current_path = node_info.get("path", "") method = node_info.get("method") api_id = node_info.get("id") name = node_info.get("name", "") if current_path and parent_path: full_path = f"{parent_path}/{current_path}" elif current_path: full_path = current_path else: full_path = parent_path # 使用与 _traverse_api_tree 相同的路径清理逻辑 full_path = _clean_path(full_path) if method and full_path: # 生成与 _traverse_api_tree 完全相同的display字符串 display = f"{method} {full_path}" if name and name != current_path: display += f" [{name}]" if api_id: results.append({ "id": api_id, "path": full_path, "method": method, "name": name, "display": display, "groupId": node_info.get("groupId"), }) for child in node.get("children", []) or []: _collect_all_endpoints(child, full_path, results) def _get_full_path_by_api_details(client, api_id: str, method: str, path: str, name: str = "") -> str: """根据API详情获取完整的资源树路径,符合DRY原则。 Args: client: HTTP客户端 api_id: 接口ID method: HTTP方法 path: 接口路径(可能不完整) name: 接口名称 Returns: str: 完整的路径,如 "/db/advance/other/number/convert" """ try: # 获取所有端点的详细信息 tree = load_resource_tree(client=client) all_endpoint_details = [] for child in tree.api_nodes: _collect_all_endpoints(child, "", all_endpoint_details) # 首先尝试通过ID直接匹配 for detail in all_endpoint_details: if detail.get("id") == api_id: path_from_tree = detail.get("path", "") if path_from_tree: return path_from_tree # 如果ID匹配失败,尝试通过method+path+name匹配 for detail in all_endpoint_details: detail_method = detail.get("method", "").upper() detail_path = detail.get("path", "") detail_name = detail.get("name", "") if (detail_method == method.upper() and detail_path.endswith(path) and # path可能是部分路径,检查结尾匹配 (not name or detail_name == name)): return detail_path # 如果都没匹配上,返回简单格式 return path except Exception as exc: # pragma: no cover - 容错分支 # 如果获取资源树失败,返回简单格式 return path class QueryTools: """查询工具模块。""" def register_tools(self, mcp_app: "FastMCP", context: "ToolContext") -> None: # pragma: no cover - 装饰器环境 """注册查询相关工具。""" @mcp_app.tool( name="get_api_details_by_path", description="根据API路径直接获取接口的详细信息,支持模糊匹配。", tags={"resource", "details", "path", "api"}, ) def path_detail( path: Annotated[ str, Field(description="API路径,用于查找接口详细信息,如'/db/sql'或'GET /db/db/sql'") ], fuzzy: Annotated[ bool, Field(description="是否启用模糊匹配,true时支持部分路径匹配,false时要求精确匹配") ] = True ) -> Dict[str, Any]: id_result = path_to_id_impl(context.http_client, path, fuzzy) if "error" in id_result: return id_result details = [] for node in id_result.get("matches", []): file_id = node.get("id") if not file_id: details.append({"meta": node, "error": {"code": "missing_id", "message": "节点缺少 ID"}}) continue ok_detail, detail_payload = context.http_client.api_detail(file_id) if ok_detail: details.append({"meta": node, "detail": detail_payload}) else: details.append({"meta": node, "error": detail_payload}) return {"path": path, "fuzzy": fuzzy, "results": details} @mcp_app.tool( name="get_api_details_by_id", description="根据接口ID获取完整的接口详细信息和配置。返回包含method、path和full_path(完整路径)的详细信息。", tags={"resource", "details", "id", "api"}, ) def api_detail( file_id: Annotated[ str, Field(description="API接口的文件ID,用于获取接口的详细信息") ] ) -> Dict[str, Any]: ok, payload = context.http_client.api_detail(file_id) if not ok: logger.error(f"查询API详情失败: {payload.get('message', '无法获取接口详情')}") logger.error(f" API ID: {file_id}") logger.debug(f" 错误详情: {payload}") return error_response(payload.get("code"), payload.get("message", "无法获取接口详情"), payload.get("detail")) if payload is None: logger.warning(f"API详情数据为空: {file_id}") logger.debug(f" 原始响应: {payload}") return error_response("no_data", f"接口 {file_id} 的详情数据为空") # 获取基础信息 method = payload.get("method", "").upper() path = payload.get("path", "") name = payload.get("name", "") # 使用可复用函数获取完整的资源树路径 full_path = _get_full_path_by_api_details(context.http_client, file_id, method, path, name) return { **payload, "full_path": full_path # 完整的路径,包含分组路径,如 "/db/advance/other/number/convert" } @mcp_app.tool( name="search_api_endpoints", description="搜索和过滤 Magic-API 接口端点,支持按方法、路径、名称等条件过滤。返回包含ID、方法、路径、名称等完整信息的端点列表。", tags={"search", "filter", "api", "endpoints"}, ) def search_endpoints( method_filter: Annotated[ Optional[str], Field(description="按 HTTP 方法过滤,如 'GET'、'POST'、'PUT'、'DELETE'") ] = None, path_filter: Annotated[ Optional[str], Field(description="按路径正则表达式过滤,如 '^/api/users' 或 'user'") ] = None, name_filter: Annotated[ Optional[str], Field(description="按名称正则表达式过滤,如 '用户' 或 '.*管理.*'") ] = None, query_filter: Annotated[ Optional[str], Field(description="路径/名称模糊匹配,支持正则表达式") ] = None, ) -> Dict[str, Any]: try: tree = load_resource_tree(client=context.http_client) endpoints = extract_api_endpoints(tree) filtered_endpoints = filter_endpoints( endpoints, method_filter=method_filter, path_filter=path_filter, name_filter=name_filter, query_filter=query_filter, ) # 获取所有端点的详细信息(包含ID和display字符串) all_endpoint_details = [] for child in tree.api_nodes: _collect_all_endpoints(child, "", all_endpoint_details) # 创建端点字符串到ID的映射 endpoint_to_id_map = {} for detail in all_endpoint_details: display = detail.get("display") api_id = detail.get("id") if display and api_id: endpoint_to_id_map[display] = api_id results = [] for endpoint in filtered_endpoints: if "[" in endpoint and "]" in endpoint: method_path, name = endpoint.split(" [", 1) name = name.rstrip("]") else: method_path, name = endpoint, "" method, path_value = method_path.split(" ", 1) # 获取对应的ID api_id = endpoint_to_id_map.get(endpoint) results.append( { "method": method, "path": path_value, "name": name, "id": api_id, "display": endpoint, } ) return { "total_count": len(endpoints), "filtered_count": len(filtered_endpoints), "filters": { "method": method_filter, "path": path_filter, "name": name_filter, "query": query_filter, }, "endpoints": results, } except Exception as exc: # pragma: no cover - 容错分支 return error_response("search_error", f"搜索API端点失败: {exc}", str(exc))

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Dwsy/magic-api-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server