Skip to content

norm613/cpp-martus-mcp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cpp-martus-mcp

MCP server exposing the Martus budgeting/reporting API to Claude Code. Read-only wrapper — every tool is a GET. One codebase, two registrations (one per Martus tenant CPP operates).

Why two registrations?

CPP runs two Martus tenants, each fed from a different upstream accounting system:

Tenant Registration Upstream Sync mode
CPP (shared expenses) martus-cpp QuickBooks Online Modern API sync
ANN (Annunciation + multi-org) martus-ann QuickBooks Desktop (delivered over Citrix) CSV export → manipulation → upload

Same code, same server URL (prod5.martus.app/api), different Token header. Following the mp / mp-sandbox precedent, this MCP is registered twice in each vault's .mcp.json with a different token per registration. Tool names land as mcp__martus-cpp__list-accounts vs mcp__martus-ann__list-accounts, so which tenant is being queried is always visible in the call.

See CPP RunBook/3-Resources/martus/curated-use/martus-data-flow.md for the full pipeline story.

Install

Requires Node 20+.

cd %USERPROFILE%\code
git clone git@github.com:norm613/cpp-martus-mcp.git
cd cpp-martus-mcp
npm install
npm run build

Configure

Copy .env.example to .env and set your tokens. Each tenant needs its own value — use .env.cpp / .env.ann convention (both gitignored), or keep the tokens out of any file entirely and inject them via the MCP registration's env block (preferred — see below).

Note — shared-token model at CPP. A single pair of Martus tokens (one CPP tenant, one ANN tenant) is shared across every CPP user who deploys this MCP (Fr. Norman, Steph, Ann, etc.). Unlike cpp-espace-mcp (per-user keys; eSpace audits by key) and MS365 (per-user OAuth), Martus uses a shared-token model at CPP. New users receive the existing tokens from Fr. Norman directly — do not generate per-user tokens.

# .env (gitignored)
MARTUS_API_TOKEN=<shared token — from Fr. Norman>
MARTUS_TENANT_LABEL=martus-cpp

Register in a vault

Add two entries to your vault's .mcp.json:

{
  "mcpServers": {
    "martus-cpp": {
      "command": "node",
      "args": [
        "C:\\Users\\jmnorman\\code\\cpp-martus-mcp\\dist\\index.js"
      ],
      "env": {
        "MARTUS_API_TOKEN": "<CPP token>",
        "MARTUS_TENANT_LABEL": "martus-cpp"
      }
    },
    "martus-ann": {
      "command": "node",
      "args": [
        "C:\\Users\\jmnorman\\code\\cpp-martus-mcp\\dist\\index.js"
      ],
      "env": {
        "MARTUS_API_TOKEN": "<ANN token>",
        "MARTUS_TENANT_LABEL": "martus-ann"
      }
    }
  }
}

Restart Claude Code. You should now see martus-cpp and martus-ann both listed as Connected in claude mcp list.

Tools

All tools are list-* or get-*. Every endpoint uses paginated responses (1000 rows/page); call with Page: 1 first, then walk forward using the TotalPages field in the response.

Tool Martus endpoint Purpose
list-accounts GET /Accounts.List Paged chart of accounts
list-account-categories GET /Accounts.Categories Account category definitions
list-account-reporting-groups GET /Accounts.ReportingGroups Account reporting groups
list-budgets GET /Budgets.List All budgets (filterable by fiscal Year)
get-budget-data GET /Budgets.Data Actuals/planned numbers for a budget
list-budget-worksheets GET /Budgets.Worksheets Worksheets within a budget
list-budget-worksheet-lines GET /Budgets.WorksheetLines Per-line account + dimension values
list-budget-spws GET /Budgets.Spws Static Period Worksheets
list-budget-spw-lines GET /Budgets.SpwLines Per-period SPW line items
list-dimensions GET /Dimensions.List Dimensions defined in the tenant
list-dimension-items GET /Dimensions.Items Values within a single dimension

Architecture

src/index.ts           MCP tool registrations
src/provider.ts        Singleton — composes client + services
src/client/
  http-client.ts       Generic HTTP w/ Token header
  martus-client.ts     Auth-aware wrapper
src/services/
  accounts.service.ts
  budgets.service.ts
  dimensions.service.ts
src/models/            AUTO-GENERATED from swagger.json — regenerate with
                       `npm run generate:types`
src/scripts/
  generate-types.ts    Codegen reading ../swagger.json
swagger.json           Archived OpenAPI 3.1 spec (refreshed alongside the
                       RunBook KB pack's quarterly schema refresh)

Commands

npm install
npm run generate:types   # Regenerate src/models/ from swagger.json
npm run build            # tsc → dist/
npm run dev              # tsx src/index.ts (stdio loop; useful for smoke)
npm run typecheck        # tsc --noEmit

Refreshing the Martus spec

When Martus ships new endpoints (watch release notes in the Freshdesk help mirror at CPP RunBook/3-Resources/martus/raw/):

cd %USERPROFILE%\code\cpp-martus-mcp
curl -o swagger.json https://prod5.martus.app/swagger/v1/swagger.json
npm run generate:types
npm run build

Safety

Read-only by design. If this repo ever grows write tools:

  1. Stop before executing.
  2. Show the user exactly what will be created/modified (tenant, budget, account, amount).
  3. Wait for explicit confirmation.

A mistake in Martus can propagate back into finance committee pre-reads / the annual report. Treat writes with the same care as direct QuickBooks writes.

License

MIT — see LICENSE.

About

MCP server exposing the Martus budgeting/reporting API (prod5.martus.app) to Claude Code. Read-only wrapper. One codebase, two registrations (martus-cpp + martus-ann) via env var — same precedent as mp / mp-sandbox.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors