@crscreditapi/finstack-mcp-server 0.6.0-06bd3a8 → 0.7.0-3660044
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 +9 -0
- package/dist/api/customer.api.d.ts +43 -0
- package/dist/api/customer.api.js +19 -0
- package/dist/handlers/customer.handler.d.ts +6 -0
- package/dist/handlers/customer.handler.js +8 -1
- package/dist/handlers/index.js +2 -1
- package/dist/tools/customer.tools.js +23 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ MCP server exposing CRS B2B (Finstack) customer data over the [Model Context Pro
|
|
|
12
12
|
| `finstack_get_customer_invoices` | Paginated list of finalized invoices for one customer with full line-item breakdown. |
|
|
13
13
|
| `finstack_get_customer_pricing_config` | Full configured pricing state for one customer: products, pricing addendums, and environment-user productPricing. Use to cross-check invoiced rates against contracted pricing. |
|
|
14
14
|
| `finstack_get_pricing_addendum_text` | Extracted text from a pricing addendum PDF (with or without disclaimer). Server-side parses the PDF and returns plain text. |
|
|
15
|
+
| `finstack_get_customer_users` | Portal users and no-portal-access contacts for one customer, with names, emails, phones, roles (labelled as on the portal Users tab), owner flag, invitation status and lock/MFA state. The only tool that returns people for an account. |
|
|
15
16
|
| `finstack_get_customer_cohort` | Bulk, paginated list of all customers with portal status, account type, and sales opportunities (type + stage). For cohort/tracker refreshes instead of per-customer summary calls. Optional `status` / `stage` filters. |
|
|
16
17
|
| `finstack_get_leadiq_analytics` | System-wide LeadsIQ analytics for a trailing window: totals vs previous period, the complete list of customers that pulled (revenue desc), entitled/active/invited split, and enrolled-but-never-pulled accounts. |
|
|
17
18
|
| `finstack_get_leadiq_customer` | One customer's LeadsIQ detail: enrollment state, window/YTD/lifetime stats, match rate and append hit rates, most commonly used pull filters, recent pulls. |
|
|
@@ -139,6 +140,14 @@ Identify the customer with either `customer_code` or `user_id`. `page` defaults
|
|
|
139
140
|
|
|
140
141
|
Returns `{products, pricing_addendums, environment_users}`. `pricing_addendums[]` entries carry `id`, `name`, `status`, `quote_id`, timestamps, `has_pdf`, `has_pdf_without_disclaimer`, and `pdf_url` / `pdf_without_disclaimer_url`. The PDF URLs require the chatbase secret to hit directly — use `finstack_get_pricing_addendum_text` to read the contents instead.
|
|
141
142
|
|
|
143
|
+
### `finstack_get_customer_users`
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{ "customer_code": "CID18068" }
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Identify the customer with either `customer_code` or `user_id`. Returns `{customer, users_count, users: [...], contacts_count, contacts: [...]}`. Each `users[]` row is a portal login: `id`, `name`, `email`, `phone`, `title`, `roles` (customer-facing role names), `role_labels` (as shown on the portal Users tab — `Admin (owner)`, `Admin`, `Developer`, `Agent`, `Billing`, `Main billing contact`, `Generic`), `is_owner`, `status` (`active` / `invited` / `not_invited`), `locked`, `mfa_enabled`, `sign_in_count`, and sign-in / invitation timestamps; the owner comes first, then alphabetical. `contacts[]` are the "Contacts (No portal access)" rows: `id`, `name`, `email`, `phone`, `title`, `roles`. This payload carries PII by design and is backed by the MCP-only `customer_users` chatbase endpoint (mware-portal); credentials and tokens are never included.
|
|
150
|
+
|
|
142
151
|
### `finstack_get_customer_cohort`
|
|
143
152
|
|
|
144
153
|
```json
|
|
@@ -187,3 +187,46 @@ export declare function getCustomerCohort(client: ApiClient, args: {
|
|
|
187
187
|
status?: string;
|
|
188
188
|
stage?: string;
|
|
189
189
|
}): Promise<CustomerCohortPage>;
|
|
190
|
+
export interface CustomerPortalUser {
|
|
191
|
+
id: number;
|
|
192
|
+
name: string;
|
|
193
|
+
email: string;
|
|
194
|
+
phone: string | null;
|
|
195
|
+
title: string | null;
|
|
196
|
+
/** Customer-facing rolify role names, e.g. ["customer"], ["developer"]. Empty = generic user. */
|
|
197
|
+
roles: string[];
|
|
198
|
+
/** Same labels the portal Users tab shows: "Admin (owner)", "Admin", "Developer", "Agent", "Billing", "Main billing contact", "Generic". */
|
|
199
|
+
role_labels: string[];
|
|
200
|
+
is_owner: boolean;
|
|
201
|
+
status: 'active' | 'invited' | 'not_invited';
|
|
202
|
+
locked: boolean;
|
|
203
|
+
mfa_enabled: boolean;
|
|
204
|
+
sign_in_count: number;
|
|
205
|
+
last_sign_in_at: string | null;
|
|
206
|
+
invitation_sent_at: string | null;
|
|
207
|
+
invitation_accepted_at: string | null;
|
|
208
|
+
created_at: string | null;
|
|
209
|
+
}
|
|
210
|
+
export interface CustomerContact {
|
|
211
|
+
id: number;
|
|
212
|
+
name: string;
|
|
213
|
+
email: string | null;
|
|
214
|
+
phone: string | null;
|
|
215
|
+
title: string | null;
|
|
216
|
+
/** Portal CustomerRole names (free-form, e.g. "Billing"). */
|
|
217
|
+
roles: string[];
|
|
218
|
+
}
|
|
219
|
+
export interface CustomerUsers {
|
|
220
|
+
customer: {
|
|
221
|
+
name: string;
|
|
222
|
+
customer_code: string | null;
|
|
223
|
+
};
|
|
224
|
+
users_count: number;
|
|
225
|
+
users: CustomerPortalUser[];
|
|
226
|
+
contacts_count: number;
|
|
227
|
+
contacts: CustomerContact[];
|
|
228
|
+
}
|
|
229
|
+
export declare function getCustomerUsers(client: ApiClient, args: {
|
|
230
|
+
customerCode?: string;
|
|
231
|
+
userId?: string;
|
|
232
|
+
}): Promise<CustomerUsers>;
|
package/dist/api/customer.api.js
CHANGED
|
@@ -126,3 +126,22 @@ export async function getCustomerCohort(client, args) {
|
|
|
126
126
|
}
|
|
127
127
|
return response.data;
|
|
128
128
|
}
|
|
129
|
+
export async function getCustomerUsers(client, args) {
|
|
130
|
+
const secret = process.env.FINSTACK_CHATBASE_SECRET;
|
|
131
|
+
if (!secret) {
|
|
132
|
+
throw new Error('FINSTACK_CHATBASE_SECRET must be set');
|
|
133
|
+
}
|
|
134
|
+
if (!args.customerCode && !args.userId) {
|
|
135
|
+
throw new Error('Provide either customer_code or user_id');
|
|
136
|
+
}
|
|
137
|
+
const params = new URLSearchParams();
|
|
138
|
+
if (args.customerCode)
|
|
139
|
+
params.set('customer_code', args.customerCode);
|
|
140
|
+
if (args.userId)
|
|
141
|
+
params.set('user_id', args.userId);
|
|
142
|
+
const response = await client.get(`/api/v1/chatbase/customer_users?${params}`, { 'X-Chatbase-Secret': secret });
|
|
143
|
+
if (response.status !== 'success' || !response.data) {
|
|
144
|
+
throw new Error(response.message || 'Failed to fetch customer users');
|
|
145
|
+
}
|
|
146
|
+
return response.data;
|
|
147
|
+
}
|
|
@@ -45,4 +45,10 @@ interface GetPricingAddendumTextArgs {
|
|
|
45
45
|
environment?: string;
|
|
46
46
|
}
|
|
47
47
|
export declare function handleGetPricingAddendumText(args: GetPricingAddendumTextArgs): Promise<unknown>;
|
|
48
|
+
interface GetCustomerUsersArgs {
|
|
49
|
+
customer_code?: string;
|
|
50
|
+
user_id?: string;
|
|
51
|
+
environment?: string;
|
|
52
|
+
}
|
|
53
|
+
export declare function handleGetCustomerUsers(args: GetCustomerUsersArgs): Promise<unknown>;
|
|
48
54
|
export {};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Customer tool handlers
|
|
3
3
|
*/
|
|
4
4
|
import { getClient } from '../api/client.js';
|
|
5
|
-
import { getCustomerCohort, getCustomerInvoices, getCustomerPricingConfig, getCustomerRevenue, getCustomerSummary, getPricingAddendumText, searchCustomers, } from '../api/customer.api.js';
|
|
5
|
+
import { getCustomerCohort, getCustomerInvoices, getCustomerPricingConfig, getCustomerRevenue, getCustomerSummary, getCustomerUsers, getPricingAddendumText, 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);
|
|
@@ -49,3 +49,10 @@ export async function handleGetPricingAddendumText(args) {
|
|
|
49
49
|
variant: args.variant ?? 'with_disclaimer',
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
|
+
export async function handleGetCustomerUsers(args) {
|
|
53
|
+
const client = getClient(args.environment ?? 'prod');
|
|
54
|
+
return getCustomerUsers(client, {
|
|
55
|
+
customerCode: args.customer_code,
|
|
56
|
+
userId: args.user_id,
|
|
57
|
+
});
|
|
58
|
+
}
|
package/dist/handlers/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { handleGetCustomerCohort, handleGetCustomerInvoices, handleGetCustomerPricingConfig, handleGetCustomerRevenue, handleGetCustomerSummary, handleGetPricingAddendumText, handleSearchCustomer, } from './customer.handler.js';
|
|
1
|
+
import { handleGetCustomerCohort, handleGetCustomerInvoices, handleGetCustomerPricingConfig, handleGetCustomerRevenue, handleGetCustomerSummary, handleGetCustomerUsers, handleGetPricingAddendumText, handleSearchCustomer, } from './customer.handler.js';
|
|
2
2
|
import { handleGetLeadiqAnalytics, handleGetLeadiqCustomer, } from './leadiq.handler.js';
|
|
3
3
|
import { handleGetVendors } from './vendor.handler.js';
|
|
4
4
|
const wrap = (fn) => {
|
|
@@ -12,6 +12,7 @@ export const handlers = {
|
|
|
12
12
|
finstack_get_customer_pricing_config: wrap(handleGetCustomerPricingConfig),
|
|
13
13
|
finstack_get_pricing_addendum_text: wrap(handleGetPricingAddendumText),
|
|
14
14
|
finstack_get_customer_cohort: wrap(handleGetCustomerCohort),
|
|
15
|
+
finstack_get_customer_users: wrap(handleGetCustomerUsers),
|
|
15
16
|
finstack_get_leadiq_analytics: wrap(handleGetLeadiqAnalytics),
|
|
16
17
|
finstack_get_leadiq_customer: wrap(handleGetLeadiqCustomer),
|
|
17
18
|
finstack_get_vendors: wrap(handleGetVendors),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const customerTools = [
|
|
2
2
|
{
|
|
3
3
|
name: 'finstack_get_customer_summary',
|
|
4
|
-
description: "Fetch a non-PII summary of a B2B customer: portal account status (account.status: active/inactive/blacklisted/locked/obsolete/new_account) and account type (account/channel_partner), onboarding progress, products, sales opportunities (sales_opportunities[] with opportunity_type full/upsell/expand/land and stage closed_won/closed_lost/churned), API credentials, inspection status, and recent billing. Use this when asked for a customer's status or sales opp stage. Backed by the mware-portal chatbase customer_context endpoint.",
|
|
4
|
+
description: "Fetch a non-PII summary of a B2B customer: portal account status (account.status: active/inactive/blacklisted/locked/obsolete/new_account) and account type (account/channel_partner), onboarding progress, products, sales opportunities (sales_opportunities[] with opportunity_type full/upsell/expand/land and stage closed_won/closed_lost/churned), API credentials, inspection status, and recent billing. Use this when asked for a customer's status or sales opp stage. Does NOT include portal users, contacts or emails — for those call finstack_get_customer_users. Backed by the mware-portal chatbase customer_context endpoint.",
|
|
5
5
|
inputSchema: {
|
|
6
6
|
type: 'object',
|
|
7
7
|
properties: {
|
|
@@ -152,6 +152,28 @@ export const customerTools = [
|
|
|
152
152
|
required: ['customer_code', 'type', 'start_date', 'end_date'],
|
|
153
153
|
},
|
|
154
154
|
},
|
|
155
|
+
{
|
|
156
|
+
name: 'finstack_get_customer_users',
|
|
157
|
+
description: "The people attached to a B2B customer account — the ONLY tool that returns portal users and contacts with names and emails. Use whenever asked for a customer's users, contacts, contact list, emails, who has portal access, who the admin/owner is, or who to email at an account. Returns users[] (portal logins: id, name, email, phone, title, roles, role_labels matching the portal Users tab — \"Admin (owner)\", \"Admin\", \"Developer\", \"Agent\", \"Billing\", \"Main billing contact\", \"Generic\" — is_owner, status active/invited/not_invited, locked, mfa_enabled, sign-in and invitation timestamps; owner first) and contacts[] (the \"no portal access\" contacts: name, email, phone, title, roles). When asked for \"contacts\" or a \"contact list\", return the portal users AND the contacts[] rows. For a list spanning many customers, call this once per CID (use finstack_search_customer to resolve names first). Identify the customer with EITHER customer_code OR user_id.",
|
|
158
|
+
inputSchema: {
|
|
159
|
+
type: 'object',
|
|
160
|
+
properties: {
|
|
161
|
+
customer_code: {
|
|
162
|
+
type: 'string',
|
|
163
|
+
description: 'Customer code (CID) — e.g. "CID18068". Provide this OR user_id.',
|
|
164
|
+
},
|
|
165
|
+
user_id: {
|
|
166
|
+
type: 'string',
|
|
167
|
+
description: 'mware-portal user ID. Provide this OR customer_code.',
|
|
168
|
+
},
|
|
169
|
+
environment: {
|
|
170
|
+
type: 'string',
|
|
171
|
+
description: 'Target environment: "prod" (default) or "dev"',
|
|
172
|
+
enum: ['dev', 'prod'],
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
155
177
|
{
|
|
156
178
|
name: 'finstack_get_customer_cohort',
|
|
157
179
|
description: 'Bulk, paginated list of ALL B2B customers with their tracker fields: portal account status (active/inactive/blacklisted/locked/obsolete/new_account), account_type (account/channel_partner), and sales_opportunities[] (opportunity_code, opportunity_type full/upsell/expand/land, stage closed_won/closed_lost/churned, booking_value_mrr, close_date). Use this for cohort/tracker/dashboard refreshes or any question spanning many customers — NEVER call finstack_get_customer_summary once per customer for that. Default 500 customers per page (max 1000); read total_pages from the response and fetch the remaining pages. Optional server-side filters: status and stage.',
|