nc1709 1.15.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.
- nc1709/__init__.py +13 -0
- nc1709/agent/__init__.py +36 -0
- nc1709/agent/core.py +505 -0
- nc1709/agent/mcp_bridge.py +245 -0
- nc1709/agent/permissions.py +298 -0
- nc1709/agent/tools/__init__.py +21 -0
- nc1709/agent/tools/base.py +440 -0
- nc1709/agent/tools/bash_tool.py +367 -0
- nc1709/agent/tools/file_tools.py +454 -0
- nc1709/agent/tools/notebook_tools.py +516 -0
- nc1709/agent/tools/search_tools.py +322 -0
- nc1709/agent/tools/task_tool.py +284 -0
- nc1709/agent/tools/web_tools.py +555 -0
- nc1709/agents/__init__.py +17 -0
- nc1709/agents/auto_fix.py +506 -0
- nc1709/agents/test_generator.py +507 -0
- nc1709/checkpoints.py +372 -0
- nc1709/cli.py +3380 -0
- nc1709/cli_ui.py +1080 -0
- nc1709/cognitive/__init__.py +149 -0
- nc1709/cognitive/anticipation.py +594 -0
- nc1709/cognitive/context_engine.py +1046 -0
- nc1709/cognitive/council.py +824 -0
- nc1709/cognitive/learning.py +761 -0
- nc1709/cognitive/router.py +583 -0
- nc1709/cognitive/system.py +519 -0
- nc1709/config.py +155 -0
- nc1709/custom_commands.py +300 -0
- nc1709/executor.py +333 -0
- nc1709/file_controller.py +354 -0
- nc1709/git_integration.py +308 -0
- nc1709/github_integration.py +477 -0
- nc1709/image_input.py +446 -0
- nc1709/linting.py +519 -0
- nc1709/llm_adapter.py +667 -0
- nc1709/logger.py +192 -0
- nc1709/mcp/__init__.py +18 -0
- nc1709/mcp/client.py +370 -0
- nc1709/mcp/manager.py +407 -0
- nc1709/mcp/protocol.py +210 -0
- nc1709/mcp/server.py +473 -0
- nc1709/memory/__init__.py +20 -0
- nc1709/memory/embeddings.py +325 -0
- nc1709/memory/indexer.py +474 -0
- nc1709/memory/sessions.py +432 -0
- nc1709/memory/vector_store.py +451 -0
- nc1709/models/__init__.py +86 -0
- nc1709/models/detector.py +377 -0
- nc1709/models/formats.py +315 -0
- nc1709/models/manager.py +438 -0
- nc1709/models/registry.py +497 -0
- nc1709/performance/__init__.py +343 -0
- nc1709/performance/cache.py +705 -0
- nc1709/performance/pipeline.py +611 -0
- nc1709/performance/tiering.py +543 -0
- nc1709/plan_mode.py +362 -0
- nc1709/plugins/__init__.py +17 -0
- nc1709/plugins/agents/__init__.py +18 -0
- nc1709/plugins/agents/django_agent.py +912 -0
- nc1709/plugins/agents/docker_agent.py +623 -0
- nc1709/plugins/agents/fastapi_agent.py +887 -0
- nc1709/plugins/agents/git_agent.py +731 -0
- nc1709/plugins/agents/nextjs_agent.py +867 -0
- nc1709/plugins/base.py +359 -0
- nc1709/plugins/manager.py +411 -0
- nc1709/plugins/registry.py +337 -0
- nc1709/progress.py +443 -0
- nc1709/prompts/__init__.py +22 -0
- nc1709/prompts/agent_system.py +180 -0
- nc1709/prompts/task_prompts.py +340 -0
- nc1709/prompts/unified_prompt.py +133 -0
- nc1709/reasoning_engine.py +541 -0
- nc1709/remote_client.py +266 -0
- nc1709/shell_completions.py +349 -0
- nc1709/slash_commands.py +649 -0
- nc1709/task_classifier.py +408 -0
- nc1709/version_check.py +177 -0
- nc1709/web/__init__.py +8 -0
- nc1709/web/server.py +950 -0
- nc1709/web/templates/index.html +1127 -0
- nc1709-1.15.4.dist-info/METADATA +858 -0
- nc1709-1.15.4.dist-info/RECORD +86 -0
- nc1709-1.15.4.dist-info/WHEEL +5 -0
- nc1709-1.15.4.dist-info/entry_points.txt +2 -0
- nc1709-1.15.4.dist-info/licenses/LICENSE +9 -0
- nc1709-1.15.4.dist-info/top_level.txt +1 -0
nc1709/mcp/manager.py
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Manager
|
|
3
|
+
High-level manager for MCP server and client operations
|
|
4
|
+
"""
|
|
5
|
+
import asyncio
|
|
6
|
+
from typing import Dict, Any, Optional, List, Callable
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from .server import MCPServer, ToolResult
|
|
10
|
+
from .client import MCPClient
|
|
11
|
+
from .protocol import MCPTool, MCPResource, MCPToolParameter
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MCPManager:
|
|
15
|
+
"""
|
|
16
|
+
High-level manager for MCP functionality.
|
|
17
|
+
|
|
18
|
+
Provides a unified interface for:
|
|
19
|
+
- Running NC1709 as an MCP server
|
|
20
|
+
- Connecting to external MCP servers as a client
|
|
21
|
+
- Managing tools and resources across connections
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, name: str = "nc1709", version: str = "1.0.0"):
|
|
25
|
+
"""Initialize the MCP manager
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
name: Name for the local MCP server
|
|
29
|
+
version: Version for the local MCP server
|
|
30
|
+
"""
|
|
31
|
+
self._server = MCPServer(name=name, version=version)
|
|
32
|
+
self._client = MCPClient()
|
|
33
|
+
self._running = False
|
|
34
|
+
self._server_task: Optional[asyncio.Task] = None
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def server(self) -> MCPServer:
|
|
38
|
+
"""Get the local MCP server"""
|
|
39
|
+
return self._server
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def client(self) -> MCPClient:
|
|
43
|
+
"""Get the MCP client"""
|
|
44
|
+
return self._client
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def is_running(self) -> bool:
|
|
48
|
+
"""Check if the server is running"""
|
|
49
|
+
return self._running
|
|
50
|
+
|
|
51
|
+
# =========================================================================
|
|
52
|
+
# Server Management
|
|
53
|
+
# =========================================================================
|
|
54
|
+
|
|
55
|
+
def register_tool(
|
|
56
|
+
self,
|
|
57
|
+
name: str,
|
|
58
|
+
description: str,
|
|
59
|
+
handler: Callable,
|
|
60
|
+
parameters: Optional[List[MCPToolParameter]] = None
|
|
61
|
+
) -> None:
|
|
62
|
+
"""Register a tool with the local server
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
name: Tool name
|
|
66
|
+
description: Tool description
|
|
67
|
+
handler: Tool handler function
|
|
68
|
+
parameters: Tool parameters
|
|
69
|
+
"""
|
|
70
|
+
self._server.register_tool(name, description, handler, parameters)
|
|
71
|
+
|
|
72
|
+
def register_resource(
|
|
73
|
+
self,
|
|
74
|
+
uri: str,
|
|
75
|
+
name: str,
|
|
76
|
+
description: str = "",
|
|
77
|
+
mime_type: Optional[str] = None
|
|
78
|
+
) -> None:
|
|
79
|
+
"""Register a resource with the local server
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
uri: Resource URI
|
|
83
|
+
name: Resource name
|
|
84
|
+
description: Resource description
|
|
85
|
+
mime_type: MIME type
|
|
86
|
+
"""
|
|
87
|
+
self._server.register_resource(uri, name, description, mime_type)
|
|
88
|
+
|
|
89
|
+
def register_prompt(
|
|
90
|
+
self,
|
|
91
|
+
name: str,
|
|
92
|
+
description: str,
|
|
93
|
+
arguments: Optional[List[Dict]] = None
|
|
94
|
+
) -> None:
|
|
95
|
+
"""Register a prompt template
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
name: Prompt name
|
|
99
|
+
description: Prompt description
|
|
100
|
+
arguments: Prompt arguments
|
|
101
|
+
"""
|
|
102
|
+
self._server.register_prompt(name, description, arguments)
|
|
103
|
+
|
|
104
|
+
def setup_default_tools(self) -> None:
|
|
105
|
+
"""Set up default NC1709 tools on the server"""
|
|
106
|
+
self._server.create_default_tools()
|
|
107
|
+
|
|
108
|
+
async def start_server(self) -> None:
|
|
109
|
+
"""Start the MCP server"""
|
|
110
|
+
if self._running:
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
self._running = True
|
|
114
|
+
self._server_task = asyncio.create_task(self._server.run_stdio())
|
|
115
|
+
|
|
116
|
+
async def stop_server(self) -> None:
|
|
117
|
+
"""Stop the MCP server"""
|
|
118
|
+
if not self._running:
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
self._running = False
|
|
122
|
+
if self._server_task:
|
|
123
|
+
self._server_task.cancel()
|
|
124
|
+
try:
|
|
125
|
+
await self._server_task
|
|
126
|
+
except asyncio.CancelledError:
|
|
127
|
+
pass
|
|
128
|
+
self._server_task = None
|
|
129
|
+
|
|
130
|
+
# =========================================================================
|
|
131
|
+
# Client Management
|
|
132
|
+
# =========================================================================
|
|
133
|
+
|
|
134
|
+
async def connect_server(
|
|
135
|
+
self,
|
|
136
|
+
name: str,
|
|
137
|
+
command: str,
|
|
138
|
+
args: Optional[List[str]] = None,
|
|
139
|
+
env: Optional[Dict[str, str]] = None
|
|
140
|
+
) -> bool:
|
|
141
|
+
"""Connect to an external MCP server
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
name: Server name
|
|
145
|
+
command: Command to start the server
|
|
146
|
+
args: Command arguments
|
|
147
|
+
env: Environment variables
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
True if connected successfully
|
|
151
|
+
"""
|
|
152
|
+
return await self._client.connect(name, command, args, env)
|
|
153
|
+
|
|
154
|
+
async def disconnect_server(self, name: str) -> bool:
|
|
155
|
+
"""Disconnect from an external MCP server
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
name: Server name
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
True if disconnected
|
|
162
|
+
"""
|
|
163
|
+
return await self._client.disconnect(name)
|
|
164
|
+
|
|
165
|
+
async def disconnect_all_servers(self) -> None:
|
|
166
|
+
"""Disconnect from all external servers"""
|
|
167
|
+
await self._client.disconnect_all()
|
|
168
|
+
|
|
169
|
+
async def auto_discover_servers(self, config_path: Optional[str] = None) -> int:
|
|
170
|
+
"""Auto-discover and connect to MCP servers from config
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
config_path: Path to MCP config file
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Number of servers connected
|
|
177
|
+
"""
|
|
178
|
+
return await self._client.auto_discover(config_path)
|
|
179
|
+
|
|
180
|
+
# =========================================================================
|
|
181
|
+
# Tool Operations
|
|
182
|
+
# =========================================================================
|
|
183
|
+
|
|
184
|
+
def get_local_tools(self) -> List[MCPTool]:
|
|
185
|
+
"""Get tools registered on the local server
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
List of local tools
|
|
189
|
+
"""
|
|
190
|
+
return list(self._server._tools.values())
|
|
191
|
+
|
|
192
|
+
def get_remote_tools(self, server_name: Optional[str] = None) -> List[MCPTool]:
|
|
193
|
+
"""Get tools from connected servers
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
server_name: Filter by server (None for all)
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
List of remote tools
|
|
200
|
+
"""
|
|
201
|
+
return self._client.get_tools(server_name)
|
|
202
|
+
|
|
203
|
+
def get_all_tools(self) -> Dict[str, List[MCPTool]]:
|
|
204
|
+
"""Get all available tools (local and remote)
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
Dict with 'local' and 'remote' tool lists
|
|
208
|
+
"""
|
|
209
|
+
return {
|
|
210
|
+
"local": self.get_local_tools(),
|
|
211
|
+
"remote": self.get_remote_tools()
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async def call_remote_tool(
|
|
215
|
+
self,
|
|
216
|
+
server_name: str,
|
|
217
|
+
tool_name: str,
|
|
218
|
+
arguments: Optional[Dict[str, Any]] = None
|
|
219
|
+
) -> Dict[str, Any]:
|
|
220
|
+
"""Call a tool on a remote server
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
server_name: Server name
|
|
224
|
+
tool_name: Tool name
|
|
225
|
+
arguments: Tool arguments
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Tool result
|
|
229
|
+
"""
|
|
230
|
+
return await self._client.call_tool(server_name, tool_name, arguments)
|
|
231
|
+
|
|
232
|
+
async def call_tool(
|
|
233
|
+
self,
|
|
234
|
+
tool_name: str,
|
|
235
|
+
arguments: Optional[Dict[str, Any]] = None,
|
|
236
|
+
server_name: Optional[str] = None
|
|
237
|
+
) -> Dict[str, Any]:
|
|
238
|
+
"""Call a tool (local or remote)
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
tool_name: Tool name
|
|
242
|
+
arguments: Tool arguments
|
|
243
|
+
server_name: Server name (None for local)
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
Tool result
|
|
247
|
+
"""
|
|
248
|
+
if server_name:
|
|
249
|
+
return await self.call_remote_tool(server_name, tool_name, arguments)
|
|
250
|
+
|
|
251
|
+
# Call local tool
|
|
252
|
+
if tool_name not in self._server._tools:
|
|
253
|
+
return {"error": f"Tool not found: {tool_name}"}
|
|
254
|
+
|
|
255
|
+
tool = self._server._tools[tool_name]
|
|
256
|
+
if tool.handler is None:
|
|
257
|
+
return {"error": f"Tool {tool_name} has no handler"}
|
|
258
|
+
|
|
259
|
+
try:
|
|
260
|
+
if asyncio.iscoroutinefunction(tool.handler):
|
|
261
|
+
result = await tool.handler(**(arguments or {}))
|
|
262
|
+
else:
|
|
263
|
+
result = tool.handler(**(arguments or {}))
|
|
264
|
+
|
|
265
|
+
if isinstance(result, ToolResult):
|
|
266
|
+
return {
|
|
267
|
+
"content": result.content,
|
|
268
|
+
"isError": result.is_error
|
|
269
|
+
}
|
|
270
|
+
elif isinstance(result, str):
|
|
271
|
+
return {
|
|
272
|
+
"content": [{"type": "text", "text": result}],
|
|
273
|
+
"isError": False
|
|
274
|
+
}
|
|
275
|
+
elif isinstance(result, dict):
|
|
276
|
+
return result
|
|
277
|
+
else:
|
|
278
|
+
return {
|
|
279
|
+
"content": [{"type": "text", "text": str(result)}],
|
|
280
|
+
"isError": False
|
|
281
|
+
}
|
|
282
|
+
except Exception as e:
|
|
283
|
+
return {"error": str(e)}
|
|
284
|
+
|
|
285
|
+
# =========================================================================
|
|
286
|
+
# Resource Operations
|
|
287
|
+
# =========================================================================
|
|
288
|
+
|
|
289
|
+
def get_local_resources(self) -> List[MCPResource]:
|
|
290
|
+
"""Get resources registered on the local server
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
List of local resources
|
|
294
|
+
"""
|
|
295
|
+
return list(self._server._resources.values())
|
|
296
|
+
|
|
297
|
+
def get_remote_resources(self, server_name: Optional[str] = None) -> List[MCPResource]:
|
|
298
|
+
"""Get resources from connected servers
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
server_name: Filter by server (None for all)
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
List of remote resources
|
|
305
|
+
"""
|
|
306
|
+
return self._client.get_resources(server_name)
|
|
307
|
+
|
|
308
|
+
def get_all_resources(self) -> Dict[str, List[MCPResource]]:
|
|
309
|
+
"""Get all available resources (local and remote)
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
Dict with 'local' and 'remote' resource lists
|
|
313
|
+
"""
|
|
314
|
+
return {
|
|
315
|
+
"local": self.get_local_resources(),
|
|
316
|
+
"remote": self.get_remote_resources()
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async def read_remote_resource(
|
|
320
|
+
self,
|
|
321
|
+
server_name: str,
|
|
322
|
+
uri: str
|
|
323
|
+
) -> Dict[str, Any]:
|
|
324
|
+
"""Read a resource from a remote server
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
server_name: Server name
|
|
328
|
+
uri: Resource URI
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
Resource contents
|
|
332
|
+
"""
|
|
333
|
+
return await self._client.read_resource(server_name, uri)
|
|
334
|
+
|
|
335
|
+
# =========================================================================
|
|
336
|
+
# Status and Info
|
|
337
|
+
# =========================================================================
|
|
338
|
+
|
|
339
|
+
def list_connected_servers(self) -> List[Dict[str, Any]]:
|
|
340
|
+
"""List all connected external servers
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
List of server info dicts
|
|
344
|
+
"""
|
|
345
|
+
return self._client.list_servers()
|
|
346
|
+
|
|
347
|
+
def get_status(self) -> Dict[str, Any]:
|
|
348
|
+
"""Get overall MCP status
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
Status information
|
|
352
|
+
"""
|
|
353
|
+
return {
|
|
354
|
+
"server": {
|
|
355
|
+
"name": self._server.name,
|
|
356
|
+
"version": self._server.version,
|
|
357
|
+
"running": self._running,
|
|
358
|
+
"tools": len(self._server._tools),
|
|
359
|
+
"resources": len(self._server._resources),
|
|
360
|
+
"prompts": len(self._server._prompts)
|
|
361
|
+
},
|
|
362
|
+
"client": {
|
|
363
|
+
"connected_servers": len(self._client._servers),
|
|
364
|
+
"servers": self.list_connected_servers()
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
# =========================================================================
|
|
369
|
+
# Cleanup
|
|
370
|
+
# =========================================================================
|
|
371
|
+
|
|
372
|
+
async def shutdown(self) -> None:
|
|
373
|
+
"""Shutdown all MCP connections"""
|
|
374
|
+
await self.stop_server()
|
|
375
|
+
await self.disconnect_all_servers()
|
|
376
|
+
|
|
377
|
+
async def __aenter__(self) -> "MCPManager":
|
|
378
|
+
"""Async context manager entry"""
|
|
379
|
+
return self
|
|
380
|
+
|
|
381
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
382
|
+
"""Async context manager exit"""
|
|
383
|
+
await self.shutdown()
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
# Convenience function to create a configured manager
|
|
387
|
+
def create_mcp_manager(
|
|
388
|
+
name: str = "nc1709",
|
|
389
|
+
version: str = "1.0.0",
|
|
390
|
+
with_default_tools: bool = True
|
|
391
|
+
) -> MCPManager:
|
|
392
|
+
"""Create and configure an MCP manager
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
name: Server name
|
|
396
|
+
version: Server version
|
|
397
|
+
with_default_tools: Whether to register default tools
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
Configured MCPManager instance
|
|
401
|
+
"""
|
|
402
|
+
manager = MCPManager(name=name, version=version)
|
|
403
|
+
|
|
404
|
+
if with_default_tools:
|
|
405
|
+
manager.setup_default_tools()
|
|
406
|
+
|
|
407
|
+
return manager
|
nc1709/mcp/protocol.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Protocol Definitions
|
|
3
|
+
Implements the Model Context Protocol message types and structures
|
|
4
|
+
"""
|
|
5
|
+
from dataclasses import dataclass, field, asdict
|
|
6
|
+
from typing import List, Dict, Any, Optional, Union
|
|
7
|
+
from enum import Enum
|
|
8
|
+
import json
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MCPMessageType(Enum):
|
|
12
|
+
"""MCP message types"""
|
|
13
|
+
# Requests
|
|
14
|
+
INITIALIZE = "initialize"
|
|
15
|
+
LIST_TOOLS = "tools/list"
|
|
16
|
+
CALL_TOOL = "tools/call"
|
|
17
|
+
LIST_RESOURCES = "resources/list"
|
|
18
|
+
READ_RESOURCE = "resources/read"
|
|
19
|
+
LIST_PROMPTS = "prompts/list"
|
|
20
|
+
GET_PROMPT = "prompts/get"
|
|
21
|
+
|
|
22
|
+
# Responses
|
|
23
|
+
RESULT = "result"
|
|
24
|
+
ERROR = "error"
|
|
25
|
+
|
|
26
|
+
# Notifications
|
|
27
|
+
NOTIFICATION = "notification"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class MCPMessage:
|
|
32
|
+
"""Base MCP message structure"""
|
|
33
|
+
jsonrpc: str = "2.0"
|
|
34
|
+
id: Optional[Union[str, int]] = None
|
|
35
|
+
method: Optional[str] = None
|
|
36
|
+
params: Optional[Dict[str, Any]] = None
|
|
37
|
+
result: Optional[Any] = None
|
|
38
|
+
error: Optional[Dict[str, Any]] = None
|
|
39
|
+
|
|
40
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
41
|
+
"""Convert to dictionary, excluding None values"""
|
|
42
|
+
data = {"jsonrpc": self.jsonrpc}
|
|
43
|
+
if self.id is not None:
|
|
44
|
+
data["id"] = self.id
|
|
45
|
+
if self.method is not None:
|
|
46
|
+
data["method"] = self.method
|
|
47
|
+
if self.params is not None:
|
|
48
|
+
data["params"] = self.params
|
|
49
|
+
if self.result is not None:
|
|
50
|
+
data["result"] = self.result
|
|
51
|
+
if self.error is not None:
|
|
52
|
+
data["error"] = self.error
|
|
53
|
+
return data
|
|
54
|
+
|
|
55
|
+
def to_json(self) -> str:
|
|
56
|
+
"""Convert to JSON string"""
|
|
57
|
+
return json.dumps(self.to_dict())
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def from_json(cls, data: str) -> "MCPMessage":
|
|
61
|
+
"""Parse from JSON string"""
|
|
62
|
+
parsed = json.loads(data)
|
|
63
|
+
return cls(**parsed)
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def request(cls, id: Union[str, int], method: str, params: Optional[Dict] = None) -> "MCPMessage":
|
|
67
|
+
"""Create a request message"""
|
|
68
|
+
return cls(id=id, method=method, params=params)
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def response(cls, id: Union[str, int], result: Any) -> "MCPMessage":
|
|
72
|
+
"""Create a success response"""
|
|
73
|
+
return cls(id=id, result=result)
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def error_response(cls, id: Union[str, int], code: int, message: str, data: Any = None) -> "MCPMessage":
|
|
77
|
+
"""Create an error response"""
|
|
78
|
+
error = {"code": code, "message": message}
|
|
79
|
+
if data is not None:
|
|
80
|
+
error["data"] = data
|
|
81
|
+
return cls(id=id, error=error)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass
|
|
85
|
+
class MCPToolParameter:
|
|
86
|
+
"""Parameter definition for an MCP tool"""
|
|
87
|
+
name: str
|
|
88
|
+
type: str # "string", "number", "boolean", "array", "object"
|
|
89
|
+
description: str = ""
|
|
90
|
+
required: bool = False
|
|
91
|
+
default: Any = None
|
|
92
|
+
|
|
93
|
+
def to_json_schema(self) -> Dict[str, Any]:
|
|
94
|
+
"""Convert to JSON Schema format"""
|
|
95
|
+
schema = {
|
|
96
|
+
"type": self.type,
|
|
97
|
+
"description": self.description
|
|
98
|
+
}
|
|
99
|
+
if self.default is not None:
|
|
100
|
+
schema["default"] = self.default
|
|
101
|
+
return schema
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@dataclass
|
|
105
|
+
class MCPTool:
|
|
106
|
+
"""Represents an MCP tool (capability)"""
|
|
107
|
+
name: str
|
|
108
|
+
description: str
|
|
109
|
+
parameters: List[MCPToolParameter] = field(default_factory=list)
|
|
110
|
+
handler: Optional[Any] = None # Callable for local tools
|
|
111
|
+
|
|
112
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
113
|
+
"""Convert to MCP tool format"""
|
|
114
|
+
properties = {}
|
|
115
|
+
required = []
|
|
116
|
+
|
|
117
|
+
for param in self.parameters:
|
|
118
|
+
properties[param.name] = param.to_json_schema()
|
|
119
|
+
if param.required:
|
|
120
|
+
required.append(param.name)
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
"name": self.name,
|
|
124
|
+
"description": self.description,
|
|
125
|
+
"inputSchema": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"properties": properties,
|
|
128
|
+
"required": required
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@dataclass
|
|
134
|
+
class MCPResource:
|
|
135
|
+
"""Represents an MCP resource"""
|
|
136
|
+
uri: str
|
|
137
|
+
name: str
|
|
138
|
+
description: str = ""
|
|
139
|
+
mimeType: Optional[str] = None
|
|
140
|
+
|
|
141
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
142
|
+
"""Convert to MCP resource format"""
|
|
143
|
+
data = {
|
|
144
|
+
"uri": self.uri,
|
|
145
|
+
"name": self.name,
|
|
146
|
+
"description": self.description
|
|
147
|
+
}
|
|
148
|
+
if self.mimeType:
|
|
149
|
+
data["mimeType"] = self.mimeType
|
|
150
|
+
return data
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@dataclass
|
|
154
|
+
class MCPPrompt:
|
|
155
|
+
"""Represents an MCP prompt template"""
|
|
156
|
+
name: str
|
|
157
|
+
description: str
|
|
158
|
+
arguments: List[Dict[str, Any]] = field(default_factory=list)
|
|
159
|
+
|
|
160
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
161
|
+
"""Convert to MCP prompt format"""
|
|
162
|
+
return {
|
|
163
|
+
"name": self.name,
|
|
164
|
+
"description": self.description,
|
|
165
|
+
"arguments": self.arguments
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@dataclass
|
|
170
|
+
class MCPServerInfo:
|
|
171
|
+
"""MCP server capabilities and information"""
|
|
172
|
+
name: str
|
|
173
|
+
version: str
|
|
174
|
+
capabilities: Dict[str, Any] = field(default_factory=dict)
|
|
175
|
+
|
|
176
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
177
|
+
return {
|
|
178
|
+
"name": self.name,
|
|
179
|
+
"version": self.version,
|
|
180
|
+
"capabilities": self.capabilities
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@dataclass
|
|
185
|
+
class MCPClientInfo:
|
|
186
|
+
"""MCP client information"""
|
|
187
|
+
name: str
|
|
188
|
+
version: str
|
|
189
|
+
|
|
190
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
191
|
+
return {
|
|
192
|
+
"name": self.name,
|
|
193
|
+
"version": self.version
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# Error codes following JSON-RPC 2.0
|
|
198
|
+
class MCPErrorCode:
|
|
199
|
+
"""Standard MCP error codes"""
|
|
200
|
+
PARSE_ERROR = -32700
|
|
201
|
+
INVALID_REQUEST = -32600
|
|
202
|
+
METHOD_NOT_FOUND = -32601
|
|
203
|
+
INVALID_PARAMS = -32602
|
|
204
|
+
INTERNAL_ERROR = -32603
|
|
205
|
+
|
|
206
|
+
# Custom MCP errors
|
|
207
|
+
TOOL_NOT_FOUND = -32000
|
|
208
|
+
RESOURCE_NOT_FOUND = -32001
|
|
209
|
+
PERMISSION_DENIED = -32002
|
|
210
|
+
EXECUTION_ERROR = -32003
|