mcp-testkit 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.
- mcp_testkit-0.1.0/.github/workflows/ci.yml +111 -0
- mcp_testkit-0.1.0/.gitignore +6 -0
- mcp_testkit-0.1.0/AGENTS.md +148 -0
- mcp_testkit-0.1.0/LICENSE +21 -0
- mcp_testkit-0.1.0/PKG-INFO +259 -0
- mcp_testkit-0.1.0/README.md +237 -0
- mcp_testkit-0.1.0/docs/superpowers/plans/2026-04-05-mcp-test-server.md +1730 -0
- mcp_testkit-0.1.0/docs/superpowers/specs/2026-04-05-mcp-test-server-design.md +170 -0
- mcp_testkit-0.1.0/mcp_test_server/__init__.py +3 -0
- mcp_testkit-0.1.0/mcp_test_server/api.py +659 -0
- mcp_testkit-0.1.0/mcp_test_server/server.py +83 -0
- mcp_testkit-0.1.0/mcp_test_server/tools/__init__.py +29 -0
- mcp_testkit-0.1.0/mcp_test_server/tools/collection_tools.py +144 -0
- mcp_testkit-0.1.0/mcp_test_server/tools/conversion_tools.py +75 -0
- mcp_testkit-0.1.0/mcp_test_server/tools/datetime_tools.py +101 -0
- mcp_testkit-0.1.0/mcp_test_server/tools/echo_tools.py +95 -0
- mcp_testkit-0.1.0/mcp_test_server/tools/encoding_tools.py +63 -0
- mcp_testkit-0.1.0/mcp_test_server/tools/math_tools.py +61 -0
- mcp_testkit-0.1.0/mcp_test_server/tools/string_tools.py +45 -0
- mcp_testkit-0.1.0/mcp_test_server/tools/validation_tools.py +80 -0
- mcp_testkit-0.1.0/pyproject.toml +45 -0
- mcp_testkit-0.1.0/tests/__init__.py +0 -0
- mcp_testkit-0.1.0/tests/test_api.py +244 -0
- mcp_testkit-0.1.0/tests/test_collection_tools.py +365 -0
- mcp_testkit-0.1.0/tests/test_conversion_tools.py +255 -0
- mcp_testkit-0.1.0/tests/test_datetime_tools.py +301 -0
- mcp_testkit-0.1.0/tests/test_echo_tools.py +313 -0
- mcp_testkit-0.1.0/tests/test_encoding_tools.py +228 -0
- mcp_testkit-0.1.0/tests/test_integration.py +86 -0
- mcp_testkit-0.1.0/tests/test_math_tools.py +233 -0
- mcp_testkit-0.1.0/tests/test_string_tools.py +247 -0
- mcp_testkit-0.1.0/tests/test_validation_tools.py +283 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install package
|
|
25
|
+
run: pip install -e ".[dev]" 2>/dev/null || pip install -e . && pip install pytest
|
|
26
|
+
|
|
27
|
+
- name: Run tests
|
|
28
|
+
run: python -m pytest tests/ -v --tb=short
|
|
29
|
+
|
|
30
|
+
lint:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
|
|
35
|
+
- name: Set up Python
|
|
36
|
+
uses: actions/setup-python@v5
|
|
37
|
+
with:
|
|
38
|
+
python-version: "3.12"
|
|
39
|
+
|
|
40
|
+
- name: Install
|
|
41
|
+
run: pip install -e . ruff
|
|
42
|
+
|
|
43
|
+
- name: Check formatting
|
|
44
|
+
run: ruff format --check .
|
|
45
|
+
|
|
46
|
+
- name: Check linting
|
|
47
|
+
run: ruff check .
|
|
48
|
+
|
|
49
|
+
integration:
|
|
50
|
+
runs-on: ubuntu-latest
|
|
51
|
+
needs: test
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v4
|
|
54
|
+
|
|
55
|
+
- name: Set up Python
|
|
56
|
+
uses: actions/setup-python@v5
|
|
57
|
+
with:
|
|
58
|
+
python-version: "3.12"
|
|
59
|
+
|
|
60
|
+
- name: Install package
|
|
61
|
+
run: pip install -e . pytest
|
|
62
|
+
|
|
63
|
+
- name: Verify CLI entry point
|
|
64
|
+
run: mcp-test-server --help
|
|
65
|
+
|
|
66
|
+
- name: Verify SSE server starts and responds
|
|
67
|
+
run: |
|
|
68
|
+
mcp-test-server --transport sse --port 3001 &
|
|
69
|
+
sleep 3
|
|
70
|
+
curl -sf http://127.0.0.1:3001/api-docs | python -c "import sys,json; d=json.load(sys.stdin); assert d['openapi']=='3.0.3'; assert len(d['paths'])==65"
|
|
71
|
+
curl -sf "http://127.0.0.1:3001/api/math/add?a=2&b=3" | python -c "import sys,json; d=json.load(sys.stdin); assert d['result']==5.0"
|
|
72
|
+
curl -sf -X POST http://127.0.0.1:3001/api/string/reverse -H 'Content-Type: application/json' -d '{"text":"hello"}' | python -c "import sys,json; d=json.load(sys.stdin); assert d['result']=='olleh'"
|
|
73
|
+
kill %1
|
|
74
|
+
|
|
75
|
+
- name: Verify auth mode
|
|
76
|
+
run: |
|
|
77
|
+
mcp-test-server --transport sse --port 3002 --auth testkey &
|
|
78
|
+
sleep 3
|
|
79
|
+
STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:3002/api/math/add?a=1\&b=2)
|
|
80
|
+
[ "$STATUS" = "401" ] || (echo "Expected 401, got $STATUS" && exit 1)
|
|
81
|
+
curl -sf -H "Authorization: Bearer testkey" "http://127.0.0.1:3002/api/math/add?a=1&b=2" | python -c "import sys,json; d=json.load(sys.stdin); assert d['result']==3.0"
|
|
82
|
+
kill %1
|
|
83
|
+
|
|
84
|
+
build:
|
|
85
|
+
runs-on: ubuntu-latest
|
|
86
|
+
needs: [test, lint]
|
|
87
|
+
steps:
|
|
88
|
+
- uses: actions/checkout@v4
|
|
89
|
+
|
|
90
|
+
- name: Set up Python
|
|
91
|
+
uses: actions/setup-python@v5
|
|
92
|
+
with:
|
|
93
|
+
python-version: "3.12"
|
|
94
|
+
|
|
95
|
+
- name: Install build tools
|
|
96
|
+
run: pip install build
|
|
97
|
+
|
|
98
|
+
- name: Build sdist and wheel
|
|
99
|
+
run: python -m build
|
|
100
|
+
|
|
101
|
+
- name: Verify wheel contents
|
|
102
|
+
run: |
|
|
103
|
+
pip install dist/*.whl
|
|
104
|
+
mcp-test-server --help
|
|
105
|
+
python -c "from mcp_test_server import __version__; print(f'Version: {__version__}')"
|
|
106
|
+
|
|
107
|
+
- name: Upload artifacts
|
|
108
|
+
uses: actions/upload-artifact@v4
|
|
109
|
+
with:
|
|
110
|
+
name: dist
|
|
111
|
+
path: dist/
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Agent Contributing Guide
|
|
2
|
+
|
|
3
|
+
Instructions for AI agents contributing to this project.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
This is an MCP test server with 65 deterministic tools across 8 groups, exposed via MCP (stdio/SSE) and REST API. Every tool returns identical output for identical input — no randomness, no `datetime.now()`, no external state.
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
mcp_test_server/
|
|
13
|
+
server.py → Entry point. Creates FastMCP instance, registers tools, starts transport.
|
|
14
|
+
api.py → REST API. Data-driven endpoint registry generates Starlette routes + OpenAPI spec.
|
|
15
|
+
tools/*.py → Tool modules. Each exports register(mcp) that decorates tools with @mcp.tool().
|
|
16
|
+
tests/*.py → Unit + integration tests using unittest + FastMCP's call_tool().
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Key pattern:** Tools are registered as closures inside `register(mcp)` via `@mcp.tool()` decorators. Helper functions live at module level.
|
|
20
|
+
|
|
21
|
+
## How to Add a New Tool
|
|
22
|
+
|
|
23
|
+
### 1. Choose the right module
|
|
24
|
+
|
|
25
|
+
| Group | File | When to use |
|
|
26
|
+
|-------|------|-------------|
|
|
27
|
+
| math | `mcp_test_server/tools/math_tools.py` | Numeric computation |
|
|
28
|
+
| string | `mcp_test_server/tools/string_tools.py` | String manipulation |
|
|
29
|
+
| collection | `mcp_test_server/tools/collection_tools.py` | Array/dict operations |
|
|
30
|
+
| encoding | `mcp_test_server/tools/encoding_tools.py` | Encode/decode/hash |
|
|
31
|
+
| datetime | `mcp_test_server/tools/datetime_tools.py` | Date computations (no current time!) |
|
|
32
|
+
| validation | `mcp_test_server/tools/validation_tools.py` | Input checking (returns `{valid, reason}`) |
|
|
33
|
+
| conversion | `mcp_test_server/tools/conversion_tools.py` | Unit/format conversions |
|
|
34
|
+
| echo | `mcp_test_server/tools/echo_tools.py` | Protocol testing, fixtures, misc |
|
|
35
|
+
|
|
36
|
+
If a tool doesn't fit any group, add it to `mcp_test_server/tools/echo_tools.py` (the catch-all for test fixtures).
|
|
37
|
+
|
|
38
|
+
### 2. Implement the tool
|
|
39
|
+
|
|
40
|
+
Add it inside the `register(mcp)` function in the chosen module:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
@mcp.tool()
|
|
44
|
+
def my_new_tool(param: str, count: int) -> str:
|
|
45
|
+
"""Clear one-line description of what the tool does."""
|
|
46
|
+
result = param * count
|
|
47
|
+
return json.dumps({"result": result})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Rules:
|
|
51
|
+
- **Return JSON strings** via `json.dumps({"result": ...})` for success
|
|
52
|
+
- **Return error objects** via `json.dumps({"error": "message"})` for expected errors
|
|
53
|
+
- **Raise `ToolError`** only for tools that intentionally test error handling
|
|
54
|
+
- **Be deterministic** — no randomness, no current time, no network calls, no file I/O
|
|
55
|
+
- **Use stdlib only** — no imports beyond Python stdlib + `mcp` + `requests`
|
|
56
|
+
- **Type-annotate all parameters** — FastMCP uses these to generate JSON schemas
|
|
57
|
+
- Validation tools return `json.dumps({"valid": bool, "reason": str})`
|
|
58
|
+
|
|
59
|
+
### 3. Add the REST API endpoint
|
|
60
|
+
|
|
61
|
+
In `mcp_test_server/api.py`, add an entry to the `ENDPOINTS` list:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
# For GET (simple scalar params):
|
|
65
|
+
("/api/group/my-tool", "GET", "my_new_tool",
|
|
66
|
+
"Description for OpenAPI",
|
|
67
|
+
[("param", "string", True, "Param description"),
|
|
68
|
+
("count", "integer", True, "Count description")]),
|
|
69
|
+
|
|
70
|
+
# For POST (complex or text-heavy params):
|
|
71
|
+
("/api/group/my-tool", "POST", "my_new_tool",
|
|
72
|
+
"Description for OpenAPI",
|
|
73
|
+
[("param", "string", True, "Param description"),
|
|
74
|
+
("count", "integer", True, "Count description")]),
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The tuple format is: `(path, method, tool_name, summary, params)` where params is `[(name, json_schema_type, required, description), ...]`.
|
|
78
|
+
|
|
79
|
+
Use GET for simple scalar inputs. Use POST for text bodies, arrays, objects, or anything complex.
|
|
80
|
+
|
|
81
|
+
The route handler and OpenAPI spec are generated automatically from this entry.
|
|
82
|
+
|
|
83
|
+
### 4. Write tests
|
|
84
|
+
|
|
85
|
+
Add tests in the corresponding `tests/test_<group>_tools.py`:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
def test_my_new_tool(self):
|
|
89
|
+
result = _call(self.mcp, "my_new_tool", {"param": "abc", "count": 3})
|
|
90
|
+
self.assertEqual(result, {"result": "abcabcabc"})
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Also add an API test in `tests/test_api.py`:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
def test_my_new_tool(self):
|
|
97
|
+
r = self.client.post("/api/group/my-tool", json={"param": "abc", "count": 3})
|
|
98
|
+
self.assertEqual(r.json(), {"result": "abcabcabc"})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 5. Update integration test
|
|
102
|
+
|
|
103
|
+
In `tests/test_integration.py`, update the expected tool count in `test_all_65_tools_registered` and adjust group counts if applicable.
|
|
104
|
+
|
|
105
|
+
### 6. Verify
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
python3 -m pytest tests/ -v
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
All tests must pass. Current count: 389 tests.
|
|
112
|
+
|
|
113
|
+
## How to Add a New Tool Group
|
|
114
|
+
|
|
115
|
+
1. Create `mcp_test_server/tools/new_group_tools.py` with a `register(mcp)` function
|
|
116
|
+
2. Import it in `mcp_test_server/tools/__init__.py` and add to `ALL_GROUPS`
|
|
117
|
+
3. Add REST endpoints in `mcp_test_server/api.py` `ENDPOINTS` list
|
|
118
|
+
4. Create `tests/test_new_group_tools.py`
|
|
119
|
+
5. Update `tests/test_integration.py` tool counts and prefix checks
|
|
120
|
+
|
|
121
|
+
## Conventions
|
|
122
|
+
|
|
123
|
+
- **Tool names**: `group_action` (e.g., `math_add`, `string_reverse`)
|
|
124
|
+
- **API paths**: `/api/group/action-name` with kebab-case (e.g., `/api/math/add`, `/api/string/char-count`)
|
|
125
|
+
- **Responses**: Always JSON. Success: `{"result": ...}`. Error: `{"error": "message"}`. Validation: `{"valid": bool, "reason": str}`.
|
|
126
|
+
- **No external state**: Tools must be pure functions of their inputs
|
|
127
|
+
- **Test helpers**: Use `_make_server()` and `_call(mcp, name, args)` pattern (see existing tests)
|
|
128
|
+
|
|
129
|
+
## Running the Server
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# stdio (for MCP client testing)
|
|
133
|
+
mcp-test-server
|
|
134
|
+
|
|
135
|
+
# SSE + REST API
|
|
136
|
+
mcp-test-server --transport sse
|
|
137
|
+
|
|
138
|
+
# With auth
|
|
139
|
+
mcp-test-server --transport sse --auth <key>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Default SSE port is 3001. Override with `--port`.
|
|
143
|
+
|
|
144
|
+
## Dependencies
|
|
145
|
+
|
|
146
|
+
Runtime: `mcp[cli]`, `requests`, Python stdlib.
|
|
147
|
+
|
|
148
|
+
The server must not add dependencies beyond these. Use stdlib for all tool implementations.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AgentSpan
|
|
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.
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mcp-testkit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP test server with 65 deterministic tools for protocol testing
|
|
5
|
+
Project-URL: Repository, https://github.com/agentspan-dev/mcp-test-server
|
|
6
|
+
Author: AgentSpan
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: mcp,model-context-protocol,testing,tools
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development :: Testing
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Requires-Dist: mcp[cli]>=1.0.0
|
|
20
|
+
Requires-Dist: requests
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# MCP Test Server
|
|
24
|
+
|
|
25
|
+
A Python MCP server with **65 deterministic tools** across 8 groups, supporting **stdio**, **SSE**, and **REST API** transports. Built for consistent, repeatable MCP protocol testing.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install mcp-testkit
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or from source:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
git clone https://github.com/agentspan-dev/mcp-test-server.git
|
|
37
|
+
cd mcp-test-server
|
|
38
|
+
pip install -e .
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# stdio (default)
|
|
45
|
+
mcp-test-server
|
|
46
|
+
|
|
47
|
+
# SSE + REST API on port 3001
|
|
48
|
+
mcp-test-server --transport sse
|
|
49
|
+
|
|
50
|
+
# With bearer token authentication
|
|
51
|
+
mcp-test-server --transport sse --auth super_secret_key
|
|
52
|
+
|
|
53
|
+
# Custom host/port
|
|
54
|
+
mcp-test-server --transport sse --host 0.0.0.0 --port 8080
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Transports
|
|
58
|
+
|
|
59
|
+
| Transport | Command | Endpoints |
|
|
60
|
+
|-----------|---------|-----------|
|
|
61
|
+
| **stdio** | `python3 server.py` | MCP JSON-RPC over stdin/stdout |
|
|
62
|
+
| **SSE** | `python3 server.py --transport sse` | MCP at `/sse` + REST at `/api/*` + OpenAPI at `/api-docs` |
|
|
63
|
+
|
|
64
|
+
## Authentication
|
|
65
|
+
|
|
66
|
+
Pass `--auth <key>` to require a Bearer token on all requests (both MCP SSE and REST):
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
python3 server.py --transport sse --auth my_secret
|
|
70
|
+
|
|
71
|
+
# Clients must include:
|
|
72
|
+
# Authorization: Bearer my_secret
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Tools (65 total)
|
|
76
|
+
|
|
77
|
+
All tools are **deterministic** — same input always produces same output. No randomness, no current time, no external state.
|
|
78
|
+
|
|
79
|
+
### Math (8 tools)
|
|
80
|
+
|
|
81
|
+
| Tool | Params | Description |
|
|
82
|
+
|------|--------|-------------|
|
|
83
|
+
| `math_add` | `a`, `b` | Add two numbers |
|
|
84
|
+
| `math_subtract` | `a`, `b` | Subtract b from a |
|
|
85
|
+
| `math_multiply` | `a`, `b` | Multiply two numbers |
|
|
86
|
+
| `math_divide` | `a`, `b` | Divide (errors on zero) |
|
|
87
|
+
| `math_modulo` | `a`, `b` | Remainder (errors on zero) |
|
|
88
|
+
| `math_power` | `base`, `exponent` | Exponentiation |
|
|
89
|
+
| `math_factorial` | `n` | n! (errors on negative) |
|
|
90
|
+
| `math_fibonacci` | `n` | Nth Fibonacci number (0-indexed) |
|
|
91
|
+
|
|
92
|
+
### String (8 tools)
|
|
93
|
+
|
|
94
|
+
| Tool | Params | Description |
|
|
95
|
+
|------|--------|-------------|
|
|
96
|
+
| `string_reverse` | `text` | Reverse a string |
|
|
97
|
+
| `string_uppercase` | `text` | Convert to uppercase |
|
|
98
|
+
| `string_lowercase` | `text` | Convert to lowercase |
|
|
99
|
+
| `string_length` | `text` | Character count |
|
|
100
|
+
| `string_char_count` | `text`, `char` | Count occurrences |
|
|
101
|
+
| `string_replace` | `text`, `old`, `new` | Replace all occurrences |
|
|
102
|
+
| `string_split` | `text`, `delimiter` | Split by delimiter |
|
|
103
|
+
| `string_join` | `items`, `delimiter` | Join list with delimiter |
|
|
104
|
+
|
|
105
|
+
### Collection (8 tools)
|
|
106
|
+
|
|
107
|
+
| Tool | Params | Description |
|
|
108
|
+
|------|--------|-------------|
|
|
109
|
+
| `collection_sort` | `items`, `reverse?` | Sort a list |
|
|
110
|
+
| `collection_flatten` | `items` | Recursively flatten nested lists |
|
|
111
|
+
| `collection_merge` | `dict_a`, `dict_b` | Merge dicts (b wins on conflict) |
|
|
112
|
+
| `collection_filter_gt` | `items`, `threshold` | Filter numbers > threshold |
|
|
113
|
+
| `collection_unique` | `items` | Remove duplicates (order preserved) |
|
|
114
|
+
| `collection_group_by` | `items`, `key` | Group objects by key |
|
|
115
|
+
| `collection_zip` | `list_a`, `list_b` | Zip into list of pairs |
|
|
116
|
+
| `collection_chunk` | `items`, `size` | Split into chunks |
|
|
117
|
+
|
|
118
|
+
### Encoding (8 tools)
|
|
119
|
+
|
|
120
|
+
| Tool | Params | Description |
|
|
121
|
+
|------|--------|-------------|
|
|
122
|
+
| `encoding_base64_encode` | `text` | Base64 encode |
|
|
123
|
+
| `encoding_base64_decode` | `data` | Base64 decode |
|
|
124
|
+
| `encoding_url_encode` | `text` | URL-encode |
|
|
125
|
+
| `encoding_url_decode` | `text` | URL-decode |
|
|
126
|
+
| `encoding_hex_encode` | `text` | Hex encode |
|
|
127
|
+
| `encoding_hex_decode` | `data` | Hex decode |
|
|
128
|
+
| `encoding_md5` | `text` | MD5 hash |
|
|
129
|
+
| `encoding_sha256` | `text` | SHA-256 hash |
|
|
130
|
+
|
|
131
|
+
### DateTime (8 tools)
|
|
132
|
+
|
|
133
|
+
All operate on provided dates — never use current time.
|
|
134
|
+
|
|
135
|
+
| Tool | Params | Description |
|
|
136
|
+
|------|--------|-------------|
|
|
137
|
+
| `datetime_parse` | `date_string` | Parse ISO date to components |
|
|
138
|
+
| `datetime_format` | `year`, `month`, `day`, `format` | Format date to string |
|
|
139
|
+
| `datetime_add_days` | `date_string`, `days` | Add/subtract days |
|
|
140
|
+
| `datetime_diff` | `date_a`, `date_b` | Days between two dates |
|
|
141
|
+
| `datetime_day_of_week` | `date_string` | Weekday name |
|
|
142
|
+
| `datetime_is_leap_year` | `year` | Leap year check |
|
|
143
|
+
| `datetime_days_in_month` | `year`, `month` | Days in month |
|
|
144
|
+
| `datetime_week_number` | `date_string` | ISO week number |
|
|
145
|
+
|
|
146
|
+
### Validation (8 tools)
|
|
147
|
+
|
|
148
|
+
All return `{valid: bool, reason: string}`.
|
|
149
|
+
|
|
150
|
+
| Tool | Params | Description |
|
|
151
|
+
|------|--------|-------------|
|
|
152
|
+
| `validation_is_email` | `text` | Email format check |
|
|
153
|
+
| `validation_is_url` | `text` | URL format check |
|
|
154
|
+
| `validation_is_ipv4` | `text` | IPv4 address check |
|
|
155
|
+
| `validation_is_ipv6` | `text` | IPv6 address check |
|
|
156
|
+
| `validation_is_uuid` | `text` | UUID format check |
|
|
157
|
+
| `validation_is_json` | `text` | Valid JSON check |
|
|
158
|
+
| `validation_is_palindrome` | `text` | Palindrome check |
|
|
159
|
+
| `validation_matches_regex` | `text`, `pattern` | Regex match check |
|
|
160
|
+
|
|
161
|
+
### Conversion (8 tools)
|
|
162
|
+
|
|
163
|
+
| Tool | Params | Description |
|
|
164
|
+
|------|--------|-------------|
|
|
165
|
+
| `conversion_celsius_to_fahrenheit` | `value` | C to F |
|
|
166
|
+
| `conversion_fahrenheit_to_celsius` | `value` | F to C |
|
|
167
|
+
| `conversion_km_to_miles` | `value` | km to miles |
|
|
168
|
+
| `conversion_miles_to_km` | `value` | miles to km |
|
|
169
|
+
| `conversion_bytes_to_human` | `bytes` | Bytes to human string |
|
|
170
|
+
| `conversion_rgb_to_hex` | `r`, `g`, `b` | RGB to hex color |
|
|
171
|
+
| `conversion_hex_to_rgb` | `hex_color` | Hex to RGB |
|
|
172
|
+
| `conversion_decimal_to_binary` | `value` | Decimal to binary string |
|
|
173
|
+
|
|
174
|
+
### Echo / Protocol Testing (8 tools)
|
|
175
|
+
|
|
176
|
+
| Tool | Params | Description |
|
|
177
|
+
|------|--------|-------------|
|
|
178
|
+
| `echo` | `message` | Echo input unchanged |
|
|
179
|
+
| `echo_error` | `message` | Always raises ToolError |
|
|
180
|
+
| `echo_large` | `size_kb` | Deterministic ~N KB text |
|
|
181
|
+
| `echo_nested` | `depth` | Nested JSON to depth N |
|
|
182
|
+
| `echo_types` | — | All JSON types |
|
|
183
|
+
| `echo_empty` | — | Empty string |
|
|
184
|
+
| `echo_multiple` | `messages` | Multiple TextContent blocks |
|
|
185
|
+
| `echo_schema` | `str_param`, `int_param`, `float_param`, `bool_param`, `list_param`, `obj_param` | Complex schema test |
|
|
186
|
+
|
|
187
|
+
### Standalone
|
|
188
|
+
|
|
189
|
+
| Tool | Params | Description |
|
|
190
|
+
|------|--------|-------------|
|
|
191
|
+
| `get_weather` | `city` | Fixed response: 77°F, sunny, always |
|
|
192
|
+
|
|
193
|
+
## REST API
|
|
194
|
+
|
|
195
|
+
When running with `--transport sse`, all tools are also available as HTTP endpoints:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
# GET endpoints (math, conversion, some echo, weather)
|
|
199
|
+
curl "http://localhost:3001/api/math/add?a=3&b=5"
|
|
200
|
+
# {"result": 8.0}
|
|
201
|
+
|
|
202
|
+
curl "http://localhost:3001/api/weather?city=NYC"
|
|
203
|
+
# {"city":"NYC","temperature_f":77,...}
|
|
204
|
+
|
|
205
|
+
# POST endpoints (string, collection, encoding, datetime, validation, some echo)
|
|
206
|
+
curl -X POST "http://localhost:3001/api/string/reverse" \
|
|
207
|
+
-H "Content-Type: application/json" \
|
|
208
|
+
-d '{"text":"hello"}'
|
|
209
|
+
# {"result": "olleh"}
|
|
210
|
+
|
|
211
|
+
curl -X POST "http://localhost:3001/api/encoding/sha256" \
|
|
212
|
+
-H "Content-Type: application/json" \
|
|
213
|
+
-d '{"text":"hello"}'
|
|
214
|
+
# {"result": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**OpenAPI spec** at `GET /api-docs` (OpenAPI 3.0.3).
|
|
218
|
+
|
|
219
|
+
With auth enabled, include `Authorization: Bearer <key>` on all requests.
|
|
220
|
+
|
|
221
|
+
## Testing
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
python3 -m pytest tests/ -v
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
389 tests covering all tools, REST API endpoints, auth, OpenAPI spec, and integration.
|
|
228
|
+
|
|
229
|
+
## Project Structure
|
|
230
|
+
|
|
231
|
+
```
|
|
232
|
+
mcp-test-server/
|
|
233
|
+
├── pyproject.toml # Package config, deps, CLI entry point
|
|
234
|
+
├── mcp_test_server/
|
|
235
|
+
│ ├── __init__.py # Package version
|
|
236
|
+
│ ├── server.py # Entry point — MCP server + REST API + auth
|
|
237
|
+
│ ├── api.py # REST API routes + OpenAPI spec generation
|
|
238
|
+
│ └── tools/
|
|
239
|
+
│ ├── __init__.py # Tool group registry
|
|
240
|
+
│ ├── math_tools.py
|
|
241
|
+
│ ├── string_tools.py
|
|
242
|
+
│ ├── collection_tools.py
|
|
243
|
+
│ ├── encoding_tools.py
|
|
244
|
+
│ ├── datetime_tools.py
|
|
245
|
+
│ ├── validation_tools.py
|
|
246
|
+
│ ├── conversion_tools.py
|
|
247
|
+
│ └── echo_tools.py # Includes get_weather
|
|
248
|
+
└── tests/
|
|
249
|
+
├── test_math_tools.py
|
|
250
|
+
├── test_string_tools.py
|
|
251
|
+
├── test_collection_tools.py
|
|
252
|
+
├── test_encoding_tools.py
|
|
253
|
+
├── test_datetime_tools.py
|
|
254
|
+
├── test_validation_tools.py
|
|
255
|
+
├── test_conversion_tools.py
|
|
256
|
+
├── test_echo_tools.py
|
|
257
|
+
├── test_api.py
|
|
258
|
+
└── test_integration.py
|
|
259
|
+
```
|