zan-tapd-mcp 0.0.1__tar.gz

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.
@@ -0,0 +1,121 @@
1
+ Metadata-Version: 2.4
2
+ Name: zan-tapd-mcp
3
+ Version: 0.0.1
4
+ Summary: TAPD MCP Server
5
+ Author-email: ryanjhzheng <zhengjianhong95@gmail.com>
6
+ Keywords: mcp,tapd
7
+ Requires-Python: >=3.13
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: httpx
10
+ Requires-Dist: requests
11
+ Requires-Dist: markdown
12
+ Requires-Dist: mcp[cli]<2,>=1.0.0
13
+
14
+ # TAPD MCP Server
15
+
16
+ TAPD 是腾讯敏捷研发管理平台,覆盖需求、计划、研发、测试、发布研发全生命周期。支持用自然语言与 TAPD 对话,实现需求、缺陷、任务、迭代等管理。
17
+
18
+ * 与 TAPD API 无缝集成,提升开发效率
19
+
20
+ ## 系统要求
21
+
22
+ * uv
23
+ * TAPD Access Token(推荐)或 TAPD API 账号密码
24
+
25
+ ## 快速开始
26
+ ### Install uv
27
+ ```
28
+ brew install uv
29
+ # OR
30
+ curl -LsSf https://astral.sh/uv/install.sh | sh
31
+ ```
32
+
33
+ ### 参数
34
+ - TAPD_ACCESS_TOKEN: 个人令牌(推荐)
35
+ - TAPD_API_USER: API账号
36
+ - TAPD_API_PASSWORD: API密钥
37
+ - BOT_URL: 企业微信机器人 webhook 地址,如果需要发送消息到企业微信群才需要填(选填)
38
+
39
+ 注意 TAPD_API_USER/TAPD_API_PASSWORD(TAPD API 账号密码) 与 TAPD_ACCESS_TOKEN(TAPD 个人授权令牌)为两种调用 MCP 的凭据,选一种即可。
40
+
41
+
42
+ ### 获取 TAPD Access Token(推荐)
43
+ TAPD_ACCESS_TOKEN 为 TAPD 个人令牌,在 [我的设置-个人访问令牌](https://www.tapd.cn/personal_settings/index?tab=personal_token) 点“创建个人访问令牌”,令牌只显示一次,注意保存下来。
44
+
45
+ ### 获取 API 账号密码(兼容)
46
+
47
+ 1. 未注册,请前往 [注册](https://www.tapd.cn?from_partner=copilot&source=tapd_operation_copilot)
48
+ 2. 已注册但未授权API,请前往API配置:登录TAPD,点击进入 [公司管理-API账号管理](https://www.tapd.cn/open_platform/open_api_redirect?from_partner=copilot&source=tapd_operation_copilot),复制API账号和API密钥
49
+
50
+
51
+ ## IDE 配置
52
+ ### CodeBuddy / Claude Desktop / Cursor
53
+ #### 本地 stdio 运行,环境变量配置示例(推荐个人 Token)
54
+ ```json
55
+ {
56
+ "mcpServers": {
57
+ "mcp-server-tapd": {
58
+ "command": "uvx",
59
+ "args": ["--from", "zan-tapd-mcp", "mcp-server-tapd"],
60
+ "env": {
61
+ "TAPD_ACCESS_TOKEN": "您的个人Token",
62
+ // 兼容账号密码方式(如未配置 Token 时生效)
63
+ "TAPD_API_USER": "",
64
+ "TAPD_API_PASSWORD": "",
65
+ "TAPD_API_BASE_URL": "https://api.tapd.cn",
66
+ "TAPD_BASE_URL": "https://www.tapd.cn",
67
+ "BOT_URL": ""
68
+ }
69
+ }
70
+ }
71
+ }
72
+ ```
73
+
74
+
75
+ ### Streamable HTTP 配置
76
+ 使用 Streamable HTTP 替代 stdio
77
+ 1. 在终端启动已发布的服务
78
+ ```
79
+ uvx --from zan-tapd-mcp mcp-server-tapd --mode=streamable-http --host="0.0.0.0" --port=8000 --api-user=your_api_user --api-password=your_api_password --api-base-url=https://api.tapd.cn --tapd-base-url=https://www.tapd.cn --bot-url="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=XXX"
80
+
81
+ # 如果使用个人 Token,则将 --api-user=your_api_user --api-password=your_api_password 替换为 --access-token=您的个人Token
82
+
83
+ ```
84
+
85
+ 2. 通过如下配置连接到已启动的服务
86
+ ```json
87
+ {
88
+ "mcpServers": {
89
+ "tapd_mcp_http": {
90
+ "url": "http://localhost:8000/mcp/"
91
+ }
92
+ }
93
+ }
94
+ ```
95
+
96
+ ## 富媒体描述
97
+ `create_bug`、`update_bug`、`create_story_or_task`、`update_story_or_task`、`create_comments`、`update_comments` 支持把外部图片或视频直链直接写进 TAPD 富文本描述/评论。
98
+
99
+ 可用参数:
100
+ - `media`: 结构化媒体列表,例如 `[{ "type": "image", "url": "https://example.com/a.png" }, { "type": "video", "url": "https://example.com/demo.mp4" }]`
101
+ - `image_url` / `image_urls`: 单个或多个图片直链
102
+ - `video_url` / `video_urls`: 单个或多个视频直链
103
+
104
+ 示例:
105
+ ```json
106
+ {
107
+ "workspace_id": 55508687,
108
+ "title": "支付回调失败",
109
+ "options": {
110
+ "description": "复现步骤见下方录屏",
111
+ "media": [
112
+ { "type": "image", "url": "https://example.com/screenshot.png", "alt": "错误截图" },
113
+ { "type": "video", "url": "https://example.com/demo.mp4" }
114
+ ]
115
+ }
116
+ }
117
+ ```
118
+
119
+ 注意:
120
+ - 图片/视频必须是浏览器可直接访问的直链
121
+ - 使用临时签名 URL 时,链接过期后 TAPD 中的媒体也会失效
@@ -0,0 +1,108 @@
1
+ # TAPD MCP Server
2
+
3
+ TAPD 是腾讯敏捷研发管理平台,覆盖需求、计划、研发、测试、发布研发全生命周期。支持用自然语言与 TAPD 对话,实现需求、缺陷、任务、迭代等管理。
4
+
5
+ * 与 TAPD API 无缝集成,提升开发效率
6
+
7
+ ## 系统要求
8
+
9
+ * uv
10
+ * TAPD Access Token(推荐)或 TAPD API 账号密码
11
+
12
+ ## 快速开始
13
+ ### Install uv
14
+ ```
15
+ brew install uv
16
+ # OR
17
+ curl -LsSf https://astral.sh/uv/install.sh | sh
18
+ ```
19
+
20
+ ### 参数
21
+ - TAPD_ACCESS_TOKEN: 个人令牌(推荐)
22
+ - TAPD_API_USER: API账号
23
+ - TAPD_API_PASSWORD: API密钥
24
+ - BOT_URL: 企业微信机器人 webhook 地址,如果需要发送消息到企业微信群才需要填(选填)
25
+
26
+ 注意 TAPD_API_USER/TAPD_API_PASSWORD(TAPD API 账号密码) 与 TAPD_ACCESS_TOKEN(TAPD 个人授权令牌)为两种调用 MCP 的凭据,选一种即可。
27
+
28
+
29
+ ### 获取 TAPD Access Token(推荐)
30
+ TAPD_ACCESS_TOKEN 为 TAPD 个人令牌,在 [我的设置-个人访问令牌](https://www.tapd.cn/personal_settings/index?tab=personal_token) 点“创建个人访问令牌”,令牌只显示一次,注意保存下来。
31
+
32
+ ### 获取 API 账号密码(兼容)
33
+
34
+ 1. 未注册,请前往 [注册](https://www.tapd.cn?from_partner=copilot&source=tapd_operation_copilot)
35
+ 2. 已注册但未授权API,请前往API配置:登录TAPD,点击进入 [公司管理-API账号管理](https://www.tapd.cn/open_platform/open_api_redirect?from_partner=copilot&source=tapd_operation_copilot),复制API账号和API密钥
36
+
37
+
38
+ ## IDE 配置
39
+ ### CodeBuddy / Claude Desktop / Cursor
40
+ #### 本地 stdio 运行,环境变量配置示例(推荐个人 Token)
41
+ ```json
42
+ {
43
+ "mcpServers": {
44
+ "mcp-server-tapd": {
45
+ "command": "uvx",
46
+ "args": ["--from", "zan-tapd-mcp", "mcp-server-tapd"],
47
+ "env": {
48
+ "TAPD_ACCESS_TOKEN": "您的个人Token",
49
+ // 兼容账号密码方式(如未配置 Token 时生效)
50
+ "TAPD_API_USER": "",
51
+ "TAPD_API_PASSWORD": "",
52
+ "TAPD_API_BASE_URL": "https://api.tapd.cn",
53
+ "TAPD_BASE_URL": "https://www.tapd.cn",
54
+ "BOT_URL": ""
55
+ }
56
+ }
57
+ }
58
+ }
59
+ ```
60
+
61
+
62
+ ### Streamable HTTP 配置
63
+ 使用 Streamable HTTP 替代 stdio
64
+ 1. 在终端启动已发布的服务
65
+ ```
66
+ uvx --from zan-tapd-mcp mcp-server-tapd --mode=streamable-http --host="0.0.0.0" --port=8000 --api-user=your_api_user --api-password=your_api_password --api-base-url=https://api.tapd.cn --tapd-base-url=https://www.tapd.cn --bot-url="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=XXX"
67
+
68
+ # 如果使用个人 Token,则将 --api-user=your_api_user --api-password=your_api_password 替换为 --access-token=您的个人Token
69
+
70
+ ```
71
+
72
+ 2. 通过如下配置连接到已启动的服务
73
+ ```json
74
+ {
75
+ "mcpServers": {
76
+ "tapd_mcp_http": {
77
+ "url": "http://localhost:8000/mcp/"
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ ## 富媒体描述
84
+ `create_bug`、`update_bug`、`create_story_or_task`、`update_story_or_task`、`create_comments`、`update_comments` 支持把外部图片或视频直链直接写进 TAPD 富文本描述/评论。
85
+
86
+ 可用参数:
87
+ - `media`: 结构化媒体列表,例如 `[{ "type": "image", "url": "https://example.com/a.png" }, { "type": "video", "url": "https://example.com/demo.mp4" }]`
88
+ - `image_url` / `image_urls`: 单个或多个图片直链
89
+ - `video_url` / `video_urls`: 单个或多个视频直链
90
+
91
+ 示例:
92
+ ```json
93
+ {
94
+ "workspace_id": 55508687,
95
+ "title": "支付回调失败",
96
+ "options": {
97
+ "description": "复现步骤见下方录屏",
98
+ "media": [
99
+ { "type": "image", "url": "https://example.com/screenshot.png", "alt": "错误截图" },
100
+ { "type": "video", "url": "https://example.com/demo.mp4" }
101
+ ]
102
+ }
103
+ }
104
+ ```
105
+
106
+ 注意:
107
+ - 图片/视频必须是浏览器可直接访问的直链
108
+ - 使用临时签名 URL 时,链接过期后 TAPD 中的媒体也会失效
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["setuptools>=45", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "zan-tapd-mcp"
7
+ version = "0.0.1"
8
+ description = "TAPD MCP Server"
9
+ authors = [{ name = "ryanjhzheng", email = "zhengjianhong95@gmail.com" }]
10
+ readme = "README.md"
11
+ requires-python = ">=3.13"
12
+ keywords = ["mcp", "tapd"]
13
+ dependencies = [
14
+ "httpx",
15
+ 'requests',
16
+ 'markdown',
17
+ "mcp[cli]>=1.0.0,<2",
18
+ ]
19
+
20
+ [project.scripts]
21
+ mcp-server-tapd = "mcp_server_tapd.server:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,14 @@
1
+ import asyncio
2
+
3
+ from . import server
4
+ from . import tapd
5
+
6
+ __version__ = "2.1.4"
7
+
8
+
9
+ def main():
10
+ """Main entry point for the package."""
11
+ asyncio.run(server.main())
12
+
13
+
14
+ __all__ = ["main", "server", "tapd", "__version__"]
@@ -0,0 +1,131 @@
1
+ import os
2
+ import argparse
3
+ from dotenv import load_dotenv
4
+ import configparser
5
+ from typing import Dict, Any, Optional
6
+
7
+
8
+ class AppConfig:
9
+ def __init__(self):
10
+ # 定义参数默认值和必填性
11
+ self.params_meta = {
12
+ "api_user": {"required": False, "type": str, "default": None},
13
+ "api_password": {"required": False, "type": str, "default": None},
14
+ "api_base_url": {"required": False, "type": str, "default": None},
15
+ "tapd_base_url": {"required": False, "type": str, "default": None},
16
+ "bot_url": {"required": False, "type": str, "default": None},
17
+ "mode": {"required": False, "type": str, "default": "stdio"},
18
+ "host": {"required": False, "type": str, "default": "0.0.0.0"},
19
+ "port": {"required": False, "type": int, "default": 8000},
20
+ "path": {"required": False, "type": str, "default": "/mcp"},
21
+ "access_token": {"required": False, "type": str, "default": None},
22
+ }
23
+ # 加载配置
24
+ self.config = self._load_all_configs()
25
+
26
+ def _load_cli_args(self) -> Dict[str, Any]:
27
+ """从命令行参数获取配置"""
28
+ parser = argparse.ArgumentParser()
29
+ parser.add_argument("--access-token", help="TAPD Access Token")
30
+ parser.add_argument("--api-user", help="API User")
31
+ parser.add_argument("--api-password", help="API Password")
32
+ parser.add_argument("--api-base-url", help="API BASE URL")
33
+ parser.add_argument("--tapd-base-url", help="TAPD BASE URL")
34
+ parser.add_argument("--bot-url", help="QiWei Bot URL")
35
+ parser.add_argument(
36
+ "--mode",
37
+ help="server mode(stdio/sse/http(alias for sse)/streamable-http)",
38
+ )
39
+ parser.add_argument("--host", help="Host")
40
+ parser.add_argument("--port", help="Port")
41
+ parser.add_argument("--path", help="Path")
42
+ args, _ = parser.parse_known_args()
43
+
44
+ return {
45
+ "access_token": args.access_token,
46
+ "api_user": args.api_user,
47
+ "api_password": args.api_password,
48
+ "api_base_url": args.api_base_url,
49
+ "tapd_base_url": args.tapd_base_url,
50
+ "bot_url": args.bot_url,
51
+ "mode": args.mode,
52
+ "host": args.host,
53
+ "port": args.port,
54
+ "path": args.path,
55
+ }
56
+
57
+ def _load_env_vars(self) -> Dict[str, Any]:
58
+ """从环境变量获取配置"""
59
+ return {
60
+ "access_token": os.getenv("TAPD_ACCESS_TOKEN"),
61
+ "api_user": os.getenv("TAPD_API_USER"),
62
+ "api_password": os.getenv("TAPD_API_PASSWORD"),
63
+ "api_base_url": os.getenv("TAPD_API_BASE_URL"),
64
+ "tapd_base_url": os.getenv("TAPD_BASE_URL"),
65
+ "bot_url": os.getenv("BOT_URL"),
66
+ # "mode": os.getenv("MODE"),
67
+ }
68
+
69
+ def _load_dotenv(self) -> Dict[str, Any]:
70
+ """从 .env 文件获取配置"""
71
+ load_dotenv()
72
+ return self._load_env_vars() # 复用环境变量加载逻辑
73
+
74
+ def _merge_configs(self, *config_sources) -> Dict[str, Any]:
75
+ """按优先级合并配置源"""
76
+ merged = {}
77
+ for source in config_sources:
78
+ for param, value in source.items():
79
+ if value is not None and merged.get(param) is None:
80
+ merged[param] = value
81
+ return merged
82
+
83
+ def _type_cast(self, raw_config: Dict[str, Any]) -> Dict[str, Any]:
84
+ """类型转换和验证"""
85
+ processed = {}
86
+ for param, meta in self.params_meta.items():
87
+ raw_value = raw_config.get(param)
88
+
89
+ # 应用默认值(当未配置且允许默认值时)
90
+ if raw_value is None and not meta["required"]:
91
+ processed[param] = meta["default"]
92
+ continue
93
+
94
+ # 必填参数校验
95
+ if meta["required"] and raw_value is None:
96
+ raise ValueError(f"参数 {param} 为必填项!")
97
+
98
+ # 类型转换
99
+ try:
100
+ if meta["type"] == bool:
101
+ # 处理布尔类型(支持 true/1/yes)
102
+ processed[param] = str(raw_value).lower() in ("true", "1", "yes")
103
+ else:
104
+ processed[param] = meta["type"](raw_value)
105
+ except (ValueError, TypeError):
106
+ raise TypeError(f"参数 {param} 类型错误,期望 {meta['type'].__name__}")
107
+
108
+ return processed
109
+
110
+ def _load_all_configs(self) -> Dict[str, Any]:
111
+ """加载并合并所有配置源"""
112
+ # 按优先级加载配置(从高到低)
113
+ cli_config = self._load_cli_args()
114
+ env_config = self._load_env_vars()
115
+ dotenv_config = self._load_dotenv()
116
+
117
+ # 合并配置
118
+ merged_raw = self._merge_configs(cli_config, env_config, dotenv_config)
119
+
120
+ # 类型转换和验证
121
+ return self._type_cast(merged_raw)
122
+
123
+ def get(self, param_name: str) -> Optional[Any]:
124
+ """获取配置项"""
125
+ return self.config.get(param_name)
126
+
127
+ def __getattr__(self, name: str) -> Any:
128
+ """支持属性式访问(config.api_password)"""
129
+ if name in self.config:
130
+ return self.config[name]
131
+ raise AttributeError(f"未找到配置项: {name}")