Skip to main content
Glama

FastAPI OpenAPI MCP Server

by jason-chang
markdown.py10.3 kB
""" Markdown 格式化器 将 OpenAPI 数据转换为 Markdown 格式的输出。 """ import json from typing import Any from openapi_mcp.formatters.base import BaseFormatter class MarkdownFormatter(BaseFormatter): """Markdown 格式化器 将 OpenAPI 数据格式化为美观的 Markdown 文本, 适合在终端或支持 Markdown 的环境中显示。 """ def format_endpoints( self, grouped_endpoints: dict[str, list[dict[str, str]]] ) -> str: """格式化接口列表为 Markdown Args: grouped_endpoints: 按标签分组的接口列表 Returns: Markdown 格式的接口列表 """ if not grouped_endpoints: return '📭 **No endpoints found.**' lines = ['🚀 **Available API Endpoints:**\n'] # 统计总接口数 total_endpoints = sum( len(endpoints) for endpoints in grouped_endpoints.values() ) # 按标签分组输出 for tag, endpoints in grouped_endpoints.items(): # 标签名称(无标签用特殊标记) tag_display = tag if tag != 'Untagged' else '📂 Untagged' lines.append(f'## {tag_display}\n') # 接口列表 for endpoint in endpoints: method = endpoint['method'].upper() path = endpoint['path'] summary = endpoint.get('summary', 'No description') lines.append(f'- **{method}** `{path}` - {summary}') lines.append('') # 空行分隔 # 总结 lines.append(f'📊 **Total endpoints:** {total_endpoints}') result = '\n'.join(lines) return self.truncate(result) def format_endpoint_details(self, endpoint_info: dict[str, Any]) -> str: """格式化接口详情为 Markdown Args: endpoint_info: 接口详细信息 Returns: Markdown 格式的接口详情 """ lines = [] # 基本信息 method = endpoint_info.get('method', 'UNKNOWN').upper() path = endpoint_info.get('path', '') summary = endpoint_info.get('summary', '') description = endpoint_info.get('description', '') lines.append(f'# {method} {path}\n') if summary: lines.append(f'**{summary}**\n') if description: lines.append(f'{description}\n') # 废弃状态 if endpoint_info.get('deprecated'): lines.append('⚠️ **Deprecated**: This endpoint is deprecated.\n') # 参数 parameters = endpoint_info.get('parameters', []) if parameters: lines.append('## 📝 Parameters\n') for param in parameters: name = param.get('name', 'unknown') in_location = param.get('in', 'unknown') required = ' *[Required]*' if param.get('required') else '' param_type = param.get('type', 'unknown') param_description = param.get('description', '') lines.append(f'- **{name}** ({in_location}){required}: `{param_type}`') if param_description: lines.append(f' {param_description}') lines.append('') # 请求体 request_body = endpoint_info.get('requestBody') if request_body: lines.append('## 📤 Request Body\n') required = ' *[Required]*' if request_body.get('required') else '' lines.append(f'**Required:** {required}\n') content = request_body.get('content', {}) for media_type, schema_info in content.items(): lines.append(f'**Content-Type:** `{media_type}`\n') if 'schema' in schema_info: schema = schema_info['schema'] if '$ref' in schema: ref = schema['$ref'].split('/')[-1] lines.append(f'**Schema:** Reference to `{ref}`\n') else: lines.append('**Schema:**\n') lines.append( f'```json\n{json.dumps(schema, indent=2, ensure_ascii=False)}\n```\n' ) # 示例 if 'example' in schema_info: lines.append('**Example:**\n') lines.append( f'```json\n{json.dumps(schema_info["example"], indent=2, ensure_ascii=False)}\n```\n' ) # 响应 responses = endpoint_info.get('responses', {}) if responses: lines.append('## 📥 Responses\n') for status_code, response_info in responses.items(): description = response_info.get('description', 'No description') lines.append(f'### {status_code} - {description}\n') content = response_info.get('content', {}) for media_type, schema_info in content.items(): lines.append(f'**Content-Type:** `{media_type}`\n') if 'schema' in schema_info: schema = schema_info['schema'] if '$ref' in schema: ref = schema['$ref'].split('/')[-1] lines.append(f'**Schema:** Reference to `{ref}`\n') else: lines.append('**Schema:**\n') lines.append( f'```json\n{json.dumps(schema, indent=2, ensure_ascii=False)}\n```\n' ) # 示例 if 'example' in schema_info: lines.append('**Example:**\n') lines.append( f'```json\n{json.dumps(schema_info["example"], indent=2, ensure_ascii=False)}\n```\n' ) # 认证要求 security = endpoint_info.get('security', []) if security: lines.append('## 🔐 Security\n') for sec_req in security: for sec_name, scopes in sec_req.items(): lines.append(f'- **{sec_name}**') if scopes: lines.append(f' - Scopes: {", ".join(scopes)}') lines.append('') result = '\n'.join(lines) return self.truncate(result) def format_models(self, models: list[dict[str, str]]) -> str: """格式化数据模型列表为 Markdown Args: models: 模型列表 Returns: Markdown 格式的模型列表 """ if not models: return '📭 **No models found.**' lines = [f'📦 **API Data Models ({len(models)} models):**\n'] for model in models: name = model['name'] description = model.get('description', 'No description') lines.append(f'- **{name}**: {description}') lines.append('\n💡 Use `get_model_details` to view full schema.') result = '\n'.join(lines) return self.truncate(result) def format_model_details(self, model_info: dict[str, Any]) -> str: """格式化数据模型详情为 Markdown Args: model_info: 模型详细信息 Returns: Markdown 格式的模型详情 """ lines = [] # 基本信息 name = model_info.get('name', 'Unknown') description = model_info.get('description', '') lines.append(f'📦 **Model: {name}**\n') if description: lines.append(f'**Description:** {description}\n') # 字段列表 fields = model_info.get('fields', []) if fields: lines.append('## 📋 Fields\n') for field in fields: field_name = field.get('name', 'unknown') field_type = field.get('type', 'unknown') field_description = field.get('description', '') required = ' *[Required]*' if field.get('required') else '' lines.append(f'- **{field_name}**{required}: `{field_type}`') if field_description: lines.append(f' {field_description}') # 验证规则 validations = [] if 'minLength' in field: validations.append(f'minLength: {field["minLength"]}') if 'maxLength' in field: validations.append(f'maxLength: {field["maxLength"]}') if 'pattern' in field: validations.append(f'pattern: {field["pattern"]}') if 'minimum' in field: validations.append(f'minimum: {field["minimum"]}') if 'maximum' in field: validations.append(f'maximum: {field["maximum"]}') if 'format' in field: validations.append(f'format: {field["format"]}') if validations: lines.append(f' ({", ".join(validations)})') # 必填字段 required_fields = model_info.get('required', []) if required_fields: lines.append( f'\n**Required fields:** {", ".join(f"`{f}`" for f in required_fields)}' ) result = '\n'.join(lines) return self.truncate(result) def format_search_results( self, results: list[dict[str, Any]], truncated: bool = False, empty_results: bool = False, ) -> str: """格式化搜索结果为 Markdown Args: results: 搜索结果列表 truncated: 是否结果被截断 empty_results: 是否为空结果(但仍需要显示搜索信息) Returns: Markdown 格式的搜索结果 """ if not results and not empty_results: return '📭 **没有找到匹配的接口**\n\n**建议**:\n- 尝试使用更通用的关键词\n- 检查拼写是否正确\n- 使用正则表达式搜索' # 从第一个结果获取搜索信息 keyword = results[0].get('keyword', '') if results else '' search_in = results[0].get('search_in', 'all') if results else 'all' search_in_display = { 'path': '路径', 'summary': '摘要', 'description': '描述', 'tags': '标签', 'all': '全部', } search_in_text = search_in_display.get(search_in, '全部') lines = [f'🔍 **搜索结果: "{keyword}"**\n'] lines.append(f'📊 **搜索范围**: {search_in_text}\n') if empty_results: lines.append('📈 **匹配数量**: 0 个接口\n') lines.append( '\n📭 没有找到匹配的接口\n\n**建议**:\n- 尝试使用更通用的关键词\n- 检查拼写是否正确\n- 使用正则表达式搜索' ) return '\n'.join(lines) else: lines.append(f'📈 **匹配数量**: {len(results)} 个接口\n') if truncated: lines.append('> ⚠️ **结果被截断** - 仅显示部分结果\n') for result in results: method = result.get('method', 'UNKNOWN').upper() path = result.get('path', '') summary = result.get('summary', 'No description') description = result.get('description', '') tags = result.get('tags', '') matched_in = result.get('matched_in', '') deprecated = result.get('deprecated', False) # 构建接口行 endpoint_line = f'- **{method}** `{path}`' # 添加废弃标记 if deprecated: endpoint_line += ' ⚠️' # 添加摘要 if summary: endpoint_line += f' - {summary}' lines.append(endpoint_line) # 添加匹配位置 if matched_in: matched_in_display = { 'path': '路径', 'summary': '摘要', 'description': '描述', 'tags': '标签', } matched_in_text = matched_in_display.get(matched_in, matched_in) lines.append(f' - *匹配于*: {matched_in_text}') # 添加标签 if tags: lines.append(f' - *标签*: {tags}') # 添加描述(如果有的话且不重复摘要) if description and description != summary: lines.append( f' - *描述*: {description[:100]}{"..." if len(description) > 100 else ""}' ) lines.append('') # 空行分隔 result_text = '\n'.join(lines) return self.truncate(result_text)

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/jason-chang/fastapi-openapi-mcp'

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