Skip to content

Commit 508a551

Browse files
committed
fix progress syntax error
1 parent b1aba6f commit 508a551

File tree

3 files changed

+55
-43
lines changed

3 files changed

+55
-43
lines changed

examples/claude_desktop/mcpproxy.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,20 @@ def resolve_with_system_dns(hostname):
1010
print(f"Error resolving {hostname}: {e}")
1111
return None
1212

13-
1413
hostname = "coderunner.local"
1514
address = resolve_with_system_dns(hostname)
16-
proxy = FastMCP.as_proxy(f"http://{address}:8222/mcp", name="SSE to Stdio Proxy")
15+
# Create a proxy directly from a config dictionary
16+
config = {
17+
"mcpServers": {
18+
"default": { # For single server configs, 'default' is commonly used
19+
"url": f"http://{address}:8222/mcp",
20+
"transport": "http"
21+
}
22+
}
23+
}
24+
25+
26+
proxy = FastMCP.as_proxy(config, name="SSE to Stdio Proxy")
1727
# Run the proxy with stdio transport for local access
1828
if __name__ == "__main__":
1929
proxy.run()

requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11

2+
duckdb
3+
24
# Core Jupyter Server
35
jupyter-server
46

@@ -35,4 +37,4 @@ fastmcp
3537

3638
openai-agents
3739

38-
playwright==1.53.0
40+
playwright==1.53.0

server.py

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ class KernelInfo:
9696
last_health_check: datetime = field(default_factory=datetime.now)
9797
current_operation: Optional[str] = None
9898
failure_count: int = 0
99-
99+
100100
def is_available(self) -> bool:
101101
return self.state == KernelState.HEALTHY
102-
102+
103103
def needs_health_check(self) -> bool:
104104
return datetime.now() - self.last_health_check > timedelta(seconds=KERNEL_HEALTH_CHECK_INTERVAL)
105105

@@ -110,21 +110,21 @@ def __init__(self):
110110
self.busy_kernels: Set[str] = set()
111111
self._initialized = False
112112
self._health_check_task: Optional[asyncio.Task] = None
113-
113+
114114
async def initialize(self):
115115
"""Initialize the kernel pool with minimum number of kernels"""
116116
if self._initialized:
117117
return
118-
118+
119119
async with self.lock:
120120
logger.info("Initializing kernel pool...")
121-
121+
122122
# Try to use existing kernel first
123123
existing_kernel = await self._get_existing_kernel()
124124
if existing_kernel:
125125
self.kernels[existing_kernel] = KernelInfo(kernel_id=existing_kernel)
126126
logger.info(f"Added existing kernel to pool: {existing_kernel}")
127-
127+
128128
# Create additional kernels to reach minimum
129129
while len(self.kernels) < MIN_KERNELS:
130130
kernel_id = await self._create_new_kernel()
@@ -134,17 +134,17 @@ async def initialize(self):
134134
else:
135135
logger.warning("Failed to create minimum number of kernels")
136136
break
137-
137+
138138
self._initialized = True
139139
# Start health check background task
140140
self._health_check_task = asyncio.create_task(self._health_check_loop())
141141
logger.info(f"Kernel pool initialized with {len(self.kernels)} kernels")
142-
142+
143143
async def get_available_kernel(self) -> Optional[str]:
144144
"""Get an available kernel from the pool"""
145145
if not self._initialized:
146146
await self.initialize()
147-
147+
148148
async with self.lock:
149149
# Find healthy, available kernel
150150
for kernel_id, kernel_info in self.kernels.items():
@@ -154,7 +154,7 @@ async def get_available_kernel(self) -> Optional[str]:
154154
kernel_info.last_used = datetime.now()
155155
logger.info(f"Assigned kernel {kernel_id} to operation")
156156
return kernel_id
157-
157+
158158
# No available kernels, try to create a new one if under limit
159159
if len(self.kernels) < MAX_KERNELS:
160160
kernel_id = await self._create_new_kernel()
@@ -164,23 +164,23 @@ async def get_available_kernel(self) -> Optional[str]:
164164
self.busy_kernels.add(kernel_id)
165165
logger.info(f"Created and assigned new kernel: {kernel_id}")
166166
return kernel_id
167-
167+
168168
logger.warning("No available kernels in pool")
169169
return None
170-
170+
171171
async def release_kernel(self, kernel_id: str, failed: bool = False):
172172
"""Release a kernel back to the pool"""
173173
async with self.lock:
174174
if kernel_id in self.busy_kernels:
175175
self.busy_kernels.remove(kernel_id)
176-
176+
177177
if kernel_id in self.kernels:
178178
kernel_info = self.kernels[kernel_id]
179179
if failed:
180180
kernel_info.failure_count += 1
181181
kernel_info.state = KernelState.FAILED
182182
logger.warning(f"Kernel {kernel_id} marked as failed (failures: {kernel_info.failure_count})")
183-
183+
184184
# Remove failed kernel if it has too many failures
185185
if kernel_info.failure_count >= MAX_RETRY_ATTEMPTS:
186186
await self._remove_kernel(kernel_id)
@@ -192,7 +192,7 @@ async def release_kernel(self, kernel_id: str, failed: bool = False):
192192
kernel_info.state = KernelState.HEALTHY
193193
kernel_info.current_operation = None
194194
logger.info(f"Released kernel {kernel_id} back to pool")
195-
195+
196196
async def _get_existing_kernel(self) -> Optional[str]:
197197
"""Try to get kernel ID from existing file"""
198198
try:
@@ -206,7 +206,7 @@ async def _get_existing_kernel(self) -> Optional[str]:
206206
except Exception as e:
207207
logger.warning(f"Could not read or validate existing kernel from {KERNEL_ID_FILE_PATH}: {e}")
208208
return None
209-
209+
210210
async def _create_new_kernel(self) -> Optional[str]:
211211
"""Create a new Jupyter kernel"""
212212
try:
@@ -226,7 +226,7 @@ async def _create_new_kernel(self) -> Optional[str]:
226226
except Exception as e:
227227
logger.error(f"Error creating kernel: {e}")
228228
return None
229-
229+
230230
async def _remove_kernel(self, kernel_id: str):
231231
"""Remove and shutdown a kernel"""
232232
try:
@@ -238,12 +238,12 @@ async def _remove_kernel(self, kernel_id: str):
238238
logger.info(f"Removed kernel: {kernel_id}")
239239
except Exception as e:
240240
logger.warning(f"Error removing kernel {kernel_id}: {e}")
241-
241+
242242
if kernel_id in self.kernels:
243243
del self.kernels[kernel_id]
244244
if kernel_id in self.busy_kernels:
245245
self.busy_kernels.remove(kernel_id)
246-
246+
247247
async def _check_kernel_health(self, kernel_id: str) -> bool:
248248
"""Check if a kernel is healthy by sending a simple command"""
249249
try:
@@ -256,15 +256,15 @@ async def _check_kernel_health(self, kernel_id: str) -> bool:
256256
# Send simple health check command
257257
msg_id, request_json = create_jupyter_request("1+1")
258258
await ws.send(request_json)
259-
259+
260260
# Wait for response with timeout
261261
start_time = time.time()
262262
while time.time() - start_time < 10: # 10 second timeout for health check
263263
try:
264264
message_str = await asyncio.wait_for(ws.recv(), timeout=2.0)
265265
message_data = json.loads(message_str)
266266
parent_msg_id = message_data.get("parent_header", {}).get("msg_id")
267-
267+
268268
if parent_msg_id == msg_id:
269269
msg_type = message_data.get("header", {}).get("msg_type")
270270
if msg_type == "status" and message_data.get("content", {}).get("execution_state") == "idle":
@@ -275,7 +275,7 @@ async def _check_kernel_health(self, kernel_id: str) -> bool:
275275
except Exception as e:
276276
logger.warning(f"Health check failed for kernel {kernel_id}: {e}")
277277
return False
278-
278+
279279
async def _health_check_loop(self):
280280
"""Background task to monitor kernel health"""
281281
while True:
@@ -291,7 +291,7 @@ async def _health_check_loop(self):
291291
else:
292292
kernel_info.state = KernelState.UNRESPONSIVE
293293
unhealthy_kernels.append(kernel_id)
294-
294+
295295
# Remove unhealthy kernels and create replacements
296296
for kernel_id in unhealthy_kernels:
297297
logger.warning(f"Removing unhealthy kernel: {kernel_id}")
@@ -346,14 +346,14 @@ def create_jupyter_request(code: str) -> tuple[str, str]:
346346
async def execute_with_retry(command: str, ctx: Context, max_attempts: int = MAX_RETRY_ATTEMPTS) -> str:
347347
"""Execute code with retry logic and exponential backoff"""
348348
last_error = None
349-
349+
350350
for attempt in range(max_attempts):
351351
try:
352352
# Get kernel from pool
353353
kernel_id = await kernel_pool.get_available_kernel()
354354
if not kernel_id:
355355
raise NoKernelAvailableError("No available kernels in pool")
356-
356+
357357
try:
358358
result = await _execute_on_kernel(kernel_id, command, ctx)
359359
# Release kernel back to pool on success
@@ -363,7 +363,7 @@ async def execute_with_retry(command: str, ctx: Context, max_attempts: int = MAX
363363
# Release kernel as failed
364364
await kernel_pool.release_kernel(kernel_id, failed=True)
365365
raise e
366-
366+
367367
except Exception as e:
368368
last_error = e
369369
if attempt < max_attempts - 1:
@@ -372,7 +372,7 @@ async def execute_with_retry(command: str, ctx: Context, max_attempts: int = MAX
372372
await asyncio.sleep(backoff_time)
373373
else:
374374
logger.error(f"All {max_attempts} execution attempts failed. Last error: {e}")
375-
375+
376376
return f"Error: Execution failed after {max_attempts} attempts. Last error: {str(last_error)}"
377377

378378
async def _execute_on_kernel(kernel_id: str, command: str, ctx: Context) -> str:
@@ -396,34 +396,34 @@ async def _execute_on_kernel(kernel_id: str, command: str, ctx: Context) -> str:
396396
execution_complete = False
397397
start_time = time.time()
398398
last_activity = start_time
399-
399+
400400
# Progress reporting for long operations
401-
await ctx.report_progress(progress=f"Executing on kernel {kernel_id[:8]}...")
401+
await ctx.report_progress(progress=10, message=f"Executing on kernel {kernel_id[:8]}...")
402402

403403
while not execution_complete and (time.time() - start_time) < WEBSOCKET_TIMEOUT:
404404
try:
405405
# Adaptive timeout based on recent activity
406406
current_time = time.time()
407407
time_since_activity = current_time - last_activity
408-
408+
409409
# Use shorter timeout if no recent activity, longer if active
410410
recv_timeout = 30.0 if time_since_activity > 60 else 5.0
411-
411+
412412
message_str = await asyncio.wait_for(jupyter_ws.recv(), timeout=recv_timeout)
413413
last_activity = current_time
414-
414+
415415
except asyncio.TimeoutError:
416416
# Send periodic progress updates during long operations
417417
elapsed = time.time() - start_time
418-
await ctx.report_progress(progress=f"Still executing... ({elapsed:.0f}s elapsed)")
418+
await ctx.report_progress(progress=30, message=f"Still executing... ({elapsed:.0f}s elapsed)")
419419
continue
420420

421421
try:
422422
message_data = json.loads(message_str)
423423
except json.JSONDecodeError:
424424
logger.warning(f"Received invalid JSON from kernel {kernel_id}")
425425
continue
426-
426+
427427
parent_msg_id = message_data.get("parent_header", {}).get("msg_id")
428428

429429
if parent_msg_id != sent_msg_id:
@@ -436,20 +436,20 @@ async def _execute_on_kernel(kernel_id: str, command: str, ctx: Context) -> str:
436436
stream_text = content.get("text", "")
437437
final_output_lines.append(stream_text)
438438
# Stream output as progress
439-
await ctx.report_progress(progress=stream_text.strip())
439+
await ctx.report_progress(progress=50, message=stream_text.strip())
440440

441441
elif msg_type in ["execute_result", "display_data"]:
442442
result_text = content.get("data", {}).get("text/plain", "")
443443
final_output_lines.append(result_text)
444-
444+
445445
elif msg_type == "error":
446446
error_traceback = "\n".join(content.get("traceback", []))
447447
logger.error(f"Execution error on kernel {kernel_id} for msg_id {sent_msg_id}:\n{error_traceback}")
448448
raise KernelExecutionError(f"Execution Error:\n{error_traceback}")
449449

450450
elif msg_type == "status" and content.get("execution_state") == "idle":
451451
execution_complete = True
452-
await ctx.report_progress(progress="Execution completed")
452+
await ctx.report_progress(progress=100, message="Execution completed")
453453

454454
if not execution_complete:
455455
elapsed = time.time() - start_time
@@ -486,13 +486,13 @@ async def execute_python_code(command: str, ctx: Context) -> str:
486486
try:
487487
# Initialize kernel pool if not already done
488488
if not kernel_pool._initialized:
489-
await ctx.report_progress(progress="Initializing kernel pool...")
489+
await ctx.report_progress(progress=10, message="Initializing kernel pool...")
490490
await kernel_pool.initialize()
491-
491+
492492
# Execute with retry logic
493493
result = await execute_with_retry(command, ctx)
494494
return result
495-
495+
496496
except Exception as e:
497497
logger.error(f"Fatal error in execute_python_code: {e}", exc_info=True)
498498
return f"Error: Failed to execute code: {str(e)}"

0 commit comments

Comments
 (0)