Skip to content

Commit ebe0f87

Browse files
committed
Enhancing /status page for MCP
1 parent 81450a7 commit ebe0f87

1 file changed

Lines changed: 47 additions & 9 deletions

File tree

src/service/status_probes.py

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@
1111
import aiohttp
1212
from urllib.parse import urlsplit, urlunsplit
1313

14+
from agent.components.tools import MCPToolLoader
15+
1416
from .checkpointer import checkpointers
1517
from .mcp_tools_cache import get_mcp_tools, get_server_configs
1618
from .redis_client import get_redis_client
1719

1820
logger = logging.getLogger("opey.service.status")
1921

2022
_PROBE_TIMEOUT_SEC = 2.0
23+
_MCP_TEST_CALL_TIMEOUT_SEC = 5.0 # full MCP handshake + tools/list is heavier than an HTTP ping
2124
_CACHE_TTL_SEC = 15.0
2225

2326
_start_time_monotonic = time.monotonic()
@@ -70,7 +73,11 @@ def _obp_mcp_status_url() -> str | None:
7073
parts = urlsplit(cfg.url)
7174
if not parts.scheme or not parts.netloc:
7275
return None
73-
return urlunsplit((parts.scheme, parts.netloc, "/status", "format=json", ""))
76+
netloc = parts.netloc
77+
# 0.0.0.0 is a bind-any address, not a valid connect target — map to loopback.
78+
if parts.hostname == "0.0.0.0":
79+
netloc = netloc.replace("0.0.0.0", "127.0.0.1", 1)
80+
return urlunsplit((parts.scheme, netloc, "/status", "format=json", ""))
7481
return None
7582

7683

@@ -81,22 +88,51 @@ async def _probe_mcp() -> dict[str, Any]:
8188
except Exception:
8289
result = {"up": False, "tool_count": 0}
8390

84-
# Best-effort fetch of OBP-MCP's outbound auth mode. Failures here must not
85-
# demote the overall MCP component — the tools cache is the source of truth
86-
# for "up", and the auth mode is informational.
91+
# Auth-required OBP-MCP servers are never connected at startup (tools are
92+
# loaded per-request with the user's bearer token), so the tools cache says
93+
# nothing about reachability. When an OBP-MCP server is configured, probe
94+
# its /status endpoint directly: reachability drives "up", and the outbound
95+
# auth mode is included when the response parses.
8796
url = _obp_mcp_status_url()
8897
if url:
98+
start = time.monotonic()
99+
reachable = False
89100
try:
90101
timeout = aiohttp.ClientTimeout(total=_PROBE_TIMEOUT_SEC)
91102
async with aiohttp.ClientSession(timeout=timeout) as session:
92103
async with session.get(url, headers={"Accept": "application/json"}) as resp:
104+
reachable = resp.status < 500
93105
if resp.status == 200:
94-
data = await resp.json()
95-
mode = (data.get("auth") or {}).get("outbound_auth_via")
96-
if mode:
97-
result["obp_mcp_outbound_auth_via"] = mode
106+
try:
107+
data = await resp.json()
108+
mode = (data.get("auth") or {}).get("outbound_auth_via")
109+
if mode:
110+
result["obp_mcp_outbound_auth_via"] = mode
111+
except Exception:
112+
pass # auth mode is informational only
98113
except Exception:
99-
pass
114+
reachable = False
115+
result["up"] = reachable
116+
result["latency_ms"] = int((time.monotonic() - start) * 1000)
117+
118+
# Protocol-level test call: perform a real MCP handshake + tools/list over the
119+
# same loader the agent uses. MCPToolLoader is used directly (not the
120+
# create_mcp_tools_with_auth wrapper, which swallows connection errors into an
121+
# empty list). Unauthenticated — succeeds when the server's inbound auth is
122+
# disabled (e.g. consent-based OBP-MCP setups); with inbound auth enabled it
123+
# may fail, so a failure is reported but does not demote "up".
124+
configs = get_server_configs()
125+
if configs:
126+
try:
127+
loader = MCPToolLoader(servers=configs)
128+
tools = await asyncio.wait_for(
129+
loader.load_tools(), timeout=_MCP_TEST_CALL_TIMEOUT_SEC
130+
)
131+
result["test_call"] = "ok"
132+
result["tool_count"] = len(tools)
133+
except Exception as e:
134+
logger.warning(f"MCP test call failed: {type(e).__name__}: {e}")
135+
result["test_call"] = "failed"
100136

101137
return result
102138

@@ -172,6 +208,8 @@ def render_status_html(status: dict[str, Any]) -> str:
172208
extras.append(f"{int(data['latency_ms'])} ms")
173209
if "tool_count" in data:
174210
extras.append(f"{int(data['tool_count'])} tools")
211+
if "test_call" in data:
212+
extras.append(f"test call: {data['test_call']}")
175213
if "obp_mcp_outbound_auth_via" in data:
176214
extras.append(f"OBP-MCP auth: {data['obp_mcp_outbound_auth_via']}")
177215
extra_text = html.escape(" · ".join(extras)) if extras else ""

0 commit comments

Comments
 (0)