Skip to content

Commit 9e9be3d

Browse files
committed
Add README for mcpgateway.wrapper
Signed-off-by: Mihai Criveti <[email protected]>
1 parent 5c13231 commit 9e9be3d

File tree

7 files changed

+146
-71
lines changed

7 files changed

+146
-71
lines changed

.github/CODEOWNERS

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
# All files in the repo
22
* @crivetimihai
33

4-
# Ownership for the mcpgateway-wrapper
5-
/mcpgateway-wrapper/ @crivetimihai @kevalmahajan @madhav165
6-
74
# Ownership for all tests
85
/tests/ @crivetimihai @kevalmahajan @madhav165

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ repos:
8989
- id: forbid-ai-stock-phrases
9090
name: ❌ Forbid AI Stock Phrases
9191
description: Prevents common AI-generated phrases from being committed.
92-
entry: '(?i)(source=chatgpt.com|turn0search0|filecite|as an ai language model|i am an ai developed by|this response was generated by|i don''t have real-time information|i don''t have access to real-time|i can''t browse the internet|i cannot browse the internet|my knowledge cutoff|my training data|i''m not able to access|i don''t have the ability to)'
92+
entry: '(?i)(source=chatgpt.com|turn0search0|filecite|unchanged|as an ai language model|i am an ai developed by|this response was generated by|i don''t have real-time information|i don''t have access to real-time|i can''t browse the internet|i cannot browse the internet|my knowledge cutoff|my training data|i''m not able to access|i don''t have the ability to)'
9393
language: pygrep
9494
types: [text]
9595
exclude: ^\.pre-commit-config\.yaml$

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
66

77
---
88

9-
## [0.2.0] - 2025-06-08 (pending)
9+
## [0.2.0] - 2025-06-15 (pending)
10+
11+
### Added
12+
13+
* Moved mcpgateway-wrapper to mcpgateway/wrapper.py so it can run as a Python module (python3 -m mcpgateway.wrapper)
14+
* Integrated version into UI. API and separate /version endpoint also available.
15+
* Added /ready endpoint
1016

1117
### Fixed
1218

Makefile

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -342,10 +342,10 @@ lint:
342342
## --------------------------------------------------------------------------- ##
343343
autoflake: ## 🧹 Strip unused imports / vars
344344
@$(VENV_DIR)/bin/autoflake --in-place --remove-all-unused-imports \
345-
--remove-unused-variables -r mcpgateway mcpgateway-wrapper tests
345+
--remove-unused-variables -r mcpgateway tests
346346

347347
black: ## 🎨 Reformat code with black
348-
@echo "🎨 black …" && $(VENV_DIR)/bin/black -l 200 mcpgateway mcpgateway-wrapper tests
348+
@echo "🎨 black …" && $(VENV_DIR)/bin/black -l 200 mcpgateway tests
349349

350350
isort: ## 🔀 Sort imports
351351
@echo "🔀 isort …" && $(VENV_DIR)/bin/isort .
@@ -375,19 +375,19 @@ pre-commit: ## 🪄 Run pre-commit hooks
375375
@$(VENV_DIR)/bin/pre-commit run --all-files --show-diff-on-failure
376376

377377
ruff: ## ⚡ Ruff lint + format
378-
@$(VENV_DIR)/bin/ruff check mcpgateway && $(VENV_DIR)/bin/ruff format mcpgateway mcpgateway-wrapper tests
378+
@$(VENV_DIR)/bin/ruff check mcpgateway && $(VENV_DIR)/bin/ruff format mcpgateway tests
379379

380380
ty: ## ⚡ Ty type checker
381-
@$(VENV_DIR)/bin/ty check mcpgateway mcpgateway-wrapper tests
381+
@$(VENV_DIR)/bin/ty check mcpgateway tests
382382

383383
pyright: ## 🏷️ Pyright type-checking
384-
@$(VENV_DIR)/bin/pyright mcpgateway mcpgateway-wrapper tests
384+
@$(VENV_DIR)/bin/pyright mcpgateway tests
385385

386386
radon: ## 📈 Complexity / MI metrics
387-
@$(VENV_DIR)/bin/radon mi -s mcpgateway mcpgateway-wrapper tests && \
388-
$(VENV_DIR)/bin/radon cc -s mcpgateway mcpgateway-wrapper tests && \
389-
$(VENV_DIR)/bin/radon hal mcpgateway mcpgateway-wrapper tests && \
390-
$(VENV_DIR)/bin/radon raw -s mcpgateway mcpgateway-wrapper tests
387+
@$(VENV_DIR)/bin/radon mi -s mcpgateway tests && \
388+
$(VENV_DIR)/bin/radon cc -s mcpgateway tests && \
389+
$(VENV_DIR)/bin/radon hal mcpgateway tests && \
390+
$(VENV_DIR)/bin/radon raw -s mcpgateway tests
391391

392392
pyroma: ## 📦 Packaging metadata check
393393
@$(VENV_DIR)/bin/pyroma -d .
@@ -444,7 +444,7 @@ sbom: ## 🛡️ Generate SBOM & security report
444444

445445
pytype: ## 🧠 Pytype static type analysis
446446
@echo "🧠 Pytype analysis…"
447-
@$(VENV_DIR)/bin/pytype -V 3.12 -j auto mcpgateway mcpgateway-wrapper tests
447+
@$(VENV_DIR)/bin/pytype -V 3.12 -j auto mcpgateway tests
448448

449449
check-manifest: ## 📦 Verify MANIFEST.in completeness
450450
@echo "📦 Verifying MANIFEST.in completeness…"

README.md

Lines changed: 127 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ curl -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/s
120120
# To stop the running process, you can either:
121121
fg # Return the process to foreground, you can not Ctrl + C, or:
122122
pkill mcpgateway
123+
124+
# Optionally, test the stdio wrapper to mirror tools from the gateway:
125+
# This lets you connect to the gateway with tools that don't support SSE:
126+
export MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN}
127+
export MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/1
128+
python3 -m mcpgateway.wrapper
123129
```
124130

125131
See [.env.example](.env.example) for full list of ENV variables you can use to override the configuration.
@@ -172,22 +178,35 @@ curl -s -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
172178
http://localhost:4444/version | jq
173179
```
174180

175-
### Running the mcpgateway-wrapper
181+
### Running the MCP Gateway stdio wrapper
182+
183+
The `mcpgateway.wrapper` lets you connect to the gateway over **stdio**, while retaining authentication using the JWT token when the wrapper connect to a remote gateway. You should run this from a MCP client. You can test this from a shell with:
184+
185+
```bash
186+
# Set environment variables
187+
export MCPGATEWAY_BEARER_TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token --username admin --exp 10080 --secret my-test-key)
188+
export MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN}
189+
export MCP_SERVER_CATALOG_URLS='http://localhost:4444/servers/1'
190+
export MCP_TOOL_CALL_TIMEOUT=120
191+
export MCP_WRAPPER_LOG_LEVEL=DEBUG # or OFF to disable logging
192+
193+
# Run the wrapper from the installed module
194+
python3 -m mcpgateway.wrapper
195+
```
176196

177-
The mcpgateway-wrapper lets you connect to the gateway over stdio, while retaining authentication using the JWT token when the wrapper connect to a remote gateway. You should run this from a MCP client. You can test this from a shell with:
197+
**Or using the container image:**
178198

179199
```bash
180-
docker run -i --name mcpgateway-wrapper \
181-
--entrypoint uv \
182-
-e UV_CACHE_DIR=/tmp/uv-cache \
183-
-e MCP_SERVER_CATALOG_URLS=http://host.docker.internal:4444 \
200+
docker run --rm -i \
184201
-e MCP_AUTH_TOKEN=$MCPGATEWAY_BEARER_TOKEN \
202+
-e MCP_SERVER_CATALOG_URLS=http://host.docker.internal:4444/servers/1 \
203+
-e MCP_TOOL_CALL_TIMEOUT=120 \
204+
-e MCP_WRAPPER_LOG_LEVEL=DEBUG \
185205
ghcr.io/ibm/mcp-context-forge:latest \
186-
run --directory mcpgateway-wrapper mcpgateway-wrapper
187-
# You'll see a message similar to: Installed 21 packages in 6ms - it's now expecting input from an MCP client
206+
python3 -m mcpgateway.wrapper
188207
```
189208

190-
Testing `mcpgateway-wrapper` by hand:
209+
**Testing `mcpgateway-wrapper` by hand:**
191210

192211
Because the wrapper speaks JSON-RPC over stdin/stdout, you can interact with it using nothing more than a terminal or pipes.
193212

@@ -240,81 +259,135 @@ Expected:
240259

241260
```
242261

243-
### Running from a MCP Client
244262

245-
The `mcpgateway-wrapper` should be used with an MCP Client that does not support SSE. You can configure it as such.
263+
### 🧩 Running from an MCP Client (`mcpgateway.wrapper`)
264+
265+
The `mcpgateway.wrapper` exposes everything your Gateway knows about over **stdio**, so any MCP client that *can't* (or *shouldn't*) open an authenticated SSE stream still gets full tool-calling power.
266+
267+
> **Remember** to substitute your real Gateway URL (and server ID) for `http://localhost:4444/servers/1`.
268+
> When inside Docker/Podman, that often becomes `http://host.docker.internal:4444/servers/1` (macOS/Windows) or the gateway container's hostname (Linux).
269+
270+
---
271+
272+
<details>
273+
<summary><strong>🐳 Docker / Podman</strong></summary>
274+
275+
```bash
276+
docker run -i --rm \
277+
--network=host \
278+
-e MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/1 \
279+
-e MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN} \
280+
-e MCP_TOOL_CALL_TIMEOUT=120 \
281+
ghcr.io/ibm/mcp-context-forge:latest \
282+
python3 -m mcpgateway.wrapper
283+
```
284+
285+
</details>
286+
287+
---
288+
289+
<details>
290+
<summary><strong>📦 pipx (one-liner install &amp; run)</strong></summary>
246291

247-
Remember to replace the `MCP_SERVER_CATALOG_URL` with the actual URL of your MCP Gateway. Consider container networking - when running this via a container engine, this should represent a network accessible from Docker/Podman, ex: `http://host.docker.internal:4444/servers/1`
292+
```bash
293+
# Install gateway package in its own isolated venv
294+
pipx install --include-deps mcp-contextforge-gateway
295+
296+
# Run the stdio wrapper
297+
MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN} \
298+
MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/1 \
299+
python3 -m mcpgateway.wrapper
300+
```
248301

249-
You have a number of options for running the wrapper. Docker/Podman, to run it from the container. `uvx`, `uvenv` or `pipx` to run it straight from pip. Or just running it with Python from a local directory. Adjust your command accordingly.
302+
**Claude Desktop JSON** (uses the host Python that pipx injected):
250303

251304
```json
252305
{
253-
"servers": {
306+
"mcpServers": {
254307
"mcpgateway-wrapper": {
255-
"command": "docker",
256-
"args": [
257-
"run",
258-
"--rm",
259-
"--network=host",
260-
"-i",
261-
"-e",
262-
"MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/1",
263-
"-e",
264-
"MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN}",
265-
"--entrypoint",
266-
"uv",
267-
"ghcr.io/ibm/mcp-context-forge:latest",
268-
"run",
269-
"--directory",
270-
"mcpgateway-wrapper",
271-
"mcpgateway-wrapper"
272-
],
308+
"command": "python3",
309+
"args": ["-m", "mcpgateway.wrapper"],
273310
"env": {
274-
"MCPGATEWAY_BEARER_TOKEN": "${MCPGATEWAY_BEARER_TOKEN}"
311+
"MCP_AUTH_TOKEN": "<your-token>",
312+
"MCP_SERVER_CATALOG_URLS": "http://localhost:4444/servers/1",
313+
"MCP_TOOL_CALL_TIMEOUT": "120"
275314
}
276315
}
277316
}
278317
}
279318
```
280319

281-
## Quick Start (Claude Desktop)
320+
</details>
321+
322+
---
323+
324+
<details>
325+
<summary><strong>⚡ uv / uvenv (light-speed venvs)</strong></summary>
326+
327+
#### 1 · Install <code>uv</code> (<code>uvenv</code> is an alias it provides)
328+
329+
```bash
330+
# (a) official one-liner
331+
curl -Ls https://astral.sh/uv/install.sh | sh
332+
333+
# (b) or via pipx
334+
pipx install uv
335+
```
336+
337+
#### 2 · Create an on-the-spot venv & run the wrapper
338+
339+
```bash
340+
# Create venv in ~/.venv/mcpgateway (or current dir if you prefer)
341+
uv venv ~/.venv/mcpgateway
342+
source ~/.venv/mcpgateway/bin/activate
343+
344+
# Install the gateway package very quickly
345+
uv pip install mcp-contextforge-gateway
346+
347+
# Launch wrapper
348+
MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN} \
349+
MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/1 \
350+
uv python -m mcpgateway.wrapper # Use this just for testing, as the Client will run the uv command
351+
```
352+
353+
*(You can swap `uv python` for plain `python` if the venv is active.)*
282354

283-
To add the mcpgateway to Claude Desktop (or similar MCP Clients) go to `File > Settings > Developer > Edit Config` and add:
355+
#### Claude Desktop JSON (runs through **uvenv run**)
284356

285357
```json
286358
{
287359
"mcpServers": {
288360
"mcpgateway-wrapper": {
289-
"command": "docker",
361+
"command": "uvenv",
290362
"args": [
291363
"run",
292-
"--rm",
293-
"--network=host",
294-
"-i",
295-
"-e",
296-
"MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/1",
297-
"-e",
298-
"MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN}",
299-
"--entrypoint",
300-
"uv",
301-
"ghcr.io/ibm/mcp-context-forge:latest",
302-
"run",
303-
"--directory",
304-
"mcpgateway-wrapper",
305-
"mcpgateway-wrapper"
364+
"--",
365+
"python",
366+
"-m",
367+
"mcpgateway.wrapper"
306368
],
307369
"env": {
308-
"MCPGATEWAY_BEARER_TOKEN": "<place your token here>"
309-
}
370+
"MCP_AUTH_TOKEN": "<your-token>",
371+
"MCP_SERVER_CATALOG_URLS": "http://localhost:4444/servers/1"
310372
}
311373
}
312374
}
313375
```
314376

315-
Restart Claude Desktop (exiting from system tray). Go back to `File > Settings > Developer > Edit Config` to check on your configuration and view the logs.
377+
</details>
378+
379+
---
380+
381+
### 🚀 Using with Claude Desktop (or any GUI MCP client)
382+
383+
1. **Edit Config** → `File ▸ Settings ▸ Developer ▸ Edit Config`
384+
2. Paste one of the JSON blocks above (Docker / pipx / uvenv).
385+
3. Restart the app so the new stdio server is spawned.
386+
4. Open logs in the same menu to verify `mcpgateway-wrapper` started and listed your tools.
387+
388+
Need help? See:
316389

317-
For more details, see the [Claude MCP quickstart](https://modelcontextprotocol.io/quickstart/server). For issues, see [MCP Debugging](https://modelcontextprotocol.io/docs/tools/debugging).
390+
* **MCP Debugging Guide** – [https://modelcontextprotocol.io/docs/tools/debugging](https://modelcontextprotocol.io/docs/tools/debugging)
318391

319392
---
320393

mcpgateway/utils/create_jwt_token.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
CLI (default secret, default payload):
1616
$ python3 jwt_cli.py
1717
18-
Library (unchanged API):
18+
Library:
1919
```python
2020
from mcpgateway.utils.create_jwt_token import create_jwt_token, get_jwt_token
2121

tests/unit/mcpgateway/services/test_root_service.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ async def test_add_root_with_scheme():
3939
# Add an HTTP URI
4040
uri = "http://example.com/base/path"
4141
root = await service.add_root(uri)
42-
# Should remain unchanged
4342
assert root.uri == uri
4443
# Name should be the basename of the URL path
4544
assert root.name == os.path.basename(urlparse(uri).path)

0 commit comments

Comments
 (0)