yapi-mcp 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,60 +1,61 @@
1
- """Pydantic models for YApi API data structures."""
2
-
3
- from typing import Literal
4
-
5
- from pydantic import BaseModel, ConfigDict, Field
6
-
7
-
8
- class YApiInterface(BaseModel):
9
- """YApi interface complete definition from API responses."""
10
-
11
- model_config = ConfigDict(populate_by_name=True)
12
-
13
- id: int = Field(..., alias="_id", description="Interface ID")
14
- title: str = Field(..., description="Interface title")
15
- path: str = Field(..., description="Interface path", examples=["/api/user/login"])
16
-
17
- method: Literal["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"] = Field(
18
- ...,
19
- description="HTTP method",
20
- )
21
-
22
- project_id: int = Field(..., description="Project ID this interface belongs to")
23
-
24
- desc: str | None = Field(None, description="Interface description")
25
- req_body_other: str | None = Field(
26
- None, description="Request parameters definition (JSON string)"
27
- )
28
- res_body: str | None = Field(None, description="Response structure definition (JSON string)")
29
- status: str | None = Field(None, description="Status code")
30
-
31
- add_time: int | None = Field(None, description="Creation timestamp (Unix epoch)")
32
- up_time: int | None = Field(None, description="Last update timestamp (Unix epoch)")
33
-
34
-
35
- class YApiInterfaceSummary(BaseModel):
36
- """YApi interface summary from search results."""
37
-
38
- model_config = ConfigDict(populate_by_name=True)
39
-
40
- id: int = Field(..., alias="_id", description="Interface ID")
41
- title: str = Field(..., description="Interface title")
42
- path: str = Field(..., description="Interface path")
43
- method: str = Field(..., description="HTTP method")
44
-
45
-
46
- class YApiErrorResponse(BaseModel):
47
- """YApi API error response structure."""
48
-
49
- errcode: int = Field(..., description="Error code (non-zero indicates error)")
50
- errmsg: str = Field(..., description="Error message")
51
-
52
-
53
- class YApiProject(BaseModel):
54
- """YApi project information (optional, for future use)."""
55
-
56
- model_config = ConfigDict(populate_by_name=True)
57
-
58
- id: int = Field(..., alias="_id", description="Project ID")
59
- name: str = Field(..., description="Project name")
60
- desc: str | None = Field(None, description="Project description")
1
+ """Pydantic models for YApi API data structures."""
2
+
3
+ from typing import Literal
4
+
5
+ from pydantic import BaseModel, ConfigDict, Field
6
+
7
+
8
+ class YApiInterface(BaseModel):
9
+ """YApi interface complete definition from API responses."""
10
+
11
+ model_config = ConfigDict(populate_by_name=True)
12
+
13
+ id: int = Field(..., alias="_id", description="Interface ID")
14
+ catid: int = Field(..., description="Category ID this interface belongs to")
15
+ title: str = Field(..., description="Interface title")
16
+ path: str = Field(..., description="Interface path", examples=["/api/user/login"])
17
+
18
+ method: Literal["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"] = Field(
19
+ ...,
20
+ description="HTTP method",
21
+ )
22
+
23
+ project_id: int = Field(..., description="Project ID this interface belongs to")
24
+
25
+ desc: str | None = Field(None, description="Interface description")
26
+ req_body_other: str | None = Field(
27
+ None, description="Request parameters definition (JSON string)"
28
+ )
29
+ res_body: str | None = Field(None, description="Response structure definition (JSON string)")
30
+ status: str | None = Field(None, description="Status code")
31
+
32
+ add_time: int | None = Field(None, description="Creation timestamp (Unix epoch)")
33
+ up_time: int | None = Field(None, description="Last update timestamp (Unix epoch)")
34
+
35
+
36
+ class YApiInterfaceSummary(BaseModel):
37
+ """YApi interface summary from search results."""
38
+
39
+ model_config = ConfigDict(populate_by_name=True)
40
+
41
+ id: int = Field(..., alias="_id", description="Interface ID")
42
+ title: str = Field(..., description="Interface title")
43
+ path: str = Field(..., description="Interface path")
44
+ method: str = Field(..., description="HTTP method")
45
+
46
+
47
+ class YApiErrorResponse(BaseModel):
48
+ """YApi API error response structure."""
49
+
50
+ errcode: int = Field(..., description="Error code (non-zero indicates error)")
51
+ errmsg: str = Field(..., description="Error message")
52
+
53
+
54
+ class YApiProject(BaseModel):
55
+ """YApi project information (optional, for future use)."""
56
+
57
+ model_config = ConfigDict(populate_by_name=True)
58
+
59
+ id: int = Field(..., alias="_id", description="Project ID")
60
+ name: str = Field(..., description="Project name")
61
+ desc: str | None = Field(None, description="Project description")
@@ -0,0 +1,174 @@
1
+ Metadata-Version: 2.4
2
+ Name: yapi-mcp
3
+ Version: 0.1.2
4
+ Summary: Model Context Protocol server for YApi 1.12.0 API management platform
5
+ Project-URL: Homepage, https://github.com/geq1fan/yapi-mcp
6
+ Project-URL: Repository, https://github.com/geq1fan/yapi-mcp
7
+ Project-URL: Issues, https://github.com/geq1fan/yapi-mcp/issues
8
+ Author: YApi MCP Team
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: api,fastmcp,mcp,model-context-protocol,yapi
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: fastmcp>=2.0.0
22
+ Requires-Dist: httpx>=0.27.0
23
+ Requires-Dist: pydantic-settings>=2.0.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
26
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
27
+ Requires-Dist: respx>=0.21.0; extra == 'dev'
28
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # YApi MCP Server
32
+
33
+ [![Python Version](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
34
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
35
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
36
+
37
+ 基于 [YApi 1.12.0](https://github.com/YMFE/yapi) 接口管理平台的 Model Context Protocol (MCP) 服务器。
38
+
39
+ 使开发者能够在支持 MCP 的 IDE 和编辑器(Claude Code、Cursor 等)中直接搜索、查看、创建和更新 YApi 接口定义,无需离开开发环境。
40
+
41
+ ## 功能特性
42
+
43
+ - 🔍 **搜索接口**: 通过标题、路径或描述查找 API 端点
44
+ - 📖 **查看定义**: 获取完整的接口规范,包括请求/响应结构
45
+ - ➕ **创建接口**: 向 YApi 项目添加新的 API 定义
46
+ - ✏️ **更新接口**: 修改现有接口配置
47
+ - 🔐 **Cookie 认证**: 基于会话的安全认证
48
+ - ⚡ **异步性能**: 基于 httpx 实现高效的并发操作
49
+
50
+ ## 环境要求
51
+
52
+ - Python 3.11 或更高版本
53
+ - YApi 1.12.0 实例(可通过 HTTP/HTTPS 访问)
54
+ - 有效的 YApi 认证 cookies
55
+
56
+ ## 配置
57
+
58
+ ### 1. 获取 YApi Cookies
59
+
60
+ 1. 在浏览器中登录 YApi 实例
61
+ 2. 打开开发者工具(F12)
62
+ 3. 导航到 **Application** → **Cookies**(Chrome) 或 **Storage** → **Cookies**(Firefox)
63
+ 4. 复制以下 cookie 值:
64
+ - `_yapi_token` (必需)
65
+ - `_yapi_uid` (必需)
66
+ - `ZYBIPSCAS` (可选,仅某些自定义部署需要)
67
+
68
+ ### 2. 配置 MCP
69
+
70
+ ```json
71
+ {
72
+ "mcpServers": {
73
+ "yapi": {
74
+ "command": "uvx",
75
+ "args": ["--from", "/path/to/yapi-mcp", "yapi-mcp"],
76
+ "env": {
77
+ "YAPI_SERVER_URL": "https://your-yapi-instance.com",
78
+ "YAPI_TOKEN": "your_token",
79
+ "YAPI_UID": "your_uid"
80
+ }
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ > **注意**: 如果你的 YApi 部署需要额外的 CAS 认证,添加 `"YAPI_CAS": "your_cas_value"` 到 `env` 中。
87
+
88
+ ## 开发
89
+
90
+ ### 运行测试
91
+
92
+ ```bash
93
+ # 安装开发依赖
94
+ pip install -e ".[dev]"
95
+
96
+ # 运行所有测试
97
+ pytest
98
+
99
+ # 运行并显示覆盖率
100
+ pytest --cov=src --cov-report=term-missing
101
+
102
+ # 运行特定测试文件
103
+ pytest tests/test_config.py
104
+ ```
105
+
106
+ ### 代码质量
107
+
108
+ ```bash
109
+ # 格式化代码
110
+ ruff format
111
+
112
+ # 代码检查
113
+ ruff check
114
+
115
+ # 自动修复检查问题
116
+ ruff check --fix
117
+ ```
118
+
119
+ ### 项目结构
120
+
121
+ ```
122
+ yapi-mcp/
123
+ ├── src/
124
+ │ ├── server.py # MCP 服务器入口点
125
+ │ ├── config.py # 配置模型
126
+ │ ├── yapi/
127
+ │ │ ├── client.py # YApi API 客户端
128
+ │ │ ├── models.py # Pydantic 数据模型
129
+ │ │ └── errors.py # 错误映射
130
+ │ └── tools/ # MCP 工具(在 server.py 中注册)
131
+ ├── tests/ # 测试套件
132
+ ├── specs/ # 设计文档
133
+ ├── pyproject.toml # 项目配置
134
+ ├── .env.example # 环境变量模板
135
+ └── README.md # 本文件
136
+ ```
137
+
138
+ ## 贡献
139
+
140
+ 欢迎贡献! 我们非常感谢各种形式的贡献,包括但不限于:
141
+
142
+ - 🐛 报告 Bug
143
+ - ✨ 建议新功能
144
+ - 📝 改进文档
145
+ - 💻 提交代码
146
+
147
+ 在开始贡献之前,请阅读我们的[贡献指南](CONTRIBUTING.md)。
148
+
149
+ ### 快速开始
150
+
151
+ 1. Fork 本仓库
152
+ 2. 创建功能分支 (`git checkout -b feature/amazing-feature`)
153
+ 3. 提交更改 (`git commit -m 'feat: 添加惊人的功能'`)
154
+ 4. 推送到分支 (`git push origin feature/amazing-feature`)
155
+ 5. 创建 Pull Request
156
+
157
+ ### 代码质量要求
158
+
159
+ - ✅ 所有测试通过: `pytest`
160
+ - ✅ 代码已格式化: `ruff format`
161
+ - ✅ 无检查错误: `ruff check`
162
+ - ✅ 新功能包含测试
163
+
164
+ 详细信息请参阅 [CONTRIBUTING.md](CONTRIBUTING.md)。
165
+
166
+ ## 许可证
167
+
168
+ 本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。
169
+
170
+ ## 相关项目
171
+
172
+ - [YApi](https://github.com/YMFE/yapi) - API 管理平台
173
+ - [fastmcp](https://github.com/jlowin/fastmcp) - Python MCP 框架
174
+ - [Model Context Protocol](https://modelcontextprotocol.io/) - MCP 规范
@@ -0,0 +1,14 @@
1
+ yapi_mcp/__init__.py,sha256=-B1Yt-BsgzhccXKx5Y6f3TWR1OQbb8I8cOvnSKYjcSU,136
2
+ yapi_mcp/__main__.py,sha256=hOOyTtbTkoNugBbb_SofxEMRf8PuoOhWa8Z8-OyfWqw,151
3
+ yapi_mcp/config.py,sha256=k19HWCCBfBJttlsWj4f2QdxvFoBvHSJ1AVvb08WT-tQ,1373
4
+ yapi_mcp/server.py,sha256=NVkl_PKBzDjBbjyl2ynpxtf8cpKRtaWz9H0BZ6UDxpQ,6564
5
+ yapi_mcp/tools/__init__.py,sha256=iCyxPtAUoQ7RU976LPMXgGghqm2l0bH-LYBCnEOLtew,33
6
+ yapi_mcp/yapi/__init__.py,sha256=gK6ta7uNWkDqen5yWSDqqXYlCxz3uU-A2bKaqeO55Qk,31
7
+ yapi_mcp/yapi/client.py,sha256=gcwYdeHT9Yjq-550HyDh61wJYS8TeeAgFMoXoeb-bIg,10058
8
+ yapi_mcp/yapi/errors.py,sha256=RQvvdl1d-CBawZDTdgf3g_ThJUhGUvwRPCI-q2I6Mfc,3815
9
+ yapi_mcp/yapi/models.py,sha256=MJZQx_esXPcQ7UTnhlo_QVU81NrIPF2pB36mxvcLJ5w,2349
10
+ yapi_mcp-0.1.2.dist-info/METADATA,sha256=NpaST-JZA4syzU8qEK9A0LrcgVmmNogsuksUdYRr3Wo,5198
11
+ yapi_mcp-0.1.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
12
+ yapi_mcp-0.1.2.dist-info/entry_points.txt,sha256=u1QWnfoawIuUR6RuyPmqi9TpySnasIRU1lvJZa315EU,43
13
+ yapi_mcp-0.1.2.dist-info/licenses/LICENSE,sha256=b-a1jI555eCf0fkRmK3mrIGQe8hYqC5aXy0nGXiEasA,1106
14
+ yapi_mcp-0.1.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ yapi-mcp = yapi_mcp:main
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 YApi MCP Server Contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2025 YApi MCP Server Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
__init__.py DELETED
@@ -1 +0,0 @@
1
- """YApi MCP Server - Model Context Protocol adapter for YApi."""
server.py DELETED
@@ -1,148 +0,0 @@
1
- """YApi MCP Server - Main server module with fastmcp."""
2
-
3
- import json
4
- from typing import Annotated
5
-
6
- import httpx
7
- from fastmcp import FastMCP
8
-
9
- from config import ServerConfig
10
- from yapi.client import YApiClient
11
- from yapi.errors import map_http_error_to_mcp
12
-
13
- # Initialize MCP server
14
- mcp = FastMCP(
15
- "YApi MCP Server",
16
- version="0.1.0",
17
- )
18
-
19
-
20
- def get_config() -> ServerConfig:
21
- """Get or create ServerConfig instance (lazy loading)."""
22
- if not hasattr(get_config, "_instance"):
23
- get_config._instance = ServerConfig()
24
- return get_config._instance
25
-
26
-
27
- # Tool implementations will be added in subsequent tasks (T019-T022)
28
-
29
-
30
- @mcp.tool()
31
- async def yapi_search_interfaces(
32
- project_id: Annotated[int, "YApi 项目 ID"],
33
- keyword: Annotated[str, "搜索关键词(匹配接口标题/路径/描述)"],
34
- ) -> str:
35
- """在指定 YApi 项目中搜索接口,支持按标题、路径、描述模糊匹配."""
36
- config = get_config()
37
- try:
38
- async with YApiClient(str(config.yapi_server_url), config.cookies) as client:
39
- results = await client.search_interfaces(project_id, keyword)
40
- # Return JSON string with search results
41
- return json.dumps(
42
- [result.model_dump(by_alias=True) for result in results],
43
- ensure_ascii=False,
44
- indent=2,
45
- )
46
- except httpx.HTTPStatusError as e:
47
- mcp_error = map_http_error_to_mcp(e)
48
- raise Exception(mcp_error.message) from e
49
- except Exception as e:
50
- raise Exception(f"搜索接口失败: {e!s}") from e
51
-
52
-
53
- @mcp.tool()
54
- async def yapi_get_interface(
55
- interface_id: Annotated[int, "接口 ID"],
56
- ) -> str:
57
- """获取 YApi 接口的完整定义(包括请求参数、响应结构、描述等)."""
58
- config = get_config()
59
- try:
60
- async with YApiClient(str(config.yapi_server_url), config.cookies) as client:
61
- interface = await client.get_interface(interface_id)
62
- return json.dumps(
63
- interface.model_dump(by_alias=True),
64
- ensure_ascii=False,
65
- indent=2,
66
- )
67
- except httpx.HTTPStatusError as e:
68
- mcp_error = map_http_error_to_mcp(e)
69
- raise Exception(mcp_error.message) from e
70
- except Exception as e:
71
- raise Exception(f"获取接口失败: {e!s}") from e
72
-
73
-
74
- @mcp.tool()
75
- async def yapi_create_interface(
76
- project_id: Annotated[int, "项目 ID"],
77
- title: Annotated[str, "接口标题"],
78
- path: Annotated[str, "接口路径(以 / 开头)"],
79
- method: Annotated[str, "HTTP 方法(GET/POST/PUT/DELETE等)"],
80
- req_body: Annotated[str, "请求参数(JSON 字符串,可选)"] = "",
81
- res_body: Annotated[str, "响应结构(JSON 字符串,可选)"] = "",
82
- desc: Annotated[str, "接口描述(可选)"] = "",
83
- ) -> str:
84
- """在 YApi 项目中创建新接口定义."""
85
- config = get_config()
86
- try:
87
- # Validate path starts with /
88
- if not path.startswith("/"):
89
- raise ValueError("接口路径必须以 / 开头")
90
-
91
- async with YApiClient(str(config.yapi_server_url), config.cookies) as client:
92
- interface_id = await client.create_interface(
93
- project_id, title, path, method, req_body, res_body, desc
94
- )
95
- return json.dumps(
96
- {"interface_id": interface_id},
97
- ensure_ascii=False,
98
- )
99
- except httpx.HTTPStatusError as e:
100
- mcp_error = map_http_error_to_mcp(e)
101
- raise Exception(mcp_error.message) from e
102
- except ValueError as e:
103
- raise Exception(f"参数验证失败: {e!s}") from e
104
- except Exception as e:
105
- raise Exception(f"创建接口失败: {e!s}") from e
106
-
107
-
108
- @mcp.tool()
109
- async def yapi_update_interface(
110
- interface_id: Annotated[int, "接口 ID"],
111
- title: Annotated[str | None, "更新的标题"] = None,
112
- path: Annotated[str | None, "更新的路径"] = None,
113
- method: Annotated[str | None, "更新的 HTTP 方法"] = None,
114
- req_body: Annotated[str | None, "更新的请求参数"] = None,
115
- res_body: Annotated[str | None, "更新的响应结构"] = None,
116
- desc: Annotated[str | None, "更新的描述"] = None,
117
- ) -> str:
118
- """增量更新 YApi 接口定义(仅更新提供的字段)."""
119
- config = get_config()
120
- try:
121
- # Validate path if provided
122
- if path is not None and not path.startswith("/"):
123
- raise ValueError("接口路径必须以 / 开头")
124
-
125
- async with YApiClient(str(config.yapi_server_url), config.cookies) as client:
126
- success = await client.update_interface(
127
- interface_id, title, path, method, req_body, res_body, desc
128
- )
129
- return json.dumps(
130
- {"success": success, "message": "接口更新成功"},
131
- ensure_ascii=False,
132
- )
133
- except httpx.HTTPStatusError as e:
134
- mcp_error = map_http_error_to_mcp(e)
135
- raise Exception(mcp_error.message) from e
136
- except ValueError as e:
137
- raise Exception(f"参数验证失败: {e!s}") from e
138
- except Exception as e:
139
- raise Exception(f"更新接口失败: {e!s}") from e
140
-
141
-
142
- def main() -> None:
143
- """Entry point for uvx yapi-mcp command."""
144
- mcp.run()
145
-
146
-
147
- if __name__ == "__main__":
148
- main()