wheel-size-mcp 0.4.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.
- wheel_size_mcp-0.4.0/.claude/skills/sync-tools-inventory/SKILL.md +141 -0
- wheel_size_mcp-0.4.0/.github/workflows/ci.yml +28 -0
- wheel_size_mcp-0.4.0/.gitignore +37 -0
- wheel_size_mcp-0.4.0/LICENSE +21 -0
- wheel_size_mcp-0.4.0/PKG-INFO +286 -0
- wheel_size_mcp-0.4.0/README.md +261 -0
- wheel_size_mcp-0.4.0/docs/tools-inventory.md +752 -0
- wheel_size_mcp-0.4.0/evals/__init__.py +0 -0
- wheel_size_mcp-0.4.0/evals/grading.py +128 -0
- wheel_size_mcp-0.4.0/evals/run_evals.py +214 -0
- wheel_size_mcp-0.4.0/manifest.json +31 -0
- wheel_size_mcp-0.4.0/pyproject.toml +66 -0
- wheel_size_mcp-0.4.0/server.json +30 -0
- wheel_size_mcp-0.4.0/src/ws_mcp/__init__.py +3 -0
- wheel_size_mcp-0.4.0/src/ws_mcp/client.py +151 -0
- wheel_size_mcp-0.4.0/src/ws_mcp/prompts.py +160 -0
- wheel_size_mcp-0.4.0/src/ws_mcp/response.py +242 -0
- wheel_size_mcp-0.4.0/src/ws_mcp/server.py +146 -0
- wheel_size_mcp-0.4.0/src/ws_mcp/slugify.py +23 -0
- wheel_size_mcp-0.4.0/src/ws_mcp/tools/__init__.py +1 -0
- wheel_size_mcp-0.4.0/src/ws_mcp/tools/_annotations.py +35 -0
- wheel_size_mcp-0.4.0/src/ws_mcp/tools/catalog.py +271 -0
- wheel_size_mcp-0.4.0/src/ws_mcp/tools/classified.py +348 -0
- wheel_size_mcp-0.4.0/src/ws_mcp/tools/search.py +554 -0
- wheel_size_mcp-0.4.0/src/ws_mcp/tools/utility.py +135 -0
- wheel_size_mcp-0.4.0/tests/__init__.py +0 -0
- wheel_size_mcp-0.4.0/tests/conftest.py +48 -0
- wheel_size_mcp-0.4.0/tests/test_classified_drilldown.py +133 -0
- wheel_size_mcp-0.4.0/tests/test_client_mock.py +158 -0
- wheel_size_mcp-0.4.0/tests/test_eval_grading.py +134 -0
- wheel_size_mcp-0.4.0/tests/test_field_coverage.py +79 -0
- wheel_size_mcp-0.4.0/tests/test_fitment_check.py +151 -0
- wheel_size_mcp-0.4.0/tests/test_hf_tire.py +98 -0
- wheel_size_mcp-0.4.0/tests/test_integration.py +519 -0
- wheel_size_mcp-0.4.0/tests/test_inventory_sync.py +159 -0
- wheel_size_mcp-0.4.0/tests/test_p5_params.py +189 -0
- wheel_size_mcp-0.4.0/tests/test_questions.json +688 -0
- wheel_size_mcp-0.4.0/tests/test_response.py +239 -0
- wheel_size_mcp-0.4.0/tests/test_response_mappers.py +59 -0
- wheel_size_mcp-0.4.0/tests/test_search_validation.py +45 -0
- wheel_size_mcp-0.4.0/tests/test_spec_validation.py +79 -0
- wheel_size_mcp-0.4.0/tests/test_token_budget.py +115 -0
- wheel_size_mcp-0.4.0/uv.lock +1554 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sync-tools-inventory
|
|
3
|
+
description: "Sync tool descriptions from docs/tools-inventory.md to Python source files in src/ws_mcp/tools/. Use after editing the inventory doc to push changes to code."
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
user-invocable: true
|
|
6
|
+
allowed-tools: Read, Edit, Grep, Glob, Bash(git diff *), Bash(uv run ruff check *), Bash(uv run pytest *)
|
|
7
|
+
effort: high
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Sync Tools Inventory → Code
|
|
11
|
+
|
|
12
|
+
Synchronize tool docstrings and parameter descriptions from `docs/tools-inventory.md` (the single source of truth) into the Python tool source files.
|
|
13
|
+
|
|
14
|
+
## Step 0: Drift Safety Check — ALWAYS RUN FIRST
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
uv run pytest tests/test_inventory_sync.py -q
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
This test compares both sides (docstrings + parameter descriptions, whitespace-normalized).
|
|
21
|
+
|
|
22
|
+
- **All green** → doc and code are already in sync. Nothing to do; report that and stop.
|
|
23
|
+
- **Failures** → read each reported diff and classify it:
|
|
24
|
+
- The doc side contains the edits the user just made → this is the change set to push to code. Proceed.
|
|
25
|
+
- The CODE side contains content absent from the doc, in sections the user did NOT just edit → **code is newer than the doc (reverse drift). STOP.** Do not overwrite it. Report the diff and ask the user to back-port code → doc first (or confirm the overwrite explicitly).
|
|
26
|
+
|
|
27
|
+
Never skip this step: a doc→code sync over a stale doc silently destroys newer docstrings, and `ruff` will not catch it.
|
|
28
|
+
|
|
29
|
+
## Step 1: Read Both Sides
|
|
30
|
+
|
|
31
|
+
Read `docs/tools-inventory.md` and all four tool source files:
|
|
32
|
+
|
|
33
|
+
| Inventory Section | Source File |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `## Catalog` | `src/ws_mcp/tools/catalog.py` |
|
|
36
|
+
| `## Search` | `src/ws_mcp/tools/search.py` |
|
|
37
|
+
| `## Classified` | `src/ws_mcp/tools/classified.py` |
|
|
38
|
+
| `## Utility` | `src/ws_mcp/tools/utility.py` |
|
|
39
|
+
|
|
40
|
+
## Step 2: Parse Inventory Format
|
|
41
|
+
|
|
42
|
+
Each tool in inventory.md follows this structure:
|
|
43
|
+
|
|
44
|
+
```markdown
|
|
45
|
+
### `tool_name`
|
|
46
|
+
|
|
47
|
+
**API**: `GET /v2/endpoint/`
|
|
48
|
+
|
|
49
|
+
**Docstring**:
|
|
50
|
+
> Line 1 of docstring
|
|
51
|
+
> Line 2 of docstring
|
|
52
|
+
> ...
|
|
53
|
+
|
|
54
|
+
**Parameters**:
|
|
55
|
+
| Parameter | Type | Description |
|
|
56
|
+
|----------|-----|----------|
|
|
57
|
+
| `param_name` | `type` | Description text |
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Format details:
|
|
61
|
+
|
|
62
|
+
- **Heading suffixes**: a heading may carry a note after the closing backtick — e.g. `` ### `check_rim_fitment_for_vehicle` (composite) ``. The tool name is only what is inside the backticks.
|
|
63
|
+
- **No parameters**: `list_regions` has `**Parameters**: none` instead of a table.
|
|
64
|
+
- **Escaped pipes**: cells may contain `\|` (e.g. type `` `"concise" \| "full"` ``) — treat `\|` as a literal `|` inside the cell, not a column separator.
|
|
65
|
+
- **Shared geometric block**: the `## Classified` section opens with a shared parameter table (marked "shared geometric parameters"). Tool tables reference it with the row `| *+ shared geometric parameters* | | see block above |` instead of repeating those rows. When comparing a tool's parameters, expand the shared block into its table.
|
|
66
|
+
- **API lines** may have suffixes too (`+ MCP-side year filtering`) — informational only, not synced.
|
|
67
|
+
|
|
68
|
+
Extract for each tool:
|
|
69
|
+
- **Tool name**: from the backticks in the `### ` heading
|
|
70
|
+
- **Docstring lines**: all `> ` prefixed lines under **Docstring** (strip the `> ` prefix; a bare `>` is a blank line; indentation after `> ` is preserved)
|
|
71
|
+
- **Parameter names and descriptions**: from the "Parameter" and "Description" columns (strip backticks from names)
|
|
72
|
+
|
|
73
|
+
## Step 3: Compare and Update Docstrings
|
|
74
|
+
|
|
75
|
+
For each tool, find the function in the source file by matching `async def tool_name(`.
|
|
76
|
+
|
|
77
|
+
**Docstring update rule**: Replace the entire `"""..."""` block under the function signature with the content from inventory. Preserve the indentation level (8 spaces for tool functions).
|
|
78
|
+
|
|
79
|
+
Format:
|
|
80
|
+
```python
|
|
81
|
+
async def tool_name(...) -> dict:
|
|
82
|
+
"""First line of docstring.
|
|
83
|
+
|
|
84
|
+
Remaining lines of docstring.
|
|
85
|
+
Preserving blank lines from the inventory.
|
|
86
|
+
"""
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
- The first `> ` line becomes the first line of the docstring (on same line as `"""`)
|
|
90
|
+
- Subsequent `> ` lines follow with 8-space indentation
|
|
91
|
+
- Empty `> ` lines (just `>`) become blank lines in the docstring
|
|
92
|
+
- Close with `"""` on its own line at 8-space indent
|
|
93
|
+
|
|
94
|
+
## Step 4: Compare and Update Parameter Descriptions
|
|
95
|
+
|
|
96
|
+
For each parameter in the inventory table, find the matching `Field(description="...")` and update the description string. The Description column maps to the code **verbatim** — do not add or drop range/default annotations.
|
|
97
|
+
|
|
98
|
+
**Where the Field lives depends on the module:**
|
|
99
|
+
|
|
100
|
+
- `catalog.py`, `search.py`, `utility.py`: inline in the function signature — `param: Annotated[type, Field(description="...")]`.
|
|
101
|
+
- `classified.py`: mostly in **module-level Annotated aliases** at the top of the file (`_BoltPattern`, `_RimDiameter`, `_Cb`, `_Sort`, …) shared by several tools. The doc's shared geometric block maps to these aliases. Editing an alias changes every tool that uses it — that is intended. If the doc asks for different descriptions of the same shared parameter in different tools, report a WARNING instead of editing (per-tool divergence requires restructuring the aliases first).
|
|
102
|
+
|
|
103
|
+
**Update rule**: Replace only the `description="old text"` value inside `Field(...)`. Do NOT change the type annotation, default value, or other Field arguments (ge, le, etc.). Unescape `\|` → `|` when writing into Python strings.
|
|
104
|
+
|
|
105
|
+
## Step 5: Detect Structural Changes
|
|
106
|
+
|
|
107
|
+
Compare the set of parameter names in inventory (with the shared block expanded) vs code for each tool.
|
|
108
|
+
|
|
109
|
+
- **New parameter in inventory but not in code**: Report as WARNING — cannot auto-add because type annotation, default value, and Field constraints are needed. List the parameter name and suggest the user add it manually.
|
|
110
|
+
- **Parameter in code but removed from inventory**: Report as WARNING — do not auto-remove. List the parameter and ask the user to confirm deletion.
|
|
111
|
+
|
|
112
|
+
## Step 6: Validate
|
|
113
|
+
|
|
114
|
+
After all edits:
|
|
115
|
+
|
|
116
|
+
1. `uv run ruff check src/ws_mcp/tools/` — no syntax/lint errors
|
|
117
|
+
2. `uv run pytest tests/test_inventory_sync.py -q` — MUST be green now: doc and code are byte-consistent again
|
|
118
|
+
3. Show a summary of all changes made:
|
|
119
|
+
- Which tools had docstring updates
|
|
120
|
+
- Which parameters had description updates
|
|
121
|
+
- Any structural warnings (added/removed params)
|
|
122
|
+
|
|
123
|
+
## Step 7: Cross-File Consistency Check
|
|
124
|
+
|
|
125
|
+
Tool behavior and parameter contracts are also duplicated in two places this skill does NOT edit:
|
|
126
|
+
|
|
127
|
+
- `src/ws_mcp/server.py` — the FastMCP `instructions` block (workflows, critical rules, parameter requirements)
|
|
128
|
+
- `README.md` — the "Available Tools" table
|
|
129
|
+
|
|
130
|
+
After a sync, grep both for statements about the tools whose descriptions changed. If a synced change contradicts them (e.g. a parameter requirement changed), report the exact lines as WARNINGS for the user to update manually. Do not auto-edit these files.
|
|
131
|
+
|
|
132
|
+
## Important Rules
|
|
133
|
+
|
|
134
|
+
- NEVER proceed past Step 0 when code-side content is newer than the doc
|
|
135
|
+
- NEVER change function signatures, return types, or logic — only docstrings and Field descriptions
|
|
136
|
+
- NEVER change Field constraints (ge, le, etc.) — only the description string
|
|
137
|
+
- NEVER change type annotations — only descriptions
|
|
138
|
+
- Preserve existing indentation style in each file
|
|
139
|
+
- If a tool exists in code but NOT in inventory — skip it, report as info
|
|
140
|
+
- If a tool exists in inventory but NOT in code — report as WARNING
|
|
141
|
+
- Use the Edit tool for all changes (not Write) to preserve file content
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.12", "3.13"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- uses: astral-sh/setup-uv@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: uv sync --dev
|
|
23
|
+
|
|
24
|
+
- name: Lint
|
|
25
|
+
run: uv run ruff check .
|
|
26
|
+
|
|
27
|
+
- name: Unit tests
|
|
28
|
+
run: uv run pytest -m "not integration"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Python bytecode
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Build / packaging
|
|
7
|
+
*.egg-info/
|
|
8
|
+
*.egg
|
|
9
|
+
.eggs/
|
|
10
|
+
dist/
|
|
11
|
+
build/
|
|
12
|
+
|
|
13
|
+
# Virtual environments
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
|
|
17
|
+
# Environment variables
|
|
18
|
+
.env
|
|
19
|
+
|
|
20
|
+
# Tool caches
|
|
21
|
+
.pytest_cache/
|
|
22
|
+
.ruff_cache/
|
|
23
|
+
.playwright-mcp/
|
|
24
|
+
|
|
25
|
+
# AI assistant configs
|
|
26
|
+
CLAUDE.md
|
|
27
|
+
.mcp.json
|
|
28
|
+
claudedocs/
|
|
29
|
+
.claude/settings.local.json
|
|
30
|
+
.codex-review/
|
|
31
|
+
|
|
32
|
+
# OS files
|
|
33
|
+
.DS_Store
|
|
34
|
+
|
|
35
|
+
# Symlinks to external repos (local dev convenience — never publish)
|
|
36
|
+
# NB: no trailing slash — must match the SYMLINK, not just a directory
|
|
37
|
+
/development
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 WHEEL SIZE KZ LLP
|
|
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,286 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wheel-size-mcp
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: MCP server for Wheel Fitment API — enables Claude Code and LLM agents to query vehicle wheel/tire compatibility data
|
|
5
|
+
Project-URL: Homepage, https://www.wheel-size.com
|
|
6
|
+
Project-URL: Repository, https://github.com/driveate/wheel-size-mcp
|
|
7
|
+
Project-URL: Issues, https://github.com/driveate/wheel-size-mcp/issues
|
|
8
|
+
Project-URL: Documentation, https://developer.wheel-size.com
|
|
9
|
+
Project-URL: API Reference, https://api.wheel-size.com/v2/openapi/
|
|
10
|
+
Author-email: WHEEL SIZE KZ LLP <info@wheel-size.com>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: claude-code,fastmcp,mcp,wheel-fitment,wheel-size
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
|
+
Requires-Dist: fastmcp>=3.4.1
|
|
23
|
+
Requires-Dist: httpx>=0.27
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# wheel-size-mcp
|
|
27
|
+
|
|
28
|
+
<!-- mcp-name: io.github.driveate/wheel-size-mcp -->
|
|
29
|
+
|
|
30
|
+
The **official** MCP server for the [Wheel Fitment API](https://api.wheel-size.com/v2/openapi/) — built and maintained by [Wheel-Size.com](https://www.wheel-size.com), the API provider. Gives LLM agents access to vehicle wheel and tire compatibility data.
|
|
31
|
+
|
|
32
|
+
Ask your AI assistant things like:
|
|
33
|
+
- "What are the OEM wheel specs for a 2024 Toyota Camry?"
|
|
34
|
+
- "Which vehicles fit 5x114.3 18x8 ET35 rims?"
|
|
35
|
+
- "Calculate plus-size options for 225/50R17 on 7Jx17 ET40"
|
|
36
|
+
- "Generate a product card for this wheel showing all compatible vehicles"
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
### 1. Get an API key
|
|
41
|
+
|
|
42
|
+
Sign up at [developer.wheel-size.com](https://developer.wheel-size.com) and copy your API key.
|
|
43
|
+
|
|
44
|
+
### 2. Set the API key in your shell
|
|
45
|
+
|
|
46
|
+
Add to your `~/.zshrc` (or `~/.bashrc`):
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
export WHEELSIZE_API_KEY="your-api-key-here"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Then reload your shell: `source ~/.zshrc`
|
|
53
|
+
|
|
54
|
+
### 3. Add to your AI client
|
|
55
|
+
|
|
56
|
+
Choose your client below — each config block is copy-paste ready.
|
|
57
|
+
|
|
58
|
+
#### Claude Code
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
claude mcp add wheel-size-api -- uvx wheel-size-mcp
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Or add to `.mcp.json` in your project root:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"mcpServers": {
|
|
69
|
+
"wheel-size-api": {
|
|
70
|
+
"command": "uvx",
|
|
71
|
+
"args": ["wheel-size-mcp"],
|
|
72
|
+
"env": {
|
|
73
|
+
"WHEELSIZE_API_KEY": "${WHEELSIZE_API_KEY}"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### Claude Desktop
|
|
81
|
+
|
|
82
|
+
Add to `claude_desktop_config.json` (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows):
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"mcpServers": {
|
|
87
|
+
"wheel-size-api": {
|
|
88
|
+
"command": "uvx",
|
|
89
|
+
"args": ["wheel-size-mcp"],
|
|
90
|
+
"env": {
|
|
91
|
+
"WHEELSIZE_API_KEY": "your-api-key-here"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### Cursor
|
|
99
|
+
|
|
100
|
+
Add to `.cursor/mcp.json` in your project root:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"mcpServers": {
|
|
105
|
+
"wheel-size-api": {
|
|
106
|
+
"command": "uvx",
|
|
107
|
+
"args": ["wheel-size-mcp"],
|
|
108
|
+
"env": {
|
|
109
|
+
"WHEELSIZE_API_KEY": "your-api-key-here"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
#### Windsurf
|
|
117
|
+
|
|
118
|
+
Add to `~/.codeium/windsurf/mcp_config.json`:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"mcpServers": {
|
|
123
|
+
"wheel-size-api": {
|
|
124
|
+
"command": "uvx",
|
|
125
|
+
"args": ["wheel-size-mcp"],
|
|
126
|
+
"env": {
|
|
127
|
+
"WHEELSIZE_API_KEY": "your-api-key-here"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### Zed
|
|
135
|
+
|
|
136
|
+
Add to your Zed `settings.json` (Cmd+, → Open Settings):
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"context_servers": {
|
|
141
|
+
"wheel-size-api": {
|
|
142
|
+
"command": {
|
|
143
|
+
"path": "uvx",
|
|
144
|
+
"args": ["wheel-size-mcp"],
|
|
145
|
+
"env": {
|
|
146
|
+
"WHEELSIZE_API_KEY": "your-api-key-here"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 4. Restart your client
|
|
155
|
+
|
|
156
|
+
The MCP server starts automatically when the client launches.
|
|
157
|
+
|
|
158
|
+
## Remote Server (Streamable HTTP)
|
|
159
|
+
|
|
160
|
+
Besides stdio, the server can run as a standalone HTTP service — useful for hosting one shared instance instead of installing Python on every machine:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
wheel-size-mcp --transport http --port 8000
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The MCP endpoint is served at `http://127.0.0.1:8000/mcp/`. Point HTTP-capable clients at it:
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"mcpServers": {
|
|
171
|
+
"wheel-size-api": {
|
|
172
|
+
"url": "http://127.0.0.1:8000/mcp/"
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Security**: the server binds to `127.0.0.1` by default. The `WHEELSIZE_API_KEY` lives on the server side, so anyone who can reach the port consumes your API quota — expose it beyond localhost (`--host 0.0.0.0`) only behind a reverse proxy that handles authentication.
|
|
179
|
+
|
|
180
|
+
## Available Tools (21)
|
|
181
|
+
|
|
182
|
+
### Catalog — vehicle lookup
|
|
183
|
+
|
|
184
|
+
| Tool | Description |
|
|
185
|
+
|------|-------------|
|
|
186
|
+
| `ws_list_makes` | List all manufacturers. **Start here.** |
|
|
187
|
+
| `ws_list_models` | Models for a make (e.g. Toyota → Camry, Corolla…). |
|
|
188
|
+
| `ws_list_years` | Available years for a make/model. |
|
|
189
|
+
| `ws_list_generations` | Generations for a make/model (alternative to years). |
|
|
190
|
+
| `ws_list_modifications` | Trims for a specific vehicle (e.g. 2.0i, 3.0 V6…). |
|
|
191
|
+
| `ws_list_regions` | Market regions (USDM, EUDM, JDM…). |
|
|
192
|
+
|
|
193
|
+
### Search — fitment data
|
|
194
|
+
|
|
195
|
+
| Tool | Description |
|
|
196
|
+
|------|-------------|
|
|
197
|
+
| `ws_search_by_vehicle` | OEM wheel/tire specs for a vehicle. Requires `modification` or `region`, plus `year` or `generation` (unless `modification` is given). |
|
|
198
|
+
| `ws_search_by_rim` | Find vehicles compatible with a rim (exact specs or min/max ranges). |
|
|
199
|
+
| `ws_search_by_tire` | Find vehicles by metric tire size, with speed/load/staggered filters and refinement facets. |
|
|
200
|
+
| `ws_search_by_hf_tire` | Find vehicles by high-flotation (LT) inch size (e.g. 31x10.50R15). |
|
|
201
|
+
| `ws_check_rim_fitment_for_vehicle` | "Will these rims fit my 2020 Civic?" — one-call fitment check. |
|
|
202
|
+
| `ws_check_tire_fitment_for_vehicle` | Same for a metric tire size. |
|
|
203
|
+
| `ws_check_hf_tire_fitment_for_vehicle` | Same for a high-flotation tire size. |
|
|
204
|
+
| `ws_calculate_upsteps` | Plus/minus sizing calculator with width/diameter tolerances. |
|
|
205
|
+
|
|
206
|
+
### Classified — product cards for e-commerce
|
|
207
|
+
|
|
208
|
+
| Tool | Description |
|
|
209
|
+
|------|-------------|
|
|
210
|
+
| `ws_find_tires_for_rim` | Compatible tire sizes for a rim spec. |
|
|
211
|
+
| `ws_find_vehicles_for_rim` | Vehicles that fit a given rim (geometric 2D filtering). |
|
|
212
|
+
| `ws_find_vehicle_modifications_for_rim` | Drill down into trims for a specific generation. |
|
|
213
|
+
| `ws_find_vehicles_for_tire` | Vehicles that use a specific tire size. |
|
|
214
|
+
| `ws_find_vehicles_for_package` | Vehicles compatible with a rim + tire combo. |
|
|
215
|
+
| `ws_find_vehicle_modifications_for_package` | Drill down into trims for a rim + tire package. |
|
|
216
|
+
|
|
217
|
+
### Utility
|
|
218
|
+
|
|
219
|
+
| Tool | Description |
|
|
220
|
+
|------|-------------|
|
|
221
|
+
| `ws_get_spec_metadata` | Computed geometry, population stats, and intelligence hints for any spec. |
|
|
222
|
+
|
|
223
|
+
## MCP Prompts
|
|
224
|
+
|
|
225
|
+
Pre-built workflow prompts that guide LLM agents through multi-step operations:
|
|
226
|
+
|
|
227
|
+
| Prompt | Description |
|
|
228
|
+
|--------|-------------|
|
|
229
|
+
| `vehicle_fitment_lookup` | Complete catalog→search chain for a vehicle description |
|
|
230
|
+
| `rim_compatibility_check` | Metadata→classified flow for rim compatibility |
|
|
231
|
+
| `product_card_generation` | E-commerce product card workflow for wheels/packages |
|
|
232
|
+
|
|
233
|
+
## Environment Variables
|
|
234
|
+
|
|
235
|
+
| Variable | Required | Default | Description |
|
|
236
|
+
|----------|----------|---------|-------------|
|
|
237
|
+
| `WHEELSIZE_API_KEY` | **Yes** | — | API key from [developer.wheel-size.com](https://developer.wheel-size.com) |
|
|
238
|
+
| `API_BASE_URL` | No | `https://api.wheel-size.com` | API base URL |
|
|
239
|
+
| `API_HOST_HEADER` | No | — | Host header override (only needed for local Docker routing) |
|
|
240
|
+
| `MCP_TRANSPORT` | No | `stdio` | `stdio` or `http` (same as `--transport`) |
|
|
241
|
+
| `MCP_HOST` | No | `127.0.0.1` | Bind address for http transport (same as `--host`) |
|
|
242
|
+
| `MCP_PORT` | No | `8000` | Port for http transport (same as `--port`) |
|
|
243
|
+
|
|
244
|
+
## API Terms of Service
|
|
245
|
+
|
|
246
|
+
Search tools (`ws_search_by_vehicle`, `ws_search_by_rim`, `ws_search_by_tire`, `ws_search_by_hf_tire`, and the `ws_check_*_fitment_for_vehicle` checks) **must be initiated by real users** per [API Terms of Usage](https://developer.wheel-size.com/api-tos). Do not call in autonomous agent loops. Catalog, classified, utility tools and `ws_calculate_upsteps` have no such restriction.
|
|
247
|
+
|
|
248
|
+
## Evals
|
|
249
|
+
|
|
250
|
+
[`tests/test_questions.json`](tests/test_questions.json) contains 89 natural-language questions across 12 categories (catalog navigation, fitment lookups, reverse searches, fitment checks, upstep calculation, e-commerce product cards, spec metadata, multi-step workflows, edge cases, tool selection). Each entry includes `expected_tools`, optional `expected_params` / `expected_params_search`, and a free-text `tests` note.
|
|
251
|
+
|
|
252
|
+
`evals/run_evals.py` feeds these questions to a real Claude model with the MCP tools attached, records which tools it calls with which parameters, and grades them against the expectations — catching regressions in tool descriptions and server instructions:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
# needs ANTHROPIC_API_KEY and a reachable Wheel Fitment API; costs money
|
|
256
|
+
uv sync --group evals
|
|
257
|
+
uv run --group evals python evals/run_evals.py # all questions
|
|
258
|
+
uv run --group evals python evals/run_evals.py -n 10 # smoke run
|
|
259
|
+
uv run --group evals python evals/run_evals.py --category catalog_flow
|
|
260
|
+
uv run --group evals python evals/run_evals.py --json report.json --min-pass 0.8
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Grading is deterministic (no LLM judge): every expected tool must be called (multiset — repeats counted, extra navigation calls allowed), and some single call must carry the expected parameters. Questions without machine-checkable expectations are reported as SKIP and excluded from the pass rate. The default model is pinned (`claude-sonnet-5`) so pass-rate history stays comparable; override with `--model`.
|
|
264
|
+
|
|
265
|
+
The eval runner is **not** part of pytest or CI — it bills the Anthropic API. The grading logic itself is unit-tested in CI (`tests/test_eval_grading.py`). ToS note: every question simulates a user-initiated request, so the search-tool restriction is respected.
|
|
266
|
+
|
|
267
|
+
## Development
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
# Install dev dependencies
|
|
271
|
+
uv sync --dev
|
|
272
|
+
|
|
273
|
+
# Unit tests (no API needed — this is what CI runs)
|
|
274
|
+
uv run pytest -m "not integration"
|
|
275
|
+
|
|
276
|
+
# Full test suite (requires a private API instance, see note below)
|
|
277
|
+
uv run pytest
|
|
278
|
+
|
|
279
|
+
# Lint
|
|
280
|
+
uv run ruff check .
|
|
281
|
+
|
|
282
|
+
# Run server (stdio)
|
|
283
|
+
wheel-size-mcp
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
**Note on tests**: integration tests run against a private test instance of the API and auto-skip when it is unreachable. External contributors should rely on the unit suite (`pytest -m "not integration"`), which mocks all HTTP and is what CI runs on every push and pull request.
|