eulerian-marketing-platform 0.2.5__py3-none-any.whl → 0.2.7__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.
- eulerian_marketing_platform/__init__.py +1 -1
- eulerian_marketing_platform/server.py +85 -61
- {eulerian_marketing_platform-0.2.5.dist-info → eulerian_marketing_platform-0.2.7.dist-info}/METADATA +1 -1
- eulerian_marketing_platform-0.2.7.dist-info/RECORD +8 -0
- eulerian_marketing_platform-0.2.5.dist-info/RECORD +0 -8
- {eulerian_marketing_platform-0.2.5.dist-info → eulerian_marketing_platform-0.2.7.dist-info}/LICENSE +0 -0
- {eulerian_marketing_platform-0.2.5.dist-info → eulerian_marketing_platform-0.2.7.dist-info}/WHEEL +0 -0
- {eulerian_marketing_platform-0.2.5.dist-info → eulerian_marketing_platform-0.2.7.dist-info}/entry_points.txt +0 -0
- {eulerian_marketing_platform-0.2.5.dist-info → eulerian_marketing_platform-0.2.7.dist-info}/top_level.txt +0 -0
|
@@ -74,13 +74,12 @@ def forward_request(request_data: dict) -> dict:
|
|
|
74
74
|
request_data: The JSON-RPC request to forward
|
|
75
75
|
|
|
76
76
|
Returns:
|
|
77
|
-
The JSON-RPC response from the remote server
|
|
77
|
+
The JSON-RPC response from the remote server
|
|
78
78
|
"""
|
|
79
79
|
timeout = float(os.environ.get("EMP_TIMEOUT", "300"))
|
|
80
80
|
|
|
81
81
|
request_id = request_data.get("id")
|
|
82
82
|
method = request_data.get("method")
|
|
83
|
-
is_notification = request_id is None
|
|
84
83
|
|
|
85
84
|
logger.info(f">>> REQUEST: {method} (id: {request_id})")
|
|
86
85
|
logger.debug(f" Full request: {json.dumps(request_data)[:200]}...")
|
|
@@ -101,30 +100,14 @@ def forward_request(request_data: dict) -> dict:
|
|
|
101
100
|
|
|
102
101
|
logger.info(f"<<< RESPONSE: HTTP {response.status_code}")
|
|
103
102
|
|
|
104
|
-
# Handle HTTP 204 No Content (
|
|
103
|
+
# Handle HTTP 204 No Content (common for notifications)
|
|
105
104
|
if response.status_code == 204:
|
|
106
|
-
logger.info(" HTTP 204 No Content -
|
|
107
|
-
|
|
108
|
-
if is_notification:
|
|
109
|
-
logger.info(" Notification processed - no response sent")
|
|
110
|
-
return None
|
|
111
|
-
else:
|
|
112
|
-
# For regular requests, 204 is unusual but we'll treat it as success with empty result
|
|
113
|
-
return {
|
|
114
|
-
"jsonrpc": "2.0",
|
|
115
|
-
"id": request_id,
|
|
116
|
-
"result": None
|
|
117
|
-
}
|
|
105
|
+
logger.info(" HTTP 204 No Content - creating empty success response")
|
|
106
|
+
return None
|
|
118
107
|
|
|
119
108
|
if response.status_code != 200:
|
|
120
109
|
error_msg = f"HTTP {response.status_code}: {response.reason_phrase}"
|
|
121
110
|
logger.error(f" Error: {response.text[:200]}")
|
|
122
|
-
|
|
123
|
-
# For notifications, don't return error responses
|
|
124
|
-
if is_notification:
|
|
125
|
-
logger.info(" Notification error - no response sent")
|
|
126
|
-
return None
|
|
127
|
-
|
|
128
111
|
return {
|
|
129
112
|
"jsonrpc": "2.0",
|
|
130
113
|
"id": request_id,
|
|
@@ -134,36 +117,77 @@ def forward_request(request_data: dict) -> dict:
|
|
|
134
117
|
}
|
|
135
118
|
}
|
|
136
119
|
|
|
137
|
-
# Parse response
|
|
120
|
+
# Parse response for HTTP 200
|
|
138
121
|
try:
|
|
139
122
|
response_data = response.json()
|
|
140
123
|
logger.debug(f" Response: {json.dumps(response_data)[:200]}...")
|
|
141
124
|
|
|
142
|
-
#
|
|
143
|
-
if
|
|
144
|
-
logger.info(
|
|
145
|
-
|
|
146
|
-
|
|
125
|
+
# Add detailed logging for different MCP methods
|
|
126
|
+
if "result" in response_data:
|
|
127
|
+
logger.info(" Has 'result' field [OK]")
|
|
128
|
+
|
|
129
|
+
result = response_data.get("result", {})
|
|
130
|
+
|
|
131
|
+
if method == "initialize":
|
|
132
|
+
logger.info(f" Initialize result: protocolVersion={result.get('protocolVersion')}")
|
|
133
|
+
capabilities = result.get("capabilities", {})
|
|
134
|
+
logger.info(f" Server capabilities: {list(capabilities.keys())}")
|
|
135
|
+
server_info = result.get("serverInfo", {})
|
|
136
|
+
logger.info(f" Server info: name={server_info.get('name')}, version={server_info.get('version')}")
|
|
137
|
+
|
|
138
|
+
elif method == "tools/list":
|
|
139
|
+
tools = result.get("tools", [])
|
|
140
|
+
logger.info(f" Tools available: {len(tools)} tools")
|
|
141
|
+
for i, tool in enumerate(tools):
|
|
142
|
+
tool_name = tool.get("name", "unnamed")
|
|
143
|
+
tool_desc = tool.get("description", "no description")[:50]
|
|
144
|
+
logger.info(f" Tool {i+1}: {tool_name} - {tool_desc}")
|
|
145
|
+
# Log input schema info
|
|
146
|
+
input_schema = tool.get("inputSchema", {})
|
|
147
|
+
if "properties" in input_schema:
|
|
148
|
+
props = list(input_schema["properties"].keys())[:3]
|
|
149
|
+
logger.info(f" Input properties: {props}")
|
|
150
|
+
|
|
151
|
+
elif method == "resources/list":
|
|
152
|
+
resources = result.get("resources", [])
|
|
153
|
+
logger.info(f" Resources available: {len(resources)} resources")
|
|
154
|
+
for i, resource in enumerate(resources[:3]):
|
|
155
|
+
res_uri = resource.get("uri", "no uri")
|
|
156
|
+
res_name = resource.get("name", "unnamed")
|
|
157
|
+
logger.info(f" Resource {i+1}: {res_name} ({res_uri})")
|
|
158
|
+
if len(resources) > 3:
|
|
159
|
+
logger.info(f" ... and {len(resources) - 3} more resources")
|
|
160
|
+
|
|
161
|
+
elif method.startswith("tools/call"):
|
|
162
|
+
logger.info(f" Tool call result keys: {list(result.keys())}")
|
|
163
|
+
if "content" in result:
|
|
164
|
+
content = result["content"]
|
|
165
|
+
if isinstance(content, list) and len(content) > 0:
|
|
166
|
+
logger.info(f" Tool returned {len(content)} content items")
|
|
167
|
+
first_item = content[0]
|
|
168
|
+
if isinstance(first_item, dict):
|
|
169
|
+
logger.info(f" First content type: {first_item.get('type', 'unknown')}")
|
|
170
|
+
elif isinstance(content, str):
|
|
171
|
+
logger.info(f" Tool returned string content: {len(content)} chars")
|
|
172
|
+
|
|
173
|
+
else:
|
|
174
|
+
# Generic logging for unknown methods
|
|
175
|
+
logger.info(f" Method '{method}' result keys: {list(result.keys())}")
|
|
176
|
+
|
|
177
|
+
elif "error" in response_data:
|
|
178
|
+
error = response_data["error"]
|
|
179
|
+
error_code = error.get("code", "no code")
|
|
180
|
+
error_message = error.get("message", "no message")
|
|
181
|
+
logger.info(f" Has 'error' field: code={error_code}, message={error_message}")
|
|
147
182
|
|
|
148
183
|
# Validate JSON-RPC response
|
|
149
184
|
if "jsonrpc" not in response_data:
|
|
150
185
|
logger.warning(" WARNING: Missing 'jsonrpc' field")
|
|
151
186
|
|
|
152
|
-
if "result" in response_data:
|
|
153
|
-
logger.info(" Has 'result' field [OK]")
|
|
154
|
-
elif "error" in response_data:
|
|
155
|
-
logger.info(f" Has 'error' field: {response_data['error']}")
|
|
156
|
-
|
|
157
187
|
return response_data
|
|
158
188
|
|
|
159
189
|
except json.JSONDecodeError as e:
|
|
160
190
|
logger.error(f" ERROR: Invalid JSON - {e}")
|
|
161
|
-
|
|
162
|
-
# For notifications, don't return error responses
|
|
163
|
-
if is_notification:
|
|
164
|
-
logger.info(" Notification JSON error - no response sent")
|
|
165
|
-
return None
|
|
166
|
-
|
|
167
191
|
return {
|
|
168
192
|
"jsonrpc": "2.0",
|
|
169
193
|
"id": request_id,
|
|
@@ -175,12 +199,6 @@ def forward_request(request_data: dict) -> dict:
|
|
|
175
199
|
|
|
176
200
|
except httpx.TimeoutException:
|
|
177
201
|
logger.error("ERROR: Request timeout")
|
|
178
|
-
|
|
179
|
-
# For notifications, don't return error responses
|
|
180
|
-
if is_notification:
|
|
181
|
-
logger.info(" Notification timeout - no response sent")
|
|
182
|
-
return None
|
|
183
|
-
|
|
184
202
|
return {
|
|
185
203
|
"jsonrpc": "2.0",
|
|
186
204
|
"id": request_id,
|
|
@@ -192,12 +210,6 @@ def forward_request(request_data: dict) -> dict:
|
|
|
192
210
|
|
|
193
211
|
except httpx.RequestError as e:
|
|
194
212
|
logger.error(f"ERROR: Request failed - {str(e)}")
|
|
195
|
-
|
|
196
|
-
# For notifications, don't return error responses
|
|
197
|
-
if is_notification:
|
|
198
|
-
logger.info(" Notification request error - no response sent")
|
|
199
|
-
return None
|
|
200
|
-
|
|
201
213
|
return {
|
|
202
214
|
"jsonrpc": "2.0",
|
|
203
215
|
"id": request_id,
|
|
@@ -211,12 +223,6 @@ def forward_request(request_data: dict) -> dict:
|
|
|
211
223
|
logger.error(f"ERROR: Unexpected error - {str(e)}")
|
|
212
224
|
import traceback
|
|
213
225
|
logger.error(f"Traceback: {traceback.format_exc()}")
|
|
214
|
-
|
|
215
|
-
# For notifications, don't return error responses
|
|
216
|
-
if is_notification:
|
|
217
|
-
logger.info(" Notification unexpected error - no response sent")
|
|
218
|
-
return None
|
|
219
|
-
|
|
220
226
|
return {
|
|
221
227
|
"jsonrpc": "2.0",
|
|
222
228
|
"id": request_id,
|
|
@@ -267,21 +273,32 @@ def main() -> None:
|
|
|
267
273
|
# Forward to remote server
|
|
268
274
|
response_data = forward_request(request_data)
|
|
269
275
|
|
|
270
|
-
# Only send response
|
|
276
|
+
# Only send response if response_data is not None (handles HTTP 204 notifications)
|
|
271
277
|
if response_data is not None:
|
|
272
278
|
response_json = json.dumps(response_data)
|
|
273
279
|
print(response_json, flush=True)
|
|
274
280
|
sys.stdout.flush()
|
|
275
281
|
logger.info(" Response forwarded [OK]")
|
|
276
282
|
else:
|
|
277
|
-
logger.info("
|
|
278
|
-
|
|
283
|
+
logger.info(" No response sent (notification or HTTP 204)")
|
|
284
|
+
|
|
279
285
|
except json.JSONDecodeError as e:
|
|
280
286
|
logger.error(f"ERROR: Invalid JSON in request - {e}")
|
|
281
287
|
logger.error(f" Problematic line: {line[:200]}")
|
|
288
|
+
|
|
289
|
+
# Try to extract request_id from partial JSON, fallback to None
|
|
290
|
+
request_id = None
|
|
291
|
+
try:
|
|
292
|
+
# Attempt to get ID from partial JSON
|
|
293
|
+
if '"id"' in line:
|
|
294
|
+
partial = json.loads(line.split('"method"')[0] + '"}')
|
|
295
|
+
request_id = partial.get("id")
|
|
296
|
+
except:
|
|
297
|
+
pass
|
|
298
|
+
|
|
282
299
|
error_response = {
|
|
283
300
|
"jsonrpc": "2.0",
|
|
284
|
-
"id":
|
|
301
|
+
"id": request_id,
|
|
285
302
|
"error": {
|
|
286
303
|
"code": -32700,
|
|
287
304
|
"message": f"Parse error: {str(e)}"
|
|
@@ -294,9 +311,16 @@ def main() -> None:
|
|
|
294
311
|
logger.error(f"ERROR: Unexpected error processing request - {str(e)}")
|
|
295
312
|
import traceback
|
|
296
313
|
logger.error(f"Traceback: {traceback.format_exc()}")
|
|
314
|
+
|
|
315
|
+
# Try to get request_id if request_data was parsed successfully
|
|
316
|
+
try:
|
|
317
|
+
request_id = request_data.get("id") if 'request_data' in locals() else None
|
|
318
|
+
except:
|
|
319
|
+
request_id = None
|
|
320
|
+
|
|
297
321
|
error_response = {
|
|
298
322
|
"jsonrpc": "2.0",
|
|
299
|
-
"id":
|
|
323
|
+
"id": request_id,
|
|
300
324
|
"error": {
|
|
301
325
|
"code": -32000,
|
|
302
326
|
"message": f"Error: {str(e)}"
|
{eulerian_marketing_platform-0.2.5.dist-info → eulerian_marketing_platform-0.2.7.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: eulerian-marketing-platform
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: MCP server for Eulerian Marketing Platform - enables AI assistants to interact with Eulerian's marketing analytics and campaign management APIs
|
|
5
5
|
Author-email: Eulerian Technologies <mathieu@eulerian.com>
|
|
6
6
|
License: MIT
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
eulerian_marketing_platform/__init__.py,sha256=HCpqdNcYBPdxvYqqIALnHKB4fKV0LQu9EqB2i9VYGW4,428
|
|
2
|
+
eulerian_marketing_platform/server.py,sha256=p32McUsqrDcCrczMVurCQzfnZGrYVaz3yUsmxOTbeCM,14235
|
|
3
|
+
eulerian_marketing_platform-0.2.7.dist-info/LICENSE,sha256=eIqBqE_fRsqQJ8F-2v0e-8WzZqdshsCqnzmqLAWrNHU,1078
|
|
4
|
+
eulerian_marketing_platform-0.2.7.dist-info/METADATA,sha256=r2vU63lIHznbqlcHe0NIDtg4Aif4iuH7k5QsbEWcGtM,12169
|
|
5
|
+
eulerian_marketing_platform-0.2.7.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
|
|
6
|
+
eulerian_marketing_platform-0.2.7.dist-info/entry_points.txt,sha256=rrPZptATSS9PUtH9gzCYq0WuP6eahkF-DkdUP1FaYfk,88
|
|
7
|
+
eulerian_marketing_platform-0.2.7.dist-info/top_level.txt,sha256=nidh3T6fw-mLjUqZwQ8AiMScS4usuH0WXW4ZgG4HYCo,28
|
|
8
|
+
eulerian_marketing_platform-0.2.7.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
eulerian_marketing_platform/__init__.py,sha256=Vy3v2bzqOBm-2QuGgb87QkLjojhqRDvzRTeNPqVMaOE,428
|
|
2
|
-
eulerian_marketing_platform/server.py,sha256=3Iz8fZCug6DnvI1pd3o5Ilbvcu61Zb9KpnLXYjZs0Yk,12119
|
|
3
|
-
eulerian_marketing_platform-0.2.5.dist-info/LICENSE,sha256=eIqBqE_fRsqQJ8F-2v0e-8WzZqdshsCqnzmqLAWrNHU,1078
|
|
4
|
-
eulerian_marketing_platform-0.2.5.dist-info/METADATA,sha256=lmDkaF4qVTcsHgM3Yw20d7xCjPRw8M-eVlD1eerYkTU,12169
|
|
5
|
-
eulerian_marketing_platform-0.2.5.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
|
|
6
|
-
eulerian_marketing_platform-0.2.5.dist-info/entry_points.txt,sha256=rrPZptATSS9PUtH9gzCYq0WuP6eahkF-DkdUP1FaYfk,88
|
|
7
|
-
eulerian_marketing_platform-0.2.5.dist-info/top_level.txt,sha256=nidh3T6fw-mLjUqZwQ8AiMScS4usuH0WXW4ZgG4HYCo,28
|
|
8
|
-
eulerian_marketing_platform-0.2.5.dist-info/RECORD,,
|
{eulerian_marketing_platform-0.2.5.dist-info → eulerian_marketing_platform-0.2.7.dist-info}/LICENSE
RENAMED
|
File without changes
|
{eulerian_marketing_platform-0.2.5.dist-info → eulerian_marketing_platform-0.2.7.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|