Skip to content

feat(mcp-server): balance report tool (MCP Prompt 15)#4016

Open
gilgardosh wants to merge 2 commits into
claude/mcp-prompt-14-lookup-toolsfrom
claude/mcp-prompt-15-report-tool
Open

feat(mcp-server): balance report tool (MCP Prompt 15)#4016
gilgardosh wants to merge 2 commits into
claude/mcp-prompt-14-lookup-toolsfrom
claude/mcp-prompt-15-report-tool

Conversation

@gilgardosh

Copy link
Copy Markdown
Collaborator

Summary

Implements Prompt 15 – Tool 3: selected report read operation (see
docs/mcp/spec.md §8.2). Adds one high-value
read-only report.

Stacked PR: targets claude/mcp-prompt-14-lookup-tools (Prompt 14, #4015).
Review/merge Prompts 09→14 first; this diff shows only the Prompt 15 changes.

Changes

  • src/tools/reports.tsaccounter_balance_report (transactionsForBalanceReport):
    • Strict input with tight limits: a single businessId, fromDate/toDate (bounded to 366 days, format-checked), and a reportType enum (BALANCE, the only phase-1 value).
    • Role-gated (business_owner / accountant, matching the server's @requiresAnyRole).
    • Output rows capped at 500 with a truncated flag to avoid large payloads; normalized rows + period + count.
  • src/tools/execute.ts — the scope-narrowing convention now also accepts a singular businessId (not just businessIds[]), so the report's business is validated as a subset of the caller's memberships (AUTHORIZATION_ERROR when out of scope) and passed as the report's ownerId.
  • src/tools/registry-instance.ts — the report tool registered.
  • Tests — valid report (owner scoping + normalization), invalid range (inverted, too-wide, bad format), oversized-result truncation, and role/scope denial. 167 tests total.

Validation

  • yarn workspace @accounter/mcp-server test → 167 passed
  • yarn workspace @accounter/mcp-server lint / typecheck / build → pass

Notes / open decisions

  • Report choice. I picked the balance report because it has a clean bounded date-range + single-owner shape and is role-gated — good coverage of the policy machinery. The reportType enum has one value now so adding more reports later is non-breaking.
  • Row cap. Capped at 500 rows in-tool with a truncated flag; the shared truncation framework (Prompt 16) will generalize this and add continuation hints.
  • Query fields verified against 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

@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 21, 2026 16:00 — with GitHub Actions Inactive
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 21, 2026 16:00 — with GitHub Actions Inactive

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +87 to +89
const ownerId = context.readScope.businessIds[0];

const data = await context.client.query<{ transactionsForBalanceReport: RawBalanceRow[] }>(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

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.

Suggested change
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[] }>(

Comment thread packages/mcp-server/src/tools/reports.ts Outdated
claude added 2 commits July 21, 2026 16:18
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
@gilgardosh
gilgardosh force-pushed the claude/mcp-prompt-15-report-tool branch from 7dc10a7 to 2ef1248 Compare July 21, 2026 16:19
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 21, 2026 16:19 — with GitHub Actions Inactive
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 21, 2026 16:19 — with GitHub Actions Inactive
@gilgardosh gilgardosh self-assigned this Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants