Skip to content

Commit fa25725

Browse files
committed
cache resources and prompts
1 parent f564705 commit fa25725

File tree

4 files changed

+49
-40
lines changed

4 files changed

+49
-40
lines changed

hud/environment/connection.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,11 @@ async def disconnect(self) -> None:
134134
self._resources_cache = None
135135

136136
async def list_tools(self) -> list[mcp_types.Tool]:
137-
"""Fetch tools from server, apply filters/transforms/prefix, and cache."""
137+
"""Fetch tools from server, apply filters/transforms/prefix, and cache.
138+
139+
Always fetches fresh data from the server (no caching check).
140+
The result is cached for use by router.build() via cached_tools property.
141+
"""
138142
if self.client is None:
139143
raise RuntimeError("Not connected - call connect() first")
140144
tools = await self.client.list_tools()
@@ -189,21 +193,25 @@ async def call_tool(
189193
name = name[len(self.config.prefix) + 1 :]
190194
return await self.client.call_tool_mcp(name, arguments or {})
191195

192-
async def list_resources(self, *, refresh: bool = False) -> list[mcp_types.Resource]:
193-
"""Fetch resources from server and cache."""
196+
async def list_resources(self) -> list[mcp_types.Resource]:
197+
"""Fetch resources from server and cache.
198+
199+
Always fetches fresh data from the server (no caching check).
200+
The result is cached for use by router.build_resources() via cached_resources property.
201+
"""
194202
if self.client is None:
195203
raise RuntimeError("Not connected - call connect() first")
196-
if self._resources_cache is not None and not refresh:
197-
return self._resources_cache
198204
self._resources_cache = await self.client.list_resources()
199205
return self._resources_cache
200206

201-
async def list_prompts(self, *, refresh: bool = False) -> list[mcp_types.Prompt]:
202-
"""Fetch prompts from server and cache."""
207+
async def list_prompts(self) -> list[mcp_types.Prompt]:
208+
"""Fetch prompts from server and cache.
209+
210+
Always fetches fresh data from the server (no caching check).
211+
The result is cached for use by router.build_prompts() via cached_prompts property.
212+
"""
203213
if self.client is None:
204214
raise RuntimeError("Not connected - call connect() first")
205-
if self._prompts_cache is not None and not refresh:
206-
return self._prompts_cache
207215
self._prompts_cache = await self.client.list_prompts()
208216
return self._prompts_cache
209217

hud/environment/environment.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -455,14 +455,14 @@ async def _build_prompt_routing(self) -> None:
455455
"""Build prompt routing from local prompts and connections."""
456456
local_prompts_dict = await self._prompt_manager.get_prompts()
457457
local_prompts = [p.to_mcp_prompt() for p in local_prompts_dict.values()]
458-
await self._router.build_prompts(local_prompts, self._connections)
458+
self._router.build_prompts(local_prompts, self._connections)
459459
self._prompt_routing_built = True
460460

461461
async def _build_resource_routing(self) -> None:
462462
"""Build resource routing from local resources and connections."""
463463
local_resources_dict = await self._resource_manager.get_resources()
464464
local_resources = [r.to_mcp_resource() for r in local_resources_dict.values()]
465-
await self._router.build_resources(local_resources, self._connections)
465+
self._router.build_resources(local_resources, self._connections)
466466
self._resource_routing_built = True
467467

468468
# =========================================================================
@@ -540,7 +540,7 @@ async def _execute_tool(self, name: str, arguments: dict[str, Any]) -> MCPToolRe
540540
async def list_resources(self) -> list[mcp_types.Resource]:
541541
"""Refresh resources from all connections and rebuild resource routing."""
542542
if self._connections:
543-
await asyncio.gather(*[c.list_resources(refresh=True) for c in self._connections.values()])
543+
await asyncio.gather(*[c.list_resources() for c in self._connections.values()])
544544
await self._build_resource_routing()
545545
return self._router.resources
546546

@@ -588,7 +588,7 @@ async def read_resource(
588588
async def list_prompts(self) -> list[mcp_types.Prompt]:
589589
"""Refresh prompts from all connections and rebuild prompt routing."""
590590
if self._connections:
591-
await asyncio.gather(*[c.list_prompts(refresh=True) for c in self._connections.values()])
591+
await asyncio.gather(*[c.list_prompts() for c in self._connections.values()])
592592
await self._build_prompt_routing()
593593
return self._router.prompts
594594

hud/environment/router.py

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from __future__ import annotations
44

5-
import asyncio
65
import logging
76
from dataclasses import dataclass, field
87
from enum import Enum
@@ -162,14 +161,14 @@ def build(
162161

163162
logger.debug("Router: %d tools (%d local)", len(self._tools), len(self._local_tool_names))
164163

165-
async def build_prompts(
164+
def build_prompts(
166165
self,
167166
local_prompts: list[mcp_types.Prompt],
168167
connections: dict[str, Connector],
169168
) -> None:
170169
"""Build prompt routing from local prompts and connections.
171170
172-
Fetches prompts from all connections in parallel for performance.
171+
Uses cached prompts from connections (populated during __aenter__).
173172
"""
174173
self._prompts.clear()
175174
self._prompt_routing.clear()
@@ -182,17 +181,10 @@ async def build_prompts(
182181
self._prompt_routing[prompt.name] = LOCAL_CONNECTION
183182
self._prompts.append(prompt)
184183

185-
# Fetch remote prompts in parallel
186-
async def fetch_prompts(
187-
conn_name: str, conn: Connector
188-
) -> tuple[str, list[mcp_types.Prompt]]:
189-
try:
190-
return conn_name, conn.cached_prompts or await conn.list_prompts()
191-
except Exception as e:
192-
logger.debug("Failed to list prompts from %s: %s", conn_name, e)
193-
return conn_name, []
194-
195-
results = await asyncio.gather(*[fetch_prompts(n, c) for n, c in connections.items()])
184+
# Use cached prompts from each connection (populated during __aenter__)
185+
results: list[tuple[str, list[mcp_types.Prompt]]] = [
186+
(conn_name, conn.cached_prompts) for conn_name, conn in connections.items()
187+
]
196188

197189
# Process results in connection order (dict preserves insertion order)
198190
for conn_name, remote_prompts in results:
@@ -213,14 +205,14 @@ async def fetch_prompts(
213205

214206
logger.debug("Router: %d prompts", len(self._prompts))
215207

216-
async def build_resources(
208+
def build_resources(
217209
self,
218210
local_resources: list[mcp_types.Resource],
219211
connections: dict[str, Connector],
220212
) -> None:
221213
"""Build resource routing from local resources and connections.
222214
223-
Fetches resources from all connections in parallel for performance.
215+
Uses cached resources from connections (populated during __aenter__).
224216
"""
225217
self._resources.clear()
226218
self._resource_routing.clear()
@@ -234,17 +226,10 @@ async def build_resources(
234226
self._resource_routing[uri] = LOCAL_CONNECTION
235227
self._resources.append(resource)
236228

237-
# Fetch remote resources in parallel
238-
async def fetch_resources(
239-
conn_name: str, conn: Connector
240-
) -> tuple[str, list[mcp_types.Resource]]:
241-
try:
242-
return conn_name, conn.cached_resources or await conn.list_resources()
243-
except Exception as e:
244-
logger.debug("Failed to list resources from %s: %s", conn_name, e)
245-
return conn_name, []
246-
247-
results = await asyncio.gather(*[fetch_resources(n, c) for n, c in connections.items()])
229+
# Use cached resources from each connection (populated during __aenter__)
230+
results: list[tuple[str, list[mcp_types.Resource]]] = [
231+
(conn_name, conn.cached_resources) for conn_name, conn in connections.items()
232+
]
248233

249234
# Process results in connection order (dict preserves insertion order)
250235
for conn_name, remote_resources in results:

hud/environment/tests/test_environment.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,14 @@ class MockConnector:
214214
def cached_tools(self) -> list[mcp_types.Tool]:
215215
return self._tools_cache
216216

217+
@property
218+
def cached_prompts(self) -> list[mcp_types.Prompt]:
219+
return []
220+
221+
@property
222+
def cached_resources(self) -> list[mcp_types.Resource]:
223+
return []
224+
217225
async def connect(self) -> None:
218226
pass
219227

@@ -294,6 +302,14 @@ class MockConnector:
294302
def cached_tools(self) -> list[mcp_types.Tool]:
295303
return self._tools_cache
296304

305+
@property
306+
def cached_prompts(self) -> list[mcp_types.Prompt]:
307+
return []
308+
309+
@property
310+
def cached_resources(self) -> list[mcp_types.Resource]:
311+
return []
312+
297313
async def connect(self) -> None:
298314
pass
299315

0 commit comments

Comments
 (0)