@sodiumhq/mcp-pm 0.1.0-beta.6357 → 0.1.0-beta.6359

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/dist/http.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as buildServer } from "./src-BVx86BgG.mjs";
2
+ import { n as buildServer, t as buildCatalogue } from "./src-B3pnHPhY.mjs";
3
3
  import { createRequire } from "node:module";
4
4
  import { SUPPORTED_PROTOCOL_VERSIONS, createMcpHandler } from "@modelcontextprotocol/server";
5
5
  import { createHash, randomUUID } from "node:crypto";
@@ -270,6 +270,47 @@ const mcpHandler = createMcpHandler((ctx) => {
270
270
  console.error(`[sodium-pm-mcp-http] handler: ${error.message}`);
271
271
  } });
272
272
  const serveMcp = toNodeHandler(mcpHandler);
273
+ let catalogueJson;
274
+ async function listRegisteredTools() {
275
+ const state = {
276
+ apiToken: "catalogue",
277
+ tenant: void 0,
278
+ writesAllowed: true,
279
+ wantsInstructions: false,
280
+ onToolCall: () => {}
281
+ };
282
+ const request = new Request("http://internal/", {
283
+ method: "POST",
284
+ headers: {
285
+ "Content-Type": "application/json",
286
+ Accept: "application/json, text/event-stream",
287
+ "MCP-Protocol-Version": "2025-06-18"
288
+ },
289
+ body: JSON.stringify({
290
+ jsonrpc: "2.0",
291
+ id: 1,
292
+ method: "tools/list",
293
+ params: {}
294
+ })
295
+ });
296
+ const text = await (await mcpHandler.fetch(request, { authInfo: {
297
+ token: "catalogue",
298
+ clientId: "catalogue",
299
+ scopes: [],
300
+ extra: { sodium: state }
301
+ } })).text();
302
+ const payload = text.trimStart().startsWith("{") ? text : text.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim()).pop() ?? "";
303
+ const tools = JSON.parse(payload).result?.tools;
304
+ if (!tools) throw new Error("tools/list returned no tools.");
305
+ return tools;
306
+ }
307
+ function getCatalogueJson() {
308
+ catalogueJson ??= listRegisteredTools().then((tools) => JSON.stringify(buildCatalogue(tools))).catch((error) => {
309
+ catalogueJson = void 0;
310
+ throw error;
311
+ });
312
+ return catalogueJson;
313
+ }
273
314
  function jsonRpcMethod(body) {
274
315
  if (typeof body === "object" && body !== null && "method" in body) return String(body.method);
275
316
  }
@@ -393,6 +434,14 @@ async function handle(req, res) {
393
434
  }));
394
435
  return;
395
436
  }
437
+ if (req.method === "GET" && url.pathname === "/tools.json") {
438
+ const body = await getCatalogueJson();
439
+ res.writeHead(200, {
440
+ "Content-Type": "application/json",
441
+ "Cache-Control": "public, max-age=300"
442
+ }).end(body);
443
+ return;
444
+ }
396
445
  if (AUTH && req.method === "GET" && url.pathname.startsWith("/.well-known/oauth-protected-resource")) {
397
446
  res.writeHead(200, { "Content-Type": "application/json" }).end(JSON.stringify(protectedResourceMetadata(AUTH)));
398
447
  return;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as buildServer } from "./src-BVx86BgG.mjs";
2
+ import { n as buildServer } from "./src-B3pnHPhY.mjs";
3
3
  import { createRequire } from "node:module";
4
4
  import { serveStdio } from "@modelcontextprotocol/server/stdio";
5
5
  import { parseArgs } from "node:util";
@@ -10284,4 +10284,278 @@ async function buildServer(config) {
10284
10284
  return server;
10285
10285
  }
10286
10286
  //#endregion
10287
- export { buildServer as t };
10287
+ //#region ../mcp-core/src/tool-groups.json
10288
+ var groups = [
10289
+ {
10290
+ "name": "Clients & contacts",
10291
+ "tools": [
10292
+ "list_clients",
10293
+ "get_client_summary",
10294
+ "list_client_notes",
10295
+ "list_client_links",
10296
+ "list_contacts",
10297
+ "get_contact",
10298
+ "get_custom_field_details",
10299
+ "list_portal_users",
10300
+ "create_client",
10301
+ "create_contact",
10302
+ "update_contact",
10303
+ "link_contact_to_client",
10304
+ "update_client_business_details",
10305
+ "update_client_dates",
10306
+ "update_client_custom_fields",
10307
+ "add_client_note"
10308
+ ]
10309
+ },
10310
+ {
10311
+ "name": "Tasks & workflows",
10312
+ "tools": [
10313
+ "list_tasks",
10314
+ "get_task_context",
10315
+ "get_task_history",
10316
+ "list_recurring_tasks",
10317
+ "list_workflows",
10318
+ "get_workflow",
10319
+ "list_workflow_occurrences",
10320
+ "list_pending_workflow_steps",
10321
+ "create_task",
10322
+ "update_task",
10323
+ "add_task_note",
10324
+ "update_workflow_step",
10325
+ "add_workflow_step_note"
10326
+ ]
10327
+ },
10328
+ {
10329
+ "name": "Services & pricing",
10330
+ "tools": [
10331
+ "list_services",
10332
+ "get_service_details",
10333
+ "list_service_packages",
10334
+ "list_service_categories",
10335
+ "add_client_service",
10336
+ "apply_service_package",
10337
+ "update_client_service_stage",
10338
+ "create_service_category",
10339
+ "update_service_category"
10340
+ ]
10341
+ },
10342
+ {
10343
+ "name": "Proposals & engagements",
10344
+ "tools": [
10345
+ "list_proposals",
10346
+ "get_proposal_summary",
10347
+ "list_engagement_letters",
10348
+ "get_engagement_letter_summary",
10349
+ "get_engagement_pipeline_summary",
10350
+ "create_proposal"
10351
+ ]
10352
+ },
10353
+ {
10354
+ "name": "Documents & forms",
10355
+ "tools": [
10356
+ "list_document_requests",
10357
+ "create_document_request",
10358
+ "list_client_documents",
10359
+ "list_data_forms",
10360
+ "list_data_form_requests",
10361
+ "send_data_form"
10362
+ ]
10363
+ },
10364
+ {
10365
+ "name": "Time & billing",
10366
+ "tools": [
10367
+ "list_time_entries",
10368
+ "log_time_entry",
10369
+ "list_unbilled_time_clients",
10370
+ "list_billing_line_items",
10371
+ "list_invoice_submissions",
10372
+ "get_service_time_report",
10373
+ "get_team_time_report"
10374
+ ]
10375
+ },
10376
+ {
10377
+ "name": "Email & Communication",
10378
+ "tools": [
10379
+ "list_client_emails",
10380
+ "get_client_email_thread",
10381
+ "list_sent_emails",
10382
+ "get_sent_email",
10383
+ "list_email_broadcasts",
10384
+ "get_email_broadcast"
10385
+ ]
10386
+ },
10387
+ {
10388
+ "name": "Deadlines & reports",
10389
+ "tools": [
10390
+ "get_practice_dashboard",
10391
+ "get_deadline_compliance_report",
10392
+ "get_service_delivery_report",
10393
+ "get_service_delivery_steps",
10394
+ "get_client_responsiveness_report",
10395
+ "get_team_workload_report",
10396
+ "get_wip_ageing_report",
10397
+ "get_workflow_step_analysis"
10398
+ ]
10399
+ },
10400
+ {
10401
+ "name": "Compliance & lookups",
10402
+ "tools": [
10403
+ "list_aml_clients",
10404
+ "get_aml_client",
10405
+ "get_client_agent_authorisations",
10406
+ "get_onboarding_status",
10407
+ "search_companies_house",
10408
+ "get_companies_house_profile"
10409
+ ]
10410
+ },
10411
+ {
10412
+ "name": "Practice & setup",
10413
+ "tools": [
10414
+ "get_practice_details",
10415
+ "whoami",
10416
+ "list_practices",
10417
+ "get_usage_examples",
10418
+ "list_users",
10419
+ "list_teams",
10420
+ "list_task_categories",
10421
+ "list_saved_task_filters",
10422
+ "list_saved_client_filters",
10423
+ "create_saved_task_filter"
10424
+ ]
10425
+ }
10426
+ ];
10427
+ var summaries = {
10428
+ "list_clients": "Find and filter your clients by name, status, type, manager, service and more.",
10429
+ "get_client_summary": "A full overview of one client: details, contacts, services, key dates and tasks.",
10430
+ "list_client_notes": "Read the notes on a client.",
10431
+ "list_client_links": "See the links saved against a client, such as a website or shared folder.",
10432
+ "list_contacts": "Search contacts across the practice, or list a client's contacts.",
10433
+ "get_contact": "Full details of one contact, including linked clients.",
10434
+ "get_custom_field_details": "The definition of a custom field, including its allowed options.",
10435
+ "list_portal_users": "See who has client portal access and when they last logged in.",
10436
+ "create_client": "Create a new client or prospect.",
10437
+ "create_contact": "Create a new contact.",
10438
+ "update_contact": "Update a contact's details.",
10439
+ "link_contact_to_client": "Link an existing contact to a client and set their role.",
10440
+ "update_client_business_details": "Update a client's trading name, address, VAT and tax details.",
10441
+ "update_client_dates": "Set a client's key dates, such as year end and VAT or filing due dates.",
10442
+ "update_client_custom_fields": "Update a client's custom field values.",
10443
+ "add_client_note": "Add a note to a client.",
10444
+ "list_tasks": "Find tasks by assignee, client, status, due date, category and more.",
10445
+ "get_task_context": "Everything about one task: details, notes, workflow progress and client.",
10446
+ "get_task_history": "The audit trail of a task: who changed what, and when.",
10447
+ "list_recurring_tasks": "See the recurring task templates that generate repeating work.",
10448
+ "list_workflows": "List your workflow templates.",
10449
+ "get_workflow": "The groups and steps of one workflow template.",
10450
+ "list_workflow_occurrences": "The completed runs of a workflow, slowest first.",
10451
+ "list_pending_workflow_steps": "Workflow steps ready to be worked on now, for you, your team or nobody.",
10452
+ "create_task": "Create a task for a client or the practice.",
10453
+ "update_task": "Update a task's status, dates, assignee or details.",
10454
+ "add_task_note": "Add a note to a task.",
10455
+ "update_workflow_step": "Complete, skip, block or reassign a workflow step.",
10456
+ "add_workflow_step_note": "Add a note to one step of a task's workflow.",
10457
+ "list_services": "List the services your practice offers.",
10458
+ "get_service_details": "A service's pricing, client types, stages and authorisations.",
10459
+ "list_service_packages": "List bundled service packages and what is in them.",
10460
+ "list_service_categories": "List your service categories.",
10461
+ "add_client_service": "Put a service on a client, with frequency, price and start date.",
10462
+ "apply_service_package": "Apply a service package to a client in one go.",
10463
+ "update_client_service_stage": "Move a client's service to another stage on the board.",
10464
+ "create_service_category": "Create a service category.",
10465
+ "update_service_category": "Rename, recolour or reorder a service category.",
10466
+ "list_proposals": "List proposals by status, client or date.",
10467
+ "get_proposal_summary": "Full detail of one proposal: services, pricing and acceptance.",
10468
+ "list_engagement_letters": "List letters of engagement by status, client or date.",
10469
+ "get_engagement_letter_summary": "Full detail of one letter of engagement.",
10470
+ "get_engagement_pipeline_summary": "Counts and values of proposals by stage: what is open and what is won.",
10471
+ "create_proposal": "Draft a proposal or letter of engagement for a client.",
10472
+ "list_document_requests": "See document requests sent to clients and what is still outstanding.",
10473
+ "create_document_request": "Draft a document request for a client.",
10474
+ "list_client_documents": "List documents held against clients and their review status.",
10475
+ "list_data_forms": "List your questionnaire templates.",
10476
+ "list_data_form_requests": "See which questionnaires were sent and who has responded.",
10477
+ "send_data_form": "Send a questionnaire to a client.",
10478
+ "list_time_entries": "List recorded time by person, client, task or date.",
10479
+ "log_time_entry": "Record time spent on a client or task.",
10480
+ "list_unbilled_time_clients": "Which clients have time recorded but not yet billed.",
10481
+ "list_billing_line_items": "List billing lines waiting for approval or invoicing.",
10482
+ "list_invoice_submissions": "See invoices sent to your accounting system, and any that failed.",
10483
+ "get_service_time_report": "Where your recorded time goes, by service or client.",
10484
+ "get_team_time_report": "Hours logged against expected hours for each team member.",
10485
+ "list_client_emails": "Email threads with a client from your synced mailbox.",
10486
+ "get_client_email_thread": "Read one email thread in full.",
10487
+ "list_sent_emails": "Emails Sodium sent for you, and whether they were delivered.",
10488
+ "get_sent_email": "Delivery detail for one sent email.",
10489
+ "list_email_broadcasts": "Your email campaigns and how far they have got.",
10490
+ "get_email_broadcast": "One campaign in full, with delivery results for each client.",
10491
+ "get_practice_dashboard": "A snapshot of what needs attention across the practice right now.",
10492
+ "get_deadline_compliance_report": "Which deadlines were met, missed or are at risk.",
10493
+ "get_service_delivery_report": "How quickly services are delivered, and how often on time.",
10494
+ "get_service_delivery_steps": "Where the time goes inside one service's workflow.",
10495
+ "get_client_responsiveness_report": "How quickly clients respond to requests, and how much chasing it takes.",
10496
+ "get_team_workload_report": "Who has too much on, and what each person completes.",
10497
+ "get_wip_ageing_report": "Open work that has been sitting untouched, and for how long.",
10498
+ "get_workflow_step_analysis": "Step-by-step timings for one workflow template.",
10499
+ "list_aml_clients": "AML and identity-check records with risk level and outstanding checks.",
10500
+ "get_aml_client": "One AML record: status, linked contacts, checks and risk assessments.",
10501
+ "get_client_agent_authorisations": "HMRC agent authorisation status for a client, by tax regime.",
10502
+ "get_onboarding_status": "Is a client ready for work: engagement, AML, authorisations and data.",
10503
+ "search_companies_house": "Search Companies House by company name.",
10504
+ "get_companies_house_profile": "The live Companies House record for a company.",
10505
+ "get_practice_details": "Your practice's details, counts, connections and settings.",
10506
+ "whoami": "Who you are signed in as, and which practice you are working in.",
10507
+ "list_practices": "The practices you belong to.",
10508
+ "get_usage_examples": "Example requests to try, grouped by topic.",
10509
+ "list_users": "Your team members and their roles.",
10510
+ "list_teams": "Your teams and who is in them.",
10511
+ "list_task_categories": "List your task categories.",
10512
+ "list_saved_task_filters": "Your saved task views.",
10513
+ "list_saved_client_filters": "Your saved client views.",
10514
+ "create_saved_task_filter": "Save a task search as a reusable view."
10515
+ };
10516
+ //#endregion
10517
+ //#region ../mcp-core/src/catalogue.ts
10518
+ const SUMMARY_MAX = 200;
10519
+ const OTHER_GROUP = "Other";
10520
+ const summaryOverrides = summaries;
10521
+ function summarise(tool) {
10522
+ const override = summaryOverrides[tool.name];
10523
+ if (override) return override;
10524
+ const text = (tool.description ?? "").replace(/\s+/g, " ").trim();
10525
+ const match = text.match(/^(.+?[.!?])(?=\s+[A-Z'"(]|$)/);
10526
+ const sentence = match ? match[1] : text;
10527
+ return sentence.length > SUMMARY_MAX ? `${sentence.slice(0, SUMMARY_MAX - 1).trimEnd()}…` : sentence;
10528
+ }
10529
+ function buildCatalogue(tools) {
10530
+ const byName = new Map(tools.map((t) => [t.name, t]));
10531
+ const placed = /* @__PURE__ */ new Set();
10532
+ const groups$1 = [];
10533
+ const toEntry = (tool) => ({
10534
+ name: tool.name,
10535
+ title: tool.title ?? tool.name,
10536
+ access: tool.annotations?.readOnlyHint === true ? "read" : "write",
10537
+ summary: summarise(tool)
10538
+ });
10539
+ for (const group of groups) {
10540
+ const entries = group.tools.map((name) => byName.get(name)).filter((t) => t !== void 0).map(toEntry);
10541
+ entries.forEach((e) => placed.add(e.name));
10542
+ if (entries.length > 0) groups$1.push({
10543
+ name: group.name,
10544
+ tools: entries
10545
+ });
10546
+ }
10547
+ const unmapped = tools.filter((t) => !placed.has(t.name)).map(toEntry);
10548
+ if (unmapped.length > 0) groups$1.push({
10549
+ name: OTHER_GROUP,
10550
+ tools: unmapped
10551
+ });
10552
+ const all = groups$1.flatMap((g) => g.tools);
10553
+ return {
10554
+ toolCount: all.length,
10555
+ readCount: all.filter((t) => t.access === "read").length,
10556
+ writeCount: all.filter((t) => t.access === "write").length,
10557
+ groups: groups$1
10558
+ };
10559
+ }
10560
+ //#endregion
10561
+ export { buildServer as n, buildCatalogue as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sodiumhq/mcp-pm",
3
- "version": "0.1.0-beta.6357",
3
+ "version": "0.1.0-beta.6359",
4
4
  "description": "Sodium Practice Management MCP server — lets AI assistants interact with your Sodium tenant",
5
5
  "type": "module",
6
6
  "bin": {