feat(mcp-server): balance report tool (MCP Prompt 15)#4016
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces the accounter_balance_report tool, which generates read-only balance reports for a business over a bounded date range. It also updates the scope-narrowing helper to support a singular businessId input and registers the new tool. The review feedback highlights two important improvements: adding an explicit check to ensure ownerId is defined to prevent unauthorized queries, and using nullish coalescing on the upstream GraphQL response to avoid potential runtime crashes if the transaction list is nullish.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const ownerId = context.readScope.businessIds[0]; | ||
|
|
||
| const data = await context.client.query<{ transactionsForBalanceReport: RawBalanceRow[] }>( |
There was a problem hiding this comment.
To enforce strict tenant isolation and prevent potential unauthorized queries or runtime errors, we should explicitly assert that ownerId is defined and is a valid string before passing it to the upstream GraphQL query.
| const ownerId = context.readScope.businessIds[0]; | |
| const data = await context.client.query<{ transactionsForBalanceReport: RawBalanceRow[] }>( | |
| const ownerId = context.readScope.businessIds[0]; | |
| if (!ownerId) { | |
| throw new Error('No authorized business ID found in scope'); | |
| } | |
| const data = await context.client.query<{ transactionsForBalanceReport: RawBalanceRow[] }>( | |
0929ded to
57e3d25
Compare
Add one read-only report tool (docs/mcp/spec.md §8.2). - src/tools/reports.ts: accounter_balance_report — transactionsForBalanceReport for a single authorized business over a bounded date range (<= 366 days), reportType enum (BALANCE), role-gated (business_owner/accountant); output rows capped at 500 with a truncated flag to avoid large payloads - execute.ts: scope-narrowing convention also accepts a singular businessId (in addition to businessIds[]), so the report's business is validated as a subset of the caller's memberships (AUTHORIZATION_ERROR when out of scope) - registered in the process-wide registry - tests: valid report (owner scoping + normalization), invalid/oversized date range, bad format, oversized-result truncation, and role/scope denial Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SPMszz9gdZFhNjobRm6Ndo
Address review: - assert a concrete ownerId is in scope before querying (a business-scoped tool must never reach upstream without one) - default transactionsForBalanceReport to [] to tolerate a null/absent list Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SPMszz9gdZFhNjobRm6Ndo
7dc10a7 to
2ef1248
Compare
Summary
Implements Prompt 15 – Tool 3: selected report read operation (see
docs/mcp/spec.md§8.2). Adds one high-valueread-only report.
Changes
src/tools/reports.ts—accounter_balance_report(transactionsForBalanceReport):businessId,fromDate/toDate(bounded to 366 days, format-checked), and areportTypeenum (BALANCE, the only phase-1 value).business_owner/accountant, matching the server's@requiresAnyRole).truncatedflag to avoid large payloads; normalized rows + period + count.src/tools/execute.ts— the scope-narrowing convention now also accepts a singularbusinessId(not justbusinessIds[]), so the report's business is validated as a subset of the caller's memberships (AUTHORIZATION_ERRORwhen out of scope) and passed as the report'sownerId.src/tools/registry-instance.ts— the report tool registered.Validation
yarn workspace @accounter/mcp-server test→ 167 passedyarn workspace @accounter/mcp-server lint/typecheck/build→ passNotes / open decisions
reportTypeenum has one value now so adding more reports later is non-breaking.truncatedflag; the shared truncation framework (Prompt 16) will generalize this and add continuation hints.packages/server(transactionsForBalanceReport,BalanceTransactions,FinancialAmount); upstream is mocked in tests, so worth a live smoke check before release.🤖 Generated with Claude Code
Generated by Claude Code