Skip to content

Commit e28e3a6

Browse files
authored
Merge branch 'camel-ai:main' into add_devcontainer
2 parents 19f8c67 + 04ffb68 commit e28e3a6

File tree

14 files changed

+183
-170
lines changed

14 files changed

+183
-170
lines changed

examples/fastapi-todo-server/test_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
BASE_URL = "http://localhost:8000"
1212

1313

14-
def test_api():
14+
def test_api() -> None:
1515
"""Test the FastAPI todo server endpoints."""
1616
print("🧪 Testing FastAPI Todo Server")
1717
print("=" * 50)

examples/python-cmd-tool/cmd-tool.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import time
44

55

6-
def main():
6+
def main() -> None:
77
# 设置命令行参数解析器
88
parser = argparse.ArgumentParser(
99
description="A simple command-line tool that processes commands."

examples/python-server-project/server.json

Lines changed: 0 additions & 40 deletions
This file was deleted.

examples/python-server-project/server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import time
33

44

5-
def main():
5+
def main() -> None:
66
print("Server started. Waiting for input...", flush=True)
77

88
while True:

mcpify/backend.py

Lines changed: 72 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import subprocess
88
import time
99
from abc import ABC, abstractmethod
10+
from typing import Any
1011

1112
import aiohttp
1213

@@ -15,39 +16,39 @@ class BackendAdapter(ABC):
1516
"""Backend program adapter base class"""
1617

1718
@abstractmethod
18-
async def execute_tool(self, tool_config, parameters):
19+
async def execute_tool(self, tool_config: dict[str, Any], parameters: Any) -> str:
1920
"""Execute tool call"""
2021
pass
2122

2223
@abstractmethod
23-
async def start(self):
24+
async def start(self) -> None:
2425
"""Start backend program"""
2526
pass
2627

2728
@abstractmethod
28-
async def stop(self):
29+
async def stop(self) -> None:
2930
"""Stop backend program"""
3031
pass
3132

3233

3334
class CommandLineAdapter(BackendAdapter):
3435
"""Command line program adapter"""
3536

36-
def __init__(self, config):
37+
def __init__(self, config: dict[str, Any]) -> None:
3738
self.config = config
3839
self.command = config["command"]
3940
self.base_args = config.get("args", [])
4041
self.cwd = config.get("cwd", ".")
4142

42-
async def start(self):
43+
async def start(self) -> None:
4344
"""Command line programs don't need startup"""
4445
pass
4546

46-
async def stop(self):
47+
async def stop(self) -> None:
4748
"""Command line programs don't need shutdown"""
4849
pass
4950

50-
async def execute_tool(self, tool_config, parameters):
51+
async def execute_tool(self, tool_config: dict[str, Any], parameters: Any) -> str:
5152
"""Execute command line tool"""
5253
cmd_args = []
5354
args_template = tool_config.get("args", [])
@@ -74,18 +75,18 @@ async def execute_tool(self, tool_config, parameters):
7475
class ServerAdapter(BackendAdapter):
7576
"""Server program adapter"""
7677

77-
def __init__(self, config):
78+
def __init__(self, config: dict[str, Any]) -> None:
7879
self.config = config
7980
self.command = config["command"]
8081
self.args = config.get("args", [])
8182
self.cwd = config.get("cwd", ".")
8283
self.startup_timeout = config.get("startup_timeout", 5)
8384
self.ready_signal = config.get("ready_signal", "")
84-
self.process = None
85+
self.process: subprocess.Popen | None = None
8586
self.ready = False
8687
self.lock = asyncio.Lock()
8788

88-
async def start(self):
89+
async def start(self) -> None:
8990
"""Start server program"""
9091
if self.process is not None:
9192
return
@@ -110,46 +111,60 @@ async def start(self):
110111

111112
print("✅ Server startup complete")
112113

113-
async def _wait_for_ready(self):
114+
async def _wait_for_ready(self) -> None:
114115
"""Wait for server ready signal"""
116+
if self.process is None:
117+
raise RuntimeError("Server process is not started")
118+
115119
start_time = time.time()
116120

117121
while time.time() - start_time < self.startup_timeout:
118122
if self.process.poll() is not None:
119-
stderr = self.process.stderr.read()
123+
if self.process.stderr is not None:
124+
stderr = self.process.stderr.read()
125+
else:
126+
raise RuntimeError("Process stderr is not available")
120127
raise RuntimeError(f"Server startup failed: {stderr}")
121128

122129
# Non-blocking read output
123130
try:
124-
line = self.process.stdout.readline()
125-
if line and self.ready_signal in line:
126-
self.ready = True
127-
return
131+
if self.process.stdout is not None:
132+
line = self.process.stdout.readline()
133+
if line and self.ready_signal in line:
134+
self.ready = True
135+
return
136+
else:
137+
raise RuntimeError("Process stdout is not available")
128138
except Exception:
129139
pass
130140

131141
await asyncio.sleep(0.1)
132142

133143
raise TimeoutError(f"Server startup timeout ({self.startup_timeout}s)")
134144

135-
async def stop(self):
145+
async def stop(self) -> None:
136146
"""Stop server program"""
137147
if self.process is None:
138148
return
139149

140150
print("🛑 Stopping server...")
141151

142152
try:
143-
# Send quit command
144-
self.process.stdin.write("quit\n")
145-
self.process.stdin.flush()
153+
if self.process.stdin is not None:
154+
# Send quit command
155+
self.process.stdin.write("quit\n")
156+
self.process.stdin.flush()
146157

147-
# Wait for process to end
148-
try:
149-
self.process.wait(timeout=3)
150-
except subprocess.TimeoutExpired:
151-
self.process.terminate()
152-
self.process.wait(timeout=3)
158+
# Wait for process to end
159+
try:
160+
self.process.wait(timeout=3)
161+
except subprocess.TimeoutExpired:
162+
self.process.terminate()
163+
self.process.wait(timeout=3)
164+
else:
165+
raise RuntimeError(
166+
"Process stdin is not available for sending quit command"
167+
)
153168
except Exception:
154169
if self.process.poll() is None:
155170
self.process.kill()
@@ -158,7 +173,7 @@ async def stop(self):
158173
self.ready = False
159174
print("✅ Server stopped")
160175

161-
async def execute_tool(self, tool_config, parameters):
176+
async def execute_tool(self, tool_config: dict[str, Any], parameters: Any) -> str:
162177
"""Execute server tool"""
163178
if not self.ready or self.process is None:
164179
await self.start()
@@ -172,13 +187,26 @@ async def execute_tool(self, tool_config, parameters):
172187
command = command.replace(f"{{{param_name}}}", str(value))
173188

174189
try:
175-
# Send command
176-
self.process.stdin.write(f"{command}\n")
177-
self.process.stdin.flush()
178-
179-
# Read response
180-
response = self.process.stdout.readline().strip()
181-
return response
190+
if self.process is None:
191+
raise RuntimeError("Server process is not running")
192+
193+
if self.process.stdin is not None:
194+
# Send command
195+
self.process.stdin.write(f"{command}\n")
196+
self.process.stdin.flush()
197+
else:
198+
raise RuntimeError(
199+
"Process stdin is not available for sending commands"
200+
)
201+
202+
if self.process.stdout is not None:
203+
# Read response
204+
response: str = self.process.stdout.readline().strip()
205+
return response
206+
else:
207+
raise RuntimeError(
208+
"Process stdout is not available for reading response"
209+
)
182210

183211
except Exception as e:
184212
return f"Error communicating with server: {str(e)}"
@@ -187,28 +215,28 @@ async def execute_tool(self, tool_config, parameters):
187215
class HttpAdapter(BackendAdapter):
188216
"""HTTP API adapter"""
189217

190-
def __init__(self, config):
218+
def __init__(self, config: dict[str, Any]) -> None:
191219
self.config = config
192220
self.base_url = config["base_url"]
193221
self.timeout = config.get("timeout", 10)
194222
self.headers = config.get("headers", {})
195-
self.session = None
223+
self.session: aiohttp.ClientSession | None = None
196224

197-
async def start(self):
225+
async def start(self) -> None:
198226
"""Start HTTP session"""
199227
if self.session is None:
200228
timeout = aiohttp.ClientTimeout(total=self.timeout)
201229
self.session = aiohttp.ClientSession(timeout=timeout, headers=self.headers)
202230
print(f"🌐 HTTP session started: {self.base_url}")
203231

204-
async def stop(self):
232+
async def stop(self) -> None:
205233
"""Close HTTP session"""
206234
if self.session:
207235
await self.session.close()
208236
self.session = None
209237
print("🌐 HTTP session closed")
210238

211-
async def execute_tool(self, tool_config, parameters):
239+
async def execute_tool(self, tool_config: dict[str, Any], parameters: Any) -> str:
212240
"""Execute HTTP API call"""
213241
if self.session is None:
214242
await self.start()
@@ -219,6 +247,10 @@ async def execute_tool(self, tool_config, parameters):
219247
url = f"{self.base_url}{endpoint}"
220248

221249
try:
250+
result: str
251+
if self.session is None:
252+
raise RuntimeError("HTTP session is not started")
253+
222254
if method == "GET":
223255
# GET request uses parameters as query parameters
224256
async with self.session.get(url, params=parameters) as response:
@@ -256,7 +288,7 @@ async def execute_tool(self, tool_config, parameters):
256288
return f"HTTP request failed: {str(e)}"
257289

258290

259-
def create_adapter(backend_config):
291+
def create_adapter(backend_config: dict[str, Any]) -> BackendAdapter:
260292
"""Create adapter based on configuration"""
261293
backend_type = backend_config["type"]
262294
config = backend_config["config"]

0 commit comments

Comments
 (0)