wunder-mobility-mcp 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 (26) hide show
  1. wunder_mobility_mcp-0.1.0/LICENSE +21 -0
  2. wunder_mobility_mcp-0.1.0/PKG-INFO +243 -0
  3. wunder_mobility_mcp-0.1.0/README.md +214 -0
  4. wunder_mobility_mcp-0.1.0/pyproject.toml +50 -0
  5. wunder_mobility_mcp-0.1.0/setup.cfg +4 -0
  6. wunder_mobility_mcp-0.1.0/src/wunder_mcp/__init__.py +0 -0
  7. wunder_mobility_mcp-0.1.0/src/wunder_mcp/app.py +28 -0
  8. wunder_mobility_mcp-0.1.0/src/wunder_mcp/catalog.py +109 -0
  9. wunder_mobility_mcp-0.1.0/src/wunder_mcp/docs_links.py +77 -0
  10. wunder_mobility_mcp-0.1.0/src/wunder_mcp/docs_source.py +112 -0
  11. wunder_mobility_mcp-0.1.0/src/wunder_mcp/openapi.py +97 -0
  12. wunder_mobility_mcp-0.1.0/src/wunder_mcp/search.py +125 -0
  13. wunder_mobility_mcp-0.1.0/src/wunder_mcp/server.py +153 -0
  14. wunder_mobility_mcp-0.1.0/src/wunder_mcp/stdio.py +19 -0
  15. wunder_mobility_mcp-0.1.0/src/wunder_mobility_mcp.egg-info/PKG-INFO +243 -0
  16. wunder_mobility_mcp-0.1.0/src/wunder_mobility_mcp.egg-info/SOURCES.txt +24 -0
  17. wunder_mobility_mcp-0.1.0/src/wunder_mobility_mcp.egg-info/dependency_links.txt +1 -0
  18. wunder_mobility_mcp-0.1.0/src/wunder_mobility_mcp.egg-info/entry_points.txt +2 -0
  19. wunder_mobility_mcp-0.1.0/src/wunder_mobility_mcp.egg-info/requires.txt +11 -0
  20. wunder_mobility_mcp-0.1.0/src/wunder_mobility_mcp.egg-info/top_level.txt +1 -0
  21. wunder_mobility_mcp-0.1.0/tests/test_catalog.py +47 -0
  22. wunder_mobility_mcp-0.1.0/tests/test_docs_links.py +33 -0
  23. wunder_mobility_mcp-0.1.0/tests/test_docs_source.py +14 -0
  24. wunder_mobility_mcp-0.1.0/tests/test_mcp_tools.py +122 -0
  25. wunder_mobility_mcp-0.1.0/tests/test_openapi.py +31 -0
  26. wunder_mobility_mcp-0.1.0/tests/test_search.py +94 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Wunder Mobility GmbH
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,243 @@
1
+ Metadata-Version: 2.4
2
+ Name: wunder-mobility-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server exposing the Wunder Mobility public API documentation
5
+ Author: Wunder Mobility
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Wunder-Mobility/public-api-mcp
8
+ Project-URL: Documentation, https://docs.wundermobility.services
9
+ Project-URL: Bug Tracker, https://github.com/Wunder-Mobility/public-api-mcp/issues
10
+ Keywords: mcp,wunder,mobility,api,documentation
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Python: >=3.12
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: certifi>=2024.0.0
19
+ Requires-Dist: fastmcp>=3.3.1
20
+ Requires-Dist: platformdirs>=4.0.0
21
+ Requires-Dist: pyyaml>=6.0.2
22
+ Requires-Dist: rank-bm25>=0.2.2
23
+ Requires-Dist: uvicorn[standard]>=0.32.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=8.3.0; extra == "dev"
26
+ Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
27
+ Requires-Dist: httpx>=0.27.0; extra == "dev"
28
+ Dynamic: license-file
29
+
30
+ # Wunder Mobility MCP
31
+
32
+ > **Give your AI coding assistant live knowledge of the Wunder Mobility API.**
33
+
34
+ This is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that puts the full Wunder Mobility public API documentation — every guide, every endpoint, every request/response schema — directly inside your AI assistant.
35
+
36
+ Instead of copy-pasting docs or hoping your assistant's training data is up to date, you can just ask:
37
+
38
+ > *"What endpoints do I need to build a payment flow for my users?"*
39
+ > *"Show me the full spec for creating a booking."*
40
+ > *"How does vehicle discovery work in the User API?"*
41
+
42
+ …and get back ranked, cited answers with deep-links straight to [docs.wundermobility.services](https://docs.wundermobility.services/).
43
+
44
+ Works with **Claude Code**, **Cursor**, **Windsurf**, **VS Code with Copilot**, and any other MCP-compatible client.
45
+
46
+ ---
47
+
48
+ ## What it does
49
+
50
+ The MCP server exposes four tools your assistant can call automatically:
51
+
52
+ | Tool | What your assistant uses it for |
53
+ |---|---|
54
+ | `search_api_docs(query)` | *"Find the right endpoint or guide for this flow"* — full-text search across all guides and OpenAPI specs. |
55
+ | `list_endpoints(api, tag?)` | *"What endpoints exist in the User API under Payments?"* — browse by API namespace and tag. |
56
+ | `get_endpoint_spec(operation_id)` | *"Give me the exact request/response schema for this endpoint."* — returns the raw OpenAPI operation object. |
57
+ | `get_guide(slug)` | *"Explain how authentication works."* — returns the full markdown guide. |
58
+
59
+ Every result includes a Stoplight deep-link so you (or your assistant) can open the same content in a browser.
60
+
61
+ Docs are pulled **live from GitHub** on every startup and cached locally — so your assistant always has the current API, not a stale snapshot.
62
+
63
+ ---
64
+
65
+ ## Install
66
+
67
+ ### Option A — `uvx` (recommended, no cloning needed)
68
+
69
+ [`uvx`](https://docs.astral.sh/uv/) runs the package directly from PyPI — no Python environment setup, no cloning.
70
+
71
+ **Install `uv` if you don't have it:**
72
+ ```bash
73
+ curl -LsSf https://astral.sh/uv/install.sh | sh # macOS / Linux
74
+ # or: brew install uv
75
+ # or: winget install astral-sh.uv # Windows
76
+ ```
77
+
78
+ **Claude Code:**
79
+ ```bash
80
+ claude mcp add wunder-api uvx wunder-mobility-mcp
81
+ ```
82
+
83
+ **Cursor** — add to your MCP settings (`~/.cursor/mcp.json` or via *Settings → MCP*):
84
+ ```json
85
+ {
86
+ "mcpServers": {
87
+ "wunder-api": {
88
+ "command": "uvx",
89
+ "args": ["wunder-mobility-mcp"]
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ **Windsurf** — add to `~/.codeium/windsurf/mcp_config.json`:
96
+ ```json
97
+ {
98
+ "mcpServers": {
99
+ "wunder-api": {
100
+ "command": "uvx",
101
+ "args": ["wunder-mobility-mcp"]
102
+ }
103
+ }
104
+ }
105
+ ```
106
+
107
+ **Any client that accepts a command:** use `uvx` as the command and `["wunder-mobility-mcp"]` as the args.
108
+
109
+ On first start, the server fetches the latest docs from GitHub (~5–10s) and caches them locally. Every subsequent start is instant.
110
+
111
+ ### Option B — clone and run
112
+
113
+ If you'd rather run from source:
114
+
115
+ **Prerequisites:** Python 3.12+, git.
116
+
117
+ ```bash
118
+ git clone https://github.com/Wunder-Mobility/public-api-mcp.git
119
+ cd public-api-mcp
120
+ make install
121
+ ```
122
+
123
+ **Claude Code:**
124
+ ```bash
125
+ claude mcp add wunder-api "$(pwd)/.venv/bin/wunder-mobility-mcp"
126
+ ```
127
+
128
+ **Cursor:**
129
+ ```json
130
+ {
131
+ "mcpServers": {
132
+ "wunder-api": {
133
+ "command": "/path/to/public-api-mcp/.venv/bin/wunder-mobility-mcp"
134
+ }
135
+ }
136
+ }
137
+ ```
138
+
139
+ Replace `/path/to/public-api-mcp` with your actual clone path (`pwd` inside the repo).
140
+
141
+ ### Option C — hosted URL (no local install at all)
142
+
143
+ Point your client at our hosted server — nothing to install or run locally.
144
+
145
+ **Claude Code:**
146
+ ```bash
147
+ claude mcp add wunder-api --transport http https://mcp.wundermobility.services/mcp
148
+ ```
149
+
150
+ **Cursor / Windsurf / other clients:**
151
+ ```json
152
+ {
153
+ "mcpServers": {
154
+ "wunder-api": {
155
+ "url": "https://mcp.wundermobility.services/mcp"
156
+ }
157
+ }
158
+ }
159
+ ```
160
+
161
+ ---
162
+
163
+ ## Verify it's working
164
+
165
+ Once connected, open your assistant and try:
166
+
167
+ - *"Search the Wunder API docs for payment flow"*
168
+ - *"List all endpoints in the Wunder operations API"*
169
+ - *"Get the guide for user authentication"*
170
+
171
+ You should see the tools being called and results coming back with Stoplight links.
172
+
173
+ ---
174
+
175
+ ## How docs stay current
176
+
177
+ The docs live in [`Wunder-Mobility/public-api-docs`](https://github.com/Wunder-Mobility/public-api-docs) — a public GitHub repo. On every server startup, this MCP downloads the latest tarball from that repo and caches it locally. No manual sync, no stale snapshots.
178
+
179
+ **To get the latest docs locally:** restart your MCP server (or restart your editor). The cache lives at:
180
+
181
+ | OS | Cache path |
182
+ |---|---|
183
+ | macOS | `~/Library/Caches/wunder-mobility-mcp/public-api-docs` |
184
+ | Linux | `~/.cache/wunder-mobility-mcp/public-api-docs` |
185
+ | Windows | `%LOCALAPPDATA%\wunder-mobility-mcp\Cache\public-api-docs` |
186
+
187
+ ---
188
+
189
+ ## Development
190
+
191
+ ```bash
192
+ make install # create .venv, install package + dev deps
193
+ make test # run the test suite (requires network on first run)
194
+ make run # start HTTP server on http://localhost:8080/mcp
195
+ ```
196
+
197
+ > `make test` fetches live docs from GitHub on the first run (~10s). Subsequent runs use the local cache.
198
+
199
+ ### Project structure
200
+
201
+ ```
202
+ src/wunder_mcp/
203
+ docs_source.py # fetches & caches the docs tarball from GitHub
204
+ catalog.py # builds the in-memory guide + endpoint catalog
205
+ docs_links.py # parses toc.json and builds Stoplight deep-link URLs
206
+ openapi.py # loads and indexes OpenAPI specs
207
+ search.py # BM25 search index
208
+ server.py # MCP tool definitions (FastMCP)
209
+ app.py # HTTP entrypoint (uvicorn)
210
+ stdio.py # stdio entrypoint
211
+ ```
212
+
213
+ ### Publishing a new version
214
+
215
+ 1. Bump `version` in `pyproject.toml`
216
+ 2. Commit and tag: `git tag v0.x.x && git push --tags`
217
+ 3. The `publish.yml` workflow builds and uploads to PyPI automatically via trusted publishing
218
+
219
+ **One-time PyPI setup** (before the first publish): go to [pypi.org](https://pypi.org) → your project → Publishing → add a trusted publisher with owner `Wunder-Mobility`, repo `public-api-mcp`, workflow `publish.yml`, environment `pypi`.
220
+
221
+ ---
222
+
223
+ ## Deploying your own hosted instance
224
+
225
+ The server runs on AWS Lambda behind a Function URL. See `template.yaml` for the SAM definition.
226
+
227
+ **Prerequisites:** AWS CLI, SAM CLI (`brew install aws-sam-cli`), Python 3.12.
228
+
229
+ ```bash
230
+ sam build
231
+ sam deploy --guided # first time; saves config to samconfig.toml
232
+ sam build && sam deploy # subsequent deploys
233
+ ```
234
+
235
+ Doc changes don't require a redeploy — the Lambda fetches the latest docs on every cold start.
236
+
237
+ ---
238
+
239
+ ## Roadmap
240
+
241
+ - **v0.1 (current):** read-only docs search, public and unauthenticated, docs fetched live on startup.
242
+ - **v0.2:** per-tenant API key auth + usage telemetry.
243
+ - **v1.0:** tools that call the live Wunder API directly (e.g. `create_test_booking`), gated by a customer API key.
@@ -0,0 +1,214 @@
1
+ # Wunder Mobility MCP
2
+
3
+ > **Give your AI coding assistant live knowledge of the Wunder Mobility API.**
4
+
5
+ This is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that puts the full Wunder Mobility public API documentation — every guide, every endpoint, every request/response schema — directly inside your AI assistant.
6
+
7
+ Instead of copy-pasting docs or hoping your assistant's training data is up to date, you can just ask:
8
+
9
+ > *"What endpoints do I need to build a payment flow for my users?"*
10
+ > *"Show me the full spec for creating a booking."*
11
+ > *"How does vehicle discovery work in the User API?"*
12
+
13
+ …and get back ranked, cited answers with deep-links straight to [docs.wundermobility.services](https://docs.wundermobility.services/).
14
+
15
+ Works with **Claude Code**, **Cursor**, **Windsurf**, **VS Code with Copilot**, and any other MCP-compatible client.
16
+
17
+ ---
18
+
19
+ ## What it does
20
+
21
+ The MCP server exposes four tools your assistant can call automatically:
22
+
23
+ | Tool | What your assistant uses it for |
24
+ |---|---|
25
+ | `search_api_docs(query)` | *"Find the right endpoint or guide for this flow"* — full-text search across all guides and OpenAPI specs. |
26
+ | `list_endpoints(api, tag?)` | *"What endpoints exist in the User API under Payments?"* — browse by API namespace and tag. |
27
+ | `get_endpoint_spec(operation_id)` | *"Give me the exact request/response schema for this endpoint."* — returns the raw OpenAPI operation object. |
28
+ | `get_guide(slug)` | *"Explain how authentication works."* — returns the full markdown guide. |
29
+
30
+ Every result includes a Stoplight deep-link so you (or your assistant) can open the same content in a browser.
31
+
32
+ Docs are pulled **live from GitHub** on every startup and cached locally — so your assistant always has the current API, not a stale snapshot.
33
+
34
+ ---
35
+
36
+ ## Install
37
+
38
+ ### Option A — `uvx` (recommended, no cloning needed)
39
+
40
+ [`uvx`](https://docs.astral.sh/uv/) runs the package directly from PyPI — no Python environment setup, no cloning.
41
+
42
+ **Install `uv` if you don't have it:**
43
+ ```bash
44
+ curl -LsSf https://astral.sh/uv/install.sh | sh # macOS / Linux
45
+ # or: brew install uv
46
+ # or: winget install astral-sh.uv # Windows
47
+ ```
48
+
49
+ **Claude Code:**
50
+ ```bash
51
+ claude mcp add wunder-api uvx wunder-mobility-mcp
52
+ ```
53
+
54
+ **Cursor** — add to your MCP settings (`~/.cursor/mcp.json` or via *Settings → MCP*):
55
+ ```json
56
+ {
57
+ "mcpServers": {
58
+ "wunder-api": {
59
+ "command": "uvx",
60
+ "args": ["wunder-mobility-mcp"]
61
+ }
62
+ }
63
+ }
64
+ ```
65
+
66
+ **Windsurf** — add to `~/.codeium/windsurf/mcp_config.json`:
67
+ ```json
68
+ {
69
+ "mcpServers": {
70
+ "wunder-api": {
71
+ "command": "uvx",
72
+ "args": ["wunder-mobility-mcp"]
73
+ }
74
+ }
75
+ }
76
+ ```
77
+
78
+ **Any client that accepts a command:** use `uvx` as the command and `["wunder-mobility-mcp"]` as the args.
79
+
80
+ On first start, the server fetches the latest docs from GitHub (~5–10s) and caches them locally. Every subsequent start is instant.
81
+
82
+ ### Option B — clone and run
83
+
84
+ If you'd rather run from source:
85
+
86
+ **Prerequisites:** Python 3.12+, git.
87
+
88
+ ```bash
89
+ git clone https://github.com/Wunder-Mobility/public-api-mcp.git
90
+ cd public-api-mcp
91
+ make install
92
+ ```
93
+
94
+ **Claude Code:**
95
+ ```bash
96
+ claude mcp add wunder-api "$(pwd)/.venv/bin/wunder-mobility-mcp"
97
+ ```
98
+
99
+ **Cursor:**
100
+ ```json
101
+ {
102
+ "mcpServers": {
103
+ "wunder-api": {
104
+ "command": "/path/to/public-api-mcp/.venv/bin/wunder-mobility-mcp"
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
110
+ Replace `/path/to/public-api-mcp` with your actual clone path (`pwd` inside the repo).
111
+
112
+ ### Option C — hosted URL (no local install at all)
113
+
114
+ Point your client at our hosted server — nothing to install or run locally.
115
+
116
+ **Claude Code:**
117
+ ```bash
118
+ claude mcp add wunder-api --transport http https://mcp.wundermobility.services/mcp
119
+ ```
120
+
121
+ **Cursor / Windsurf / other clients:**
122
+ ```json
123
+ {
124
+ "mcpServers": {
125
+ "wunder-api": {
126
+ "url": "https://mcp.wundermobility.services/mcp"
127
+ }
128
+ }
129
+ }
130
+ ```
131
+
132
+ ---
133
+
134
+ ## Verify it's working
135
+
136
+ Once connected, open your assistant and try:
137
+
138
+ - *"Search the Wunder API docs for payment flow"*
139
+ - *"List all endpoints in the Wunder operations API"*
140
+ - *"Get the guide for user authentication"*
141
+
142
+ You should see the tools being called and results coming back with Stoplight links.
143
+
144
+ ---
145
+
146
+ ## How docs stay current
147
+
148
+ The docs live in [`Wunder-Mobility/public-api-docs`](https://github.com/Wunder-Mobility/public-api-docs) — a public GitHub repo. On every server startup, this MCP downloads the latest tarball from that repo and caches it locally. No manual sync, no stale snapshots.
149
+
150
+ **To get the latest docs locally:** restart your MCP server (or restart your editor). The cache lives at:
151
+
152
+ | OS | Cache path |
153
+ |---|---|
154
+ | macOS | `~/Library/Caches/wunder-mobility-mcp/public-api-docs` |
155
+ | Linux | `~/.cache/wunder-mobility-mcp/public-api-docs` |
156
+ | Windows | `%LOCALAPPDATA%\wunder-mobility-mcp\Cache\public-api-docs` |
157
+
158
+ ---
159
+
160
+ ## Development
161
+
162
+ ```bash
163
+ make install # create .venv, install package + dev deps
164
+ make test # run the test suite (requires network on first run)
165
+ make run # start HTTP server on http://localhost:8080/mcp
166
+ ```
167
+
168
+ > `make test` fetches live docs from GitHub on the first run (~10s). Subsequent runs use the local cache.
169
+
170
+ ### Project structure
171
+
172
+ ```
173
+ src/wunder_mcp/
174
+ docs_source.py # fetches & caches the docs tarball from GitHub
175
+ catalog.py # builds the in-memory guide + endpoint catalog
176
+ docs_links.py # parses toc.json and builds Stoplight deep-link URLs
177
+ openapi.py # loads and indexes OpenAPI specs
178
+ search.py # BM25 search index
179
+ server.py # MCP tool definitions (FastMCP)
180
+ app.py # HTTP entrypoint (uvicorn)
181
+ stdio.py # stdio entrypoint
182
+ ```
183
+
184
+ ### Publishing a new version
185
+
186
+ 1. Bump `version` in `pyproject.toml`
187
+ 2. Commit and tag: `git tag v0.x.x && git push --tags`
188
+ 3. The `publish.yml` workflow builds and uploads to PyPI automatically via trusted publishing
189
+
190
+ **One-time PyPI setup** (before the first publish): go to [pypi.org](https://pypi.org) → your project → Publishing → add a trusted publisher with owner `Wunder-Mobility`, repo `public-api-mcp`, workflow `publish.yml`, environment `pypi`.
191
+
192
+ ---
193
+
194
+ ## Deploying your own hosted instance
195
+
196
+ The server runs on AWS Lambda behind a Function URL. See `template.yaml` for the SAM definition.
197
+
198
+ **Prerequisites:** AWS CLI, SAM CLI (`brew install aws-sam-cli`), Python 3.12.
199
+
200
+ ```bash
201
+ sam build
202
+ sam deploy --guided # first time; saves config to samconfig.toml
203
+ sam build && sam deploy # subsequent deploys
204
+ ```
205
+
206
+ Doc changes don't require a redeploy — the Lambda fetches the latest docs on every cold start.
207
+
208
+ ---
209
+
210
+ ## Roadmap
211
+
212
+ - **v0.1 (current):** read-only docs search, public and unauthenticated, docs fetched live on startup.
213
+ - **v0.2:** per-tenant API key auth + usage telemetry.
214
+ - **v1.0:** tools that call the live Wunder API directly (e.g. `create_test_booking`), gated by a customer API key.
@@ -0,0 +1,50 @@
1
+ [project]
2
+ name = "wunder-mobility-mcp"
3
+ version = "0.1.0"
4
+ description = "MCP server exposing the Wunder Mobility public API documentation"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ authors = [{ name = "Wunder Mobility" }]
8
+ license = { text = "MIT" }
9
+ keywords = ["mcp", "wunder", "mobility", "api", "documentation"]
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "Intended Audience :: Developers",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Programming Language :: Python :: 3.12",
15
+ ]
16
+ dependencies = [
17
+ "certifi>=2024.0.0",
18
+ "fastmcp>=3.3.1",
19
+ "platformdirs>=4.0.0",
20
+ "pyyaml>=6.0.2",
21
+ "rank-bm25>=0.2.2",
22
+ "uvicorn[standard]>=0.32.0",
23
+ ]
24
+
25
+ [project.urls]
26
+ Homepage = "https://github.com/Wunder-Mobility/public-api-mcp"
27
+ Documentation = "https://docs.wundermobility.services"
28
+ "Bug Tracker" = "https://github.com/Wunder-Mobility/public-api-mcp/issues"
29
+
30
+ [project.optional-dependencies]
31
+ dev = [
32
+ "pytest>=8.3.0",
33
+ "pytest-asyncio>=0.24.0",
34
+ "httpx>=0.27.0",
35
+ ]
36
+
37
+ [project.scripts]
38
+ wunder-mobility-mcp = "wunder_mcp.stdio:main"
39
+
40
+ [build-system]
41
+ requires = ["setuptools>=68"]
42
+ build-backend = "setuptools.build_meta"
43
+
44
+ [tool.setuptools.packages.find]
45
+ where = ["src"]
46
+
47
+ [tool.pytest.ini_options]
48
+ asyncio_mode = "auto"
49
+ testpaths = ["tests"]
50
+ pythonpath = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,28 @@
1
+ """Entrypoint for both local dev (uvicorn) and Lambda (via Lambda Web Adapter).
2
+
3
+ Local dev: `python -m wunder_mcp.app` (serves on http://localhost:8080/mcp)
4
+ Lambda: Lambda Web Adapter forwards Function URL invocations to this same
5
+ uvicorn process inside the container.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+
12
+ import uvicorn
13
+
14
+ from .server import build_server
15
+
16
+ _server = build_server()
17
+ # ASGI app for HTTP/SSE transport at path /mcp.
18
+ app = _server.http_app(path="/mcp")
19
+
20
+
21
+ def main() -> None:
22
+ host = os.environ.get("HOST", "0.0.0.0")
23
+ port = int(os.environ.get("PORT", "8080"))
24
+ uvicorn.run(app, host=host, port=port, log_level=os.environ.get("LOG_LEVEL", "info"))
25
+
26
+
27
+ if __name__ == "__main__":
28
+ main()
@@ -0,0 +1,109 @@
1
+ """Builds the in-memory catalog of guides + endpoints, ready to serve.
2
+
3
+ A Catalog is loaded once at module init and reused across all requests.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import re
9
+ from dataclasses import dataclass, field
10
+ from pathlib import Path
11
+
12
+ from .docs_links import GuideEntry, endpoint_url, load_toc, to_kebab
13
+ from .openapi import Operation, load_operations
14
+
15
+
16
+ @dataclass
17
+ class Guide:
18
+ """One markdown guide with its rendered Stoplight URL and full body."""
19
+
20
+ title: str
21
+ slug: str
22
+ file_stem: str # filename without extension
23
+ body: str
24
+ url: str
25
+
26
+ @property
27
+ def search_text(self) -> str:
28
+ return f"{self.title}\n{self.body}"
29
+
30
+
31
+ @dataclass
32
+ class Catalog:
33
+ """The full set of guides and endpoints, indexed for lookup."""
34
+
35
+ guides: list[Guide] = field(default_factory=list)
36
+ operations: list[Operation] = field(default_factory=list)
37
+ _guides_by_slug: dict[str, Guide] = field(default_factory=dict)
38
+ _guides_by_stem: dict[str, Guide] = field(default_factory=dict)
39
+ _ops_by_id: dict[str, Operation] = field(default_factory=dict)
40
+
41
+ def __post_init__(self) -> None:
42
+ self._guides_by_slug = {g.slug: g for g in self.guides}
43
+ self._guides_by_stem = {g.file_stem: g for g in self.guides}
44
+ self._ops_by_id = {op.operation_id: op for op in self.operations}
45
+
46
+ def guide(self, ref: str) -> Guide | None:
47
+ return self._guides_by_slug.get(ref) or self._guides_by_stem.get(ref)
48
+
49
+ def operation(self, operation_id: str) -> Operation | None:
50
+ return self._ops_by_id.get(operation_id)
51
+
52
+ def operations_for(self, api: str | None = None, tag: str | None = None) -> list[Operation]:
53
+ results = self.operations
54
+ if api:
55
+ results = [op for op in results if op.api == api]
56
+ if tag:
57
+ results = [op for op in results if tag in op.tags]
58
+ return results
59
+
60
+
61
+ _HEADING_RE = re.compile(r"^# (.+)$", re.MULTILINE)
62
+
63
+
64
+ def _extract_title(file_stem: str, body: str) -> str:
65
+ match = _HEADING_RE.search(body)
66
+ if match:
67
+ return match.group(1).strip()
68
+ # Fall back to the file stem in Title Case.
69
+ return file_stem.replace("-", " ").title()
70
+
71
+
72
+ def load_catalog(vendor_dir: Path) -> Catalog:
73
+ docs_dir = vendor_dir / "docs"
74
+ ref_dir = vendor_dir / "reference"
75
+ toc_path = vendor_dir / "toc.json"
76
+
77
+ if not docs_dir.exists() or not ref_dir.exists() or not toc_path.exists():
78
+ raise FileNotFoundError(
79
+ f"Docs not found in {vendor_dir}. Check your network connection or WUNDER_MCP_CACHE_DIR."
80
+ )
81
+
82
+ toc_entries: list[GuideEntry] = load_toc(toc_path)
83
+ guides: list[Guide] = []
84
+ seen: set[str] = set()
85
+ for entry in toc_entries:
86
+ md_path = docs_dir / entry.filename
87
+ if not md_path.exists() or entry.slug in seen:
88
+ continue
89
+ seen.add(entry.slug)
90
+ body = md_path.read_text()
91
+ guides.append(
92
+ Guide(
93
+ title=_extract_title(entry.file_stem, body) or entry.title,
94
+ slug=entry.slug,
95
+ file_stem=entry.file_stem,
96
+ body=body,
97
+ url=entry.url,
98
+ )
99
+ )
100
+
101
+ operations: list[Operation] = load_operations(ref_dir)
102
+ return Catalog(guides=guides, operations=operations)
103
+
104
+
105
+ def stoplight_endpoint_url(operation: Operation) -> str | None:
106
+ """Return Stoplight URL for an endpoint, or None if the API namespace is unknown."""
107
+ if not operation.api:
108
+ return None
109
+ return endpoint_url(operation.api, to_kebab(operation.operation_id))