kailash 0.6.2__py3-none-any.whl → 0.6.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kailash/__init__.py +3 -3
- kailash/api/custom_nodes_secure.py +3 -3
- kailash/api/gateway.py +1 -1
- kailash/api/studio.py +2 -3
- kailash/api/workflow_api.py +3 -4
- kailash/core/resilience/bulkhead.py +460 -0
- kailash/core/resilience/circuit_breaker.py +92 -10
- kailash/edge/discovery.py +86 -0
- kailash/mcp_server/__init__.py +334 -0
- kailash/mcp_server/advanced_features.py +1022 -0
- kailash/{mcp → mcp_server}/ai_registry_server.py +29 -4
- kailash/mcp_server/auth.py +789 -0
- kailash/mcp_server/client.py +712 -0
- kailash/mcp_server/discovery.py +1593 -0
- kailash/mcp_server/errors.py +673 -0
- kailash/mcp_server/oauth.py +1727 -0
- kailash/mcp_server/protocol.py +1126 -0
- kailash/mcp_server/registry_integration.py +587 -0
- kailash/mcp_server/server.py +1747 -0
- kailash/{mcp → mcp_server}/servers/ai_registry.py +2 -2
- kailash/mcp_server/transports.py +1169 -0
- kailash/mcp_server/utils/cache.py +510 -0
- kailash/middleware/auth/auth_manager.py +3 -3
- kailash/middleware/communication/api_gateway.py +2 -9
- kailash/middleware/communication/realtime.py +1 -1
- kailash/middleware/mcp/client_integration.py +1 -1
- kailash/middleware/mcp/enhanced_server.py +2 -2
- kailash/nodes/__init__.py +2 -0
- kailash/nodes/admin/audit_log.py +6 -6
- kailash/nodes/admin/permission_check.py +8 -8
- kailash/nodes/admin/role_management.py +32 -28
- kailash/nodes/admin/schema.sql +6 -1
- kailash/nodes/admin/schema_manager.py +13 -13
- kailash/nodes/admin/security_event.py +16 -20
- kailash/nodes/admin/tenant_isolation.py +3 -3
- kailash/nodes/admin/transaction_utils.py +3 -3
- kailash/nodes/admin/user_management.py +21 -22
- kailash/nodes/ai/a2a.py +11 -11
- kailash/nodes/ai/ai_providers.py +9 -12
- kailash/nodes/ai/embedding_generator.py +13 -14
- kailash/nodes/ai/intelligent_agent_orchestrator.py +19 -19
- kailash/nodes/ai/iterative_llm_agent.py +3 -3
- kailash/nodes/ai/llm_agent.py +213 -36
- kailash/nodes/ai/self_organizing.py +2 -2
- kailash/nodes/alerts/discord.py +4 -4
- kailash/nodes/api/graphql.py +6 -6
- kailash/nodes/api/http.py +12 -17
- kailash/nodes/api/rate_limiting.py +4 -4
- kailash/nodes/api/rest.py +15 -15
- kailash/nodes/auth/mfa.py +3 -4
- kailash/nodes/auth/risk_assessment.py +2 -2
- kailash/nodes/auth/session_management.py +5 -5
- kailash/nodes/auth/sso.py +143 -0
- kailash/nodes/base.py +6 -2
- kailash/nodes/base_async.py +16 -2
- kailash/nodes/base_with_acl.py +2 -2
- kailash/nodes/cache/__init__.py +9 -0
- kailash/nodes/cache/cache.py +1172 -0
- kailash/nodes/cache/cache_invalidation.py +870 -0
- kailash/nodes/cache/redis_pool_manager.py +595 -0
- kailash/nodes/code/async_python.py +2 -1
- kailash/nodes/code/python.py +196 -35
- kailash/nodes/compliance/data_retention.py +6 -6
- kailash/nodes/compliance/gdpr.py +5 -5
- kailash/nodes/data/__init__.py +10 -0
- kailash/nodes/data/optimistic_locking.py +906 -0
- kailash/nodes/data/readers.py +8 -8
- kailash/nodes/data/redis.py +349 -0
- kailash/nodes/data/sql.py +314 -3
- kailash/nodes/data/streaming.py +21 -0
- kailash/nodes/enterprise/__init__.py +8 -0
- kailash/nodes/enterprise/audit_logger.py +285 -0
- kailash/nodes/enterprise/batch_processor.py +22 -3
- kailash/nodes/enterprise/data_lineage.py +1 -1
- kailash/nodes/enterprise/mcp_executor.py +205 -0
- kailash/nodes/enterprise/service_discovery.py +150 -0
- kailash/nodes/enterprise/tenant_assignment.py +108 -0
- kailash/nodes/logic/async_operations.py +2 -2
- kailash/nodes/logic/convergence.py +1 -1
- kailash/nodes/logic/operations.py +1 -1
- kailash/nodes/monitoring/__init__.py +11 -1
- kailash/nodes/monitoring/health_check.py +456 -0
- kailash/nodes/monitoring/log_processor.py +817 -0
- kailash/nodes/monitoring/metrics_collector.py +627 -0
- kailash/nodes/monitoring/performance_benchmark.py +137 -11
- kailash/nodes/rag/advanced.py +7 -7
- kailash/nodes/rag/agentic.py +49 -2
- kailash/nodes/rag/conversational.py +3 -3
- kailash/nodes/rag/evaluation.py +3 -3
- kailash/nodes/rag/federated.py +3 -3
- kailash/nodes/rag/graph.py +3 -3
- kailash/nodes/rag/multimodal.py +3 -3
- kailash/nodes/rag/optimized.py +5 -5
- kailash/nodes/rag/privacy.py +3 -3
- kailash/nodes/rag/query_processing.py +6 -6
- kailash/nodes/rag/realtime.py +1 -1
- kailash/nodes/rag/registry.py +2 -6
- kailash/nodes/rag/router.py +1 -1
- kailash/nodes/rag/similarity.py +7 -7
- kailash/nodes/rag/strategies.py +4 -4
- kailash/nodes/security/abac_evaluator.py +6 -6
- kailash/nodes/security/behavior_analysis.py +5 -6
- kailash/nodes/security/credential_manager.py +1 -1
- kailash/nodes/security/rotating_credentials.py +11 -11
- kailash/nodes/security/threat_detection.py +8 -8
- kailash/nodes/testing/credential_testing.py +2 -2
- kailash/nodes/transform/processors.py +5 -5
- kailash/runtime/local.py +162 -14
- kailash/runtime/parameter_injection.py +425 -0
- kailash/runtime/parameter_injector.py +657 -0
- kailash/runtime/testing.py +2 -2
- kailash/testing/fixtures.py +2 -2
- kailash/workflow/builder.py +99 -18
- kailash/workflow/builder_improvements.py +207 -0
- kailash/workflow/input_handling.py +170 -0
- {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/METADATA +21 -8
- {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/RECORD +126 -101
- kailash/mcp/__init__.py +0 -53
- kailash/mcp/client.py +0 -445
- kailash/mcp/server.py +0 -292
- kailash/mcp/server_enhanced.py +0 -449
- kailash/mcp/utils/cache.py +0 -267
- /kailash/{mcp → mcp_server}/client_new.py +0 -0
- /kailash/{mcp → mcp_server}/utils/__init__.py +0 -0
- /kailash/{mcp → mcp_server}/utils/config.py +0 -0
- /kailash/{mcp → mcp_server}/utils/formatters.py +0 -0
- /kailash/{mcp → mcp_server}/utils/metrics.py +0 -0
- {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/WHEEL +0 -0
- {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/entry_points.txt +0 -0
- {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/top_level.txt +0 -0
kailash/mcp/client.py
DELETED
@@ -1,445 +0,0 @@
|
|
1
|
-
"""MCP Client Service using official Anthropic SDK.
|
2
|
-
|
3
|
-
This module provides a comprehensive interface to the Model Context Protocol
|
4
|
-
using the official Anthropic MCP Python SDK. It enables seamless integration
|
5
|
-
with MCP servers for tool discovery, resource access, and dynamic capability
|
6
|
-
extension in workflow nodes.
|
7
|
-
|
8
|
-
Note:
|
9
|
-
This module requires the official Anthropic MCP SDK to be installed.
|
10
|
-
Install with: pip install mcp
|
11
|
-
|
12
|
-
Examples:
|
13
|
-
Basic tool discovery and execution:
|
14
|
-
|
15
|
-
>>> client = MCPClient()
|
16
|
-
>>> # Discover available tools
|
17
|
-
>>> tools = await client.discover_tools({
|
18
|
-
... "transport": "stdio",
|
19
|
-
... "command": "python",
|
20
|
-
... "args": ["-m", "my_mcp_server"]
|
21
|
-
... })
|
22
|
-
>>> # Execute a tool
|
23
|
-
>>> result = await client.call_tool(
|
24
|
-
... server_config,
|
25
|
-
... "search_knowledge",
|
26
|
-
... {"query": "workflow optimization"}
|
27
|
-
... )
|
28
|
-
|
29
|
-
Resource access:
|
30
|
-
|
31
|
-
>>> # List available resources
|
32
|
-
>>> resources = await client.list_resources(server_config)
|
33
|
-
>>> # Read specific resource
|
34
|
-
>>> content = await client.read_resource(
|
35
|
-
... server_config,
|
36
|
-
... "file:///docs/api.md"
|
37
|
-
... )
|
38
|
-
"""
|
39
|
-
|
40
|
-
import json
|
41
|
-
import logging
|
42
|
-
import os
|
43
|
-
from contextlib import AsyncExitStack
|
44
|
-
from typing import Any
|
45
|
-
|
46
|
-
logger = logging.getLogger(__name__)
|
47
|
-
|
48
|
-
|
49
|
-
class MCPClient:
|
50
|
-
"""MCP client service using official Anthropic SDK.
|
51
|
-
|
52
|
-
This is a service class that provides MCP functionality to nodes.
|
53
|
-
It handles connection management, tool discovery, and tool execution
|
54
|
-
using the official MCP Python SDK.
|
55
|
-
|
56
|
-
Examples:
|
57
|
-
Used internally by LLMAgentNode:
|
58
|
-
|
59
|
-
>>> client = MCPClient()
|
60
|
-
>>> tools = await client.discover_tools("http://localhost:8080")
|
61
|
-
>>> result = await client.call_tool(
|
62
|
-
... "http://localhost:8080",
|
63
|
-
... "search",
|
64
|
-
... {"query": "AI applications"}
|
65
|
-
... )
|
66
|
-
"""
|
67
|
-
|
68
|
-
def __init__(self):
|
69
|
-
"""Initialize the MCP client."""
|
70
|
-
self._sessions = {} # Cache active sessions
|
71
|
-
self._discovered_tools = {} # Cache discovered tools
|
72
|
-
self._discovered_resources = {} # Cache discovered resources
|
73
|
-
|
74
|
-
async def discover_tools(
|
75
|
-
self, server_config: str | dict[str, Any]
|
76
|
-
) -> list[dict[str, Any]]:
|
77
|
-
"""Discover available tools from an MCP server.
|
78
|
-
|
79
|
-
Args:
|
80
|
-
server_config: Either a URL string or server configuration dict.
|
81
|
-
For stdio servers, use dict with 'transport', 'command', 'args'.
|
82
|
-
|
83
|
-
Returns:
|
84
|
-
List of tool definitions with name, description, and parameters.
|
85
|
-
Returns empty list if server unavailable or on error.
|
86
|
-
|
87
|
-
Examples:
|
88
|
-
>>> config = {
|
89
|
-
... "transport": "stdio",
|
90
|
-
... "command": "python",
|
91
|
-
... "args": ["-m", "my_server"]
|
92
|
-
... }
|
93
|
-
>>> tools = await client.discover_tools(config)
|
94
|
-
>>> print([tool["name"] for tool in tools])
|
95
|
-
"""
|
96
|
-
server_key = self._get_server_key(server_config)
|
97
|
-
|
98
|
-
# Return cached tools if available
|
99
|
-
if server_key in self._discovered_tools:
|
100
|
-
return self._discovered_tools[server_key]
|
101
|
-
|
102
|
-
try:
|
103
|
-
# Import MCP SDK
|
104
|
-
from mcp import ClientSession, StdioServerParameters
|
105
|
-
from mcp.client.stdio import stdio_client
|
106
|
-
|
107
|
-
# Parse server configuration
|
108
|
-
if isinstance(server_config, str):
|
109
|
-
# URL-based server (not implemented in this example)
|
110
|
-
logger.warning(
|
111
|
-
f"URL-based MCP servers not yet supported: {server_config}"
|
112
|
-
)
|
113
|
-
return []
|
114
|
-
|
115
|
-
# Extract stdio configuration
|
116
|
-
transport = server_config.get("transport", "stdio")
|
117
|
-
if transport != "stdio":
|
118
|
-
logger.warning(
|
119
|
-
f"Only stdio transport currently supported, got: {transport}"
|
120
|
-
)
|
121
|
-
return []
|
122
|
-
|
123
|
-
command = server_config.get("command", "python")
|
124
|
-
args = server_config.get("args", [])
|
125
|
-
env = server_config.get("env", {})
|
126
|
-
|
127
|
-
# Merge environment
|
128
|
-
server_env = os.environ.copy()
|
129
|
-
server_env.update(env)
|
130
|
-
|
131
|
-
# Create server parameters
|
132
|
-
server_params = StdioServerParameters(
|
133
|
-
command=command, args=args, env=server_env
|
134
|
-
)
|
135
|
-
|
136
|
-
# Connect and discover tools
|
137
|
-
async with AsyncExitStack() as stack:
|
138
|
-
stdio = await stack.enter_async_context(stdio_client(server_params))
|
139
|
-
session = await stack.enter_async_context(
|
140
|
-
ClientSession(stdio[0], stdio[1])
|
141
|
-
)
|
142
|
-
|
143
|
-
# Initialize session
|
144
|
-
await session.initialize()
|
145
|
-
|
146
|
-
# List tools
|
147
|
-
result = await session.list_tools()
|
148
|
-
|
149
|
-
tools = []
|
150
|
-
for tool in result.tools:
|
151
|
-
tools.append(
|
152
|
-
{
|
153
|
-
"name": tool.name,
|
154
|
-
"description": tool.description,
|
155
|
-
"parameters": tool.inputSchema,
|
156
|
-
}
|
157
|
-
)
|
158
|
-
|
159
|
-
# Cache the tools
|
160
|
-
self._discovered_tools[server_key] = tools
|
161
|
-
return tools
|
162
|
-
|
163
|
-
except ImportError:
|
164
|
-
logger.error("MCP SDK not available. Install with: pip install mcp")
|
165
|
-
return []
|
166
|
-
except Exception as e:
|
167
|
-
logger.error(f"Failed to discover tools: {e}")
|
168
|
-
return []
|
169
|
-
|
170
|
-
async def call_tool(
|
171
|
-
self,
|
172
|
-
server_config: str | dict[str, Any],
|
173
|
-
tool_name: str,
|
174
|
-
arguments: dict[str, Any],
|
175
|
-
) -> Any:
|
176
|
-
"""Call a tool on an MCP server.
|
177
|
-
|
178
|
-
Args:
|
179
|
-
server_config: Either a URL string or server configuration dict.
|
180
|
-
tool_name: Name of the tool to call.
|
181
|
-
arguments: Arguments to pass to the tool.
|
182
|
-
|
183
|
-
Returns:
|
184
|
-
Dict containing tool execution result. On success, includes
|
185
|
-
'success': True and 'content' or 'result'. On error, includes
|
186
|
-
'error' with description.
|
187
|
-
|
188
|
-
Examples:
|
189
|
-
>>> result = await client.call_tool(
|
190
|
-
... server_config,
|
191
|
-
... "search",
|
192
|
-
... {"query": "python examples"}
|
193
|
-
... )
|
194
|
-
>>> if result.get("success"):
|
195
|
-
... print(result["content"])
|
196
|
-
"""
|
197
|
-
try:
|
198
|
-
# Import MCP SDK
|
199
|
-
from mcp import ClientSession, StdioServerParameters
|
200
|
-
from mcp.client.stdio import stdio_client
|
201
|
-
|
202
|
-
# Parse server configuration
|
203
|
-
if isinstance(server_config, str):
|
204
|
-
logger.warning(
|
205
|
-
f"URL-based MCP servers not yet supported: {server_config}"
|
206
|
-
)
|
207
|
-
return {"error": "URL-based servers not supported"}
|
208
|
-
|
209
|
-
# Extract stdio configuration
|
210
|
-
transport = server_config.get("transport", "stdio")
|
211
|
-
if transport != "stdio":
|
212
|
-
logger.warning(
|
213
|
-
f"Only stdio transport currently supported, got: {transport}"
|
214
|
-
)
|
215
|
-
return {"error": f"Transport {transport} not supported"}
|
216
|
-
|
217
|
-
command = server_config.get("command", "python")
|
218
|
-
args = server_config.get("args", [])
|
219
|
-
env = server_config.get("env", {})
|
220
|
-
|
221
|
-
# Merge environment
|
222
|
-
server_env = os.environ.copy()
|
223
|
-
server_env.update(env)
|
224
|
-
|
225
|
-
# Create server parameters
|
226
|
-
server_params = StdioServerParameters(
|
227
|
-
command=command, args=args, env=server_env
|
228
|
-
)
|
229
|
-
|
230
|
-
# Connect and call tool
|
231
|
-
async with AsyncExitStack() as stack:
|
232
|
-
stdio = await stack.enter_async_context(stdio_client(server_params))
|
233
|
-
session = await stack.enter_async_context(
|
234
|
-
ClientSession(stdio[0], stdio[1])
|
235
|
-
)
|
236
|
-
|
237
|
-
# Initialize session
|
238
|
-
await session.initialize()
|
239
|
-
|
240
|
-
# Call tool
|
241
|
-
result = await session.call_tool(name=tool_name, arguments=arguments)
|
242
|
-
|
243
|
-
# Extract content from result
|
244
|
-
if hasattr(result, "content"):
|
245
|
-
content = []
|
246
|
-
for item in result.content:
|
247
|
-
if hasattr(item, "text"):
|
248
|
-
content.append(item.text)
|
249
|
-
else:
|
250
|
-
content.append(str(item))
|
251
|
-
return {"success": True, "content": content}
|
252
|
-
else:
|
253
|
-
return {"success": True, "result": str(result)}
|
254
|
-
|
255
|
-
except ImportError:
|
256
|
-
logger.error("MCP SDK not available. Install with: pip install mcp")
|
257
|
-
return {"error": "MCP SDK not available"}
|
258
|
-
except Exception as e:
|
259
|
-
logger.error(f"Failed to call tool: {e}")
|
260
|
-
return {"error": str(e)}
|
261
|
-
|
262
|
-
async def list_resources(
|
263
|
-
self, server_config: str | dict[str, Any]
|
264
|
-
) -> list[dict[str, Any]]:
|
265
|
-
"""List available resources from an MCP server.
|
266
|
-
|
267
|
-
Args:
|
268
|
-
server_config: Either a URL string or server configuration dict.
|
269
|
-
|
270
|
-
Returns:
|
271
|
-
List of resource definitions with uri, name, description, mimeType.
|
272
|
-
Returns empty list if server unavailable or on error.
|
273
|
-
|
274
|
-
Examples:
|
275
|
-
>>> resources = await client.list_resources(server_config)
|
276
|
-
>>> for resource in resources:
|
277
|
-
... print(f"Resource: {resource['name']} ({resource['uri']})")
|
278
|
-
"""
|
279
|
-
server_key = self._get_server_key(server_config)
|
280
|
-
|
281
|
-
# Return cached resources if available
|
282
|
-
if server_key in self._discovered_resources:
|
283
|
-
return self._discovered_resources[server_key]
|
284
|
-
|
285
|
-
try:
|
286
|
-
# Import MCP SDK
|
287
|
-
from mcp import ClientSession, StdioServerParameters
|
288
|
-
from mcp.client.stdio import stdio_client
|
289
|
-
|
290
|
-
# Parse server configuration (similar to discover_tools)
|
291
|
-
if isinstance(server_config, str):
|
292
|
-
logger.warning(
|
293
|
-
f"URL-based MCP servers not yet supported: {server_config}"
|
294
|
-
)
|
295
|
-
return []
|
296
|
-
|
297
|
-
# Extract stdio configuration
|
298
|
-
transport = server_config.get("transport", "stdio")
|
299
|
-
if transport != "stdio":
|
300
|
-
logger.warning(
|
301
|
-
f"Only stdio transport currently supported, got: {transport}"
|
302
|
-
)
|
303
|
-
return []
|
304
|
-
|
305
|
-
command = server_config.get("command", "python")
|
306
|
-
args = server_config.get("args", [])
|
307
|
-
env = server_config.get("env", {})
|
308
|
-
|
309
|
-
# Merge environment
|
310
|
-
server_env = os.environ.copy()
|
311
|
-
server_env.update(env)
|
312
|
-
|
313
|
-
# Create server parameters
|
314
|
-
server_params = StdioServerParameters(
|
315
|
-
command=command, args=args, env=server_env
|
316
|
-
)
|
317
|
-
|
318
|
-
# Connect and list resources
|
319
|
-
async with AsyncExitStack() as stack:
|
320
|
-
stdio = await stack.enter_async_context(stdio_client(server_params))
|
321
|
-
session = await stack.enter_async_context(
|
322
|
-
ClientSession(stdio[0], stdio[1])
|
323
|
-
)
|
324
|
-
|
325
|
-
# Initialize session
|
326
|
-
await session.initialize()
|
327
|
-
|
328
|
-
# List resources
|
329
|
-
result = await session.list_resources()
|
330
|
-
|
331
|
-
resources = []
|
332
|
-
for resource in result.resources:
|
333
|
-
resources.append(
|
334
|
-
{
|
335
|
-
"uri": resource.uri,
|
336
|
-
"name": resource.name,
|
337
|
-
"description": resource.description,
|
338
|
-
"mimeType": resource.mimeType,
|
339
|
-
}
|
340
|
-
)
|
341
|
-
|
342
|
-
# Cache the resources
|
343
|
-
self._discovered_resources[server_key] = resources
|
344
|
-
return resources
|
345
|
-
|
346
|
-
except ImportError:
|
347
|
-
logger.error("MCP SDK not available. Install with: pip install mcp")
|
348
|
-
return []
|
349
|
-
except Exception as e:
|
350
|
-
logger.error(f"Failed to list resources: {e}")
|
351
|
-
return []
|
352
|
-
|
353
|
-
async def read_resource(self, server_config: str | dict[str, Any], uri: str) -> Any:
|
354
|
-
"""Read a resource from an MCP server.
|
355
|
-
|
356
|
-
Args:
|
357
|
-
server_config: Either a URL string or server configuration dict.
|
358
|
-
uri: URI of the resource to read.
|
359
|
-
|
360
|
-
Returns:
|
361
|
-
Dict containing resource content. On success, includes 'success': True,
|
362
|
-
'content', and 'uri'. On error, includes 'error' with description.
|
363
|
-
|
364
|
-
Examples:
|
365
|
-
>>> content = await client.read_resource(
|
366
|
-
... server_config,
|
367
|
-
... "file:///docs/readme.md"
|
368
|
-
... )
|
369
|
-
>>> if content.get("success"):
|
370
|
-
... print(content["content"])
|
371
|
-
"""
|
372
|
-
try:
|
373
|
-
# Import MCP SDK
|
374
|
-
from mcp import ClientSession, StdioServerParameters
|
375
|
-
from mcp.client.stdio import stdio_client
|
376
|
-
|
377
|
-
# Parse server configuration (similar to call_tool)
|
378
|
-
if isinstance(server_config, str):
|
379
|
-
logger.warning(
|
380
|
-
f"URL-based MCP servers not yet supported: {server_config}"
|
381
|
-
)
|
382
|
-
return {"error": "URL-based servers not supported"}
|
383
|
-
|
384
|
-
# Extract stdio configuration
|
385
|
-
transport = server_config.get("transport", "stdio")
|
386
|
-
if transport != "stdio":
|
387
|
-
logger.warning(
|
388
|
-
f"Only stdio transport currently supported, got: {transport}"
|
389
|
-
)
|
390
|
-
return {"error": f"Transport {transport} not supported"}
|
391
|
-
|
392
|
-
command = server_config.get("command", "python")
|
393
|
-
args = server_config.get("args", [])
|
394
|
-
env = server_config.get("env", {})
|
395
|
-
|
396
|
-
# Merge environment
|
397
|
-
server_env = os.environ.copy()
|
398
|
-
server_env.update(env)
|
399
|
-
|
400
|
-
# Create server parameters
|
401
|
-
server_params = StdioServerParameters(
|
402
|
-
command=command, args=args, env=server_env
|
403
|
-
)
|
404
|
-
|
405
|
-
# Connect and read resource
|
406
|
-
async with AsyncExitStack() as stack:
|
407
|
-
stdio = await stack.enter_async_context(stdio_client(server_params))
|
408
|
-
session = await stack.enter_async_context(
|
409
|
-
ClientSession(stdio[0], stdio[1])
|
410
|
-
)
|
411
|
-
|
412
|
-
# Initialize session
|
413
|
-
await session.initialize()
|
414
|
-
|
415
|
-
# Read resource
|
416
|
-
result = await session.read_resource(uri=uri)
|
417
|
-
|
418
|
-
# Extract content
|
419
|
-
if hasattr(result, "contents"):
|
420
|
-
content = []
|
421
|
-
for item in result.contents:
|
422
|
-
if hasattr(item, "text"):
|
423
|
-
content.append(item.text)
|
424
|
-
elif hasattr(item, "blob"):
|
425
|
-
content.append({"blob": item.blob})
|
426
|
-
else:
|
427
|
-
content.append(str(item))
|
428
|
-
return {"success": True, "content": content, "uri": uri}
|
429
|
-
else:
|
430
|
-
return {"success": True, "result": str(result), "uri": uri}
|
431
|
-
|
432
|
-
except ImportError:
|
433
|
-
logger.error("MCP SDK not available. Install with: pip install mcp")
|
434
|
-
return {"error": "MCP SDK not available"}
|
435
|
-
except Exception as e:
|
436
|
-
logger.error(f"Failed to read resource: {e}")
|
437
|
-
return {"error": str(e)}
|
438
|
-
|
439
|
-
def _get_server_key(self, server_config: str | dict[str, Any]) -> str:
|
440
|
-
"""Generate a unique key for caching server data."""
|
441
|
-
if isinstance(server_config, str):
|
442
|
-
return server_config
|
443
|
-
else:
|
444
|
-
# Create a key from server config
|
445
|
-
return json.dumps(server_config, sort_keys=True)
|