Maheen001 commited on
Commit
2cb3622
·
verified ·
1 Parent(s): 7eb32cf

Create agent/mcp_client.py

Browse files
Files changed (1) hide show
  1. agent/mcp_client.py +203 -0
agent/mcp_client.py ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ MCP Client - JSONRPC communication with MCP tool servers
3
+ """
4
+
5
+ import asyncio
6
+ import json
7
+ from typing import List, Dict, Any, Optional
8
+ import aiohttp
9
+
10
+
11
+ class MCPClient:
12
+ """Client for communicating with MCP tool servers via JSONRPC"""
13
+
14
+ def __init__(self, base_port: int = 8100):
15
+ self.base_port = base_port
16
+ self.servers = {
17
+ 'ocr': f'http://localhost:{base_port}',
18
+ 'pdf': f'http://localhost:{base_port + 1}',
19
+ 'form_filler': f'http://localhost:{base_port + 2}',
20
+ 'calendar': f'http://localhost:{base_port + 3}',
21
+ 'email': f'http://localhost:{base_port + 4}',
22
+ 'file_manager': f'http://localhost:{base_port + 5}',
23
+ 'rag': f'http://localhost:{base_port + 6}',
24
+ }
25
+ self.request_id = 0
26
+
27
+ # Tool registry (in production, this would be discovered from servers)
28
+ self.tool_registry = {
29
+ 'ocr_extract_text': {
30
+ 'server': 'ocr',
31
+ 'description': 'Extract text from images or scanned documents using OCR',
32
+ 'params': {'file_path': 'string', 'language': 'string'}
33
+ },
34
+ 'pdf_summarize': {
35
+ 'server': 'pdf',
36
+ 'description': 'Summarize PDF documents',
37
+ 'params': {'file_path': 'string', 'max_length': 'int'}
38
+ },
39
+ 'pdf_extract_metadata': {
40
+ 'server': 'pdf',
41
+ 'description': 'Extract metadata from PDF (dates, amounts, etc.)',
42
+ 'params': {'file_path': 'string'}
43
+ },
44
+ 'form_fill': {
45
+ 'server': 'form_filler',
46
+ 'description': 'Auto-fill form with extracted data',
47
+ 'params': {'template_path': 'string', 'data': 'object'}
48
+ },
49
+ 'calendar_create_event': {
50
+ 'server': 'calendar',
51
+ 'description': 'Create ICS calendar event',
52
+ 'params': {'title': 'string', 'start': 'string', 'end': 'string', 'description': 'string'}
53
+ },
54
+ 'email_draft': {
55
+ 'server': 'email',
56
+ 'description': 'Draft an email based on context',
57
+ 'params': {'recipient': 'string', 'subject': 'string', 'context': 'string', 'tone': 'string'}
58
+ },
59
+ 'file_organize': {
60
+ 'server': 'file_manager',
61
+ 'description': 'Organize files by category',
62
+ 'params': {'directory': 'string', 'strategy': 'string'}
63
+ },
64
+ 'file_rename': {
65
+ 'server': 'file_manager',
66
+ 'description': 'Rename file with semantic name',
67
+ 'params': {'file_path': 'string', 'new_name': 'string'}
68
+ },
69
+ 'rag_search': {
70
+ 'server': 'rag',
71
+ 'description': 'Search documents semantically',
72
+ 'params': {'query': 'string', 'k': 'int'}
73
+ },
74
+ }
75
+
76
+ def _get_next_id(self) -> int:
77
+ """Get next request ID"""
78
+ self.request_id += 1
79
+ return self.request_id
80
+
81
+ async def call_tool(self, tool_name: str, args: Dict[str, Any]) -> Any:
82
+ """
83
+ Call an MCP tool
84
+
85
+ Args:
86
+ tool_name: Name of the tool to call
87
+ args: Arguments for the tool
88
+
89
+ Returns:
90
+ Tool result
91
+ """
92
+ if tool_name not in self.tool_registry:
93
+ raise ValueError(f"Unknown tool: {tool_name}")
94
+
95
+ tool_info = self.tool_registry[tool_name]
96
+ server_name = tool_info['server']
97
+ server_url = self.servers.get(server_name)
98
+
99
+ if not server_url:
100
+ # Fallback to local execution if server not available
101
+ return await self._execute_tool_locally(tool_name, args)
102
+
103
+ # Create JSONRPC request
104
+ request = {
105
+ 'jsonrpc': '2.0',
106
+ 'id': self._get_next_id(),
107
+ 'method': 'tools/call',
108
+ 'params': {
109
+ 'name': tool_name,
110
+ 'arguments': args
111
+ }
112
+ }
113
+
114
+ try:
115
+ async with aiohttp.ClientSession() as session:
116
+ async with session.post(
117
+ server_url + '/rpc',
118
+ json=request,
119
+ timeout=aiohttp.ClientTimeout(total=30)
120
+ ) as response:
121
+ if response.status != 200:
122
+ raise Exception(f"Server returned {response.status}")
123
+
124
+ result = await response.json()
125
+
126
+ if 'error' in result:
127
+ raise Exception(result['error'].get('message', 'Unknown error'))
128
+
129
+ return result.get('result', {})
130
+
131
+ except (aiohttp.ClientError, asyncio.TimeoutError):
132
+ # Server not available, execute locally
133
+ return await self._execute_tool_locally(tool_name, args)
134
+
135
+ async def _execute_tool_locally(self, tool_name: str, args: Dict[str, Any]) -> Any:
136
+ """Execute tool locally when server is not available"""
137
+
138
+ # Import tool implementations
139
+ if tool_name == 'ocr_extract_text':
140
+ from tools.ocr_server import extract_text_ocr
141
+ return await extract_text_ocr(args.get('file_path'), args.get('language', 'en'))
142
+
143
+ elif tool_name == 'pdf_summarize':
144
+ from tools.pdf_server import summarize_pdf
145
+ return await summarize_pdf(args.get('file_path'), args.get('max_length', 500))
146
+
147
+ elif tool_name == 'pdf_extract_metadata':
148
+ from tools.pdf_server import extract_pdf_metadata
149
+ return await extract_pdf_metadata(args.get('file_path'))
150
+
151
+ elif tool_name == 'form_fill':
152
+ from tools.form_filler_server import fill_form
153
+ return await fill_form(args.get('template_path'), args.get('data'))
154
+
155
+ elif tool_name == 'calendar_create_event':
156
+ from tools.calendar_server import create_calendar_event
157
+ return await create_calendar_event(
158
+ args.get('title'),
159
+ args.get('start'),
160
+ args.get('end'),
161
+ args.get('description', '')
162
+ )
163
+
164
+ elif tool_name == 'email_draft':
165
+ from tools.email_server import draft_email
166
+ return await draft_email(
167
+ args.get('recipient'),
168
+ args.get('subject'),
169
+ args.get('context'),
170
+ args.get('tone', 'professional')
171
+ )
172
+
173
+ elif tool_name == 'file_organize':
174
+ from tools.file_manager_server import organize_files
175
+ return await organize_files(args.get('directory'), args.get('strategy', 'by_type'))
176
+
177
+ elif tool_name == 'file_rename':
178
+ from tools.file_manager_server import rename_file
179
+ return await rename_file(args.get('file_path'), args.get('new_name'))
180
+
181
+ elif tool_name == 'rag_search':
182
+ from tools.rag_server import search_documents
183
+ return await search_documents(args.get('query'), args.get('k', 5))
184
+
185
+ else:
186
+ raise ValueError(f"Cannot execute tool locally: {tool_name}")
187
+
188
+ async def list_tools(self) -> List[Dict[str, Any]]:
189
+ """List all available tools"""
190
+ return [
191
+ {
192
+ 'name': name,
193
+ 'description': info['description'],
194
+ 'params': info['params']
195
+ }
196
+ for name, info in self.tool_registry.items()
197
+ ]
198
+
199
+ async def get_tool_schema(self, tool_name: str) -> Dict[str, Any]:
200
+ """Get schema for a specific tool"""
201
+ if tool_name not in self.tool_registry:
202
+ raise ValueError(f"Unknown tool: {tool_name}")
203
+ return self.tool_registry[tool_name]