eulerian-marketing-platform 0.2.5__py3-none-any.whl → 0.2.6__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.
@@ -4,7 +4,7 @@ This package provides a Model Context Protocol (MCP) server that enables
4
4
  AI assistants to interact with Eulerian Marketing Platform APIs.
5
5
  """
6
6
 
7
- __version__ = "0.2.5"
7
+ __version__ = "0.2.6"
8
8
  __author__ = "Eulerian Technologies"
9
9
  __all__ = []
10
10
 
@@ -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, or None for notifications
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,18 @@ 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 (proper response for notifications)
103
+ # Handle HTTP 204 No Content (common for notifications)
105
104
  if response.status_code == 204:
106
- logger.info(" HTTP 204 No Content - notification acknowledged")
107
- # For notifications, this is expected and correct
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 {
107
+ "jsonrpc": "2.0",
108
+ "id": request_id,
109
+ "result": {}
110
+ }
118
111
 
119
112
  if response.status_code != 200:
120
113
  error_msg = f"HTTP {response.status_code}: {response.reason_phrase}"
121
114
  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
115
  return {
129
116
  "jsonrpc": "2.0",
130
117
  "id": request_id,
@@ -134,36 +121,77 @@ def forward_request(request_data: dict) -> dict:
134
121
  }
135
122
  }
136
123
 
137
- # Parse response (only for HTTP 200 responses)
124
+ # Parse response for HTTP 200
138
125
  try:
139
126
  response_data = response.json()
140
127
  logger.debug(f" Response: {json.dumps(response_data)[:200]}...")
141
128
 
142
- # For notifications, we should not send back any response, regardless of what the remote server returns
143
- if is_notification:
144
- logger.info(f" Notification received response from server: {json.dumps(response_data)}")
145
- logger.info(" Notification processed - no response sent")
146
- return None
129
+ # Add detailed logging for different MCP methods
130
+ if "result" in response_data:
131
+ logger.info(" Has 'result' field [OK]")
132
+
133
+ result = response_data.get("result", {})
134
+
135
+ if method == "initialize":
136
+ logger.info(f" Initialize result: protocolVersion={result.get('protocolVersion')}")
137
+ capabilities = result.get("capabilities", {})
138
+ logger.info(f" Server capabilities: {list(capabilities.keys())}")
139
+ server_info = result.get("serverInfo", {})
140
+ logger.info(f" Server info: name={server_info.get('name')}, version={server_info.get('version')}")
141
+
142
+ elif method == "tools/list":
143
+ tools = result.get("tools", [])
144
+ logger.info(f" Tools available: {len(tools)} tools")
145
+ for i, tool in enumerate(tools):
146
+ tool_name = tool.get("name", "unnamed")
147
+ tool_desc = tool.get("description", "no description")[:50]
148
+ logger.info(f" Tool {i+1}: {tool_name} - {tool_desc}")
149
+ # Log input schema info
150
+ input_schema = tool.get("inputSchema", {})
151
+ if "properties" in input_schema:
152
+ props = list(input_schema["properties"].keys())[:3]
153
+ logger.info(f" Input properties: {props}")
154
+
155
+ elif method == "resources/list":
156
+ resources = result.get("resources", [])
157
+ logger.info(f" Resources available: {len(resources)} resources")
158
+ for i, resource in enumerate(resources[:3]):
159
+ res_uri = resource.get("uri", "no uri")
160
+ res_name = resource.get("name", "unnamed")
161
+ logger.info(f" Resource {i+1}: {res_name} ({res_uri})")
162
+ if len(resources) > 3:
163
+ logger.info(f" ... and {len(resources) - 3} more resources")
164
+
165
+ elif method.startswith("tools/call"):
166
+ logger.info(f" Tool call result keys: {list(result.keys())}")
167
+ if "content" in result:
168
+ content = result["content"]
169
+ if isinstance(content, list) and len(content) > 0:
170
+ logger.info(f" Tool returned {len(content)} content items")
171
+ first_item = content[0]
172
+ if isinstance(first_item, dict):
173
+ logger.info(f" First content type: {first_item.get('type', 'unknown')}")
174
+ elif isinstance(content, str):
175
+ logger.info(f" Tool returned string content: {len(content)} chars")
176
+
177
+ else:
178
+ # Generic logging for unknown methods
179
+ logger.info(f" Method '{method}' result keys: {list(result.keys())}")
180
+
181
+ elif "error" in response_data:
182
+ error = response_data["error"]
183
+ error_code = error.get("code", "no code")
184
+ error_message = error.get("message", "no message")
185
+ logger.info(f" Has 'error' field: code={error_code}, message={error_message}")
147
186
 
148
187
  # Validate JSON-RPC response
149
188
  if "jsonrpc" not in response_data:
150
189
  logger.warning(" WARNING: Missing 'jsonrpc' field")
151
190
 
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
191
  return response_data
158
192
 
159
193
  except json.JSONDecodeError as e:
160
194
  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
195
  return {
168
196
  "jsonrpc": "2.0",
169
197
  "id": request_id,
@@ -175,12 +203,6 @@ def forward_request(request_data: dict) -> dict:
175
203
 
176
204
  except httpx.TimeoutException:
177
205
  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
206
  return {
185
207
  "jsonrpc": "2.0",
186
208
  "id": request_id,
@@ -192,12 +214,6 @@ def forward_request(request_data: dict) -> dict:
192
214
 
193
215
  except httpx.RequestError as e:
194
216
  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
217
  return {
202
218
  "jsonrpc": "2.0",
203
219
  "id": request_id,
@@ -211,12 +227,6 @@ def forward_request(request_data: dict) -> dict:
211
227
  logger.error(f"ERROR: Unexpected error - {str(e)}")
212
228
  import traceback
213
229
  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
230
  return {
221
231
  "jsonrpc": "2.0",
222
232
  "id": request_id,
@@ -267,15 +277,12 @@ def main() -> None:
267
277
  # Forward to remote server
268
278
  response_data = forward_request(request_data)
269
279
 
270
- # Only send response for non-notifications (when response_data is not None)
271
- if response_data is not None:
272
- response_json = json.dumps(response_data)
273
- print(response_json, flush=True)
274
- sys.stdout.flush()
275
- logger.info(" Response forwarded [OK]")
276
- else:
277
- logger.info(" Notification processed (no response sent)")
278
-
280
+ # Always send response back to Claude Desktop (MCP requires this)
281
+ response_json = json.dumps(response_data)
282
+ print(response_json, flush=True)
283
+ sys.stdout.flush()
284
+ logger.info(" Response forwarded [OK]")
285
+
279
286
  except json.JSONDecodeError as e:
280
287
  logger.error(f"ERROR: Invalid JSON in request - {e}")
281
288
  logger.error(f" Problematic line: {line[:200]}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: eulerian-marketing-platform
3
- Version: 0.2.5
3
+ Version: 0.2.6
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=Rchunx1TsnHjirVTZtfMlg6LHXlQupKPi-tYLVZTI00,428
2
+ eulerian_marketing_platform/server.py,sha256=GBuksSr38MWXKNR36mbHaMz8589eElGM6qGt2-zOJdw,13321
3
+ eulerian_marketing_platform-0.2.6.dist-info/LICENSE,sha256=eIqBqE_fRsqQJ8F-2v0e-8WzZqdshsCqnzmqLAWrNHU,1078
4
+ eulerian_marketing_platform-0.2.6.dist-info/METADATA,sha256=VR6nemyIP3yUTR_bbLV1V9K5e48M9WVO3zOssc1jIpM,12169
5
+ eulerian_marketing_platform-0.2.6.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
6
+ eulerian_marketing_platform-0.2.6.dist-info/entry_points.txt,sha256=rrPZptATSS9PUtH9gzCYq0WuP6eahkF-DkdUP1FaYfk,88
7
+ eulerian_marketing_platform-0.2.6.dist-info/top_level.txt,sha256=nidh3T6fw-mLjUqZwQ8AiMScS4usuH0WXW4ZgG4HYCo,28
8
+ eulerian_marketing_platform-0.2.6.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,,