diff --git a/server/odoo_mcp.py b/server/odoo_mcp.py index 2890503..8d75c5f 100644 --- a/server/odoo_mcp.py +++ b/server/odoo_mcp.py @@ -9,6 +9,8 @@ Purchase, Inventory, Employees, and Knowledge Templates. import os import re import xmlrpc.client +import urllib.request +import urllib.error from typing import Optional from mcp.server.fastmcp import FastMCP @@ -18,6 +20,28 @@ ODOO_DB = os.environ.get("ODOO_DB", "mpmedia-odoo-sh-main-13285275") ODOO_USERNAME = os.environ.get("ODOO_USERNAME", "bgilliom@mpmedia.tv") ODOO_API_KEY = os.environ.get("ODOO_API_KEY", "") +# ── Proxy-aware XML-RPC transport ───────────────────────────────────────────── +class ProxyAwareTransport(xmlrpc.client.SafeTransport): + """Routes xmlrpc through the system HTTPS proxy (respects HTTPS_PROXY env var).""" + def request(self, host, handler, request_body, verbose=False): + url = f"https://{host}{handler}" + headers = { + "Content-Type": "text/xml", + "Accept-Encoding": "identity", + "User-Agent": "xmlrpc-odoo-mpm/1.0", + } + req = urllib.request.Request(url, request_body, headers) + opener = urllib.request.build_opener(urllib.request.ProxyHandler()) + try: + with opener.open(req, timeout=30) as resp: + return self.parse_response(resp) + except urllib.error.HTTPError as e: + raise xmlrpc.client.ProtocolError(url, e.code, e.msg, dict(e.headers)) + except urllib.error.URLError as e: + raise xmlrpc.client.ProtocolError(url, 0, str(e.reason), {}) + +_proxy_transport = ProxyAwareTransport() + # ── Odoo client ─────────────────────────────────────────────────────────────── _uid: Optional[int] = None _models = None @@ -26,11 +50,11 @@ def _connect(): global _uid, _models if _uid is not None: return - common = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/common") + common = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/common", transport=_proxy_transport) _uid = common.authenticate(ODOO_DB, ODOO_USERNAME, ODOO_API_KEY, {}) if not _uid: raise RuntimeError("Odoo authentication failed. Check ODOO_USERNAME and ODOO_API_KEY.") - _models = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/object") + _models = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/object", transport=_proxy_transport) def _call(model: str, method: str, args=None, kwargs=None): _connect() @@ -98,6 +122,7 @@ def get_product(product_id: int) -> dict: def get_product_stock(product_id: int) -> list: """Get current stock quantities for a product (by product.template ID) across all internal locations.""" + # Get all product.product IDs under this template variants = _search_read("product.product", [["product_tmpl_id", "=", product_id]], ["id", "display_name"])