diff --git a/README.md b/README.md index 9c10eb4..7837e31 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,8 @@ Each answer's log line shows the cached percentage. points, Sharesight holdings flattened into tables, markdown tables (Yahoo) squeezed (padding and rule rows dropped, `1.20067e+11` shown as `120.067B`, NaN as `-`), results capped at 12,000 characters; an identical call within one request gets a short "same as earlier" note instead of a second copy; - `cache_ttl` shares results between concurrent users. + `cache_ttl` shares results between users: 30 seconds for Yahoo, 30 minutes for Sharesight (so a portfolio + answer can be up to 30 minutes old). - **Compact history:** stored HTML is shown to the model as plain text (ticker links become the symbol, tags and Yahoo URLs dropped), lines cut to 240 characters and the bot's own to 160. - **Smaller prompts:** the system prompt is about half its previous size, the repeated instruction diff --git a/mcp_servers.json b/mcp_servers.json index 07a64ca..6e5ddad 100644 --- a/mcp_servers.json +++ b/mcp_servers.json @@ -39,7 +39,8 @@ ], "slim": "sharesight", "gate": "portfolio", - "max_concurrent": 1 + "max_concurrent": 1, + "cache_ttl": 1800 } } } diff --git a/tests/test_mcp.py b/tests/test_mcp.py index 5dc30d1..a2894b7 100644 --- a/tests/test_mcp.py +++ b/tests/test_mcp.py @@ -1,5 +1,6 @@ import asyncio import json +from pathlib import Path from types import SimpleNamespace as N from lib.mcp.results import diet, slim_result, squeeze_tables, table_records, tidy_sharesight @@ -112,6 +113,21 @@ async def test_call_slims_and_caches_identical_concurrent_calls(): assert len(s.session.calls) == 2 +async def test_sharesight_results_are_cached_for_thirty_minutes(monkeypatch): + cfg = json.loads((Path(__file__).parent.parent / "mcp_servers.json").read_text())["mcpServers"]["sharesight"] + assert cfg["cache_ttl"] == 1800 + clock = [1000.0] + monkeypatch.setattr("lib.mcp.server.time", N(monotonic=lambda: clock[0])) + s = make_server(cache_ttl=cfg["cache_ttl"]) + await s.call("get_a", {"portfolio_id": 1}) + clock[0] += 1799 + await s.call("get_a", {"portfolio_id": 1}) + assert len(s.session.calls) == 1 # still inside the 30 minutes + clock[0] += 2 + await s.call("get_a", {"portfolio_id": 1}) + assert len(s.session.calls) == 2 # expired, fetched again + + async def test_no_cache_by_default_and_not_connected_message(): s = make_server() await s.call("get_a", {})