@crscreditapi/finstack-mcp-server 0.1.0-a04547f → 0.1.0-eff2af8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # finstack-mcp-server
2
+
3
+ MCP server exposing CRS B2B (Finstack) customer data over the [Model Context Protocol](https://modelcontextprotocol.io). Backed by the mware-portal chatbase API.
4
+
5
+ ## Tools
6
+
7
+ | Name | Purpose |
8
+ |---|---|
9
+ | `finstack_search_customer` | Fuzzy-match a customer by name and return candidate CIDs. Use when only a name is known. |
10
+ | `finstack_get_customer_summary` | Non-PII summary for one customer: onboarding progress, products, API credentials, inspection status, recent billing. |
11
+ | `finstack_get_customer_revenue` | Revenue for one customer over a date range, bucketed by `day` / `week` / `month` / `quarter` / `year`. |
12
+ | `finstack_get_customer_invoices` | Paginated list of finalized invoices for one customer with full line-item breakdown. |
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npm install -g @crscreditapi/finstack-mcp-server
18
+ ```
19
+
20
+ After install, the `finstack-mcp-server` binary is on your `PATH`.
21
+
22
+ ## Configuration
23
+
24
+ The server reads configuration from environment variables (also loaded from a local `.env` file via `dotenv`).
25
+
26
+ | Variable | Required | Default | Description |
27
+ |---|---|---|---|
28
+ | `FINSTACK_PROD_BASE_URL` | yes | — | Base URL of the mware-portal API (production). |
29
+ | `FINSTACK_CHATBASE_SECRET` | yes | — | Shared secret sent as `X-Chatbase-Secret` on every request. |
30
+ | `FINSTACK_DEV_BASE_URL` | no | falls back to prod | Base URL for the dev environment. Used when a tool call passes `environment: "dev"`. |
31
+ | `FINSTACK_TIMEOUT_MS` | no | `30000` | Per-request timeout in milliseconds. |
32
+ | `MCP_TRANSPORT` | no | `stdio` | Transport mode: `stdio` (for Claude Code and other CLI clients) or `http` (for n8n and remote clients). |
33
+ | `MCP_PORT` | no | `3002` | Port for the HTTP transport. Ignored when `MCP_TRANSPORT=stdio`. |
34
+
35
+ A starter file is included as `.env.example`.
36
+
37
+ ## Usage
38
+
39
+ ### Stdio (Claude Code, Claude Desktop, other CLI clients)
40
+
41
+ Add an entry to your MCP config (`.mcp.json` for Claude Code, `claude_desktop_config.json` for Claude Desktop):
42
+
43
+ ```json
44
+ {
45
+ "mcpServers": {
46
+ "finstack": {
47
+ "command": "finstack-mcp-server",
48
+ "env": {
49
+ "MCP_TRANSPORT": "stdio",
50
+ "FINSTACK_PROD_BASE_URL": "https://portal.example.com",
51
+ "FINSTACK_CHATBASE_SECRET": "<your-secret>"
52
+ }
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ The client launches the process; tool calls travel over stdio. No port to open.
59
+
60
+ ### HTTP (n8n, remote MCP clients)
61
+
62
+ Run it as a long-lived service:
63
+
64
+ ```bash
65
+ MCP_TRANSPORT=http \
66
+ MCP_PORT=3002 \
67
+ FINSTACK_PROD_BASE_URL=https://portal.example.com \
68
+ FINSTACK_CHATBASE_SECRET=<your-secret> \
69
+ finstack-mcp-server
70
+ ```
71
+
72
+ Endpoints:
73
+
74
+ - `POST /` and `POST /mcp` — MCP requests (streamable HTTP transport)
75
+ - `GET /health` — health check, returns `{"status":"ok","uptime":<seconds>}`
76
+
77
+ Point your client at `http://<host>:3002/mcp`.
78
+
79
+ ### Docker
80
+
81
+ ```dockerfile
82
+ FROM node:20-alpine
83
+ RUN npm install -g @crscreditapi/finstack-mcp-server
84
+ ENV MCP_TRANSPORT=http MCP_PORT=3002
85
+ EXPOSE 3002
86
+ CMD ["finstack-mcp-server"]
87
+ ```
88
+
89
+ Pass the `FINSTACK_*` variables at runtime (e.g. via `--env-file` or an orchestrator's secret store).
90
+
91
+ ## Tool call reference
92
+
93
+ ### `finstack_search_customer`
94
+
95
+ ```json
96
+ { "q": "rentvine", "threshold": 0.8 }
97
+ ```
98
+
99
+ `q` (required, min 2 chars) is matched case-insensitively. `threshold` (optional, 0.0–1.0, default `0.9`) controls fuzziness — lower it if nothing comes back.
100
+
101
+ ### `finstack_get_customer_summary`
102
+
103
+ ```json
104
+ { "customer_code": "CID18068" }
105
+ ```
106
+
107
+ ### `finstack_get_customer_revenue`
108
+
109
+ ```json
110
+ {
111
+ "customer_code": "CID18068",
112
+ "type": "month",
113
+ "start_date": "2025-05-08",
114
+ "end_date": "2026-05-08"
115
+ }
116
+ ```
117
+
118
+ Range caps depend on `type`: `day` ≤ 92 days, `week` ≤ 2 years, `month`/`quarter` ≤ 10 years, `year` ≤ 50 years.
119
+
120
+ ### `finstack_get_customer_invoices`
121
+
122
+ ```json
123
+ { "customer_code": "CID18068", "page": 1, "per_page": 50 }
124
+ ```
125
+
126
+ Identify the customer with either `customer_code` or `user_id`. `page` defaults to `1`, `per_page` defaults to `50` and is silently capped at `100`. Returns `{customer_code, page, per_page, total_count, total_pages, invoices: [...]}`, where each invoice carries its full `items[]` line-item breakdown.
127
+
128
+ All tools accept an optional `environment: "prod" | "dev"` (default `"prod"`).
129
+
130
+ ## Development
131
+
132
+ ```bash
133
+ git clone https://github.com/StitchCredit/finstack-mcp-server.git
134
+ cd finstack-mcp-server
135
+ npm install
136
+ cp .env.example .env # fill in FINSTACK_PROD_BASE_URL and FINSTACK_CHATBASE_SECRET
137
+ npm run build
138
+ npm start
139
+ ```
140
+
141
+ For stdio mode while iterating, set `MCP_TRANSPORT=stdio` in `.env` and wire the local `dist/index.js` into your MCP client's config.
142
+
143
+ ## License
144
+
145
+ Private — internal use within CRS Credit API.
@@ -74,3 +74,46 @@ export declare function getCustomerRevenue(client: ApiClient, args: {
74
74
  startDate: string;
75
75
  endDate: string;
76
76
  }): Promise<CustomerRevenue>;
77
+ export interface InvoicePermissiblePurpose {
78
+ id: number;
79
+ name: string;
80
+ code: string;
81
+ onboarding_path: string;
82
+ }
83
+ export interface InvoiceItem {
84
+ description: string;
85
+ item_code: string;
86
+ netsuite_id: string | null;
87
+ quantity: number;
88
+ rate_in_cents: number | null;
89
+ amount_in_cents: number;
90
+ amount: number;
91
+ }
92
+ export interface CustomerInvoice {
93
+ invoice_number: string;
94
+ accounting_period: string;
95
+ date: string;
96
+ due_date: string | null;
97
+ amount: number;
98
+ status: string;
99
+ netsuite_status: string | null;
100
+ memo: string | null;
101
+ created_at: string;
102
+ updated_at: string;
103
+ permissible_purpose: InvoicePermissiblePurpose | null;
104
+ items: InvoiceItem[];
105
+ }
106
+ export interface CustomerInvoicesPage {
107
+ customer_code: string;
108
+ page: number;
109
+ per_page: number;
110
+ total_count: number;
111
+ total_pages: number;
112
+ invoices: CustomerInvoice[];
113
+ }
114
+ export declare function getCustomerInvoices(client: ApiClient, args: {
115
+ customerCode?: string;
116
+ userId?: string;
117
+ page?: number;
118
+ perPage?: number;
119
+ }): Promise<CustomerInvoicesPage>;
@@ -46,3 +46,26 @@ export async function getCustomerRevenue(client, args) {
46
46
  }
47
47
  return response.data;
48
48
  }
49
+ export async function getCustomerInvoices(client, args) {
50
+ const secret = process.env.FINSTACK_CHATBASE_SECRET;
51
+ if (!secret) {
52
+ throw new Error('FINSTACK_CHATBASE_SECRET must be set');
53
+ }
54
+ if (!args.customerCode && !args.userId) {
55
+ throw new Error('Provide either customer_code or user_id');
56
+ }
57
+ const params = new URLSearchParams();
58
+ if (args.customerCode)
59
+ params.set('customer_code', args.customerCode);
60
+ if (args.userId)
61
+ params.set('user_id', args.userId);
62
+ if (args.page !== undefined)
63
+ params.set('page', String(args.page));
64
+ if (args.perPage !== undefined)
65
+ params.set('per_page', String(args.perPage));
66
+ const response = await client.get(`/api/v1/chatbase/customer_invoices?${params}`, { 'X-Chatbase-Secret': secret });
67
+ if (response.status !== 'success' || !response.data) {
68
+ throw new Error(response.message || 'Failed to fetch customer invoices');
69
+ }
70
+ return response.data;
71
+ }
@@ -18,4 +18,12 @@ interface GetCustomerRevenueArgs {
18
18
  environment?: string;
19
19
  }
20
20
  export declare function handleGetCustomerRevenue(args: GetCustomerRevenueArgs): Promise<unknown>;
21
+ interface GetCustomerInvoicesArgs {
22
+ customer_code?: string;
23
+ user_id?: string;
24
+ page?: number;
25
+ per_page?: number;
26
+ environment?: string;
27
+ }
28
+ export declare function handleGetCustomerInvoices(args: GetCustomerInvoicesArgs): Promise<unknown>;
21
29
  export {};
@@ -2,7 +2,7 @@
2
2
  * Customer tool handlers
3
3
  */
4
4
  import { getClient } from '../api/client.js';
5
- import { getCustomerRevenue, getCustomerSummary, searchCustomers, } from '../api/customer.api.js';
5
+ import { getCustomerInvoices, getCustomerRevenue, getCustomerSummary, searchCustomers, } from '../api/customer.api.js';
6
6
  export async function handleGetCustomerSummary(args) {
7
7
  const client = getClient(args.environment ?? 'prod');
8
8
  return getCustomerSummary(client, args.customer_code);
@@ -20,3 +20,12 @@ export async function handleGetCustomerRevenue(args) {
20
20
  endDate: args.end_date,
21
21
  });
22
22
  }
23
+ export async function handleGetCustomerInvoices(args) {
24
+ const client = getClient(args.environment ?? 'prod');
25
+ return getCustomerInvoices(client, {
26
+ customerCode: args.customer_code,
27
+ userId: args.user_id,
28
+ page: args.page,
29
+ perPage: args.per_page,
30
+ });
31
+ }
@@ -1,4 +1,4 @@
1
- import { handleGetCustomerRevenue, handleGetCustomerSummary, handleSearchCustomer, } from './customer.handler.js';
1
+ import { handleGetCustomerInvoices, handleGetCustomerRevenue, handleGetCustomerSummary, handleSearchCustomer, } from './customer.handler.js';
2
2
  const wrap = (fn) => {
3
3
  return async (args) => fn(args);
4
4
  };
@@ -6,4 +6,5 @@ export const handlers = {
6
6
  finstack_get_customer_summary: wrap(handleGetCustomerSummary),
7
7
  finstack_search_customer: wrap(handleSearchCustomer),
8
8
  finstack_get_customer_revenue: wrap(handleGetCustomerRevenue),
9
+ finstack_get_customer_invoices: wrap(handleGetCustomerInvoices),
9
10
  };
@@ -44,6 +44,39 @@ export const customerTools = [
44
44
  required: ['q'],
45
45
  },
46
46
  },
47
+ {
48
+ name: 'finstack_get_customer_invoices',
49
+ description: "Paginated list of finalized invoices for a B2B customer with full line-item breakdown. Use when the user asks for billing history, invoice details, what they were charged for, or itemized lines (vs. finstack_get_customer_summary which only shows the 3 most recent invoices without line items). Each invoice includes status, permissible_purpose, and items with quantity / rate_in_cents / amount. Identify the customer with EITHER customer_code OR user_id.",
50
+ inputSchema: {
51
+ type: 'object',
52
+ properties: {
53
+ customer_code: {
54
+ type: 'string',
55
+ description: 'Customer code (CID) — e.g. "CID18068". Provide this OR user_id.',
56
+ },
57
+ user_id: {
58
+ type: 'string',
59
+ description: 'mware-portal user ID. Provide this OR customer_code.',
60
+ },
61
+ page: {
62
+ type: 'number',
63
+ description: 'Page number, 1-indexed. Default 1. Invalid values fall back to 1 server-side.',
64
+ minimum: 1,
65
+ },
66
+ per_page: {
67
+ type: 'number',
68
+ description: 'Page size. Default 50, silently capped at 100. Invalid values fall back to 50 server-side.',
69
+ minimum: 1,
70
+ maximum: 100,
71
+ },
72
+ environment: {
73
+ type: 'string',
74
+ description: 'Target environment: "prod" (default) or "dev"',
75
+ enum: ['dev', 'prod'],
76
+ },
77
+ },
78
+ },
79
+ },
47
80
  {
48
81
  name: 'finstack_get_customer_revenue',
49
82
  description: 'Fetch revenue for a B2B customer over a date range, bucketed by period (day/week/month/quarter/year). Use when the user asks how much a customer paid, revenue trends, MRR/ARR-style figures, or billing over time. Returns periods[{period,revenue}] plus a total. Max range: day=92d, week=2y, month/quarter=10y, year=50y; pick the coarsest type that covers the range.',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crscreditapi/finstack-mcp-server",
3
- "version": "0.1.0-a04547f",
3
+ "version": "0.1.0-eff2af8",
4
4
  "description": "MCP server for Finstack",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",