@sodiumhq/mcp-pm 0.1.0-beta.6299 → 0.1.0-beta.6353
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 +1 -1
- package/dist/index.js +1 -1
- package/dist/{src-b0BBk3Y8.mjs → src-C2ETa30c.mjs} +40 -17
- package/package.json +1 -1
package/dist/http.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as buildServer } from "./src-
|
|
2
|
+
import { t as buildServer } from "./src-C2ETa30c.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";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as buildServer } from "./src-
|
|
2
|
+
import { t as buildServer } from "./src-C2ETa30c.mjs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
@@ -2491,6 +2491,16 @@ var SodiumApiClient = class {
|
|
|
2491
2491
|
if (error !== void 0 || !data) throw this.toError(response, error, correlationId, "get current user");
|
|
2492
2492
|
return data;
|
|
2493
2493
|
}
|
|
2494
|
+
async getCurrentTenantUserCode() {
|
|
2495
|
+
const me = await this.getCurrentUser();
|
|
2496
|
+
if (!me.code || !me.email) throw new Error("Could not work out who you are signed in as.");
|
|
2497
|
+
const tenantUser = (await this.listUsers({
|
|
2498
|
+
search: me.email,
|
|
2499
|
+
limit: 50
|
|
2500
|
+
})).data?.find((u) => u.userCode === me.code);
|
|
2501
|
+
if (!tenantUser?.code) throw new Error(`You are not a user of practice '${this.tenant}'.`);
|
|
2502
|
+
return tenantUser.code;
|
|
2503
|
+
}
|
|
2494
2504
|
async listClients(query = {}) {
|
|
2495
2505
|
const correlationId = randomUUID();
|
|
2496
2506
|
const { data, error, response } = await listClients({
|
|
@@ -3809,8 +3819,9 @@ var SodiumApiClient = class {
|
|
|
3809
3819
|
//#region ../mcp-core/src/context/instructions.ts
|
|
3810
3820
|
const ROSTER_CAP = 20;
|
|
3811
3821
|
async function buildInstructions(api, ctx, selection) {
|
|
3812
|
-
const [user, tenant, practice, team] = await Promise.allSettled([
|
|
3822
|
+
const [user, userCode, tenant, practice, team] = await Promise.allSettled([
|
|
3813
3823
|
api.getCurrentUser(),
|
|
3824
|
+
api.getCurrentTenantUserCode(),
|
|
3814
3825
|
api.getTenantDetails(),
|
|
3815
3826
|
api.getPracticeDetails(),
|
|
3816
3827
|
api.listUsers({
|
|
@@ -3845,13 +3856,13 @@ async function buildInstructions(api, ctx, selection) {
|
|
|
3845
3856
|
`Current UTC time: ${now.toISOString()}`
|
|
3846
3857
|
];
|
|
3847
3858
|
if (user.status === "fulfilled") {
|
|
3848
|
-
const name = user.value.fullName ?? user.value.email ??
|
|
3849
|
-
lines.push(`Current user: ${name}${
|
|
3859
|
+
const name = user.value.fullName ?? user.value.email ?? "unknown";
|
|
3860
|
+
lines.push(`Current user: ${name}${userCode.status === "fulfilled" ? ` (${userCode.value})` : ""}`);
|
|
3850
3861
|
}
|
|
3851
3862
|
if (tenant.status === "fulfilled") lines.push(`Tenant: ${tenant.value.name} (${tenant.value.code})`);
|
|
3852
3863
|
if (practice.status === "fulfilled") lines.push(`Practice: ${practice.value.name}`);
|
|
3853
3864
|
if (selection.kind === "choice") {
|
|
3854
|
-
lines.push("", "PRACTICE SELECTION — this user belongs to more than one practice.", "Data tools take an optional 'practice' argument (a practice code). Ask the user which practice to work in, then pass its code on EVERY tool call for the rest of the conversation. Calls without it are refused with a reminder.");
|
|
3865
|
+
lines.push("", "PRACTICE SELECTION — this user belongs to more than one practice.", "Data tools take an optional 'practice' argument (a practice code). Ask the user which practice to work in, then pass its code on EVERY tool call for the rest of the conversation. Calls without it are refused with a reminder.", "The user's own code differs in each practice. For 'my tasks' / 'my time' style filters, call whoami with the chosen practice and use the user code it shows.");
|
|
3855
3866
|
if (selection.practices.length > 0) {
|
|
3856
3867
|
lines.push("Practices available:");
|
|
3857
3868
|
for (const p of selection.practices) lines.push(`- ${p.name} (${p.code})`);
|
|
@@ -5122,11 +5133,11 @@ const AddTaskNoteInputSchema = {
|
|
|
5122
5133
|
};
|
|
5123
5134
|
async function handleAddTaskNote(api, args) {
|
|
5124
5135
|
try {
|
|
5125
|
-
const
|
|
5136
|
+
const authorCode = await api.getCurrentTenantUserCode();
|
|
5126
5137
|
const note = await api.createTaskNote(args.taskCode, {
|
|
5127
5138
|
text: args.text,
|
|
5128
5139
|
date: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5129
|
-
noteFromUserCode:
|
|
5140
|
+
noteFromUserCode: authorCode,
|
|
5130
5141
|
pinnedLevel: args.pinnedLevel ?? 0
|
|
5131
5142
|
});
|
|
5132
5143
|
return { content: [{
|
|
@@ -6015,8 +6026,9 @@ async function handleUpdateContact(api, args) {
|
|
|
6015
6026
|
//#region ../mcp-core/src/tools/whoami.ts
|
|
6016
6027
|
async function handleWhoami(api) {
|
|
6017
6028
|
try {
|
|
6018
|
-
const [userResult, tenantResult, practiceResult] = await Promise.allSettled([
|
|
6029
|
+
const [userResult, userCodeResult, tenantResult, practiceResult] = await Promise.allSettled([
|
|
6019
6030
|
api.getCurrentUser(),
|
|
6031
|
+
api.getCurrentTenantUserCode(),
|
|
6020
6032
|
api.getTenantDetails(),
|
|
6021
6033
|
api.getPracticeDetails()
|
|
6022
6034
|
]);
|
|
@@ -6026,7 +6038,8 @@ async function handleWhoami(api) {
|
|
|
6026
6038
|
lines.push("--- User ---");
|
|
6027
6039
|
lines.push(`Name: ${u.fullName ?? "(not set)"}`);
|
|
6028
6040
|
lines.push(`Email: ${u.email ?? "(not set)"}`);
|
|
6029
|
-
lines.push(`
|
|
6041
|
+
if (userCodeResult.status === "fulfilled") lines.push(`Practice user code: ${userCodeResult.value}`);
|
|
6042
|
+
lines.push(`Platform user code (not accepted by practice filters): ${u.code ?? "(not set)"}`);
|
|
6030
6043
|
lines.push(`Email verified: ${u.isEmailVerified ? "yes" : "no"}`);
|
|
6031
6044
|
lines.push(`Auth provider: ${u.authProvider ?? "(not set)"}`);
|
|
6032
6045
|
if (u.isSuperAdmin) lines.push("Super admin: yes");
|
|
@@ -9376,15 +9389,25 @@ function captureClientInfo(server, api) {
|
|
|
9376
9389
|
version: info.version
|
|
9377
9390
|
});
|
|
9378
9391
|
}
|
|
9392
|
+
function withAnnotationTitle(config) {
|
|
9393
|
+
if (!config.title) return config;
|
|
9394
|
+
return {
|
|
9395
|
+
...config,
|
|
9396
|
+
annotations: {
|
|
9397
|
+
title: config.title,
|
|
9398
|
+
...config.annotations
|
|
9399
|
+
}
|
|
9400
|
+
};
|
|
9401
|
+
}
|
|
9379
9402
|
function registerTenantTool(server, api, name, config, cb) {
|
|
9380
9403
|
const inputSchema = {
|
|
9381
9404
|
...config.inputSchema ?? {},
|
|
9382
9405
|
practice: practiceField
|
|
9383
9406
|
};
|
|
9384
|
-
server.registerTool(name, {
|
|
9407
|
+
server.registerTool(name, withAnnotationTitle({
|
|
9385
9408
|
...config,
|
|
9386
9409
|
inputSchema
|
|
9387
|
-
}, async (args, extra) => {
|
|
9410
|
+
}), async (args, extra) => {
|
|
9388
9411
|
const started = Date.now();
|
|
9389
9412
|
captureClientInfo(server, api);
|
|
9390
9413
|
const { practice, ...rest } = args;
|
|
@@ -9464,21 +9487,21 @@ async function buildServer(config) {
|
|
|
9464
9487
|
openWorldHint: true
|
|
9465
9488
|
}
|
|
9466
9489
|
}, () => handleGetPracticeDetails(api));
|
|
9467
|
-
server.registerTool("whoami", {
|
|
9490
|
+
server.registerTool("whoami", withAnnotationTitle({
|
|
9468
9491
|
title: "Show current user and tenant info",
|
|
9469
|
-
description: "Returns the identity of the authenticated user (name, email, code), the tenant/practice the call runs against (name, code, status), and the practice name. Use this when the user asks 'who am I?', 'which tenant am I connected to?', 'what account is this?', or when debugging authentication/permission issues. The optional 'practice' argument picks the practice section shown; without it the user's lone practice is used, and the identity section still answers when no practice can be resolved.",
|
|
9492
|
+
description: "Returns the identity of the authenticated user (name, email, and their user code in the practice, which is the code to use for 'my tasks' / 'my time' filters and 'assign to me'), the tenant/practice the call runs against (name, code, status), and the practice name. Use this when the user asks 'who am I?', 'which tenant am I connected to?', 'what account is this?', or when debugging authentication/permission issues. The optional 'practice' argument picks the practice section shown; without it the user's lone practice is used, and the identity section still answers when no practice can be resolved.",
|
|
9470
9493
|
inputSchema: { practice: practiceField },
|
|
9471
9494
|
annotations: {
|
|
9472
9495
|
readOnlyHint: true,
|
|
9473
9496
|
idempotentHint: true,
|
|
9474
9497
|
openWorldHint: true
|
|
9475
9498
|
}
|
|
9476
|
-
}, async (args) => {
|
|
9499
|
+
}), async (args) => {
|
|
9477
9500
|
captureClientInfo(server, api);
|
|
9478
9501
|
await resolveTenantForCall(api, args.practice);
|
|
9479
9502
|
return handleWhoami(api);
|
|
9480
9503
|
});
|
|
9481
|
-
server.registerTool("list_practices", {
|
|
9504
|
+
server.registerTool("list_practices", withAnnotationTitle({
|
|
9482
9505
|
title: "List the practices you can work in",
|
|
9483
9506
|
description: "List the practices (tenants) the signed-in user belongs to. Use this when the user belongs to more than one practice and you need the codes to pass in other tools' 'practice' argument, or when the user asks 'which practices can I access?'. Practices flagged 'unavailable' cannot be worked in.",
|
|
9484
9507
|
inputSchema: {},
|
|
@@ -9487,7 +9510,7 @@ async function buildServer(config) {
|
|
|
9487
9510
|
idempotentHint: true,
|
|
9488
9511
|
openWorldHint: true
|
|
9489
9512
|
}
|
|
9490
|
-
}, () => {
|
|
9513
|
+
}), () => {
|
|
9491
9514
|
captureClientInfo(server, api);
|
|
9492
9515
|
return handleListPractices(api);
|
|
9493
9516
|
});
|
|
@@ -9694,7 +9717,7 @@ async function buildServer(config) {
|
|
|
9694
9717
|
inputSchema: ListWorkflowOccurrencesInputSchema,
|
|
9695
9718
|
annotations: reportAnnotations
|
|
9696
9719
|
}, (args) => handleListWorkflowOccurrences(api, args));
|
|
9697
|
-
server.registerTool("get_usage_examples", {
|
|
9720
|
+
server.registerTool("get_usage_examples", withAnnotationTitle({
|
|
9698
9721
|
title: "Show example prompts for this integration",
|
|
9699
9722
|
description: "Returns a curated cookbook of example prompts this Sodium integration answers well, grouped by topic (daily-workflow, clients, tasks-and-deadlines, client-requests, time-tracking, reports, companies-house, proposals-and-engagements, making-changes). Use when the user asks 'what can you do?', 'what can I ask about my Sodium data?', 'give me some examples', or seems unsure how to get value from the integration. Also useful onboarding a new user: show the overview, then drill into the topic nearest their role. Static content — makes no API call.",
|
|
9700
9723
|
inputSchema: GetUsageExamplesInputSchema,
|
|
@@ -9703,7 +9726,7 @@ async function buildServer(config) {
|
|
|
9703
9726
|
idempotentHint: true,
|
|
9704
9727
|
openWorldHint: false
|
|
9705
9728
|
}
|
|
9706
|
-
}, (args) => handleGetUsageExamples(args));
|
|
9729
|
+
}), (args) => handleGetUsageExamples(args));
|
|
9707
9730
|
const lookupAnnotations = {
|
|
9708
9731
|
readOnlyHint: true,
|
|
9709
9732
|
idempotentHint: true,
|