eulerian-marketing-platform 0.1.1__py3-none-any.whl → 0.2.0__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 +8 -4
- eulerian_marketing_platform/server.py +163 -226
- {eulerian_marketing_platform-0.1.1.dist-info → eulerian_marketing_platform-0.2.0.dist-info}/METADATA +1 -2
- eulerian_marketing_platform-0.2.0.dist-info/RECORD +8 -0
- eulerian_marketing_platform-0.1.1.dist-info/RECORD +0 -8
- {eulerian_marketing_platform-0.1.1.dist-info → eulerian_marketing_platform-0.2.0.dist-info}/LICENSE +0 -0
- {eulerian_marketing_platform-0.1.1.dist-info → eulerian_marketing_platform-0.2.0.dist-info}/WHEEL +0 -0
- {eulerian_marketing_platform-0.1.1.dist-info → eulerian_marketing_platform-0.2.0.dist-info}/entry_points.txt +0 -0
- {eulerian_marketing_platform-0.1.1.dist-info → eulerian_marketing_platform-0.2.0.dist-info}/top_level.txt +0 -0
|
@@ -4,8 +4,12 @@ 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
|
-
|
|
8
|
-
|
|
9
|
-
__version__ = "0.1.1"
|
|
7
|
+
__version__ = "0.2.0"
|
|
10
8
|
__author__ = "Eulerian Technologies"
|
|
11
|
-
__all__ = [
|
|
9
|
+
__all__ = []
|
|
10
|
+
|
|
11
|
+
# Import main only when explicitly requested
|
|
12
|
+
def get_main():
|
|
13
|
+
"""Lazy import of main function to avoid side effects."""
|
|
14
|
+
from .server import main
|
|
15
|
+
return main
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""Eulerian Marketing Platform MCP Proxy Server.
|
|
3
3
|
|
|
4
|
-
This server acts as a proxy
|
|
4
|
+
This server acts as a transparent proxy between local MCP clients (Claude Desktop, etc.)
|
|
5
5
|
and a remote Eulerian Marketing Platform MCP server via HTTP.
|
|
6
6
|
|
|
7
|
-
It
|
|
8
|
-
and
|
|
7
|
+
It forwards ALL MCP requests directly to the remote server, making all remote tools
|
|
8
|
+
and resources available to the client without any intermediary logic.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
import os
|
|
@@ -14,38 +14,35 @@ import json
|
|
|
14
14
|
import logging
|
|
15
15
|
import tempfile
|
|
16
16
|
from datetime import datetime
|
|
17
|
-
from typing import Any
|
|
18
|
-
from pathlib import Path
|
|
19
17
|
import httpx
|
|
20
18
|
|
|
21
|
-
from mcp.server.fastmcp import FastMCP
|
|
22
|
-
from mcp.server import Server
|
|
23
|
-
from mcp.server.stdio import stdio_server
|
|
24
|
-
from mcp import types
|
|
25
|
-
|
|
26
19
|
# Configuration
|
|
27
20
|
EMP_API_ENDPOINT = os.environ.get("EMP_API_ENDPOINT")
|
|
28
21
|
EMP_API_TOKEN = os.environ.get("EMP_API_TOKEN")
|
|
29
22
|
|
|
30
23
|
# Logging setup - Use cross-platform temp directory
|
|
31
|
-
# Default to system temp directory if EMP_LOG_FILE not set
|
|
32
24
|
DEFAULT_LOG_FILE = os.path.join(tempfile.gettempdir(), "eulerian-mcp-proxy.log")
|
|
33
25
|
LOG_FILE = os.environ.get("EMP_LOG_FILE", DEFAULT_LOG_FILE)
|
|
34
26
|
|
|
35
27
|
# Ensure log directory exists
|
|
36
28
|
log_dir = os.path.dirname(LOG_FILE)
|
|
37
|
-
if log_dir:
|
|
29
|
+
if log_dir:
|
|
38
30
|
os.makedirs(log_dir, exist_ok=True)
|
|
39
31
|
|
|
40
|
-
# Configure logging to file and stderr
|
|
32
|
+
# Configure logging to file and stderr with UTF-8 encoding for cross-platform compatibility
|
|
33
|
+
file_handler = logging.FileHandler(LOG_FILE, encoding='utf-8')
|
|
34
|
+
file_handler.setLevel(logging.INFO)
|
|
35
|
+
file_handler.setFormatter(logging.Formatter("[%(asctime)s] %(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S"))
|
|
36
|
+
|
|
37
|
+
# For stderr, use system encoding with error handling
|
|
38
|
+
stream_handler = logging.StreamHandler(sys.stderr)
|
|
39
|
+
stream_handler.setLevel(logging.INFO)
|
|
40
|
+
stream_handler.setFormatter(logging.Formatter("[%(asctime)s] %(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S"))
|
|
41
|
+
|
|
42
|
+
# Configure root logger
|
|
41
43
|
logging.basicConfig(
|
|
42
44
|
level=logging.INFO,
|
|
43
|
-
|
|
44
|
-
datefmt="%Y-%m-%d %H:%M:%S",
|
|
45
|
-
handlers=[
|
|
46
|
-
logging.FileHandler(LOG_FILE),
|
|
47
|
-
logging.StreamHandler(sys.stderr)
|
|
48
|
-
]
|
|
45
|
+
handlers=[file_handler, stream_handler]
|
|
49
46
|
)
|
|
50
47
|
logger = logging.getLogger(__name__)
|
|
51
48
|
|
|
@@ -70,235 +67,175 @@ def validate_config() -> None:
|
|
|
70
67
|
sys.exit(1)
|
|
71
68
|
|
|
72
69
|
|
|
73
|
-
|
|
74
|
-
"""
|
|
75
|
-
|
|
76
|
-
def __init__(self):
|
|
77
|
-
"""Initialize the proxy with configuration."""
|
|
78
|
-
self.endpoint = EMP_API_ENDPOINT
|
|
79
|
-
self.token = EMP_API_TOKEN
|
|
80
|
-
self.timeout = float(os.environ.get("EMP_TIMEOUT", "300"))
|
|
81
|
-
|
|
82
|
-
logger.info("=== EULERIAN MCP PROXY START ===")
|
|
83
|
-
logger.info(f"Endpoint: {self.endpoint}")
|
|
84
|
-
logger.info(f"Token: {self.token[:10] if self.token else 'None'}...")
|
|
85
|
-
logger.info(f"Timeout: {self.timeout}s")
|
|
86
|
-
|
|
87
|
-
async def forward_request(self, method: str, params: dict = None) -> dict[str, Any]:
|
|
88
|
-
"""Forward a JSON-RPC request to the remote MCP server.
|
|
89
|
-
|
|
90
|
-
Args:
|
|
91
|
-
method: The JSON-RPC method name
|
|
92
|
-
params: The parameters for the method
|
|
93
|
-
|
|
94
|
-
Returns:
|
|
95
|
-
The response from the remote server
|
|
96
|
-
|
|
97
|
-
Raises:
|
|
98
|
-
Exception: If the request fails
|
|
99
|
-
"""
|
|
100
|
-
request_data = {
|
|
101
|
-
"jsonrpc": "2.0",
|
|
102
|
-
"id": 1,
|
|
103
|
-
"method": method,
|
|
104
|
-
}
|
|
105
|
-
if params:
|
|
106
|
-
request_data["params"] = params
|
|
107
|
-
|
|
108
|
-
logger.info(f">>> REQUEST: {method}")
|
|
109
|
-
logger.debug(f" Full request: {json.dumps(request_data)[:200]}...")
|
|
110
|
-
|
|
111
|
-
try:
|
|
112
|
-
async with httpx.AsyncClient() as client:
|
|
113
|
-
logger.info(f"Forwarding to {self.endpoint}")
|
|
114
|
-
response = await client.post(
|
|
115
|
-
self.endpoint,
|
|
116
|
-
headers={
|
|
117
|
-
"Content-Type": "application/json",
|
|
118
|
-
"Authorization": f"Bearer {self.token}"
|
|
119
|
-
},
|
|
120
|
-
json=request_data,
|
|
121
|
-
timeout=self.timeout
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
logger.info(f"<<< RESPONSE: HTTP {response.status_code}")
|
|
125
|
-
|
|
126
|
-
if response.status_code != 200:
|
|
127
|
-
error_msg = f"HTTP {response.status_code}: {response.reason_phrase}"
|
|
128
|
-
logger.error(f" Error: {response.text[:200]}")
|
|
129
|
-
raise Exception(error_msg)
|
|
130
|
-
|
|
131
|
-
response_data = response.json()
|
|
132
|
-
logger.debug(f" Response: {json.dumps(response_data)[:200]}...")
|
|
133
|
-
|
|
134
|
-
# Validate JSON-RPC response
|
|
135
|
-
if "jsonrpc" not in response_data:
|
|
136
|
-
logger.warning(" WARNING: Missing 'jsonrpc' field")
|
|
137
|
-
|
|
138
|
-
if "result" in response_data:
|
|
139
|
-
logger.info(" Has 'result' field [OK]")
|
|
140
|
-
return response_data["result"]
|
|
141
|
-
elif "error" in response_data:
|
|
142
|
-
logger.error(f" Has 'error' field: {response_data['error']}")
|
|
143
|
-
raise Exception(f"Remote error: {response_data['error']}")
|
|
144
|
-
else:
|
|
145
|
-
logger.warning(" No 'result' or 'error' field")
|
|
146
|
-
return response_data
|
|
147
|
-
|
|
148
|
-
except httpx.TimeoutException:
|
|
149
|
-
logger.error("ERROR: Request timeout")
|
|
150
|
-
raise Exception("Request timeout")
|
|
151
|
-
except httpx.RequestError as e:
|
|
152
|
-
logger.error(f"ERROR: Request failed - {str(e)}")
|
|
153
|
-
raise Exception(f"Request failed: {str(e)}")
|
|
154
|
-
except json.JSONDecodeError as e:
|
|
155
|
-
logger.error(f"ERROR: Invalid JSON response - {str(e)}")
|
|
156
|
-
raise Exception(f"Invalid JSON response: {str(e)}")
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
# Create global proxy instance
|
|
160
|
-
proxy = EulerianMCPProxy()
|
|
161
|
-
|
|
162
|
-
# Create FastMCP server
|
|
163
|
-
mcp = FastMCP("eulerian-marketing-platform")
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
# Dynamically fetch and register tools from remote server
|
|
167
|
-
@mcp.tool()
|
|
168
|
-
async def list_remote_tools() -> dict[str, Any]:
|
|
169
|
-
"""List all available tools from the remote Eulerian MCP server.
|
|
170
|
-
|
|
171
|
-
This tool queries the remote MCP server to discover what tools are available.
|
|
172
|
-
Use this to see what operations you can perform on the Eulerian platform.
|
|
173
|
-
|
|
174
|
-
Returns:
|
|
175
|
-
Dictionary containing the list of available tools with their descriptions
|
|
176
|
-
"""
|
|
177
|
-
try:
|
|
178
|
-
result = await proxy.forward_request("tools/list")
|
|
179
|
-
return result
|
|
180
|
-
except Exception as e:
|
|
181
|
-
logger.error(f"Failed to list tools: {str(e)}")
|
|
182
|
-
return {"error": str(e), "tools": []}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
@mcp.tool()
|
|
186
|
-
async def call_eulerian_tool(tool_name: str, arguments: dict[str, Any] = None) -> dict[str, Any]:
|
|
187
|
-
"""Call a tool on the remote Eulerian MCP server.
|
|
188
|
-
|
|
189
|
-
This is a generic tool that forwards requests to the remote Eulerian platform.
|
|
190
|
-
First use list_remote_tools() to see what tools are available, then call them
|
|
191
|
-
using this function.
|
|
70
|
+
def forward_request(request_data: dict) -> dict:
|
|
71
|
+
"""Forward a JSON-RPC request to the remote MCP server.
|
|
192
72
|
|
|
193
73
|
Args:
|
|
194
|
-
|
|
195
|
-
arguments: Dictionary of arguments to pass to the tool (optional)
|
|
74
|
+
request_data: The JSON-RPC request to forward
|
|
196
75
|
|
|
197
76
|
Returns:
|
|
198
|
-
The
|
|
199
|
-
|
|
200
|
-
Example:
|
|
201
|
-
To call a tool named "search_goal" with no arguments:
|
|
202
|
-
>>> call_eulerian_tool("search_goal")
|
|
203
|
-
|
|
204
|
-
To call a tool with arguments:
|
|
205
|
-
>>> call_eulerian_tool("update_goal", {"action-id": "12345","action-name":"test-mcp"})
|
|
77
|
+
The JSON-RPC response from the remote server
|
|
206
78
|
"""
|
|
207
|
-
|
|
208
|
-
arguments = {}
|
|
79
|
+
timeout = float(os.environ.get("EMP_TIMEOUT", "300"))
|
|
209
80
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
"name": tool_name,
|
|
213
|
-
"arguments": arguments
|
|
214
|
-
}
|
|
215
|
-
result = await proxy.forward_request("tools/call", params)
|
|
216
|
-
return result
|
|
217
|
-
except Exception as e:
|
|
218
|
-
logger.error(f"Failed to call tool '{tool_name}': {str(e)}")
|
|
219
|
-
return {"error": str(e), "tool": tool_name}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
@mcp.tool()
|
|
223
|
-
async def get_eulerian_resources() -> dict[str, Any]:
|
|
224
|
-
"""List all available resources from the remote Eulerian MCP server.
|
|
81
|
+
request_id = request_data.get("id")
|
|
82
|
+
method = request_data.get("method")
|
|
225
83
|
|
|
226
|
-
|
|
227
|
-
|
|
84
|
+
logger.info(f">>> REQUEST: {method} (id: {request_id})")
|
|
85
|
+
logger.debug(f" Full request: {json.dumps(request_data)[:200]}...")
|
|
228
86
|
|
|
229
|
-
Returns:
|
|
230
|
-
Dictionary containing the list of available resources
|
|
231
|
-
"""
|
|
232
87
|
try:
|
|
233
|
-
|
|
234
|
-
return result
|
|
235
|
-
except Exception as e:
|
|
236
|
-
logger.error(f"Failed to list resources: {str(e)}")
|
|
237
|
-
return {"error": str(e), "resources": []}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
@mcp.tool()
|
|
241
|
-
async def read_eulerian_resource(uri: str) -> dict[str, Any]:
|
|
242
|
-
"""Read a specific resource from the remote Eulerian MCP server.
|
|
243
|
-
|
|
244
|
-
Args:
|
|
245
|
-
uri: The URI of the resource to read (get from get_eulerian_resources())
|
|
88
|
+
logger.info(f"Forwarding to {EMP_API_ENDPOINT}")
|
|
246
89
|
|
|
247
|
-
|
|
248
|
-
|
|
90
|
+
# Use httpx for sync HTTP request
|
|
91
|
+
with httpx.Client(timeout=timeout) as client:
|
|
92
|
+
response = client.post(
|
|
93
|
+
EMP_API_ENDPOINT,
|
|
94
|
+
headers={
|
|
95
|
+
"Content-Type": "application/json",
|
|
96
|
+
"Authorization": f"Bearer {EMP_API_TOKEN}"
|
|
97
|
+
},
|
|
98
|
+
json=request_data
|
|
99
|
+
)
|
|
249
100
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
101
|
+
logger.info(f"<<< RESPONSE: HTTP {response.status_code}")
|
|
102
|
+
|
|
103
|
+
if response.status_code != 200:
|
|
104
|
+
error_msg = f"HTTP {response.status_code}: {response.reason_phrase}"
|
|
105
|
+
logger.error(f" Error: {response.text[:200]}")
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
"jsonrpc": "2.0",
|
|
109
|
+
"id": request_id,
|
|
110
|
+
"error": {
|
|
111
|
+
"code": -32000,
|
|
112
|
+
"message": error_msg
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# Parse response
|
|
117
|
+
try:
|
|
118
|
+
response_data = response.json()
|
|
119
|
+
logger.debug(f" Response: {json.dumps(response_data)[:200]}...")
|
|
120
|
+
|
|
121
|
+
# Validate JSON-RPC response
|
|
122
|
+
if "jsonrpc" not in response_data:
|
|
123
|
+
logger.warning(" WARNING: Missing 'jsonrpc' field")
|
|
124
|
+
|
|
125
|
+
if "result" in response_data:
|
|
126
|
+
logger.info(" Has 'result' field [OK]")
|
|
127
|
+
elif "error" in response_data:
|
|
128
|
+
logger.info(f" Has 'error' field: {response_data['error']}")
|
|
129
|
+
|
|
130
|
+
return response_data
|
|
131
|
+
|
|
132
|
+
except json.JSONDecodeError as e:
|
|
133
|
+
logger.error(f" ERROR: Invalid JSON - {e}")
|
|
134
|
+
return {
|
|
135
|
+
"jsonrpc": "2.0",
|
|
136
|
+
"id": request_id,
|
|
137
|
+
"error": {
|
|
138
|
+
"code": -32700,
|
|
139
|
+
"message": f"Invalid JSON: {str(e)}"
|
|
140
|
+
}
|
|
141
|
+
}
|
|
265
142
|
|
|
266
|
-
|
|
143
|
+
except httpx.TimeoutException:
|
|
144
|
+
logger.error("ERROR: Request timeout")
|
|
145
|
+
return {
|
|
146
|
+
"jsonrpc": "2.0",
|
|
147
|
+
"id": request_id,
|
|
148
|
+
"error": {
|
|
149
|
+
"code": -32000,
|
|
150
|
+
"message": "Request timeout"
|
|
151
|
+
}
|
|
152
|
+
}
|
|
267
153
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
"
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
"name": "eulerian-mcp-proxy",
|
|
277
|
-
"version": "0.1.0"
|
|
154
|
+
except httpx.RequestError as e:
|
|
155
|
+
logger.error(f"ERROR: Request failed - {str(e)}")
|
|
156
|
+
return {
|
|
157
|
+
"jsonrpc": "2.0",
|
|
158
|
+
"id": request_id,
|
|
159
|
+
"error": {
|
|
160
|
+
"code": -32000,
|
|
161
|
+
"message": f"Request failed: {str(e)}"
|
|
278
162
|
}
|
|
279
|
-
}
|
|
280
|
-
|
|
163
|
+
}
|
|
164
|
+
|
|
281
165
|
except Exception as e:
|
|
282
|
-
logger.error(f"
|
|
283
|
-
|
|
166
|
+
logger.error(f"ERROR: Unexpected error - {str(e)}")
|
|
167
|
+
import traceback
|
|
168
|
+
logger.error(f"Traceback: {traceback.format_exc()}")
|
|
169
|
+
return {
|
|
170
|
+
"jsonrpc": "2.0",
|
|
171
|
+
"id": request_id,
|
|
172
|
+
"error": {
|
|
173
|
+
"code": -32000,
|
|
174
|
+
"message": f"Error: {str(e)}"
|
|
175
|
+
}
|
|
176
|
+
}
|
|
284
177
|
|
|
285
178
|
|
|
286
179
|
def main() -> None:
|
|
287
|
-
"""Entry point for the MCP proxy server.
|
|
288
|
-
|
|
180
|
+
"""Entry point for the MCP proxy server.
|
|
181
|
+
|
|
182
|
+
Runs a transparent stdio proxy that forwards all JSON-RPC requests
|
|
183
|
+
to the remote Eulerian MCP server.
|
|
184
|
+
"""
|
|
185
|
+
# Validate configuration
|
|
289
186
|
validate_config()
|
|
290
187
|
|
|
291
|
-
logger.info("
|
|
292
|
-
logger.info("
|
|
293
|
-
logger.info("
|
|
294
|
-
logger.info("
|
|
295
|
-
logger.info("
|
|
296
|
-
logger.info(" - read_eulerian_resource: Read a specific resource")
|
|
297
|
-
logger.info(" - get_server_info: Get remote server information")
|
|
188
|
+
logger.info("=== EULERIAN MCP PROXY START ===")
|
|
189
|
+
logger.info(f"Endpoint: {EMP_API_ENDPOINT}")
|
|
190
|
+
logger.info(f"Token: {EMP_API_TOKEN[:10] if EMP_API_TOKEN else 'None'}...")
|
|
191
|
+
logger.info(f"Timeout: {float(os.environ.get('EMP_TIMEOUT', '300'))}s")
|
|
192
|
+
logger.info("Starting stdio proxy - all remote tools will be available")
|
|
298
193
|
|
|
299
|
-
# Run the server in stdio mode (default for MCP)
|
|
300
194
|
try:
|
|
301
|
-
|
|
195
|
+
# Read from stdin line by line
|
|
196
|
+
for line in sys.stdin:
|
|
197
|
+
line = line.strip()
|
|
198
|
+
if not line:
|
|
199
|
+
continue
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
# Parse request
|
|
203
|
+
request_data = json.loads(line)
|
|
204
|
+
|
|
205
|
+
# Forward to remote server
|
|
206
|
+
response_data = forward_request(request_data)
|
|
207
|
+
|
|
208
|
+
# Send response to stdout
|
|
209
|
+
response_json = json.dumps(response_data)
|
|
210
|
+
print(response_json, flush=True)
|
|
211
|
+
logger.info(" Response forwarded [OK]")
|
|
212
|
+
|
|
213
|
+
except json.JSONDecodeError as e:
|
|
214
|
+
logger.error(f"ERROR: Invalid JSON in request - {e}")
|
|
215
|
+
error_response = {
|
|
216
|
+
"jsonrpc": "2.0",
|
|
217
|
+
"id": None,
|
|
218
|
+
"error": {
|
|
219
|
+
"code": -32700,
|
|
220
|
+
"message": f"Parse error: {str(e)}"
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
print(json.dumps(error_response), flush=True)
|
|
224
|
+
|
|
225
|
+
except Exception as e:
|
|
226
|
+
logger.error(f"ERROR: Unexpected error processing request - {str(e)}")
|
|
227
|
+
import traceback
|
|
228
|
+
logger.error(f"Traceback: {traceback.format_exc()}")
|
|
229
|
+
error_response = {
|
|
230
|
+
"jsonrpc": "2.0",
|
|
231
|
+
"id": None,
|
|
232
|
+
"error": {
|
|
233
|
+
"code": -32000,
|
|
234
|
+
"message": f"Error: {str(e)}"
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
print(json.dumps(error_response), flush=True)
|
|
238
|
+
|
|
302
239
|
except KeyboardInterrupt:
|
|
303
240
|
logger.info("Server stopped by user")
|
|
304
241
|
except Exception as e:
|
{eulerian_marketing_platform-0.1.1.dist-info → eulerian_marketing_platform-0.2.0.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: eulerian-marketing-platform
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
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
|
|
@@ -24,7 +24,6 @@ Requires-Python: >=3.10
|
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
26
|
Requires-Dist: httpx (>=0.25.0)
|
|
27
|
-
Requires-Dist: mcp (>=1.2.0)
|
|
28
27
|
Provides-Extra: deployment
|
|
29
28
|
Requires-Dist: uv (>=0.5.0) ; extra == 'deployment'
|
|
30
29
|
Provides-Extra: dev
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
eulerian_marketing_platform/__init__.py,sha256=8Od_2veelx75AfbIEFrCS1UF6AOsKaX6CF4zR_z8yYU,428
|
|
2
|
+
eulerian_marketing_platform/server.py,sha256=y1vy-LvSjG18dvDv65hO875s_7vogbb5bdNwTetKKcU,8335
|
|
3
|
+
eulerian_marketing_platform-0.2.0.dist-info/LICENSE,sha256=eIqBqE_fRsqQJ8F-2v0e-8WzZqdshsCqnzmqLAWrNHU,1078
|
|
4
|
+
eulerian_marketing_platform-0.2.0.dist-info/METADATA,sha256=B0l6BxIaS-m55cFeicvSsHEhwVKZAmwy2OaFM9eWYpY,16928
|
|
5
|
+
eulerian_marketing_platform-0.2.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
|
|
6
|
+
eulerian_marketing_platform-0.2.0.dist-info/entry_points.txt,sha256=rrPZptATSS9PUtH9gzCYq0WuP6eahkF-DkdUP1FaYfk,88
|
|
7
|
+
eulerian_marketing_platform-0.2.0.dist-info/top_level.txt,sha256=nidh3T6fw-mLjUqZwQ8AiMScS4usuH0WXW4ZgG4HYCo,28
|
|
8
|
+
eulerian_marketing_platform-0.2.0.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
eulerian_marketing_platform/__init__.py,sha256=Rn4OZQAUsvh-s5Sq2un79qnJ50dnFvAFr0jMHXMVgWQ,291
|
|
2
|
-
eulerian_marketing_platform/server.py,sha256=R7qW5Fclv1sbcFEN5L4Upe6c-BwXsod6Q16Tuyxgc9c,10712
|
|
3
|
-
eulerian_marketing_platform-0.1.1.dist-info/LICENSE,sha256=eIqBqE_fRsqQJ8F-2v0e-8WzZqdshsCqnzmqLAWrNHU,1078
|
|
4
|
-
eulerian_marketing_platform-0.1.1.dist-info/METADATA,sha256=2U6mXMEf2-RecN3bIB_zspSZCgCcoiIJg67zTCa_pZ4,16957
|
|
5
|
-
eulerian_marketing_platform-0.1.1.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
|
|
6
|
-
eulerian_marketing_platform-0.1.1.dist-info/entry_points.txt,sha256=rrPZptATSS9PUtH9gzCYq0WuP6eahkF-DkdUP1FaYfk,88
|
|
7
|
-
eulerian_marketing_platform-0.1.1.dist-info/top_level.txt,sha256=nidh3T6fw-mLjUqZwQ8AiMScS4usuH0WXW4ZgG4HYCo,28
|
|
8
|
-
eulerian_marketing_platform-0.1.1.dist-info/RECORD,,
|
{eulerian_marketing_platform-0.1.1.dist-info → eulerian_marketing_platform-0.2.0.dist-info}/LICENSE
RENAMED
|
File without changes
|
{eulerian_marketing_platform-0.1.1.dist-info → eulerian_marketing_platform-0.2.0.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|