feat(mcp): concrete MCP servers become tools the agents can call during a run
Krav 3, and the operator chose the run path explicitly: the external service must be reachable WHILE the run works, not only when documents are ingested. Until now the run path had one in-process tool against a local folder — and on the bundle path the agents had no tools at all. MAF already ships the client (MCPStdioTool / MCPStreamableHTTPTool, verified in the pinned 1.9.0 with allowed_tools and request_timeout), so `mcp_tools.py` owns only what MAF cannot decide for us: which servers a run may contact, which of their tools it may call, how long it waits, and where the credential comes from. This is a DIFFERENT seam from ingest_mcp.py on purpose — that one pulls source documents before a run and speaks to null-argument tools. Same protocol, different job. Every refusal is a live hazard, not tidiness. An empty allowlist would let the far end decide what the agents may call, so naming the tools is mandatory. A non-positive timeout is an unbounded wait against a third party. An unknown field is refused rather than ignored, which is also what keeps a literal secret from being parked in the config — there is no field for one, only the NAME of an env var. A named-but-unset credential refuses instead of calling anonymously, because an anonymous call can succeed with the wrong scope. Egress is declared, always. Every server and permitted tool is named in the run announcement before the first call — including when no --mandate is given, which was a real hole: the announcement only printed with a commission, so configuring servers without one would have contacted third parties with nothing printed at all. --live-dry-run still opens nothing, because the tools are entered after the dry-run cut: the promise to stop before the first call now covers egress too. Threaded through BOTH modes. A flag accepted in one mode and silently dropped in the other is the defect class this CLI refuses by name. Load-bearing MEASURED against the whole 744-test suite, four mutations all red: build the tools but never hand them to the agents (2) · never enter the AsyncExitStack, so they are constructed and useless (1) · never declare the egress (2) · drop the allowlist on the built client (1). Two live docs claimed MCP was unwired in the run path; both corrected rather than left to rot. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ULCqjLF61rehj5cZmdUoR3
This commit is contained in:
parent
30bcdd3544
commit
9668e17f2f
8 changed files with 706 additions and 4 deletions
|
|
@ -130,3 +130,29 @@ tre tilnærminger mot samme linje ga «totalt 90 000», som ingen av dem kunne i
|
|||
og gjengir tallet hvis det finnes.
|
||||
- **Den lagrer seg ikke som kunnskap.** Bestillingen er en instruks for én kjøring. Det som lærer
|
||||
systemet noe, er dommen du avgir etterpå.
|
||||
|
||||
## Hvis kjøringen skal spørre en ekstern tjeneste
|
||||
|
||||
Skal agentene kunne slå opp i en tjeneste dere allerede har — et prisregister, målerdata, et
|
||||
avtaleregister — settes den opp med `--mcp-config`:
|
||||
|
||||
```json
|
||||
{"servers": [
|
||||
{"name": "prisregister", "transport": "http", "url": "https://intern.example/mcp",
|
||||
"allowed_tools": ["lookup_unit_price"], "timeout_seconds": 15,
|
||||
"credential_env": "PRISREGISTER_TOKEN"}
|
||||
]}
|
||||
```
|
||||
|
||||
Tre ting er verdt å vite som bestiller:
|
||||
|
||||
- **Du må navngi hvilke verktøy som er tillatt.** Tom liste er ikke lov. Uten det ville tjenesten
|
||||
selv bestemt hva agentene får lov til å kalle.
|
||||
- **Alt som vil bli kontaktet, står i kunngjøringen** — før første kall, på `Contacts:`-linja, med
|
||||
både servernavn og tillatte verktøy. En kjøring når aldri en tjeneste den ikke har navngitt. Det
|
||||
gjelder også når du ikke bruker `--mandate`.
|
||||
- **Passord og nøkler skal aldri i fila.** `credential_env` navngir en miljøvariabel; verdien leses
|
||||
derfra. Er variabelen ikke satt, blir kjøringen nektet — heller det enn å ringe tjenesten anonymt
|
||||
og få svar med feil tilgang.
|
||||
|
||||
Uten `--mcp-config` gjøres ingen nettverkskall i det hele tatt.
|
||||
|
|
|
|||
|
|
@ -191,8 +191,21 @@ tool is called with an empty argument dict, so `datasource.build_mcp_server` **c
|
|||
its `retrieve_cost_docs(query)` has a required parameter (verified — it returns an error result).
|
||||
The two are separate seams by design: `build_mcp_server` serves the agents' retrieval path.
|
||||
|
||||
**Still true:** MCP remains **unwired in the optimiser run path** — the in-process `FunctionTool`
|
||||
seam stays the default there.
|
||||
**No longer true (Trekk B, 2026-08-05):** MCP used to be unwired in the optimiser run path. It is
|
||||
now wired, as an **opt-in** — `--mcp-config` (or `run_project(mcp_servers=...)`) hands the agents
|
||||
live tools from concrete external servers *during* the debate, built by `mcp_tools.py` on MAF's
|
||||
`MCPStdioTool` / `MCPStreamableHTTPTool`. The in-process `FunctionTool` seam remains the default:
|
||||
with no config, no network call is possible and the tool list is unchanged.
|
||||
|
||||
Three properties hold that config to the repo's data rules, and each is measured by a mutation:
|
||||
the tool **allowlist is mandatory** (an empty one would let the far end decide what the agents may
|
||||
call); every configured server and permitted tool is **named in the run announcement before the
|
||||
first call**, so nothing is contacted undeclared — including when no `--mandate` is given; and
|
||||
`--live-dry-run` still opens **nothing**, because the tools are entered after the dry-run cut.
|
||||
|
||||
This is a **different seam from the ingest path above**, deliberately: that one pulls source
|
||||
documents into a bundle before a run and speaks to null-argument tools; this one hands live tools
|
||||
to the agents while they work. They share a protocol, not a job.
|
||||
|
||||
**The timeout path moved, and it is covered.** This paragraph used to say the deadline was
|
||||
`asyncio.wait_for` and that no test exercised it; both halves are now out of date. Measuring
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue