iflow-mcp_galaxyxieyu_api-auto-test 0.1.0__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.
Files changed (52) hide show
  1. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/.github/workflows/publish.yml +38 -0
  2. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/.github/workflows/tests.yml +41 -0
  3. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/.gitignore +48 -0
  4. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/PKG-INFO +409 -0
  5. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/README.md +381 -0
  6. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/README_CN.md +382 -0
  7. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/__init__.py +48 -0
  8. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/assets/__init__.py +0 -0
  9. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/assets/report.css +243 -0
  10. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/auth.py +99 -0
  11. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/case_generator.py +737 -0
  12. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/conftest.py +65 -0
  13. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/core/__init__.py +40 -0
  14. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/core/assert_handler.py +336 -0
  15. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/core/config_manager.py +111 -0
  16. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/core/globals.py +52 -0
  17. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/core/log_manager.py +52 -0
  18. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/core/login_handler.py +60 -0
  19. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/core/request_handler.py +189 -0
  20. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/core/variable_resolver.py +212 -0
  21. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/handlers/__init__.py +10 -0
  22. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/handlers/notification_handler.py +101 -0
  23. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/handlers/report_generator.py +160 -0
  24. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/handlers/teardown_handler.py +106 -0
  25. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/mcp/__init__.py +1 -0
  26. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/mcp/executor.py +469 -0
  27. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/mcp/models.py +532 -0
  28. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/mcp/tools/__init__.py +1 -0
  29. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/mcp/tools/health_tool.py +58 -0
  30. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/mcp/tools/metrics_tools.py +132 -0
  31. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/mcp/tools/runner_tools.py +380 -0
  32. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/mcp/tools/testcase_tools.py +603 -0
  33. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/mcp/tools/unittest_tools.py +189 -0
  34. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/mcp/utils.py +376 -0
  35. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/mcp_server.py +169 -0
  36. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/runner.py +134 -0
  37. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/unit_case_generator.py +337 -0
  38. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/utils/__init__.py +2 -0
  39. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/atf/utils/helpers.py +155 -0
  40. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/bin_key +1 -0
  41. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/configs/mcp.env.example +17 -0
  42. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/docs/images/mcp-architecture.png +0 -0
  43. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/docs/mcp_dev_guide.md +183 -0
  44. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/docs/mcp_ops_config.md +99 -0
  45. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/docs/mcp_spec.md +268 -0
  46. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/language.json +1 -0
  47. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/package_name +1 -0
  48. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/push_info.json +5 -0
  49. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/pyproject.toml +46 -0
  50. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/requirements-mcp.txt +1 -0
  51. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/requirements.txt +9 -0
  52. iflow_mcp_galaxyxieyu_api_auto_test-0.1.0/uv.lock +1268 -0
@@ -0,0 +1,38 @@
1
+ name: Build and Publish to GitHub Packages
2
+
3
+ on:
4
+ release:
5
+ types: [created]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ contents: read
12
+ packages: write
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Set up Python
17
+ uses: actions/setup-python@v5
18
+ with:
19
+ python-version: '3.11'
20
+
21
+ - name: Install uv
22
+ uses: astral-sh/setup-uv@v4
23
+
24
+ - name: Install dependencies
25
+ run: uv pip install --system build twine
26
+
27
+ - name: Build package
28
+ run: python -m build
29
+
30
+ - name: Publish to GitHub Packages
31
+ env:
32
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33
+ run: |
34
+ python -m twine upload \
35
+ --repository-url https://pypi.pkg.github.com/ \
36
+ dist/* \
37
+ --username __token__ \
38
+ --password $GITHUB_TOKEN
@@ -0,0 +1,41 @@
1
+ name: API Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+ branches: [main, master]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ contents: read
14
+ packages: read
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: '3.11'
23
+
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@v4
26
+
27
+ - name: Install dependencies
28
+ run: uv pip install --system -e .
29
+
30
+ - name: Install api-auto-test from GitHub Packages (optional)
31
+ if: ${{ github.event_name == 'pull_request' }}
32
+ run: |
33
+ echo "Installing from GitHub Packages..."
34
+ pip install --index-url https://pypi.pkg.github.com/ --extra-index-url https://pypi.org/simple/ api-auto-test
35
+
36
+ - name: Run tests
37
+ run: |
38
+ pytest tests/ -v --tb=short
39
+ env:
40
+ API_AUTO_TEST_HOST: ${{ secrets.API_AUTO_TEST_HOST }}
41
+ API_AUTO_TEST_DB_HOST: ${{ secrets.API_AUTO_TEST_DB_HOST }}
@@ -0,0 +1,48 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+
11
+ # IDE
12
+ .vscode/
13
+ .idea/
14
+ *.swp
15
+ *.swo
16
+ *~
17
+
18
+ # OS
19
+ .DS_Store
20
+ Thumbs.db
21
+
22
+ # Logs
23
+ logs/
24
+ *.log
25
+
26
+ # Test reports
27
+ html-reports/
28
+ allure-results/
29
+ .pytest_cache/
30
+ .coverage
31
+
32
+ # Environment
33
+ .env
34
+ .venv
35
+ venv/
36
+ ENV/
37
+
38
+ # Temporary files
39
+ *.tmp
40
+ *.bak
41
+ pip
42
+
43
+
44
+
45
+
46
+
47
+
48
+
@@ -0,0 +1,409 @@
1
+ Metadata-Version: 2.4
2
+ Name: iflow-mcp_galaxyxieyu_api-auto-test
3
+ Version: 0.1.0
4
+ Summary: API Auto Test Framework with MCP Server
5
+ Author-email: GalaxyXieyu <galaxyxieyu@github.com>
6
+ License: MIT
7
+ Keywords: api,automation,mcp,pytest,testing
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Python: >=3.10
16
+ Requires-Dist: allure-pytest>=2.13
17
+ Requires-Dist: dingtalkchatbot>=1.5.7
18
+ Requires-Dist: loguru>=0.4.1
19
+ Requires-Dist: mcp>=1.0.0
20
+ Requires-Dist: mysql-connector-python>=8.0.33
21
+ Requires-Dist: pydantic>=2.0
22
+ Requires-Dist: pytest-html>=2.1.1
23
+ Requires-Dist: pytest>=7.0
24
+ Requires-Dist: pyyaml>=6.0
25
+ Requires-Dist: requests>=2.31
26
+ Requires-Dist: urllib3<2
27
+ Description-Content-Type: text/markdown
28
+
29
+ <div align="center">
30
+ <img src="docs/images/mcp-architecture.png" alt="MCP Architecture" width="100%"/>
31
+ </div>
32
+
33
+ # API Auto Test Framework
34
+
35
+ ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
36
+ ![License](https://img.shields.io/badge/license-MIT-green)
37
+ ![pytest](https://img.shields.io/badge/pytest-8.0%2B-yellow)
38
+
39
+ **YAML Declarative API Testing Framework, Optimized for AI Coding Assistants**
40
+
41
+ [Quick Start](#quick-start) | [MCP Integration](#mcp-server-integration) | [YAML Spec](#yaml-test-case-spec) | [Unit Testing](#unit-testing)
42
+
43
+ ---
44
+
45
+ ## Why This Framework?
46
+
47
+ When asking AI to write API tests, you might encounter these issues:
48
+
49
+ **Scenario 1: Repetitive Work**
50
+
51
+ Every time you ask AI to generate tests, you need to re-describe the project structure, authentication method, and assertion style. For 10 API tests, the same fixture and setup code gets generated 10 times.
52
+
53
+ **Scenario 2: Token Black Hole**
54
+
55
+ A simple login API test generates 200 lines of code. You find an assertion is wrong, ask AI to fix it, and it generates another 200 lines. After 3 revisions, you've consumed 2000+ Tokens, and you still end up fixing it manually.
56
+
57
+ **Scenario 3: Debugging Dead Loop**
58
+
59
+ AI-generated tests fail to run. You paste the error message, AI fixes it but still wrong. After 5 rounds of conversation, the problem persists, and you've burned 5000+ Tokens.
60
+
61
+ **This Framework's Solution:**
62
+
63
+ ```
64
+ Traditional: Natural Language -> AI Generates Full Code -> Run Error -> Paste Error -> AI Regenerates -> Loop...
65
+ This Framework: Natural Language -> AI Generates YAML -> Framework Executes -> Locate Issue -> Fix 1 Line YAML
66
+ ```
67
+
68
+ | Metric | Traditional AI | This Framework |
69
+ |--------|---------------|----------------|
70
+ | Test 1 API | ~200 lines code | ~20 lines YAML |
71
+ | Modify Assertion | Regenerate all code | Fix 1-2 lines YAML |
72
+ | 10 API Tests | Repeat setup 10x | Shared config, 0 repeat |
73
+ | Debug Issue | 3-5 rounds avg | Usually 1 round |
74
+
75
+ ---
76
+
77
+ ## Key Features
78
+
79
+ | Feature | Description |
80
+ |---------|-------------|
81
+ | **YAML Declarative Tests** | Test logic separated from execution code, AI generates structured data only |
82
+ | **MCP Server** | Seamless integration with Claude/Cursor and other AI editors |
83
+ | **API Workflow Orchestration** | Multi-step API calls in single file, with data passing and assertions between steps |
84
+ | **Variable Resolution Engine** | Support for cross-step data transfer, global variables, and dynamic function calls |
85
+ | **Auto Authentication** | Token acquisition and refresh handled by framework |
86
+ | **Data Factory** | Built-in mock data generation, no Java dependencies |
87
+ | **Multi-format Reports** | Allure (offline/online), pytest-html (standalone HTML, styled) |
88
+ | **Multi-channel Notifications** | DingTalk, Feishu, WeCom |
89
+ | **Unit Testing** | Python code unit testing with automatic mock dependency injection |
90
+
91
+ ---
92
+
93
+ ## Quick Start
94
+
95
+ ### Installation
96
+
97
+ ```bash
98
+ # 1. Install uv (if not installed)
99
+ curl -LsSf https://astral.sh/uv/install.sh | sh
100
+
101
+ # 2. Install MCP server (recommended: install as a tool)
102
+ uv tool install git+https://github.com/GalaxyXieyu/Api-Test-MCP.git
103
+
104
+ # Verify
105
+ api-auto-test-mcp --help
106
+
107
+ # Manage tools
108
+ uv tool list
109
+ uv tool uninstall api-auto-test # use the tool name shown by `uv tool list`
110
+ ```
111
+
112
+ **Run without installing (uvx):**
113
+
114
+ ```bash
115
+ uvx --from git+https://github.com/GalaxyXieyu/Api-Test-MCP.git api-auto-test-mcp --help
116
+ ```
117
+
118
+ **Common mistake:** `uvx install ...` is **wrong**. `uvx` treats the first word after it as the tool name, so it will try to resolve a package literally named `install` and fail.
119
+
120
+ ### Configure Editor
121
+
122
+ Add the following to your editor's MCP settings:
123
+
124
+ ```json
125
+ {
126
+ "mcpServers": {
127
+ "api-auto-test": {
128
+ "command": "api-auto-test-mcp"
129
+ }
130
+ }
131
+ }
132
+ ```
133
+
134
+ | Editor | Config Location |
135
+ |--------|-----------------|
136
+ | Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` |
137
+ | Cursor | Settings -> MCP Servers |
138
+ | VSCode + Continue | `.vscode/mcp.json` |
139
+
140
+ ### Local Development
141
+
142
+ ```bash
143
+ # Recommended with uv
144
+ uv pip install -r requirements.txt
145
+
146
+ # Or with pip
147
+ pip install -r requirements.txt
148
+ ```
149
+
150
+ ### Create Test Case
151
+
152
+ ```yaml
153
+ # tests/cases/user_login.yaml
154
+ testcase:
155
+ name: user_login
156
+ description: User login API test
157
+ host: http://localhost:8000
158
+ steps:
159
+ - id: login
160
+ path: /api/auth/login
161
+ method: POST
162
+ data:
163
+ username: "test_user"
164
+ password: "123456"
165
+ assert:
166
+ - type: status_code
167
+ expected: 200
168
+ - type: equals
169
+ field: data.code
170
+ expected: 0
171
+ ```
172
+
173
+ ### Generate and Run
174
+
175
+ ```bash
176
+ # Generate pytest scripts
177
+ python -m atf.case_generator
178
+
179
+ # Run tests
180
+ pytest tests/scripts/ -v
181
+
182
+ # Generate Allure report
183
+ pytest tests/scripts/ --alluredir=tests/allure-results
184
+ allure serve tests/allure-results
185
+
186
+ # Generate pytest-html report
187
+ pytest tests/scripts/ --html=report.html
188
+ ```
189
+
190
+ ---
191
+
192
+ ## MCP Server Integration
193
+
194
+ Through MCP, AI editors can directly call framework tools to generate and execute tests.
195
+
196
+ ### Efficiency Comparison
197
+
198
+ | Metric | Without MCP | With MCP | Improvement |
199
+ |--------|-------------|----------|-------------|
200
+ | Total Cost | $0.0214 | $0.0099 | **-54%** |
201
+ | API Latency | 11 sec | 4 sec | **-64%** |
202
+ | Output Tokens | 585 | 238 | **-59%** |
203
+ | Cache Read | 42.0k | 21.0k | **-50%** |
204
+
205
+ **Test Scenario**: Same API test generation task (pure consultation/analysis conversation)
206
+
207
+ **Core Advantages**:
208
+ - **54% cost reduction**: MCP directly calls tools, avoiding lengthy code generation context
209
+ - **64% faster API response**: Tool calls are more efficient than natural language interaction
210
+ - **59% less token consumption**: Only necessary parameters needed, no need to repeat project structure
211
+
212
+ ### Available Tools
213
+
214
+ | Tool | Description |
215
+ |------|-------------|
216
+ | `list_testcases` | List test cases |
217
+ | `get_testcase` | Read test case content |
218
+ | `write_testcase` | Create/update test case and generate pytest script |
219
+ | `write_unittest` | Create unit test |
220
+ | `delete_testcase` | Delete test case |
221
+ | `run_tests` | Execute tests |
222
+ | `get_test_results` | Get test execution history |
223
+ | `health_check` | Service health check |
224
+
225
+ ### Usage Example
226
+
227
+ Tell AI:
228
+
229
+ ```
230
+ Create a test for /api/users interface, verify returned user list length > 0
231
+ ```
232
+
233
+ AI will call `write_testcase` to generate YAML and corresponding pytest script.
234
+
235
+ ---
236
+
237
+ ## Project Structure
238
+
239
+ ```
240
+ api-auto-test/
241
+ ├── atf/ # Framework core
242
+ │ ├── core/ # Request, assertion, variable resolution modules
243
+ │ ├── mcp/ # MCP Server implementation
244
+ │ └── handlers/ # Notification, report handlers
245
+ ├── tests/
246
+ │ ├── cases/ # YAML test cases
247
+ │ └── scripts/ # Generated pytest scripts
248
+ ├── config.yaml # Project config (environment, database, notifications)
249
+ └── pyproject.toml
250
+ ```
251
+
252
+ ---
253
+
254
+ ## YAML Test Case Spec
255
+
256
+ ### Basic Structure
257
+
258
+ ```yaml
259
+ testcase:
260
+ name: test_name # Case name, used for filename
261
+ description: Description # Optional
262
+ host: http://localhost:8000 # API host, can also be configured globally in config.yaml
263
+ steps:
264
+ - id: step1 # Step ID, used for later reference
265
+ path: /api/endpoint
266
+ method: POST
267
+ headers:
268
+ Authorization: "Bearer {{ login.data.token }}" # Reference response from other step
269
+ data:
270
+ key: value
271
+ assert:
272
+ - type: status_code
273
+ expected: 200
274
+ - type: equals
275
+ field: data.id
276
+ expected: 1
277
+ ```
278
+
279
+ ### Assertion Types
280
+
281
+ | Type | Description | Example |
282
+ |------|-------------|---------|
283
+ | `status_code` | HTTP status code | `expected: 200` |
284
+ | `equals` | Exact match | `field: data.id, expected: 1` |
285
+ | `contains` | Contains | `field: data.name, expected: "test"` |
286
+ | `length` | Array/string length | `field: data.list, expected: 10` |
287
+ | `regex` | Regex match | `field: data.email, expected: "^\\w+@"` |
288
+
289
+ ### Variable Reference
290
+
291
+ ```yaml
292
+ # Reference response data from other steps
293
+ token: "{{ login.data.token }}"
294
+
295
+ # Reference global config
296
+ host: "{{ merchant.host }}"
297
+
298
+ # Call built-in functions
299
+ timestamp: "{{ tools.get_timestamp() }}"
300
+ uuid: "{{ tools.generate_uuid() }}"
301
+ ```
302
+
303
+ ### Teardown
304
+
305
+ ```yaml
306
+ testcase:
307
+ name: create_and_delete_user
308
+ steps:
309
+ - id: create_user
310
+ path: /api/users
311
+ method: POST
312
+ data:
313
+ name: "test_user"
314
+ teardowns:
315
+ - id: delete_user
316
+ operation_type: api
317
+ path: /api/users/{{ create_user.data.id }}
318
+ method: DELETE
319
+ ```
320
+
321
+ ---
322
+
323
+ ## Unit Testing
324
+
325
+ Support for writing unit tests for Python code, automatically generating test cases through MCP tools.
326
+
327
+ ### Unit Test YAML Format
328
+
329
+ ```yaml
330
+ unittest:
331
+ name: UserService Test
332
+ target:
333
+ module: app.services.user_service
334
+ class: UserService
335
+ function: get_user
336
+ fixtures:
337
+ setup:
338
+ - type: patch
339
+ target: app.services.user_service.UserRepository
340
+ return_value:
341
+ id: 1
342
+ name: "test_user"
343
+ cases:
344
+ - id: test_get_user_success
345
+ description: Test get user success
346
+ inputs:
347
+ args: [1]
348
+ kwargs: {}
349
+ assert:
350
+ - type: equals
351
+ field: result.id
352
+ expected: 1
353
+ - type: equals
354
+ field: result.name
355
+ expected: "test_user"
356
+ ```
357
+
358
+ ### Assertion Types
359
+
360
+ | Type | Description |
361
+ |------|-------------|
362
+ | `equals` | Exact match |
363
+ | `not_equals` | Not equal |
364
+ | `contains` | Contains |
365
+ | `raises` | Expect exception to be raised |
366
+ | `is_none` | Result is None |
367
+ | `is_not_none` | Result is not None |
368
+ | `called_once` | Mock called once |
369
+ | `called_with` | Mock called with specific arguments |
370
+
371
+ ---
372
+
373
+ ## Configuration File
374
+
375
+ ```yaml
376
+ # config.yaml
377
+ projects:
378
+ merchant:
379
+ test:
380
+ host: http://192.168.1.100:8080
381
+ is_need_login: true
382
+ login:
383
+ url: http://192.168.1.100:8080/login
384
+ method: POST
385
+ data:
386
+ username: admin
387
+ password: "123456"
388
+ online:
389
+ host: https://api.example.com
390
+ is_need_login: true
391
+
392
+ notifications:
393
+ dingtalk:
394
+ webhook: "https://oapi.dingtalk.com/robot/send?access_token=xxx"
395
+ secret: "SECxxx"
396
+ ```
397
+
398
+ ---
399
+
400
+ ## License
401
+
402
+ MIT License
403
+
404
+ ---
405
+
406
+ ## Links
407
+
408
+ - [GitHub](https://github.com/GalaxyXieyu/Api-Test-MCP)
409
+ - [Issue Report](https://github.com/GalaxyXieyu/Api-Test-MCP/issues)