vaha-m commited on
Commit
3d29fa9
·
verified ·
1 Parent(s): f9f19f4

Delete src/core/mcp_client.py

Browse files
Files changed (1) hide show
  1. src/core/mcp_client.py +0 -311
src/core/mcp_client.py DELETED
@@ -1,311 +0,0 @@
1
- """Proper MCP client using official Python SDK for MCP servers."""
2
- import asyncio
3
- import os
4
- import sys
5
- from copy import deepcopy
6
- from pathlib import Path
7
- from typing import Dict, Any, List, Optional
8
- from mcp import ClientSession, StdioServerParameters
9
- from mcp.client.stdio import stdio_client
10
- from src.core.config import config
11
-
12
-
13
- class ProperMCPClient:
14
- """Base class for connecting to MCP servers using official SDK."""
15
-
16
- def __init__(self, name: str, server_command: str, server_args: List[str], env: Optional[Dict[str, str]] = None):
17
- """
18
- Initialize MCP client.
19
-
20
- Args:
21
- name: Name of the MCP server
22
- server_command: Command to start the MCP server (e.g., 'npx', 'uvx')
23
- server_args: Arguments for the server command
24
- env: Optional environment variables
25
- """
26
- self.name = name
27
- self.server_command = server_command
28
- self.server_args = server_args
29
- self.env = env or {}
30
- self.session: Optional[ClientSession] = None
31
- self.exit_stack = None
32
- self.available_tools: List[Dict[str, Any]] = []
33
-
34
- async def connect(self) -> bool:
35
- """
36
- Connect to the MCP server using stdio transport.
37
-
38
- Returns:
39
- True if connection successful
40
- """
41
- try:
42
- # Prepare environment for subprocess (ensure venv scripts on PATH)
43
- env_vars = os.environ.copy()
44
- if sys.prefix:
45
- scripts_dir = Path(sys.prefix) / ("Scripts" if os.name == "nt" else "bin")
46
- if scripts_dir.exists():
47
- current_path = env_vars.get("PATH", "")
48
- env_vars["PATH"] = f"{scripts_dir}{os.pathsep}{current_path}" if current_path else str(scripts_dir)
49
-
50
- if self.env:
51
- env_vars.update(self.env)
52
-
53
- # Create server parameters
54
- server_params = StdioServerParameters(
55
- command=self.server_command,
56
- args=self.server_args,
57
- env=env_vars
58
- )
59
-
60
- # Create stdio client and session
61
- from contextlib import AsyncExitStack
62
- self.exit_stack = AsyncExitStack()
63
-
64
- # Connect to server via stdio
65
- stdio_transport = await self.exit_stack.enter_async_context(
66
- stdio_client(server_params)
67
- )
68
-
69
- read, write = stdio_transport
70
- self.session = await self.exit_stack.enter_async_context(
71
- ClientSession(read, write)
72
- )
73
-
74
- # Initialize the session
75
- await self.session.initialize()
76
-
77
- # List available tools
78
- tools_result = await self.session.list_tools()
79
- self.available_tools = tools_result.tools if hasattr(tools_result, 'tools') else []
80
-
81
- print(f" ✅ Connected to {self.name} MCP Server")
82
- print(f" 📋 Available tools: {len(self.available_tools)}")
83
-
84
- # Print tool names for debugging
85
- for tool in self.available_tools:
86
- tool_name = tool.name if hasattr(tool, 'name') else str(tool)
87
- print(f" - {tool_name}")
88
-
89
- return True
90
-
91
- except Exception as e:
92
- print(f" ❌ Failed to connect to {self.name}: {e}")
93
- import traceback
94
- traceback.print_exc()
95
- return False
96
-
97
- async def call_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
98
- """
99
- Call a tool on the MCP server.
100
-
101
- Args:
102
- tool_name: Name of the tool to call
103
- arguments: Arguments for the tool
104
-
105
- Returns:
106
- Tool execution result
107
- """
108
- if not self.session:
109
- raise RuntimeError(f"MCP client not connected. Call connect() first.")
110
-
111
- try:
112
- # Call the tool using the session
113
- result = await self.session.call_tool(tool_name, arguments)
114
-
115
- # Parse result
116
- if hasattr(result, 'content'):
117
- # Extract content from MCP response
118
- content = []
119
- for item in result.content:
120
- if hasattr(item, 'text'):
121
- content.append(item.text)
122
- elif hasattr(item, 'data'):
123
- content.append(item.data)
124
-
125
- return {
126
- "success": True,
127
- "result": content[0] if len(content) == 1 else content
128
- }
129
-
130
- return {
131
- "success": True,
132
- "result": str(result)
133
- }
134
-
135
- except Exception as e:
136
- return {
137
- "success": False,
138
- "error": f"Tool execution failed: {str(e)}"
139
- }
140
-
141
- def get_tools_for_gemini(self) -> List[Dict[str, Any]]:
142
- """
143
- Convert MCP tools to Gemini function declarations format.
144
-
145
- Returns:
146
- List of function declarations for Gemini
147
- """
148
- def sanitize_schema(node: Any) -> None:
149
- if isinstance(node, dict):
150
- node.pop("title", None)
151
- any_of = node.pop("anyOf", None)
152
- if any_of:
153
- replacement = any_of[0] if isinstance(any_of, list) and any_of else None
154
- if isinstance(replacement, dict):
155
- # merge first option into current node
156
- for key, value in replacement.items():
157
- node.setdefault(key, deepcopy(value))
158
- sanitize_schema(replacement)
159
-
160
- for key, value in list(node.items()):
161
- sanitize_schema(value)
162
- elif isinstance(node, list):
163
- for item in node:
164
- sanitize_schema(item)
165
-
166
- function_declarations = []
167
-
168
- for tool in self.available_tools:
169
- # Extract tool information
170
- tool_name = tool.name if hasattr(tool, 'name') else str(tool)
171
- tool_description = tool.description if hasattr(tool, 'description') else f"MCP tool: {tool_name}"
172
-
173
- # Extract input schema
174
- tool_schema = tool.inputSchema if hasattr(tool, 'inputSchema') else {}
175
-
176
- if tool_schema:
177
- if hasattr(tool_schema, "to_dict"):
178
- parameters = tool_schema.to_dict()
179
- elif isinstance(tool_schema, dict):
180
- parameters = deepcopy(tool_schema)
181
- else:
182
- try:
183
- parameters = deepcopy(tool_schema)
184
- except Exception:
185
- parameters = tool_schema
186
- else:
187
- parameters = {
188
- "type": "object",
189
- "properties": {}
190
- }
191
-
192
- sanitize_schema(parameters)
193
-
194
- # Convert to Gemini format
195
- function_decl = {
196
- "name": tool_name,
197
- "description": tool_description,
198
- "parameters": parameters
199
- }
200
-
201
- function_declarations.append(function_decl)
202
-
203
- return [{"function_declarations": function_declarations}]
204
-
205
- async def cleanup(self) -> None:
206
- """Close the MCP session."""
207
- if self.exit_stack:
208
- await self.exit_stack.aclose()
209
- print(f" 🧹 {self.name} MCP client closed")
210
-
211
-
212
- class ChromaMCPClient(ProperMCPClient):
213
- """MCP client for Chroma MCP Server."""
214
-
215
- def __init__(self):
216
- """Initialize Chroma MCP client."""
217
- # Prepare environment variables for Chroma Cloud
218
- env = {
219
- "CHROMA_CLIENT_TYPE": "cloud",
220
- "CHROMA_TENANT": config.CHROMA_TENANT,
221
- "CHROMA_DATABASE": config.CHROMA_DATABASE,
222
- "CHROMA_API_KEY": config.CHROMA_API_KEY,
223
- }
224
-
225
- # Initialize with uvx command to run chroma-mcp
226
- super().__init__(
227
- name="Chroma",
228
- server_command="uvx",
229
- server_args=[
230
- "chroma-mcp",
231
- "--client-type", "cloud"
232
- ],
233
- env=env
234
- )
235
-
236
-
237
- class CoinGeckoMCPClient(ProperMCPClient):
238
- """MCP client for CoinGecko MCP Server."""
239
-
240
- def __init__(self):
241
- """Initialize CoinGecko MCP client."""
242
- # Prepare environment variables
243
- env = {}
244
- if config.COINGECKO_API_KEY:
245
- env["COINGECKO_PRO_API_KEY"] = config.COINGECKO_API_KEY
246
- env["COINGECKO_ENVIRONMENT"] = "pro"
247
-
248
- # Initialize with npx command to run CoinGecko MCP
249
- super().__init__(
250
- name="CoinGecko",
251
- server_command="npx",
252
- server_args=[
253
- "-y",
254
- "@coingecko/coingecko-mcp"
255
- ],
256
- env=env if env else None
257
- )
258
-
259
-
260
- # Alternative: Use public CoinGecko MCP endpoint
261
- class CoinGeckoPublicMCPClient(ProperMCPClient):
262
- """MCP client for CoinGecko Public MCP Server."""
263
-
264
- def __init__(self):
265
- """Initialize CoinGecko Public MCP client."""
266
- # Use the public endpoint via mcp-remote
267
- super().__init__(
268
- name="CoinGecko Public",
269
- server_command="npx",
270
- server_args=[
271
- "mcp-remote",
272
- "https://mcp.api.coingecko.com/sse"
273
- ],
274
- env=None
275
- )
276
-
277
- # Have to provide limits to the DuckDuckGo MCP server process to avoid excessive resource usage
278
- class DuckDuckGoMCPClient(ProperMCPClient):
279
- """MCP client for DuckDuckGo MCP Server."""
280
-
281
- def __init__(self):
282
- """Initialize DuckDuckGo MCP client."""
283
- super().__init__(
284
- name="DuckDuckGo",
285
- server_command="uvx",
286
- server_args=[
287
- "duckduckgo-mcp-server"
288
- ],
289
- env=None
290
- )
291
-
292
-
293
- class ElevenLabsMCPClient(ProperMCPClient):
294
- """MCP client for ElevenLabs MCP Server."""
295
-
296
- def __init__(self):
297
- """Initialize ElevenLabs MCP client."""
298
- # Prepare environment variables
299
- env = {}
300
- if config.ELEVENLABS_API_KEY:
301
- env["ELEVENLABS_API_KEY"] = config.ELEVENLABS_API_KEY
302
-
303
- # Initialize with uvx command to run elevenlabs-mcp
304
- super().__init__(
305
- name="ElevenLabs",
306
- server_command="uvx",
307
- server_args=[
308
- "elevenlabs-mcp"
309
- ],
310
- env=env
311
- )