Skip to content

Commit 6ebca95

Browse files
niechenclaudeqodo-merge-pro[bot]
authored
feat: refactor commands to v2 structure and add HTTP server support (#211)
* feat: refactor commands to v2 structure and add HTTP server support This commit refactors the command structure for MCPM v2 and adds comprehensive HTTP server support: ## Command Structure Changes - Move add/remove commands from target_operations to main commands directory - Rename commands to match v2 syntax: `add` → `install`, `remove` → `uninstall` - Remove v1-specific profile activation logic and complexity - Inline necessary helper functions into command files - Update CLI registration and imports ## HTTP Server Support - Add support for installing HTTP MCP servers via `mcpm install` - HTTP servers create RemoteServerConfig instead of STDIOServerConfig - Add dedicated headers field for HTTP server configuration - Headers support variable substitution (e.g., `"Authorization": "Bearer ${API_TOKEN}"`) - HTTP servers don't require command/args fields ## Smart Argument Filtering - Only prompt for arguments actually referenced in installation method - Scan for `${VARIABLE_NAME}` patterns in all installation fields - HTTP installations without variables prompt for no arguments - Traditional installations only prompt for used variables ## Bug Fixes - Fix AttributeError when accessing .command on RemoteServerConfig - Add proper type checking in run.py and display.py - Ensure HTTP servers are handled correctly throughout the system ## Tests - Update all tests to use new command structure - Add comprehensive tests for HTTP server installation - Add tests for argument filtering functionality - All tests passing with new architecture 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * chore: update dependencies and GitHub server config for HTTP support - Update fastmcp to v2.10.2 for improved HTTP server support - Update GitHub server configuration to use OAuth HTTP endpoint - Simplify GitHub server config to use GITHUB_PERSONAL_ACCESS_TOKEN - Remove custom Go installation method for cleaner config 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix: suppress FastMCP banner in run command Hide the FastMCP startup banner by setting show_banner=False for both stdio and HTTP modes. This provides a cleaner user experience without the verbose FastMCP branding output. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * Update src/mcpm/schemas/full_server_config.py Co-authored-by: qodo-merge-pro[bot] <151058649+qodo-merge-pro[bot]@users.noreply.github.com> * feat: remove debug option from CLI commands and keep middleware - Remove --debug option from mcpm run and mcpm profile run commands - Remove debug parameter from proxy factory and convenience functions - Keep MCPMDebugMiddleware class for future use - Add --host option to both run commands for network binding flexibility - Fix type annotations in middleware and proxy for Optional parameters - Remove unused imports and variables 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix: reduce log level for missing client config files from warning to debug Missing client config files are normal when clients aren't installed. This reduces noise in the default log output while still providing debug information when needed. --------- Co-authored-by: Claude <[email protected]> Co-authored-by: qodo-merge-pro[bot] <151058649+qodo-merge-pro[bot]@users.noreply.github.com>
1 parent 1885d05 commit 6ebca95

File tree

21 files changed

+937
-346
lines changed

21 files changed

+937
-346
lines changed
Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
# FastMCP Progress Notification Investigation
2+
3+
**Date**: July 8, 2025
4+
**Issue**: Progress notifications not working when FastMCP proxy uses HTTP-to-Stdio transport
5+
**Status**: Root cause identified, architectural limitation in FastMCP
6+
7+
## Executive Summary
8+
9+
Progress notifications work correctly when FastMCP proxy communicates with servers via HTTP-to-HTTP transport, but fail when using HTTP-to-Stdio transport. The issue is a context isolation problem in FastMCP's proxy implementation where progress tokens cannot be properly tracked across process boundaries.
10+
11+
## Issue Description
12+
13+
### Observed Behavior
14+
15+
When running a server through MCPM's FastMCP proxy:
16+
17+
**Working Case (HTTP → Proxy → HTTP):**
18+
- Progress notifications flow correctly from server to client
19+
- Progress tokens are preserved throughout the request lifecycle
20+
21+
**Failing Case (HTTP → Proxy → Stdio):**
22+
- Progress notifications are received by proxy but fail to forward to client
23+
- Error: `[CONTEXT] No progress token available, skipping notification`
24+
25+
### Error Logs
26+
27+
```
28+
2025-07-08 20:22:36,003 - timer.main - DEBUG - Sending progress notification: 10000/180000 ms
29+
[07/08/25 20:22:36] INFO FastMCP.fastmcp.server.proxy: received progress notification: progress=10000.0, total=180000.0, message=None
30+
DEBUG fastmcp.server.context: [CONTEXT] Reporting progress: progress=10000.0, total=180000.0, message=None
31+
DEBUG fastmcp.server.context: [CONTEXT] No progress token available, skipping notification
32+
```
33+
34+
## Technical Root Cause Analysis
35+
36+
### MCP Protocol Support
37+
38+
The MCP protocol **correctly supports** progress notifications across all transports:
39+
40+
1. **Progress Token Propagation**: MCP passes progress tokens in `_meta.progressToken` field
41+
2. **Request Metadata**: Found in `/mcp/types.py:43-50`:
42+
```python
43+
class RequestParams(BaseModel):
44+
class Meta(BaseModel):
45+
progressToken: ProgressToken | None = None
46+
"""
47+
If specified, the caller requests out-of-band progress notifications for
48+
this request (as represented by notifications/progress). The value of this
49+
parameter is an opaque token that will be attached to any subsequent
50+
notifications.
51+
"""
52+
```
53+
54+
3. **Session Implementation**: In `/mcp/shared/session.py:443+`, `send_request` properly sets progress tokens:
55+
```python
56+
# Set up progress token if progress callback is provided
57+
if progress_callback is not None:
58+
# Use request_id as progress token
59+
if "_meta" not in request_data["params"]:
60+
request_data["params"]["_meta"] = {}
61+
request_data["params"]["_meta"]["progressToken"] = request_id
62+
# Store the callback for this request
63+
self._progress_callbacks[request_id] = progress_callback
64+
```
65+
66+
### FastMCP Implementation Details
67+
68+
**Progress Handler Location**: `/fastmcp/server/proxy.py:514-539`
69+
```python
70+
@classmethod
71+
async def default_progress_handler(
72+
cls,
73+
progress: float,
74+
total: float | None,
75+
message: str | None,
76+
) -> None:
77+
"""
78+
A handler that forwards the progress notification from the remote server to the proxy's connected clients.
79+
"""
80+
ctx = get_context()
81+
logger.info("received progress notification: progress=%s, total=%s, message=%s", progress, total, message)
82+
await ctx.report_progress(progress, total, message)
83+
```
84+
85+
**Context Report Progress**: `/fastmcp/server/context.py:125-155`
86+
```python
87+
async def report_progress(
88+
self, progress: float, total: float | None = None, message: str | None = None
89+
) -> None:
90+
progress_token = (
91+
self.request_context.meta.progressToken
92+
if self.request_context.meta
93+
else None
94+
)
95+
96+
if progress_token is None:
97+
logger.debug("[CONTEXT] No progress token available, skipping notification")
98+
return
99+
100+
await self.session.send_progress_notification(
101+
progress_token=progress_token,
102+
progress=progress,
103+
total=total,
104+
message=message,
105+
related_request_id=self.request_id,
106+
)
107+
```
108+
109+
### Context Isolation Problem
110+
111+
**HTTP-to-HTTP Flow (✅ Working):**
112+
```
113+
Client Request (HTTP) → Proxy → HTTP Server
114+
├─ Request context with progressToken flows through HTTP session
115+
├─ Progress handler executes in same HTTP context
116+
└─ Progress notifications reference original progressToken
117+
```
118+
119+
**HTTP-to-Stdio Flow (❌ Broken):**
120+
```
121+
Client Request (HTTP) → Proxy → Stdio Subprocess
122+
├─ Request context with progressToken sent to subprocess
123+
├─ Subprocess runs in isolated process with separate context
124+
├─ Progress handler (`default_progress_handler`) runs in subprocess context
125+
├─ `get_context()` returns subprocess context (no original progressToken)
126+
└─ Progress notification dropped due to missing token
127+
```
128+
129+
### Key Technical Components
130+
131+
1. **Context Management**: `/fastmcp/server/dependencies.py:27-33`
132+
```python
133+
def get_context() -> Context:
134+
from fastmcp.server.context import _current_context
135+
context = _current_context.get()
136+
if context is None:
137+
raise RuntimeError("No active context found.")
138+
return context
139+
```
140+
141+
2. **Context Variables**: Uses Python `ContextVar` which is isolated per async task/process
142+
143+
3. **Stdio Transport**: `/fastmcp/client/transports.py:300+` - Creates subprocess with separate memory space
144+
145+
## Debug Implementation Added
146+
147+
### Debug Middleware
148+
149+
Created `MCPMDebugMiddleware` in `/src/mcpm/fastmcp_integration/middleware.py:19-198`:
150+
151+
- **Limitation Documented**: Middleware only intercepts messages flowing FROM clients TO servers
152+
- **Progress notifications flow FROM servers TO clients**, bypassing middleware pipeline
153+
- **Added debug flags** to `mcpm run --debug` and `mcpm profile run --debug`
154+
155+
### Enhanced Logging
156+
157+
Added debug logging in multiple layers:
158+
159+
1. **Proxy Progress Handler**: `/fastmcp/server/proxy.py:524+`
160+
2. **Context Report Progress**: `/fastmcp/server/context.py:134-147`
161+
3. **Debug context inspection** in progress handler
162+
163+
## Architectural Limitation
164+
165+
### Why This Is Hard to Fix
166+
167+
FastMCP's proxy architecture has a fundamental limitation:
168+
169+
1. **Process Isolation**: Stdio servers run in separate processes with isolated memory
170+
2. **Context Variables**: `_current_context` is per-process, not shared
171+
3. **Missing Mapping**: No mechanism to associate stdio server progress with original client requests
172+
173+
### Required Fix
174+
175+
To properly support HTTP-to-Stdio progress notifications, FastMCP would need:
176+
177+
1. **Request Tracking**: Maintain mapping between client requests and stdio subprocess instances
178+
2. **Token Preservation**: Store original progress tokens when spawning stdio requests
179+
3. **Context Injection**: Inject correct progress token when handling progress from stdio servers
180+
181+
Example fix architecture:
182+
```python
183+
class ProxyRequestTracker:
184+
def __init__(self):
185+
self._request_mapping: Dict[subprocess_id, ProgressToken] = {}
186+
187+
def track_request(self, subprocess_id: str, progress_token: ProgressToken):
188+
self._request_mapping[subprocess_id] = progress_token
189+
190+
def get_progress_token(self, subprocess_id: str) -> ProgressToken | None:
191+
return self._request_mapping.get(subprocess_id)
192+
```
193+
194+
## Current Workarounds
195+
196+
### For Users
197+
198+
1. **Use HTTP servers** when progress notifications are needed:
199+
```bash
200+
mcpm run server-name --http --port 8001
201+
```
202+
203+
2. **Debug with HTTP-to-HTTP** setup:
204+
```bash
205+
mcpm run timer --http --debug
206+
```
207+
208+
### For Development
209+
210+
1. **Enable comprehensive debug logging**:
211+
```bash
212+
export PYTHONUNBUFFERED=1
213+
export LOG_LEVEL=DEBUG
214+
mcpm run server-name --debug
215+
```
216+
217+
2. **Use enhanced logging** in FastMCP components (already added)
218+
219+
## Files Modified During Investigation
220+
221+
### MCPM Code Changes
222+
223+
1. **Debug Middleware**: `/src/mcpm/fastmcp_integration/middleware.py`
224+
- Added `MCPMDebugMiddleware` class with comprehensive logging
225+
- Handles all MCP operations: tools, resources, prompts, notifications
226+
- Safe serialization for complex objects
227+
228+
2. **Proxy Factory**: `/src/mcpm/fastmcp_integration/proxy.py`
229+
- Added `debug: bool` parameter to `MCPMProxyFactory`
230+
- Debug middleware added first in middleware chain when enabled
231+
- Updated convenience function `create_mcpm_proxy()`
232+
233+
3. **CLI Commands**:
234+
- `/src/mcpm/commands/run.py`: Added `--debug` flag
235+
- `/src/mcpm/commands/profile/run.py`: Added `--debug` flag
236+
- Debug parameter propagated through all execution paths
237+
238+
### FastMCP Library Changes (for debugging)
239+
240+
1. **Proxy Progress Handler**: `/fastmcp/server/proxy.py:524`
241+
- Fixed logging syntax error (string formatting)
242+
- Added comprehensive debug context inspection
243+
244+
2. **Context Report Progress**: `/fastmcp/server/context.py:134-147`
245+
- Added detailed debug logging for progress reporting
246+
- Logs progress token availability and notification sending
247+
248+
## Testing and Validation
249+
250+
### Confirmed Working Cases
251+
252+
- **HTTP-to-HTTP**: Progress notifications work correctly
253+
- **Local servers**: Progress works when not proxied
254+
- **Debug logging**: All layers now provide detailed debugging information
255+
256+
### Confirmed Failing Cases
257+
258+
- **HTTP-to-Stdio**: Progress notifications fail due to context isolation
259+
- **Any proxied stdio server**: Same issue across all stdio transport types
260+
261+
### Debug Output Examples
262+
263+
**Working HTTP-to-HTTP**:
264+
```
265+
[PROXY DEBUG] TOOL CALL: timer_tool
266+
[CONTEXT] Reporting progress: progress=10000.0, total=180000.0, message=None
267+
[CONTEXT] Sending progress notification with token=12345
268+
```
269+
270+
**Failing HTTP-to-Stdio**:
271+
```
272+
[PROXY DEBUG] TOOL CALL: timer_tool
273+
INFO: received progress notification: progress=10000.0, total=180000.0, message=None
274+
[CONTEXT] Reporting progress: progress=10000.0, total=180000.0, message=None
275+
[CONTEXT] No progress token available, skipping notification
276+
```
277+
278+
## Future Work Recommendations
279+
280+
### FastMCP Core Fix
281+
282+
The proper solution requires changes to FastMCP core:
283+
284+
1. **Enhance ProxyClient**: Maintain request-to-subprocess mapping
285+
2. **Modify default_progress_handler**: Inject correct progress token from mapping
286+
3. **Add request lifecycle tracking**: Clean up mappings when requests complete
287+
288+
### Alternative Approaches
289+
290+
1. **HTTP Wrapper Pattern**: Create HTTP servers that wrap stdio servers
291+
2. **Progress Relay Service**: Separate service to bridge context gaps
292+
3. **Enhanced Middleware**: Middleware that can intercept outgoing notifications
293+
294+
### Testing Strategy
295+
296+
1. **Unit tests** for request tracking components
297+
2. **Integration tests** for HTTP-to-Stdio progress scenarios
298+
3. **Performance testing** for overhead of request tracking
299+
300+
## References
301+
302+
### Key Files Investigated
303+
304+
- `/mcp/types.py`: MCP protocol definitions including progress tokens
305+
- `/mcp/shared/session.py`: Base session with progress token handling
306+
- `/mcp/client/session.py`: Client session implementation
307+
- `/fastmcp/server/proxy.py`: Proxy server and progress handlers
308+
- `/fastmcp/server/context.py`: Request context and progress reporting
309+
- `/fastmcp/server/dependencies.py`: Context management and retrieval
310+
- `/fastmcp/client/transports.py`: Transport implementations including stdio
311+
312+
### FastMCP Version
313+
314+
- **Version**: 2.10.2 (from `pyproject.toml`)
315+
- **Library location**: `.venv/lib/python3.13/site-packages/fastmcp/`
316+
317+
### Related Issues
318+
319+
This issue demonstrates a broader challenge in MCP proxy implementations: maintaining request context across different transport types. Similar issues may exist in other proxy scenarios where context isolation occurs.
320+
321+
---
322+
323+
**Investigation completed by**: Claude Code
324+
**Next steps**: Resume work on implementing FastMCP core fix or alternative workarounds
325+
**Priority**: Medium (affects progress notification functionality, workarounds available)

0 commit comments

Comments
 (0)