Skip to content

Commit 72b9fe4

Browse files
authored
Adding evolution case-study (#4)
* Adding evolution case-study Signed-off-by: Matthias Wessendorf <mwessend@redhat.com> * Updating README based on review feedback Signed-off-by: Matthias Wessendorf <mwessend@redhat.com> --------- Signed-off-by: Matthias Wessendorf <mwessend@redhat.com>
1 parent 23a8163 commit 72b9fe4

14 files changed

Lines changed: 624 additions & 0 deletions

File tree

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@ Think of it as integration testing for AI tool use.
2525

2626
**Perfect for:** First-time users who want to understand the basics of MCPChecker.
2727

28+
---
29+
30+
### 2. [Evolution Case Study](./evolution-case-study/)
31+
**Difficulty:** Intermediate
32+
**Time:** 15-20 minutes
33+
**What you'll learn:**
34+
- How documentation quality affects agent success
35+
- Compare bad vs good tool documentation (2 iterations)
36+
- See identical code produce different test results
37+
- Understand what makes tools discoverable when they overlap in functionality
38+
39+
**Perfect for:** Users who want to see MCPChecker's real value and learn documentation best practices.
40+
41+
**Key insight:** Same functionality (4 text processing tools), different documentation = different test results. Proves MCPChecker tests discoverability, not just functionality.
42+
43+
---
44+
2845
## Getting Help
2946

3047
- 📖 [Full Documentation](https://github.com/mcpchecker/mcpchecker)

evolution-case-study/README.md

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
# Case Study: Evolving from Bad to Good MCP Tooling
2+
3+
> **See how documentation quality affects AI agent success**
4+
5+
This case study demonstrates the power of MCPChecker by showing the same MCP server with **identical functionality** but **different documentation quality**.
6+
7+
## The Experiment
8+
9+
We have **one text processing server** with 4 tools, all text transformation-related:
10+
- `process` / `to_uppercase` - Convert to uppercase
11+
- `transform` / `to_lowercase` - Convert to lowercase
12+
- `convert` / `to_title_case` - Convert to title case
13+
- `format_text` / `capitalize_first` - Capitalize first letter only
14+
15+
**The implementation is identical across both iterations. Only the documentation changes.**
16+
17+
## Why Text Processing Tools?
18+
19+
All 4 tools "transform text" but in different ways. With bad documentation (generic names), the agent can't tell which tool does what. With good documentation, it's crystal clear.
20+
21+
## Two Iterations
22+
23+
### Iteration 1: Bad Documentation
24+
**Location:** `iteration-1-bad/`
25+
26+
Misleading generic names, vague descriptions, no type hints:
27+
28+
```python
29+
@mcp.tool()
30+
def process(text: str) -> str:
31+
"""Process text"""
32+
return text.upper()
33+
34+
@mcp.tool()
35+
def transform(text: str) -> str:
36+
"""Transform text"""
37+
return text.lower()
38+
39+
@mcp.tool()
40+
def convert(text: str) -> str:
41+
"""Convert text"""
42+
return text.title()
43+
44+
@mcp.tool()
45+
def format_text(text: str) -> str:
46+
"""Format text"""
47+
return text.capitalize()
48+
```
49+
50+
**Problems:**
51+
- ❌ Misleading generic names (`process`, `transform`, `convert`, `format_text`)
52+
- ❌ Names give ZERO hint about what transformation they perform
53+
- ❌ Vague descriptions (doesn't explain what processing/transforming means)
54+
- ❌ No examples
55+
- ❌ No guidance on **when** to use each tool
56+
- ❌ "Process text" could mean anything - uppercase? lowercase? something else?
57+
58+
### Iteration 2: Good Documentation
59+
**Location:** `iteration-2-good/`
60+
61+
Full docstrings with examples, clear names, use case guidance:
62+
63+
```python
64+
@mcp.tool()
65+
def to_uppercase(text: str) -> str:
66+
"""Convert all letters in text to uppercase (capital letters).
67+
68+
Use this tool when you need text in ALL CAPS format. Every lowercase
69+
letter becomes uppercase, while numbers and symbols remain unchanged.
70+
71+
Args:
72+
text: The text to convert to uppercase
73+
74+
Returns:
75+
The text with all letters converted to uppercase
76+
77+
Example:
78+
to_uppercase("hello world") returns "HELLO WORLD"
79+
to_uppercase("Hello World 123!") returns "HELLO WORLD 123!"
80+
"""
81+
return text.upper()
82+
83+
@mcp.tool()
84+
def to_lowercase(text: str) -> str:
85+
"""Convert all letters in text to lowercase (small letters).
86+
87+
Use this tool when you need text in all lowercase format. Every uppercase
88+
letter becomes lowercase, while numbers and symbols remain unchanged.
89+
90+
Args:
91+
text: The text to convert to lowercase
92+
93+
Returns:
94+
The text with all letters converted to lowercase
95+
96+
Example:
97+
to_lowercase("HELLO WORLD") returns "hello world"
98+
to_lowercase("Hello World 123!") returns "hello world 123!"
99+
"""
100+
return text.lower()
101+
102+
# ... and so on for to_title_case and capitalize_first
103+
```
104+
105+
**Best practices:**
106+
- ✅ Descriptive names (`to_uppercase`, `to_lowercase`, `to_title_case`)
107+
- ✅ Full docstrings with Args/Returns
108+
- ✅ Examples provided
109+
- ✅ Explains **when** to use each tool
110+
- ✅ Clarifies the differences between similar tools
111+
- ✅ "ALL CAPS format" vs "all lowercase format" - immediately clear
112+
113+
## The Test Tasks
114+
115+
Both iterations are tested with the **same natural language prompts** that don't mention tool names.
116+
117+
> **Note:** The examples below are simplified pseudocode to illustrate the test concepts. The actual test files use the full MCPChecker YAML structure. See `evals/tasks/` directories for complete task definitions.
118+
119+
### Task 1: Uppercase Conversion
120+
```yaml
121+
prompt: |
122+
I have this text: hello world
123+
124+
Please convert it to all uppercase letters.
125+
verify: Result contains "HELLO WORLD"
126+
expected_tool: process (bad) / to_uppercase (good)
127+
```
128+
129+
### Task 2: Lowercase Conversion
130+
```yaml
131+
prompt: |
132+
I have this text: HELLO WORLD
133+
134+
Please convert it to all lowercase letters.
135+
verify: Result contains "hello world"
136+
expected_tool: transform (bad) / to_lowercase (good)
137+
```
138+
139+
### Task 3: Title Case
140+
```yaml
141+
prompt: |
142+
I have this text: hello world
143+
144+
Please format it with title case (first letter of each word capitalized).
145+
verify: Result contains "Hello World"
146+
expected_tool: convert (bad) / to_title_case (good)
147+
```
148+
149+
## Actual Results
150+
151+
Since the **code is identical**, differences in test results prove documentation quality matters:
152+
153+
| Task | Iteration 1 (Bad Docs) | Iteration 2 (Good Docs) |
154+
|------|------------------------|-------------------------|
155+
| Uppercase conversion | ❌ FAILED (assertions) | ✅ PASSED |
156+
| Lowercase conversion | ❌ FAILED (assertions) | ✅ PASSED |
157+
| Title case formatting | ❌ FAILED | ✅ PASSED |
158+
159+
**Actual pass rates:**
160+
- **Iteration 1 (Bad):** 0/3 tests fully passed, 6/9 assertions passed
161+
- **Iteration 2 (Good):** 3/3 tests passed, 9/9 assertions passed
162+
163+
> **About assertions:** Each test checks multiple MCP-specific assertions:
164+
> - **minToolCalls: 1** - Agent must call at least one tool (can't just calculate the answer)
165+
> - **maxToolCalls: 5** - Agent can't try unlimited tools (prevents brute-force guessing)
166+
> - **toolsUsed** - Agent must call the specific expected tool (e.g., `to_uppercase` for uppercase conversion)
167+
>
168+
> In the bad iteration, the agent sometimes got the correct output but failed because it called the wrong tool or tried too many tools trying to figure out which generic name did what.
169+
170+
**What happened in the bad iteration:**
171+
- Agent couldn't discover which generic tool (`process`, `transform`, `convert`) does what
172+
- Even when it got correct answers (by exploring tools), it failed assertions because it didn't call the expected tools
173+
- Generic names like "process text" don't hint at uppercase conversion
174+
175+
**What happened in the good iteration:**
176+
- Clear tool names (`to_uppercase`, `to_lowercase`) made it obvious which to use
177+
- Agent found the correct tools immediately
178+
- All tests passed
179+
180+
## Running the Case Study
181+
182+
### Prerequisites
183+
184+
See [getting-started](../getting-started/) for installation of:
185+
- Claude Code
186+
- mcpchecker
187+
- uv (Python package manager)
188+
189+
Set judge LLM environment variables:
190+
```bash
191+
export JUDGE_BASE_URL="https://api.openai.com/v1"
192+
export JUDGE_API_KEY="sk-your-key-here"
193+
export JUDGE_MODEL_NAME="gpt-4o-mini"
194+
```
195+
196+
### Run Both Iterations
197+
198+
**Iteration 1 - Bad Documentation:**
199+
```bash
200+
cd iteration-1-bad
201+
202+
# Terminal 1: Start server
203+
cd server
204+
PORT=8000 ./server.py
205+
206+
# Terminal 2: Run tests
207+
cd evals
208+
mcpchecker check eval.yaml
209+
```
210+
211+
**Iteration 2 - Good Documentation:**
212+
```bash
213+
cd iteration-2-good
214+
215+
# Terminal 1: Start server (stop previous first)
216+
cd server
217+
PORT=8000 ./server.py
218+
219+
# Terminal 2: Run tests
220+
cd evals
221+
mcpchecker check eval.yaml
222+
```
223+
224+
### Compare Results
225+
226+
After running both, compare the JSON output files:
227+
- `text-processing-bad-test-out.json`
228+
- `text-processing-good-test-out.json`
229+
230+
You'll see the same code producing different test results based purely on documentation quality.
231+
232+
## Key Takeaways
233+
234+
1. **Similar tools need clear differentiation** - When multiple tools do related things (text transformations), documentation is critical
235+
2. **MCPChecker validates discoverability** - Tests pass/fail based on whether agents can find and use the RIGHT tool
236+
3. **Generic names are useless** - `process`, `transform`, `convert` give zero hint about what the tool does, compared to `to_uppercase`, `to_lowercase`, `to_title_case`
237+
4. **Examples clarify usage** - Iteration 2's examples help agents understand exactly what each transformation does
238+
5. **"Use this when..." guidance matters** - Explicitly stating use cases prevents tool confusion
239+
6. **Vague descriptions hurt** - "Process text" could mean anything; "Convert all letters to uppercase" is actionable
240+
241+
## Code Comparison
242+
243+
### Side-by-Side: Uppercase Tool
244+
245+
| Iteration 1 (Bad) | Iteration 2 (Good) |
246+
|-------------------|-------------------|
247+
| `def process(text: str):` | `def to_uppercase(text: str) -> str:` |
248+
| `"""Process text"""` | Full docstring with "Use this when...", Args, Returns, Examples |
249+
| Name gives no hint what processing means | Name clearly indicates uppercase conversion |
250+
| No examples | 2 examples showing exact usage |
251+
| Vague "process" | Specific "ALL CAPS format" description |
252+
253+
The **exact same implementation** (`return text.upper()`) but vastly different discoverability.
254+
255+
## What This Proves
256+
257+
MCPChecker doesn't just test if your tools **work** - it tests if they're **usable by AI agents**.
258+
259+
You can have perfectly functional code that fails MCPChecker tests because:
260+
- Tool names are misleading or generic (`process`, `transform` tell you nothing)
261+
- Descriptions don't explain differences between similar tools
262+
- No examples to learn from
263+
- Unclear when to use this tool vs alternatives
264+
- No guidance on what "processing" or "transforming" actually means
265+
266+
**Good documentation = passing tests = agents can actually use your tools.**
267+
268+
## Next Steps
269+
270+
- Check out the [main documentation](https://github.com/mcpchecker/mcpchecker) for advanced features
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
kind: Eval
2+
metadata:
3+
name: "text-processing-bad-test"
4+
5+
config:
6+
# Use Claude Code as the AI agent
7+
agent:
8+
type: "builtin.claude-code"
9+
10+
# MCP server configuration
11+
mcpConfigFile: mcp-config.yaml
12+
13+
# LLM judge configuration
14+
llmJudge:
15+
env:
16+
baseUrlKey: JUDGE_BASE_URL
17+
apiKeyKey: JUDGE_API_KEY
18+
modelNameKey: JUDGE_MODEL_NAME
19+
20+
# Test tasks
21+
taskSets:
22+
- path: tasks/uppercase.yaml
23+
assertions:
24+
toolsUsed:
25+
- server: text-server
26+
tool: process
27+
minToolCalls: 1
28+
maxToolCalls: 5
29+
30+
- path: tasks/lowercase.yaml
31+
assertions:
32+
toolsUsed:
33+
- server: text-server
34+
tool: transform
35+
minToolCalls: 1
36+
maxToolCalls: 5
37+
38+
- path: tasks/title-case.yaml
39+
assertions:
40+
toolsUsed:
41+
- server: text-server
42+
tool: convert
43+
minToolCalls: 1
44+
maxToolCalls: 5
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mcpServers:
2+
text-server:
3+
type: http
4+
url: http://localhost:8000/mcp
5+
enableAllTools: true
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
kind: Task
2+
apiVersion: mcpchecker/v1alpha2
3+
metadata:
4+
name: "lowercase-test"
5+
difficulty: easy
6+
7+
spec:
8+
verify:
9+
- llmJudge:
10+
contains: "hello world"
11+
12+
prompt:
13+
inline: |
14+
I have this text: HELLO WORLD
15+
16+
Please convert it to all lowercase letters.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
kind: Task
2+
apiVersion: mcpchecker/v1alpha2
3+
metadata:
4+
name: "title-case-test"
5+
difficulty: medium
6+
7+
spec:
8+
verify:
9+
- llmJudge:
10+
contains: "Hello World"
11+
12+
prompt:
13+
inline: |
14+
I have this text: hello world
15+
16+
Please format it with title case (first letter of each word capitalized).
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
kind: Task
2+
apiVersion: mcpchecker/v1alpha2
3+
metadata:
4+
name: "uppercase-test"
5+
difficulty: easy
6+
7+
spec:
8+
verify:
9+
- llmJudge:
10+
contains: "HELLO WORLD"
11+
12+
prompt:
13+
inline: |
14+
I have this text: hello world
15+
16+
Please convert it to all uppercase letters.

0 commit comments

Comments
 (0)