naragate-mcp 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Devid HW
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.4
2
+ Name: naragate-mcp
3
+ Version: 0.3.0
4
+ Summary: MCP server exposing the Naragate reality-gap evidence engine to any MCP-capable agent (desktop, CLI, IDE).
5
+ Author-email: Devid HW <devid.wahid@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/masdevid/naragate
8
+ Project-URL: Repository, https://github.com/masdevid/naragate
9
+ Project-URL: Issues, https://github.com/masdevid/naragate/issues
10
+ Project-URL: Changelog, https://github.com/masdevid/naragate/releases
11
+ Project-URL: Documentation, https://github.com/masdevid/naragate/blob/master/mcp/README.md
12
+ Keywords: mcp,model-context-protocol,ai-agents,claude,cursor,opencode,codex,stock,indonesia,idx,sectors,fact-check
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Financial and Insurance Industry
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Office/Business :: Financial :: Investment
23
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: mcp<2,>=1.2
28
+ Requires-Dist: httpx>=0.27
29
+ Provides-Extra: test
30
+ Requires-Dist: pytest>=8.0.0; extra == "test"
31
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == "test"
32
+ Dynamic: license-file
33
+
34
+ # Naragate MCP Server
35
+
36
+ One portable tool layer so the **same Naragate experience** works on the custom web UI *and* on any
37
+ MCP-capable agent surface — Claude Code, Claude Desktop, Cursor, Windsurf, Zed, VS Code, opencode,
38
+ Codex, or your own harness.
39
+
40
+ ```
41
+ ┌─ custom web UI (Angular)
42
+ Naragate backend ├─ MCP server ──► any MCP harness (desktop / CLI / IDE)
43
+ (FastAPI+cache) └─ Pi agent harness
44
+ ```
45
+
46
+ ## The three layers (what a user actually needs)
47
+
48
+ | Layer | Path | Required? | Role |
49
+ |---|---|---|---|
50
+ | **MCP server** | `mcp/` (this package) | **Yes** for non-web | The tools: analyze, history, trend, precheck, usage |
51
+ | **Skills** | `skills/` | **Yes** | Domain instructions: how to parse, challenge and score a claim |
52
+ | **Agents** | `agents/` | No | Optional orchestration for harnesses with subagents |
53
+
54
+ Skills alone can't fetch data (they declare tools but don't ship them); agents+skills add
55
+ orchestration but still have no data. **The MCP bundle is the data layer** — with it, skills run
56
+ anywhere.
57
+
58
+ ## Credit safety (hard requirement)
59
+
60
+ This server **never talks to Sectors directly**. Every tool is a thin client over the Naragate
61
+ backend, so evidence gathering, the Evidence Graph cache and credit accounting live in exactly one
62
+ place. A warm re-run costs **0 additional Sectors calls**, identical to the web UI. There is a test
63
+ asserting the MCP package never references the Sectors API.
64
+
65
+ ## Install
66
+
67
+ ```bash
68
+ uvx naragate-mcp # run without installing (recommended)
69
+ pip install naragate-mcp # or install into your environment
70
+ pipx install naragate-mcp
71
+ ```
72
+
73
+ From a checkout: `pip install -e mcp`.
74
+
75
+ Point it at your backend (default `http://127.0.0.1:5678`):
76
+
77
+ ```bash
78
+ export NARAGATE_BACKEND_URL="http://127.0.0.1:5678"
79
+ naragate-mcp
80
+ ```
81
+
82
+ Transport is **stdio**.
83
+
84
+ ## Tools
85
+
86
+ | Tool | What it does |
87
+ |---|---|
88
+ | `analyze_narrative(narrative)` | Verify a narrative end-to-end → Reality Gap score, verdict, evidence sections, policy signal, skeptic summary |
89
+ | `analyze_template(template_id)` | Run one of the 12 curated dashboard templates by id |
90
+ | `list_templates()` | The 12 curated narratives (same tiles as the web dashboard) |
91
+ | `get_claim(claim_id)` | Full stored record (claim, evidence, skeptic, score, policy) |
92
+ | `get_reality_gap(claim_id)` | Compact report for a stored claim |
93
+ | `list_history(limit)` | Recent analyses, most recent first |
94
+ | `get_trend_summary()` | Totals, average score, verdict distribution, per-ticker history |
95
+ | `get_policy_precheck(sector)` | Policy→price pre-check, scoped to the claim's sector |
96
+ | `get_usage()` | Sectors/LLM credit usage, cache hits, remaining budget |
97
+
98
+ ### Low-level tools — full `tools.yaml` parity
99
+
100
+ Every primitive declared in `skills/*/tools.yaml` is also exposed, for harnesses that want to compose
101
+ per-agent exactly like the Pi pipeline (check the cache, then fetch on a miss and merge back):
102
+
103
+ | Tool | What it does |
104
+ |---|---|
105
+ | `sectors_company_report(ticker, sections)` | Sectors v2 company report (valuation/overview/financials) |
106
+ | `sectors_subsector_report(sub_sector, sections)` | Sectors v2 subsector report |
107
+ | `sectors_quarterly_financials(ticker, n_quarters)` | Sectors v2 quarterly financials |
108
+ | `sectors_daily_transaction(ticker, start, end)` | Sectors v2 daily price/volume (Sectors caps a call at 90 days) |
109
+ | `sectors_news(ticker, limit)` | Sectors v2 news headlines |
110
+ | `sectors_corporate_actions(ticker)` | Sectors v2 corporate actions |
111
+ | `sectors_filings(ticker, filing_type)` | Sectors v2 insider-trade filings |
112
+ | `evidence_cache_get(ticker)` | Read the Evidence Graph (null on miss) |
113
+ | `evidence_cache_merge(ticker, key, value, ttl?)` | Merge one section into the cache |
114
+ | `llm_complete(prompt, system?, response_format?, role?)` | One-shot completion on Naragate's configured LLM |
115
+
116
+ A test (`tests/test_parity.py`) asserts that **every** tool declared in any `skills/*/tools.yaml`
117
+ exists on the server, so this can't drift.
118
+
119
+ ## Resources
120
+
121
+ - `naragate://templates`
122
+ - `naragate://usage`
123
+ - `naragate://history`
124
+ - `naragate://claim/{claim_id}`
125
+
126
+ ## Harness configuration
127
+
128
+ ### Claude Code
129
+
130
+ ```bash
131
+ claude mcp add naragate -e NARAGATE_BACKEND_URL=http://127.0.0.1:5678 -- uvx naragate-mcp
132
+ ```
133
+
134
+ ### Claude Desktop (`claude_desktop_config.json`)
135
+
136
+ ```json
137
+ {
138
+ "mcpServers": {
139
+ "naragate": {
140
+ "command": "uvx",
141
+ "args": ["naragate-mcp"],
142
+ "env": { "NARAGATE_BACKEND_URL": "http://127.0.0.1:5678" }
143
+ }
144
+ }
145
+ }
146
+ ```
147
+
148
+ ### opencode (`opencode.json`)
149
+
150
+ ```json
151
+ {
152
+ "mcp": {
153
+ "naragate": {
154
+ "type": "local",
155
+ "command": ["uvx", "naragate-mcp"],
156
+ "enabled": true,
157
+ "environment": { "NARAGATE_BACKEND_URL": "http://127.0.0.1:5678" }
158
+ }
159
+ }
160
+ }
161
+ ```
162
+
163
+ ### Cursor (`.cursor/mcp.json`) / Windsurf / Zed / VS Code
164
+
165
+ ```json
166
+ {
167
+ "mcpServers": {
168
+ "naragate": {
169
+ "command": "uvx",
170
+ "args": ["naragate-mcp"],
171
+ "env": { "NARAGATE_BACKEND_URL": "http://127.0.0.1:5678" }
172
+ }
173
+ }
174
+ }
175
+ ```
176
+
177
+ ### Codex (`~/.codex/config.toml`)
178
+
179
+ ```toml
180
+ [mcp_servers.naragate]
181
+ command = "uvx"
182
+ args = ["naragate-mcp"]
183
+ env = { NARAGATE_BACKEND_URL = "http://127.0.0.1:5678" }
184
+ ```
185
+
186
+ ## Example prompts
187
+
188
+ - *"Use naragate to verify: PE BBCA mahal di 25x."*
189
+ - *"List the naragate templates and run the nickel policy one."*
190
+ - *"Show my last 10 naragate analyses and the trend summary."*
191
+ - *"What's my Sectors credit usage?"*
192
+
193
+ ## Tests
194
+
195
+ ```bash
196
+ cd mcp
197
+ python -m pytest -q
198
+ ```
199
+
200
+ Covers the REST client (mock transport), the tool surface, compact-report shaping, and the
201
+ credit-safety guarantee. No live backend or Sectors access required.
202
+
203
+ ## Publishing
204
+
205
+ Releases publish automatically via [`.github/workflows/publish-mcp.yml`](https://github.com/masdevid/naragate/blob/master/.github/workflows/publish-mcp.yml)
206
+ when a GitHub release is published (using the `PYPI_TOKEN` repository secret), or locally:
207
+
208
+ ```bash
209
+ mcp/publish.sh # PyPI
210
+ mcp/publish.sh testpypi # TestPyPI
211
+ ```
212
+
213
+ `publish.sh` reads `PYPI_TOKEN` from the environment or the repo-root `.env`, builds with
214
+ `python -m build`, validates with `twine check`, and uploads — the token is never echoed. Manual
215
+ equivalent:
216
+
217
+ ```bash
218
+ python -m build
219
+ python -m twine check dist/*
220
+ TWINE_USERNAME=__token__ TWINE_PASSWORD="$PYPI_TOKEN" python -m twine upload dist/*
221
+ ```
222
+
223
+ ## Related
224
+
225
+ - [Naragate README](https://github.com/masdevid/naragate#readme) — project overview and the "Use Naragate from any MCP agent" section
226
+ - [Skills](https://github.com/masdevid/naragate/blob/master/skills/README.md) — the 13 skills whose `tools.yaml` this server satisfies
227
+ - [Agents](https://github.com/masdevid/naragate/blob/master/agents/README.md) — optional subagent topology for harnesses that support it
228
+ - [Parity test](https://github.com/masdevid/naragate/blob/master/mcp/tests/test_parity.py) — enforces that every `skills/*/tools.yaml` tool is exposed here
@@ -0,0 +1,195 @@
1
+ # Naragate MCP Server
2
+
3
+ One portable tool layer so the **same Naragate experience** works on the custom web UI *and* on any
4
+ MCP-capable agent surface — Claude Code, Claude Desktop, Cursor, Windsurf, Zed, VS Code, opencode,
5
+ Codex, or your own harness.
6
+
7
+ ```
8
+ ┌─ custom web UI (Angular)
9
+ Naragate backend ├─ MCP server ──► any MCP harness (desktop / CLI / IDE)
10
+ (FastAPI+cache) └─ Pi agent harness
11
+ ```
12
+
13
+ ## The three layers (what a user actually needs)
14
+
15
+ | Layer | Path | Required? | Role |
16
+ |---|---|---|---|
17
+ | **MCP server** | `mcp/` (this package) | **Yes** for non-web | The tools: analyze, history, trend, precheck, usage |
18
+ | **Skills** | `skills/` | **Yes** | Domain instructions: how to parse, challenge and score a claim |
19
+ | **Agents** | `agents/` | No | Optional orchestration for harnesses with subagents |
20
+
21
+ Skills alone can't fetch data (they declare tools but don't ship them); agents+skills add
22
+ orchestration but still have no data. **The MCP bundle is the data layer** — with it, skills run
23
+ anywhere.
24
+
25
+ ## Credit safety (hard requirement)
26
+
27
+ This server **never talks to Sectors directly**. Every tool is a thin client over the Naragate
28
+ backend, so evidence gathering, the Evidence Graph cache and credit accounting live in exactly one
29
+ place. A warm re-run costs **0 additional Sectors calls**, identical to the web UI. There is a test
30
+ asserting the MCP package never references the Sectors API.
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ uvx naragate-mcp # run without installing (recommended)
36
+ pip install naragate-mcp # or install into your environment
37
+ pipx install naragate-mcp
38
+ ```
39
+
40
+ From a checkout: `pip install -e mcp`.
41
+
42
+ Point it at your backend (default `http://127.0.0.1:5678`):
43
+
44
+ ```bash
45
+ export NARAGATE_BACKEND_URL="http://127.0.0.1:5678"
46
+ naragate-mcp
47
+ ```
48
+
49
+ Transport is **stdio**.
50
+
51
+ ## Tools
52
+
53
+ | Tool | What it does |
54
+ |---|---|
55
+ | `analyze_narrative(narrative)` | Verify a narrative end-to-end → Reality Gap score, verdict, evidence sections, policy signal, skeptic summary |
56
+ | `analyze_template(template_id)` | Run one of the 12 curated dashboard templates by id |
57
+ | `list_templates()` | The 12 curated narratives (same tiles as the web dashboard) |
58
+ | `get_claim(claim_id)` | Full stored record (claim, evidence, skeptic, score, policy) |
59
+ | `get_reality_gap(claim_id)` | Compact report for a stored claim |
60
+ | `list_history(limit)` | Recent analyses, most recent first |
61
+ | `get_trend_summary()` | Totals, average score, verdict distribution, per-ticker history |
62
+ | `get_policy_precheck(sector)` | Policy→price pre-check, scoped to the claim's sector |
63
+ | `get_usage()` | Sectors/LLM credit usage, cache hits, remaining budget |
64
+
65
+ ### Low-level tools — full `tools.yaml` parity
66
+
67
+ Every primitive declared in `skills/*/tools.yaml` is also exposed, for harnesses that want to compose
68
+ per-agent exactly like the Pi pipeline (check the cache, then fetch on a miss and merge back):
69
+
70
+ | Tool | What it does |
71
+ |---|---|
72
+ | `sectors_company_report(ticker, sections)` | Sectors v2 company report (valuation/overview/financials) |
73
+ | `sectors_subsector_report(sub_sector, sections)` | Sectors v2 subsector report |
74
+ | `sectors_quarterly_financials(ticker, n_quarters)` | Sectors v2 quarterly financials |
75
+ | `sectors_daily_transaction(ticker, start, end)` | Sectors v2 daily price/volume (Sectors caps a call at 90 days) |
76
+ | `sectors_news(ticker, limit)` | Sectors v2 news headlines |
77
+ | `sectors_corporate_actions(ticker)` | Sectors v2 corporate actions |
78
+ | `sectors_filings(ticker, filing_type)` | Sectors v2 insider-trade filings |
79
+ | `evidence_cache_get(ticker)` | Read the Evidence Graph (null on miss) |
80
+ | `evidence_cache_merge(ticker, key, value, ttl?)` | Merge one section into the cache |
81
+ | `llm_complete(prompt, system?, response_format?, role?)` | One-shot completion on Naragate's configured LLM |
82
+
83
+ A test (`tests/test_parity.py`) asserts that **every** tool declared in any `skills/*/tools.yaml`
84
+ exists on the server, so this can't drift.
85
+
86
+ ## Resources
87
+
88
+ - `naragate://templates`
89
+ - `naragate://usage`
90
+ - `naragate://history`
91
+ - `naragate://claim/{claim_id}`
92
+
93
+ ## Harness configuration
94
+
95
+ ### Claude Code
96
+
97
+ ```bash
98
+ claude mcp add naragate -e NARAGATE_BACKEND_URL=http://127.0.0.1:5678 -- uvx naragate-mcp
99
+ ```
100
+
101
+ ### Claude Desktop (`claude_desktop_config.json`)
102
+
103
+ ```json
104
+ {
105
+ "mcpServers": {
106
+ "naragate": {
107
+ "command": "uvx",
108
+ "args": ["naragate-mcp"],
109
+ "env": { "NARAGATE_BACKEND_URL": "http://127.0.0.1:5678" }
110
+ }
111
+ }
112
+ }
113
+ ```
114
+
115
+ ### opencode (`opencode.json`)
116
+
117
+ ```json
118
+ {
119
+ "mcp": {
120
+ "naragate": {
121
+ "type": "local",
122
+ "command": ["uvx", "naragate-mcp"],
123
+ "enabled": true,
124
+ "environment": { "NARAGATE_BACKEND_URL": "http://127.0.0.1:5678" }
125
+ }
126
+ }
127
+ }
128
+ ```
129
+
130
+ ### Cursor (`.cursor/mcp.json`) / Windsurf / Zed / VS Code
131
+
132
+ ```json
133
+ {
134
+ "mcpServers": {
135
+ "naragate": {
136
+ "command": "uvx",
137
+ "args": ["naragate-mcp"],
138
+ "env": { "NARAGATE_BACKEND_URL": "http://127.0.0.1:5678" }
139
+ }
140
+ }
141
+ }
142
+ ```
143
+
144
+ ### Codex (`~/.codex/config.toml`)
145
+
146
+ ```toml
147
+ [mcp_servers.naragate]
148
+ command = "uvx"
149
+ args = ["naragate-mcp"]
150
+ env = { NARAGATE_BACKEND_URL = "http://127.0.0.1:5678" }
151
+ ```
152
+
153
+ ## Example prompts
154
+
155
+ - *"Use naragate to verify: PE BBCA mahal di 25x."*
156
+ - *"List the naragate templates and run the nickel policy one."*
157
+ - *"Show my last 10 naragate analyses and the trend summary."*
158
+ - *"What's my Sectors credit usage?"*
159
+
160
+ ## Tests
161
+
162
+ ```bash
163
+ cd mcp
164
+ python -m pytest -q
165
+ ```
166
+
167
+ Covers the REST client (mock transport), the tool surface, compact-report shaping, and the
168
+ credit-safety guarantee. No live backend or Sectors access required.
169
+
170
+ ## Publishing
171
+
172
+ Releases publish automatically via [`.github/workflows/publish-mcp.yml`](https://github.com/masdevid/naragate/blob/master/.github/workflows/publish-mcp.yml)
173
+ when a GitHub release is published (using the `PYPI_TOKEN` repository secret), or locally:
174
+
175
+ ```bash
176
+ mcp/publish.sh # PyPI
177
+ mcp/publish.sh testpypi # TestPyPI
178
+ ```
179
+
180
+ `publish.sh` reads `PYPI_TOKEN` from the environment or the repo-root `.env`, builds with
181
+ `python -m build`, validates with `twine check`, and uploads — the token is never echoed. Manual
182
+ equivalent:
183
+
184
+ ```bash
185
+ python -m build
186
+ python -m twine check dist/*
187
+ TWINE_USERNAME=__token__ TWINE_PASSWORD="$PYPI_TOKEN" python -m twine upload dist/*
188
+ ```
189
+
190
+ ## Related
191
+
192
+ - [Naragate README](https://github.com/masdevid/naragate#readme) — project overview and the "Use Naragate from any MCP agent" section
193
+ - [Skills](https://github.com/masdevid/naragate/blob/master/skills/README.md) — the 13 skills whose `tools.yaml` this server satisfies
194
+ - [Agents](https://github.com/masdevid/naragate/blob/master/agents/README.md) — optional subagent topology for harnesses that support it
195
+ - [Parity test](https://github.com/masdevid/naragate/blob/master/mcp/tests/test_parity.py) — enforces that every `skills/*/tools.yaml` tool is exposed here
@@ -0,0 +1,6 @@
1
+ """Naragate MCP server package."""
2
+
3
+ from naragate_mcp.client import NaragateClient, NaragateError
4
+
5
+ __all__ = ["NaragateClient", "NaragateError"]
6
+ __version__ = "0.3.0"
@@ -0,0 +1,134 @@
1
+ """Thin REST client for the Naragate backend.
2
+
3
+ Every non-web surface (MCP tools, scripts) goes through the backend so that
4
+ evidence caching and credit accounting live in exactly one place. Configure the
5
+ backend with `NARAGATE_BACKEND_URL` (default `http://127.0.0.1:5678`).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ from typing import Any
12
+
13
+ import httpx
14
+
15
+ DEFAULT_BASE_URL = "http://127.0.0.1:5678"
16
+
17
+
18
+ class NaragateError(RuntimeError):
19
+ """Raised when the Naragate backend is unreachable or returns an error."""
20
+
21
+ def __init__(self, message: str, status: int | None = None, detail: Any = None):
22
+ super().__init__(message)
23
+ self.status = status
24
+ self.detail = detail
25
+
26
+
27
+ class NaragateClient:
28
+ def __init__(self, base_url: str | None = None, timeout: float = 600.0, transport: httpx.BaseTransport | None = None):
29
+ self.base_url = (
30
+ base_url or os.environ.get("NARAGATE_BACKEND_URL") or DEFAULT_BASE_URL
31
+ ).rstrip("/")
32
+ self.timeout = timeout
33
+ self._transport = transport
34
+
35
+ def _request(self, method: str, path: str, **kwargs: Any) -> Any:
36
+ url = f"{self.base_url}{path}"
37
+ try:
38
+ with httpx.Client(timeout=self.timeout, transport=self._transport) as client:
39
+ resp = client.request(method, url, **kwargs)
40
+ except httpx.HTTPError as exc: # network / timeout
41
+ raise NaragateError(
42
+ f"cannot reach Naragate backend at {self.base_url}: {exc}"
43
+ ) from exc
44
+
45
+ if resp.status_code >= 400:
46
+ detail: Any
47
+ try:
48
+ detail = resp.json().get("detail")
49
+ except Exception: # noqa: BLE001 — non-JSON error body
50
+ detail = resp.text
51
+ raise NaragateError(
52
+ f"{method} {path} -> HTTP {resp.status_code}: {detail}",
53
+ status=resp.status_code,
54
+ detail=detail,
55
+ )
56
+ return resp.json()
57
+
58
+ # ---- high-level, credit-safe operations (all reuse the backend pipeline) ----
59
+
60
+ def analyze(self, narrative: str) -> dict[str, Any]:
61
+ return self._request("POST", "/api/v1/analyze", json={"narrative": narrative})
62
+
63
+ def get_claim(self, claim_id: str) -> dict[str, Any]:
64
+ return self._request("GET", f"/api/v1/claims/{claim_id}")
65
+
66
+ def list_history(self, limit: int = 20) -> list[dict[str, Any]]:
67
+ return self._request("GET", "/api/v1/claims/", params={"limit": limit})
68
+
69
+ def get_summary(self) -> dict[str, Any]:
70
+ return self._request("GET", "/api/v1/claims/summary")
71
+
72
+ def get_precheck(self, sector: str | None = None) -> dict[str, Any]:
73
+ params = {"sector": sector} if sector else None
74
+ return self._request("GET", "/api/v1/precheck/", params=params)
75
+
76
+ def list_templates(self) -> dict[str, Any]:
77
+ return self._request("GET", "/api/v1/templates")
78
+
79
+ def get_usage(self) -> dict[str, Any]:
80
+ return self._request("GET", "/api/v1/usage")
81
+
82
+ # ---- low-level agent tools (parity with skills/*/tools.yaml) ----
83
+
84
+ def sectors_company_report(self, ticker: str, sections: list[str] | None = None) -> dict[str, Any]:
85
+ params = {"ticker": ticker, "sections": ",".join(sections)} if sections else {"ticker": ticker}
86
+ return self._request("GET", "/api/v1/tools/sectors/company-report", params=params)
87
+
88
+ def sectors_subsector_report(self, sub_sector: str, sections: list[str] | None = None) -> dict[str, Any]:
89
+ params = {"sub_sector": sub_sector}
90
+ if sections:
91
+ params["sections"] = ",".join(sections)
92
+ return self._request("GET", "/api/v1/tools/sectors/subsector-report", params=params)
93
+
94
+ def sectors_quarterly_financials(self, ticker: str, n_quarters: int = 8) -> list[dict[str, Any]]:
95
+ return self._request("GET", "/api/v1/tools/sectors/quarterly-financials",
96
+ params={"ticker": ticker, "n_quarters": n_quarters})
97
+
98
+ def sectors_daily_transaction(self, ticker: str, start: str | None = None, end: str | None = None) -> list[dict[str, Any]]:
99
+ params: dict[str, Any] = {"ticker": ticker}
100
+ if start:
101
+ params["start"] = start
102
+ if end:
103
+ params["end"] = end
104
+ return self._request("GET", "/api/v1/tools/sectors/daily-transaction", params=params)
105
+
106
+ def sectors_news(self, ticker: str, limit: int = 20) -> dict[str, Any]:
107
+ return self._request("GET", "/api/v1/tools/sectors/news", params={"ticker": ticker, "limit": limit})
108
+
109
+ def sectors_corporate_actions(self, ticker: str) -> dict[str, Any]:
110
+ return self._request("GET", "/api/v1/tools/sectors/corporate-actions", params={"ticker": ticker})
111
+
112
+ def sectors_filings(self, ticker: str, filing_type: str | None = None) -> dict[str, Any]:
113
+ params: dict[str, Any] = {"ticker": ticker}
114
+ if filing_type:
115
+ params["filing_type"] = filing_type
116
+ return self._request("GET", "/api/v1/tools/sectors/filings", params=params)
117
+
118
+ def evidence_cache_get(self, ticker: str) -> dict[str, Any]:
119
+ return self._request("GET", "/api/v1/tools/evidence-cache", params={"ticker": ticker})
120
+
121
+ def evidence_cache_merge(self, ticker: str, key: str, value: Any, ttl: int | None = None) -> dict[str, Any]:
122
+ body: dict[str, Any] = {"ticker": ticker, "key": key, "value": value}
123
+ if ttl:
124
+ body["ttl"] = ttl
125
+ return self._request("POST", "/api/v1/tools/evidence-cache", json=body)
126
+
127
+ def llm_complete(self, prompt: str, system: str | None = None,
128
+ response_format: dict | None = None, role: str = "default") -> dict[str, Any]:
129
+ body: dict[str, Any] = {"prompt": prompt, "role": role}
130
+ if system:
131
+ body["system"] = system
132
+ if response_format:
133
+ body["response_format"] = response_format
134
+ return self._request("POST", "/api/v1/tools/llm-complete", json=body)