agenticwerx-mcp-client 1.0.0__py3-none-any.whl → 1.0.5__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.
- agenticwerx_mcp_client/__init__.py +3 -3
- agenticwerx_mcp_client/__main__.py +285 -46
- agenticwerx_mcp_client/api.py +144 -69
- agenticwerx_mcp_client/client.py +292 -81
- {agenticwerx_mcp_client-1.0.0.dist-info → agenticwerx_mcp_client-1.0.5.dist-info}/METADATA +44 -11
- agenticwerx_mcp_client-1.0.5.dist-info/RECORD +9 -0
- agenticwerx_mcp_client-1.0.0.dist-info/RECORD +0 -9
- {agenticwerx_mcp_client-1.0.0.dist-info → agenticwerx_mcp_client-1.0.5.dist-info}/WHEEL +0 -0
- {agenticwerx_mcp_client-1.0.0.dist-info → agenticwerx_mcp_client-1.0.5.dist-info}/entry_points.txt +0 -0
- {agenticwerx_mcp_client-1.0.0.dist-info → agenticwerx_mcp_client-1.0.5.dist-info}/licenses/LICENSE +0 -0
agenticwerx_mcp_client/api.py
CHANGED
|
@@ -8,17 +8,17 @@ using proper JSON-RPC 2.0 protocol.
|
|
|
8
8
|
import logging
|
|
9
9
|
import random
|
|
10
10
|
import string
|
|
11
|
-
from typing import
|
|
12
|
-
import httpx
|
|
11
|
+
from typing import Any
|
|
13
12
|
|
|
13
|
+
import httpx
|
|
14
14
|
|
|
15
15
|
logger = logging.getLogger(__name__)
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class AgenticWerxAPIError(Exception):
|
|
19
19
|
"""Custom exception for API errors."""
|
|
20
|
-
|
|
21
|
-
def __init__(self, message: str, status_code:
|
|
20
|
+
|
|
21
|
+
def __init__(self, message: str, status_code: int | None = None):
|
|
22
22
|
super().__init__(message)
|
|
23
23
|
self.status_code = status_code
|
|
24
24
|
|
|
@@ -26,116 +26,125 @@ class AgenticWerxAPIError(Exception):
|
|
|
26
26
|
class AgenticWerxAPI:
|
|
27
27
|
"""
|
|
28
28
|
AgenticWerx Lambda MCP API client.
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
This client connects to the AgenticWerx Lambda MCP server using
|
|
31
31
|
proper JSON-RPC 2.0 protocol for MCP communication.
|
|
32
32
|
"""
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
def __init__(self, api_key: str):
|
|
35
35
|
"""
|
|
36
36
|
Initialize the API client.
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
Args:
|
|
39
39
|
api_key: AgenticWerx API key
|
|
40
40
|
"""
|
|
41
41
|
self.api_key = api_key
|
|
42
|
-
self.lambda_url =
|
|
43
|
-
|
|
42
|
+
self.lambda_url = (
|
|
43
|
+
"https://rph7c2jq5zpisbenj73y2hpjfm0gtwdw.lambda-url.us-west-2.on.aws/"
|
|
44
|
+
)
|
|
45
|
+
|
|
44
46
|
# Create HTTP client with proper headers and timeout
|
|
47
|
+
# Note: SSL verification disabled since this connects to your own trusted Lambda endpoint
|
|
45
48
|
self.client = httpx.AsyncClient(
|
|
46
49
|
headers={
|
|
47
50
|
"Authorization": f"Bearer {api_key}",
|
|
48
51
|
"User-Agent": "AgenticWerx-Lambda-MCP-Client/1.0.0",
|
|
49
|
-
"Content-Type": "application/json"
|
|
52
|
+
"Content-Type": "application/json",
|
|
50
53
|
},
|
|
51
|
-
timeout=httpx.Timeout(30.0, connect=10.0)
|
|
54
|
+
timeout=httpx.Timeout(30.0, connect=10.0),
|
|
55
|
+
verify=False, # Safe to disable for your own Lambda endpoint
|
|
52
56
|
)
|
|
53
|
-
|
|
57
|
+
|
|
54
58
|
logger.debug(f"Initialized Lambda MCP client for: {self.lambda_url}")
|
|
55
|
-
|
|
59
|
+
|
|
56
60
|
async def __aenter__(self):
|
|
57
61
|
"""Async context manager entry."""
|
|
58
62
|
return self
|
|
59
|
-
|
|
63
|
+
|
|
60
64
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
61
65
|
"""Async context manager exit."""
|
|
62
66
|
await self.close()
|
|
63
|
-
|
|
67
|
+
|
|
64
68
|
async def close(self) -> None:
|
|
65
69
|
"""Close the HTTP client."""
|
|
66
70
|
await self.client.aclose()
|
|
67
|
-
|
|
71
|
+
|
|
68
72
|
def _generate_request_id(self) -> str:
|
|
69
73
|
"""Generate a random request ID."""
|
|
70
|
-
return
|
|
71
|
-
|
|
72
|
-
async def _make_mcp_request(
|
|
74
|
+
return "".join(random.choices(string.ascii_lowercase + string.digits, k=7))
|
|
75
|
+
|
|
76
|
+
async def _make_mcp_request(
|
|
77
|
+
self, method: str, params: dict[str, Any] | None = None
|
|
78
|
+
) -> Any:
|
|
73
79
|
"""
|
|
74
80
|
Make an MCP request to the Lambda function.
|
|
75
|
-
|
|
81
|
+
|
|
76
82
|
Args:
|
|
77
83
|
method: MCP method name
|
|
78
84
|
params: Optional parameters for the method
|
|
79
|
-
|
|
85
|
+
|
|
80
86
|
Returns:
|
|
81
87
|
The result from the MCP server
|
|
82
88
|
"""
|
|
83
89
|
request_id = self._generate_request_id()
|
|
84
|
-
|
|
90
|
+
|
|
85
91
|
mcp_request = {
|
|
86
92
|
"jsonrpc": "2.0",
|
|
87
93
|
"id": request_id,
|
|
88
94
|
"method": method,
|
|
89
|
-
"params": params or {}
|
|
95
|
+
"params": params or {},
|
|
90
96
|
}
|
|
91
|
-
|
|
97
|
+
|
|
92
98
|
logger.debug(f"Making MCP request to Lambda: {method}")
|
|
93
99
|
logger.debug(f"Request payload: {mcp_request}")
|
|
94
|
-
|
|
100
|
+
|
|
95
101
|
try:
|
|
96
|
-
response = await self.client.post(
|
|
97
|
-
|
|
98
|
-
json=mcp_request
|
|
99
|
-
)
|
|
100
|
-
|
|
102
|
+
response = await self.client.post(self.lambda_url, json=mcp_request)
|
|
103
|
+
|
|
101
104
|
logger.debug(f"Response status: {response.status_code}")
|
|
102
105
|
logger.debug(f"Response headers: {dict(response.headers)}")
|
|
103
|
-
|
|
106
|
+
|
|
104
107
|
if response.status_code >= 400:
|
|
105
108
|
error_text = response.text
|
|
106
|
-
logger.error(
|
|
109
|
+
logger.error(
|
|
110
|
+
f"Lambda function returned error: {response.status_code} {error_text}"
|
|
111
|
+
)
|
|
107
112
|
raise AgenticWerxAPIError(
|
|
108
113
|
f"Lambda request failed: {response.status_code}",
|
|
109
|
-
status_code=response.status_code
|
|
114
|
+
status_code=response.status_code,
|
|
110
115
|
)
|
|
111
|
-
|
|
116
|
+
|
|
112
117
|
try:
|
|
113
118
|
result = response.json()
|
|
114
|
-
logger.debug(
|
|
119
|
+
logger.debug(
|
|
120
|
+
f"Lambda response received for request {request_id}: {result}"
|
|
121
|
+
)
|
|
115
122
|
except Exception as json_error:
|
|
116
123
|
logger.error(f"Failed to parse JSON response: {json_error}")
|
|
117
124
|
logger.error(f"Raw response: {response.text}")
|
|
118
|
-
raise AgenticWerxAPIError(
|
|
119
|
-
|
|
125
|
+
raise AgenticWerxAPIError(
|
|
126
|
+
f"Invalid JSON response from Lambda: {json_error}"
|
|
127
|
+
) from json_error
|
|
128
|
+
|
|
120
129
|
if "error" in result:
|
|
121
130
|
error_msg = result["error"].get("message", "Unknown MCP error")
|
|
122
131
|
logger.error(f"MCP error from Lambda: {error_msg}")
|
|
123
132
|
raise AgenticWerxAPIError(f"MCP Error: {error_msg}")
|
|
124
|
-
|
|
133
|
+
|
|
125
134
|
return result.get("result")
|
|
126
|
-
|
|
127
|
-
except httpx.TimeoutException:
|
|
128
|
-
raise AgenticWerxAPIError("Request timeout")
|
|
129
|
-
except httpx.ConnectError:
|
|
130
|
-
raise AgenticWerxAPIError("Connection error")
|
|
135
|
+
|
|
136
|
+
except httpx.TimeoutException as e:
|
|
137
|
+
raise AgenticWerxAPIError("Request timeout") from e
|
|
138
|
+
except httpx.ConnectError as e:
|
|
139
|
+
raise AgenticWerxAPIError("Connection error") from e
|
|
131
140
|
except Exception as e:
|
|
132
141
|
logger.error(f"Error making MCP request: {e}")
|
|
133
|
-
raise AgenticWerxAPIError(f"Failed to make MCP request: {str(e)}")
|
|
134
|
-
|
|
142
|
+
raise AgenticWerxAPIError(f"Failed to make MCP request: {str(e)}") from e
|
|
143
|
+
|
|
135
144
|
async def test_connection(self) -> bool:
|
|
136
145
|
"""
|
|
137
146
|
Test connection to the Lambda MCP server.
|
|
138
|
-
|
|
147
|
+
|
|
139
148
|
Returns:
|
|
140
149
|
True if connection is successful, False otherwise
|
|
141
150
|
"""
|
|
@@ -147,11 +156,11 @@ class AgenticWerxAPI:
|
|
|
147
156
|
except Exception as e:
|
|
148
157
|
logger.error(f"Lambda connection test failed: {e}")
|
|
149
158
|
return False
|
|
150
|
-
|
|
151
|
-
async def list_tools(self) ->
|
|
159
|
+
|
|
160
|
+
async def list_tools(self) -> list[dict[str, Any]]:
|
|
152
161
|
"""
|
|
153
162
|
List available tools from the MCP server.
|
|
154
|
-
|
|
163
|
+
|
|
155
164
|
Returns:
|
|
156
165
|
List of available tools
|
|
157
166
|
"""
|
|
@@ -161,48 +170,49 @@ class AgenticWerxAPI:
|
|
|
161
170
|
except Exception as e:
|
|
162
171
|
logger.error(f"Failed to list tools: {e}")
|
|
163
172
|
return []
|
|
164
|
-
|
|
165
|
-
async def call_tool(
|
|
173
|
+
|
|
174
|
+
async def call_tool(
|
|
175
|
+
self, name: str, arguments: dict[str, Any] | None = None
|
|
176
|
+
) -> dict[str, Any]:
|
|
166
177
|
"""
|
|
167
178
|
Call a tool on the MCP server.
|
|
168
|
-
|
|
179
|
+
|
|
169
180
|
Args:
|
|
170
181
|
name: Tool name
|
|
171
182
|
arguments: Tool arguments
|
|
172
|
-
|
|
183
|
+
|
|
173
184
|
Returns:
|
|
174
185
|
Tool execution result
|
|
175
186
|
"""
|
|
176
187
|
try:
|
|
177
|
-
result = await self._make_mcp_request(
|
|
178
|
-
"name": name,
|
|
179
|
-
|
|
180
|
-
})
|
|
188
|
+
result = await self._make_mcp_request(
|
|
189
|
+
"tools/call", {"name": name, "arguments": arguments or {}}
|
|
190
|
+
)
|
|
181
191
|
return result
|
|
182
192
|
except Exception as e:
|
|
183
193
|
logger.error(f"Tool call failed for {name}: {e}")
|
|
184
194
|
raise
|
|
185
|
-
|
|
186
|
-
async def get_rules(self, package_id:
|
|
195
|
+
|
|
196
|
+
async def get_rules(self, package_id: str | None = None) -> dict[str, Any]:
|
|
187
197
|
"""
|
|
188
198
|
Get rules from the AgenticWerx MCP server.
|
|
189
|
-
|
|
199
|
+
|
|
190
200
|
Args:
|
|
191
201
|
package_id: Optional specific package ID to retrieve
|
|
192
|
-
|
|
202
|
+
|
|
193
203
|
Returns:
|
|
194
204
|
Rules data from the server
|
|
195
205
|
"""
|
|
196
206
|
logger.debug("Fetching rules via MCP tool call")
|
|
197
|
-
|
|
207
|
+
|
|
198
208
|
try:
|
|
199
209
|
# Use the get_rules tool via MCP
|
|
200
210
|
arguments = {}
|
|
201
211
|
if package_id:
|
|
202
212
|
arguments["packageId"] = package_id
|
|
203
|
-
|
|
213
|
+
|
|
204
214
|
result = await self.call_tool("get_rules", arguments)
|
|
205
|
-
|
|
215
|
+
|
|
206
216
|
# Extract the actual rules data from the MCP response
|
|
207
217
|
if "content" in result and result["content"]:
|
|
208
218
|
# The content should be a list with text content
|
|
@@ -215,11 +225,12 @@ class AgenticWerxAPI:
|
|
|
215
225
|
if text.strip().startswith(("{", "[")):
|
|
216
226
|
try:
|
|
217
227
|
import json
|
|
228
|
+
|
|
218
229
|
json_content = json.loads(text)
|
|
219
230
|
break
|
|
220
231
|
except json.JSONDecodeError:
|
|
221
232
|
continue
|
|
222
|
-
|
|
233
|
+
|
|
223
234
|
if json_content:
|
|
224
235
|
logger.info("Successfully retrieved rules via MCP")
|
|
225
236
|
return json_content
|
|
@@ -228,12 +239,76 @@ class AgenticWerxAPI:
|
|
|
228
239
|
logger.info("No JSON content found, returning raw content")
|
|
229
240
|
return {
|
|
230
241
|
"content": result["content"],
|
|
231
|
-
"message": "Rules retrieved but not in JSON format"
|
|
242
|
+
"message": "Rules retrieved but not in JSON format",
|
|
232
243
|
}
|
|
233
|
-
|
|
244
|
+
|
|
234
245
|
logger.warning("No rules content found in MCP response")
|
|
235
246
|
return {"rules": [], "message": "No rules found"}
|
|
236
|
-
|
|
247
|
+
|
|
237
248
|
except Exception as e:
|
|
238
249
|
logger.error(f"Error fetching rules via MCP: {e}")
|
|
239
|
-
raise AgenticWerxAPIError(f"Failed to fetch rules: {str(e)}")
|
|
250
|
+
raise AgenticWerxAPIError(f"Failed to fetch rules: {str(e)}") from e
|
|
251
|
+
|
|
252
|
+
async def analyze_code(
|
|
253
|
+
self,
|
|
254
|
+
code: str,
|
|
255
|
+
language: str | None = None,
|
|
256
|
+
package_ids: list[str] | None = None,
|
|
257
|
+
) -> dict[str, Any]:
|
|
258
|
+
"""
|
|
259
|
+
Analyze code using AgenticWerx rules.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
code: Code to analyze
|
|
263
|
+
language: Programming language (optional, will be auto-detected)
|
|
264
|
+
package_ids: List of package IDs to use for analysis (optional)
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
Analysis results from the server
|
|
268
|
+
"""
|
|
269
|
+
logger.debug("Analyzing code via MCP tool call")
|
|
270
|
+
|
|
271
|
+
try:
|
|
272
|
+
# Use the analyze_code tool via MCP
|
|
273
|
+
arguments = {"code": code}
|
|
274
|
+
if language:
|
|
275
|
+
arguments["language"] = language
|
|
276
|
+
if package_ids:
|
|
277
|
+
arguments["packageIds"] = package_ids
|
|
278
|
+
|
|
279
|
+
result = await self.call_tool("analyze_code", arguments)
|
|
280
|
+
|
|
281
|
+
# Extract the actual analysis data from the MCP response
|
|
282
|
+
if "content" in result and result["content"]:
|
|
283
|
+
# The content should be a list with text content
|
|
284
|
+
json_content = None
|
|
285
|
+
for content_item in result["content"]:
|
|
286
|
+
if content_item.get("type") == "text":
|
|
287
|
+
text = content_item["text"]
|
|
288
|
+
# Try to parse as JSON
|
|
289
|
+
if text.strip().startswith(("{", "[")):
|
|
290
|
+
try:
|
|
291
|
+
import json
|
|
292
|
+
|
|
293
|
+
json_content = json.loads(text)
|
|
294
|
+
break
|
|
295
|
+
except json.JSONDecodeError:
|
|
296
|
+
continue
|
|
297
|
+
|
|
298
|
+
if json_content:
|
|
299
|
+
logger.info("Successfully analyzed code via MCP")
|
|
300
|
+
return json_content
|
|
301
|
+
else:
|
|
302
|
+
# If no JSON found, return the raw content
|
|
303
|
+
logger.info("No JSON content found, returning raw content")
|
|
304
|
+
return {
|
|
305
|
+
"content": result["content"],
|
|
306
|
+
"message": "Analysis completed but not in JSON format",
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
logger.warning("No analysis content found in MCP response")
|
|
310
|
+
return {"issues": [], "message": "No analysis results found"}
|
|
311
|
+
|
|
312
|
+
except Exception as e:
|
|
313
|
+
logger.error(f"Error analyzing code via MCP: {e}")
|
|
314
|
+
raise AgenticWerxAPIError(f"Failed to analyze code: {str(e)}") from e
|