refactor(mcp): split server.rs into a file per tool group (U0a)

server.rs had grown to 12,566 lines: every tool handler, every helper and
the request machinery in one file, so every unit that adds a tool touched
it and parallel worktrees conflicted on every merge (plan/served.md 8.2
U0a, 8.0.3). The 98 handlers still in it now live one file per group under
tools/, following the pattern tools/kept.rs and tools/projects.rs already
use (`#[path]` declaration, `use super::*`, one
`#[tool_router(vis = "pub(super)")] impl` per file):

  documents (invoices, bills, payments, voids, books.documents,
  books.document, the control check, the agings), entries (draft, line,
  post, reverse, get, search, the bank CSV import), reports (the
  statements, books.show, verify, digest, export, the CPA package, the
  P&L by class, with the export builders), assets, schedules, periods,
  reconciliation, parties (parties, vendors, customers, W-9s, the TIN
  vault, 1099s, entry parties), attachments (receipts, filed documents,
  links), settings (the handbook, books.settings, FX rates), chart,
  payroll, payouts, tax; add_class and list_classes join the existing
  projects file, since a project is a class with its facts.

The helpers only one group uses moved into that group's file; the ones
several groups share moved to tools/helpers.rs, re-exported by server.rs
so every group reaches them through `use super::*` and no group file
reaches into another's (resolve_instrument and the attachment/statement
shapers moved there too, since the payment, entries and attachment tools
all use them). server.rs keeps BooksServer, the store and its lock, the
request machinery, call_tool/get_info, tool_router (which now composes
the group routers; core_router is gone), and the shared helpers the plan
names (as_of, hand_over, resolve_vendor, parse_doc_ref, preview_lines_for).

This is a move, not a change: no accounting behaviour, tool name,
description, annotation or schema changed. A dump of
tool_router().list_all() — every tool's name, title, description, input
and output schema and annotations, in order — is identical to main's
(122 tools; the plan's "118" is stale). server.rs is 1,909 lines and has
no #[tool] handler. The server's cross-group tests moved to
server_tests.rs beside it (a #[path] module; fixtures in
server::test_support) and each group's own tests into its group file, so
server.rs stays under the 3,000-line bar. cargo test -p ledger-mcp: 99
passed; cargo test -p ledger-cli: 25 suites, 0 failed.
This commit is contained in:
swaits committed 2026-10-27 04:15:47 -06:00
1 parent d5385a7ee9
commit 3cab75c494
19 files changed
+10986 -10786

No files matched your search

+1 -21
View File
@@ -25,8 +25,8 @@
//! ```
use super::*;
use ledger_core::InstrumentKind;
use ledger_core::form1099::{Applied, Dated1099, Decision1099, ExemptReason, Form1099};
use ledger_core::{InstrumentId, InstrumentKind};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -189,26 +189,6 @@ fn instrument_out(store: &Store, i: &ledger_core::Instrument) -> InstrumentOut {
}
}
/// Resolve a payment method by id or label (INVARIANT 5).
pub(super) fn resolve_instrument(store: &Store, r: &str) -> Result<InstrumentId, String> {
let ledger = store.ledger();
let by_id = r
.trim()
.parse::<u64>()
.ok()
.filter(|id| ledger.get_instrument(*id).is_some());
let by_label = ledger.instrument_by_label(r).map(|i| i.id);
match (by_id, by_label) {
(Some(i), Some(l)) if i != l => Err(format!(
"payment method {r:?} is ambiguous: it is method {i}'s id and method {l}'s label; pass the id"
)),
(Some(i), _) | (None, Some(i)) => Ok(i),
(None, None) => Err(format!(
"unknown payment method {r:?}: use an id or label from books.instruments (books.add_instrument adds one)"
)),
}
}
/// The decision `books.set_vendor_1099` was given, or none when it
/// changes only the W-9 flag (INVARIANTS 1-3).
pub(super) fn decision_in(p: &SetVendor1099Params) -> Result<Option<Dated1099>, String> {