Prechádzať zdrojové kódy

mcp clients fix, config to .env

Lukas Goldschmidt 1 mesiac pred
rodič
commit
22e90fa945

+ 3 - 0
.env

@@ -0,0 +1,3 @@
+EXEC_MCP_SSE_URL=http://192.168.0.249:8560/mcp/sse
+CRYPTO_MCP_SSE_URL=http://192.168.0.200:8505/mcp/sse
+NEWS_MCP_SSE_URL=http://192.168.0.200:8506/mcp/sse

+ 4 - 15
src/trader_mcp/crypto_client.py

@@ -2,22 +2,11 @@ from __future__ import annotations
 
 from typing import Any
 
-from .mcp_client import GenericMcpClient, McpSseConfig
-
-CRYPTO_MCP_SSE_URL = "http://192.168.0.200:8505/mcp/sse"
+from .mcp_client import GenericMcpClient, McpSseConfig, sse_url_from_env
 
+CRYPTO_MCP_SSE_URL = sse_url_from_env("CRYPTO_MCP_SSE_URL", "http://192.168.0.200:8505/mcp/sse")
 _crypto = GenericMcpClient(McpSseConfig(base_url=CRYPTO_MCP_SSE_URL), timeout=30)
 
 
-def get_latest_xrp_price(symbol: str = "xrp") -> dict[str, Any]:
-    """Calls crypto-mcp.get_price and returns the raw JSON payload."""
-    payload = _crypto.call_tool("get_price", {"symbol": symbol})
-
-    if isinstance(payload, dict):
-        return payload
-
-    # Normalize list-wrapped / content-wrapped results.
-    if isinstance(payload, list) and payload and isinstance(payload[0], dict):
-        return payload[0]
-
-    raise TypeError(f"Unexpected crypto-mcp get_price payload type: {type(payload)}")
+def call_crypto_tool(tool_name: str, arguments: dict[str, Any] | None = None) -> Any:
+    return _crypto.call_tool(tool_name, arguments)

+ 11 - 24
src/trader_mcp/mcp_client.py

@@ -1,5 +1,7 @@
 from __future__ import annotations
 
+import json
+import os
 from dataclasses import dataclass
 from typing import Any, Optional
 
@@ -10,50 +12,35 @@ from fastmcp.client.transports.sse import SSETransport
 
 @dataclass(frozen=True)
 class McpSseConfig:
-    base_url: str  # e.g. http://host:port/mcp/sse
+    base_url: str
 
 
 class GenericMcpClient:
-    def __init__(
-        self,
-        sse: McpSseConfig,
-        *,
-        timeout: int | float = 30,
-    ) -> None:
+    def __init__(self, sse: McpSseConfig, *, timeout: int | float = 30) -> None:
         self._sse = sse
         self._timeout = timeout
 
     def _build_client(self) -> Client:
-        transport = SSETransport(self._sse.base_url)
-        return Client(transport, auto_initialize=True, timeout=self._timeout)
-
-    async def _call_tool_async(
-        self,
-        tool_name: str,
-        arguments: Optional[dict[str, Any]] = None,
-    ) -> Any:
+        return Client(SSETransport(self._sse.base_url), auto_initialize=True, timeout=self._timeout)
+
+    async def _call_tool_async(self, tool_name: str, arguments: Optional[dict[str, Any]] = None) -> Any:
         async with self._build_client() as client:
             result = await client.call_tool(tool_name, arguments or {})
-
-            # FastMCP may already return parsed structured content.
             if hasattr(result, "structuredContent") and result.structuredContent is not None:
                 return result.structuredContent
 
             payload = getattr(result, "content", None)
             if hasattr(payload, "text"):
-                import json
-
                 payload = json.loads(payload.text)
-
             if payload is None:
                 payload = result
-
             if isinstance(payload, list) and payload and hasattr(payload[0], "text"):
-                import json
-
                 payload = json.loads(payload[0].text)
-
             return payload
 
     def call_tool(self, tool_name: str, arguments: Optional[dict[str, Any]] = None) -> Any:
         return anyio.run(self._call_tool_async, tool_name, arguments)
+
+
+def sse_url_from_env(var_name: str, default: str) -> str:
+    return os.getenv(var_name, default)

+ 4 - 10
src/trader_mcp/news_client.py

@@ -2,17 +2,11 @@ from __future__ import annotations
 
 from typing import Any
 
-from .mcp_client import GenericMcpClient, McpSseConfig
-
-NEWS_MCP_SSE_URL = "http://192.168.0.200:8506/mcp/sse"
+from .mcp_client import GenericMcpClient, McpSseConfig, sse_url_from_env
 
+NEWS_MCP_SSE_URL = sse_url_from_env("NEWS_MCP_SSE_URL", "http://192.168.0.200:8506/mcp/sse")
 _news = GenericMcpClient(McpSseConfig(base_url=NEWS_MCP_SSE_URL), timeout=30)
 
 
-def get_news_sentiment(entity: str = "XRP", timeframe: str = "24h") -> dict[str, Any]:
-    payload = _news.call_tool("get_news_sentiment", {"entity": entity, "timeframe": timeframe})
-    if isinstance(payload, dict):
-        return payload
-    if isinstance(payload, list) and payload and isinstance(payload[0], dict):
-        return payload[0]
-    raise TypeError(f"Unexpected news-mcp get_news_sentiment payload type: {type(payload)}")
+def call_news_tool(tool_name: str, arguments: dict[str, Any] | None = None) -> Any:
+    return _news.call_tool(tool_name, arguments)

+ 3 - 14
tests/test_crypto_client.py

@@ -1,21 +1,10 @@
 from __future__ import annotations
 
-import pytest
 
+def test_crypto_get_price_shape():
+    from src.trader_mcp.crypto_client import call_crypto_tool
 
-def test_exec_list_accounts_returns_non_empty_json():
-    # already covered in test_smoke
-    from src.trader_mcp.exec_client import list_accounts
-
-    accounts = list_accounts()
-    assert isinstance(accounts, list)
-    assert len(accounts) > 0
-
-
-def test_crypto_get_latest_xrp_price_shape():
-    from src.trader_mcp.crypto_client import get_latest_xrp_price
-
-    resp = get_latest_xrp_price("xrp")
+    resp = call_crypto_tool("get_price", {"symbol": "xrp"})
     assert isinstance(resp, dict)
     assert resp.get("symbol") in {"xrp", "XRP"}
     assert isinstance(resp.get("price"), (int, float))

+ 3 - 3
tests/test_news_client.py

@@ -1,10 +1,10 @@
 from __future__ import annotations
 
 
-def test_news_get_xrp_sentiment_shape():
-    from src.trader_mcp.news_client import get_news_sentiment
+def test_news_get_sentiment_shape():
+    from src.trader_mcp.news_client import call_news_tool
 
-    resp = get_news_sentiment("XRP")
+    resp = call_news_tool("get_news_sentiment", {"entity": "XRP"})
     assert isinstance(resp, dict)
     assert resp.get("entity") == "XRP"
     assert resp.get("sentiment") in {"positive", "neutral", "negative"}