@sodiumhq/mcp-pm 0.1.0-beta.2592 → 0.1.0-beta.2593

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.
Files changed (2) hide show
  1. package/dist/index.js +110 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -622,6 +622,20 @@ const client = createClient(createConfig());
622
622
  //#endregion
623
623
  //#region ../mcp-core/src/generated/sdk.gen.ts
624
624
  /**
625
+ * Get BusinessDetails for a client
626
+ */
627
+ const getBusinessDetailsForClient = (options) => (options.client ?? client).get({
628
+ security: [{
629
+ name: "x-api-key",
630
+ type: "apiKey"
631
+ }, {
632
+ scheme: "bearer",
633
+ type: "http"
634
+ }],
635
+ url: "/tenants/{tenant}/clients/{client}/businessdetails",
636
+ ...options
637
+ });
638
+ /**
625
639
  * List Contacts for Client
626
640
  *
627
641
  * Lists all Contacts for the specified client.
@@ -638,6 +652,22 @@ const listClientContactsForClient = (options) => (options.client ?? client).get(
638
652
  ...options
639
653
  });
640
654
  /**
655
+ * Get Client Dates
656
+ *
657
+ * Returns all key dates for the specified client
658
+ */
659
+ const getClientDates = (options) => (options.client ?? client).get({
660
+ security: [{
661
+ name: "x-api-key",
662
+ type: "apiKey"
663
+ }, {
664
+ scheme: "bearer",
665
+ type: "http"
666
+ }],
667
+ url: "/tenants/{tenant}/clients/{code}/dates",
668
+ ...options
669
+ });
670
+ /**
641
671
  * List Client Services for Client
642
672
  *
643
673
  * Lists all Client Services for the specified client.
@@ -961,6 +991,30 @@ var SodiumApiClient = class {
961
991
  if (error !== void 0 || !data) throw this.toError(response, error, correlationId, `list services for client ${clientCode}`);
962
992
  return data;
963
993
  }
994
+ async getClientBusinessDetails(clientCode) {
995
+ const correlationId = randomUUID();
996
+ const { data, error, response } = await getBusinessDetailsForClient({
997
+ path: {
998
+ tenant: this.ctx.tenant,
999
+ client: clientCode
1000
+ },
1001
+ headers: { "X-Correlation-Id": correlationId }
1002
+ });
1003
+ if (error !== void 0 || !data) throw this.toError(response, error, correlationId, `get business details for client ${clientCode}`);
1004
+ return data;
1005
+ }
1006
+ async getClientDates(clientCode) {
1007
+ const correlationId = randomUUID();
1008
+ const { data, error, response } = await getClientDates({
1009
+ path: {
1010
+ tenant: this.ctx.tenant,
1011
+ code: clientCode
1012
+ },
1013
+ headers: { "X-Correlation-Id": correlationId }
1014
+ });
1015
+ if (error !== void 0 || !data) throw this.toError(response, error, correlationId, `get client dates for ${clientCode}`);
1016
+ return data;
1017
+ }
964
1018
  async listUsers(query = {}) {
965
1019
  const correlationId = randomUUID();
966
1020
  const { data, error, response } = await listTenantUsers({
@@ -1228,10 +1282,12 @@ function describeFilters$2(args) {
1228
1282
  //#region ../mcp-core/src/tools/get-client-summary.ts
1229
1283
  const GetClientSummaryInputSchema = { code: z.string().min(1, "Client code is required").describe("The client code (identifier). Usually discovered via list_clients first.") };
1230
1284
  async function handleGetClientSummary(api, { code }) {
1231
- const [clientResult, contactsResult, servicesResult, overdueResult, upcomingResult] = await Promise.allSettled([
1285
+ const [clientResult, contactsResult, servicesResult, businessResult, datesResult, overdueResult, upcomingResult] = await Promise.allSettled([
1232
1286
  api.getClient(code),
1233
1287
  api.listClientContacts(code),
1234
1288
  api.listClientServices(code),
1289
+ api.getClientBusinessDetails(code),
1290
+ api.getClientDates(code),
1235
1291
  api.listTasks({
1236
1292
  client: [code],
1237
1293
  isOverdue: true,
@@ -1260,11 +1316,15 @@ async function handleGetClientSummary(api, { code }) {
1260
1316
  client: clientResult.value,
1261
1317
  contacts: extract(contactsResult),
1262
1318
  services: extract(servicesResult),
1319
+ businessDetails: businessResult.status === "fulfilled" ? businessResult.value : null,
1320
+ clientDates: datesResult.status === "fulfilled" ? datesResult.value : [],
1263
1321
  overdueTasks: extract(overdueResult),
1264
1322
  upcomingTasks: extract(upcomingResult),
1265
1323
  gaps: [
1266
1324
  contactsResult.status === "rejected" ? "contacts" : null,
1267
1325
  servicesResult.status === "rejected" ? "services" : null,
1326
+ businessResult.status === "rejected" ? "business details" : null,
1327
+ datesResult.status === "rejected" ? "key dates" : null,
1268
1328
  overdueResult.status === "rejected" ? "overdue tasks" : null,
1269
1329
  upcomingResult.status === "rejected" ? "upcoming tasks" : null
1270
1330
  ].filter((v) => v !== null)
@@ -1276,7 +1336,7 @@ function extract(result) {
1276
1336
  return result.value.data ?? [];
1277
1337
  }
1278
1338
  function format$1(input) {
1279
- const { client, contacts, services, overdueTasks, upcomingTasks, gaps } = input;
1339
+ const { client, contacts, services, businessDetails, clientDates, overdueTasks, upcomingTasks, gaps } = input;
1280
1340
  const lines = [];
1281
1341
  const name = client.name ?? "(no name)";
1282
1342
  const code = client.code ?? "(no code)";
@@ -1289,6 +1349,15 @@ function format$1(input) {
1289
1349
  if (client.manager) lines.push(`Manager: ${client.manager.name} (${client.manager.code})`);
1290
1350
  if (client.partner) lines.push(`Partner: ${client.partner.name} (${client.partner.code})`);
1291
1351
  if (client.associate) lines.push(`Associate: ${client.associate.name} (${client.associate.code})`);
1352
+ const businessLines = formatBusinessDetails(businessDetails);
1353
+ if (businessLines.length > 0) lines.push("", "--- Business Details ---", ...businessLines);
1354
+ if (clientDates.length > 0) {
1355
+ lines.push("", `--- Key Dates (${clientDates.length}) ---`);
1356
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1357
+ const upcoming = clientDates.filter((d) => (d.date ?? "") >= today).sort((a, b) => (a.date ?? "").localeCompare(b.date ?? ""));
1358
+ const past = clientDates.filter((d) => (d.date ?? "") < today).sort((a, b) => (b.date ?? "").localeCompare(a.date ?? ""));
1359
+ for (const d of [...upcoming, ...past]) lines.push(formatClientDate(d));
1360
+ }
1292
1361
  lines.push("", `--- Contacts (${contacts.length}) ---`);
1293
1362
  if (contacts.length === 0) lines.push("No contacts.");
1294
1363
  else for (const c of contacts) {
@@ -1327,6 +1396,44 @@ function formatTask$1(t) {
1327
1396
  const taskCode = t.code ?? "(no code)";
1328
1397
  return `${t.name ?? "(unnamed)"} (${taskCode})${t.dueDate ? ` — due ${t.dueDate}` : ""}${t.assignedUser?.name ? ` — ${t.assignedUser.name}` : ""}${t.workflow ? ` — workflow ${t.workflowStepsComplete ?? 0}/${t.workflowSteps ?? 0}` : ""}`;
1329
1398
  }
1399
+ function formatBusinessDetails(bd) {
1400
+ if (!bd) return [];
1401
+ const out = [];
1402
+ const company = bd.company;
1403
+ if (company?.number) {
1404
+ const status = company.status ? ` · ${company.status}` : "";
1405
+ out.push(`Company number: ${company.number}${status}`);
1406
+ }
1407
+ if (company?.incorporationDate) out.push(`Incorporated: ${company.incorporationDate}`);
1408
+ if (bd.tradingAs) out.push(`Trading as: ${bd.tradingAs}`);
1409
+ if (company?.registeredAddress) out.push(`Registered address: ${company.registeredAddress}`);
1410
+ if (bd.postalAddress && bd.postalAddress !== company?.registeredAddress) out.push(`Postal address: ${bd.postalAddress}`);
1411
+ if (bd.natureOfBusiness) out.push(`Nature of business: ${bd.natureOfBusiness}`);
1412
+ if (bd.vat) if (bd.vat.registered) out.push(`VAT: registered${bd.vat.number ? ` (${bd.vat.number})` : ""}`);
1413
+ else out.push("VAT: not registered");
1414
+ if (bd.utr) out.push(`UTR: ${bd.utr}`);
1415
+ if (bd.payeRef) out.push(`PAYE ref: ${bd.payeRef}`);
1416
+ if (bd.accountsOfficeRef) out.push(`Accounts office ref: ${bd.accountsOfficeRef}`);
1417
+ return out;
1418
+ }
1419
+ function formatClientDate(d) {
1420
+ return `- ${humanizeDateType(d.dateType ?? "")}: ${d.date ?? "(no date)"}${d.description ? ` — ${d.description}` : ""}`;
1421
+ }
1422
+ function humanizeDateType(type) {
1423
+ if (!type) return "(unknown)";
1424
+ const words = type.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").split(" ");
1425
+ const acronyms = new Set([
1426
+ "VAT",
1427
+ "PAYE",
1428
+ "CIS",
1429
+ "UTR"
1430
+ ]);
1431
+ return words.map((w, i) => {
1432
+ const upper = w.toUpperCase();
1433
+ if (acronyms.has(upper)) return upper;
1434
+ return i === 0 ? w.charAt(0).toUpperCase() + w.slice(1).toLowerCase() : w.toLowerCase();
1435
+ }).join(" ");
1436
+ }
1330
1437
  //#endregion
1331
1438
  //#region ../mcp-core/src/tools/get-task-context.ts
1332
1439
  const GetTaskContextInputSchema = { code: z.string().min(1, "Task code is required").describe("The task code (identifier). Usually discovered via list_tasks first, or supplied directly by the user when they quote a task code.") };
@@ -1716,7 +1823,7 @@ async function buildServer(config) {
1716
1823
  }, (args) => handleListClients(api, args));
1717
1824
  server.registerTool("get_client_summary", {
1718
1825
  title: "Get a full summary of one client",
1719
- description: "Get a consolidated overview of a single client by code: identity (name, status, type, assignments), all contacts, active services with pricing, overdue task count + top 5, and tasks due in the next 7 days. Use this AFTER list_clients identifies the client of interest, or when the user references a specific client by code. Tolerates partial failures — if one section can't be loaded, the rest is still returned with a note about what's missing.",
1826
+ description: "Get a consolidated overview of a single client by code: identity (name, status, type, assignments), business details (company number, incorporation date, trading name, registered address, VAT status, UTR, PAYE ref), key statutory dates (year-end, accounts due, VAT return due, confirmation statement due, etc. — upcoming first, then past), all contacts, active services with pricing, overdue task count + top 5, and tasks due in the next 7 days. Use this AFTER list_clients identifies the client of interest, or when the user references a specific client by code. Also answers 'when is ACME's year-end?' / 'is ACME VAT registered?' in a single call. Tolerates partial failures — if one section can't be loaded, the rest is still returned with a note about what's missing.",
1720
1827
  inputSchema: GetClientSummaryInputSchema,
1721
1828
  annotations: {
1722
1829
  readOnlyHint: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sodiumhq/mcp-pm",
3
- "version": "0.1.0-beta.2592",
3
+ "version": "0.1.0-beta.2593",
4
4
  "description": "Sodium Practice Management MCP server — lets AI assistants interact with your Sodium tenant",
5
5
  "type": "module",
6
6
  "bin": {