@sodiumhq/mcp-pm 0.1.0-beta.6257 → 0.1.0-beta.6299

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-CMzdZk6e.mjs";
2
+ import { t as buildServer } from "./src-b0BBk3Y8.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-CMzdZk6e.mjs";
2
+ import { t as buildServer } from "./src-b0BBk3Y8.mjs";
3
3
  import { createRequire } from "node:module";
4
4
  import { serveStdio } from "@modelcontextprotocol/server/stdio";
5
5
  import { parseArgs } from "node:util";
@@ -2070,11 +2070,26 @@ const getTaskItem = (options) => (options.client ?? client).get({
2070
2070
  ...options
2071
2071
  });
2072
2072
  /**
2073
- * Update TaskItem
2073
+ * Patch TaskItem
2074
2074
  *
2075
- * Updates a TaskItem for the specified tenant.
2075
+ * Updates part of one task, following JSON Merge Patch. Send only the fields to change: a field left
2076
+ * out is not touched, so a quick action (moving the task on the calendar, a status button, a checklist
2077
+ * tick) never overwrites details it did not send, such as the description. A field sent as null is
2078
+ * cleared: description, statutory due date, time estimate, checklist, assignee, team, category, service
2079
+ * and primary client. The name, status, dates and times are required on a task, so sending one of those
2080
+ * as null is a 400.
2081
+ *
2082
+ * **Clients**: the same rules as a full update. The primary client always stays on the task, and a task
2083
+ * with clients but no primary gets its first client as primary. To remove every client, send the client
2084
+ * list and the primary client as null. Changing the primary client clears the task's service and
2085
+ * pipeline links (they belong to a client) unless a service code is sent.
2086
+ *
2087
+ * The change is saved through the same update as a full update, so dated workflow steps follow the
2088
+ * task when its dates move (unless MoveWorkflowStepDates is false), and colleagues see the change live.
2089
+ *
2090
+ * Only a saved task can be patched. A projected (upcoming) task must be materialised first.
2076
2091
  */
2077
- const updateTaskItem = (options) => (options.client ?? client).put({
2092
+ const patchTaskItem = (options) => (options.client ?? client).patch({
2078
2093
  security: [{
2079
2094
  name: "x-api-key",
2080
2095
  type: "apiKey"
@@ -3756,9 +3771,9 @@ var SodiumApiClient = class {
3756
3771
  if (error !== void 0 || !data) throw this.toError(response, error, correlationId, "create task");
3757
3772
  return data;
3758
3773
  }
3759
- async updateTask(code, body) {
3774
+ async patchTask(code, body) {
3760
3775
  const correlationId = randomUUID();
3761
- const { data, error, response } = await updateTaskItem({
3776
+ const { data, error, response } = await patchTaskItem({
3762
3777
  path: {
3763
3778
  tenant: this.tenant,
3764
3779
  code
@@ -7299,26 +7314,23 @@ const UpdateTaskInputSchema = {
7299
7314
  };
7300
7315
  async function handleUpdateTask(api, args) {
7301
7316
  try {
7302
- const current = await api.getTask(args.taskCode);
7303
7317
  const has = (key) => args[key] !== void 0;
7304
- const body = {
7305
- name: has("name") ? args.name : current.name ?? "",
7306
- description: has("description") ? args.description : current.description ?? null,
7307
- status: has("status") ? args.status : current.status ?? "NotStarted",
7308
- startDate: has("startDate") ? args.startDate : current.startDate ?? "",
7309
- dueDate: has("dueDate") ? args.dueDate : current.dueDate ?? "",
7310
- statutoryDueDate: has("statutoryDueDate") ? args.statutoryDueDate : current.statutoryDueDate ?? null,
7311
- timeEstimateMinutes: has("timeEstimateMinutes") ? args.timeEstimateMinutes : current.timeEstimateMinutes ?? null,
7312
- clientCodes: (current.clients ?? []).map((c) => c.code).filter((c) => Boolean(c)),
7313
- primaryClientCode: current.primaryClient?.code ?? null,
7314
- assignedUserCode: has("assignedUserCode") ? args.assignedUserCode : current.assignedUser?.code ?? null,
7315
- assignedTeamCode: has("assignedTeamCode") ? args.assignedTeamCode : current.assignedTeam?.code ?? null,
7316
- clientBillableServiceCode: current.clientBillableService?.code ?? null,
7317
- categoryCode: has("categoryCode") ? args.categoryCode : current.category?.code ?? null,
7318
- checklist: current.checklist ?? null,
7319
- recurringTaskCode: current.recurringTask?.code ?? null
7320
- };
7321
- const updated = await api.updateTask(args.taskCode, body);
7318
+ const body = {};
7319
+ if (has("status")) body.status = args.status;
7320
+ if (has("name")) body.name = args.name;
7321
+ if (has("description")) body.description = args.description;
7322
+ if (has("startDate")) body.startDate = args.startDate;
7323
+ if (has("dueDate")) body.dueDate = args.dueDate;
7324
+ if (has("statutoryDueDate")) body.statutoryDueDate = args.statutoryDueDate;
7325
+ if (has("timeEstimateMinutes")) body.timeEstimateMinutes = args.timeEstimateMinutes;
7326
+ if (has("assignedUserCode")) body.assignedUserCode = args.assignedUserCode;
7327
+ if (has("assignedTeamCode")) body.assignedTeamCode = args.assignedTeamCode;
7328
+ if (has("categoryCode")) body.categoryCode = args.categoryCode;
7329
+ if (Object.keys(body).length === 0) return { content: [{
7330
+ type: "text",
7331
+ text: `Task ${args.taskCode} not changed: no fields were passed to update.`
7332
+ }] };
7333
+ const updated = await api.patchTask(args.taskCode, body);
7322
7334
  const changes = [];
7323
7335
  if (has("status")) changes.push(`status → ${updated.status}`);
7324
7336
  if (has("name")) changes.push(`name → ${updated.name}`);
@@ -7332,7 +7344,7 @@ async function handleUpdateTask(api, args) {
7332
7344
  if (has("timeEstimateMinutes")) changes.push(`estimate → ${updated.timeEstimateMinutes ?? "cleared"}m`);
7333
7345
  return { content: [{
7334
7346
  type: "text",
7335
- text: [`Task updated: ${updated.name} (${updated.code})`, changes.length ? `Changed: ${changes.join(", ")}` : "No field changes were requested."].join("\n")
7347
+ text: [`Task updated: ${updated.name} (${updated.code})`, `Changed: ${changes.join(", ")}`].join("\n")
7336
7348
  }] };
7337
7349
  } catch (error) {
7338
7350
  return {
@@ -10064,7 +10076,7 @@ async function buildServer(config) {
10064
10076
  }, (args) => handleCreateTask(api, args));
10065
10077
  registerWriteTool(server, api, config.context, "update_task", {
10066
10078
  title: "Update a task (status, dates, assignment, details)",
10067
- description: "Update fields on an existing task by code: status (mark Completed / InProgress / Blocked / Skipped), name, description, start/due/statutory dates, time estimate, assigned user or team, and category. Only the fields you pass change — the handler fetches the current task and preserves everything else (clients, checklist, service and recurring-task links), so it's safe to pass just { taskCode, status: 'Completed' } for 'mark it done'. Pass null to clear a clearable field (description, statutory date, estimate, assignee, team, category). Use for 'mark TSK-123 complete', 'push the deadline to Friday', 'reassign it to Jane' (resolve her code from the roster), 'unassign this task' (assignedUserCode=null). Completing a task does NOT complete its workflow steps — if get_task_context showed open steps, tell the user they remain open. Cannot delete tasks — deletion isn't available through the MCP.",
10079
+ description: "Update fields on an existing task by code: status (mark Completed / InProgress / Blocked / Skipped), name, description, start/due/statutory dates, time estimate, assigned user or team, and category. Only the fields you pass change — everything else on the task (clients, checklist, start and due times, service and recurring-task links) is left exactly as it is, so it's safe to pass just { taskCode, status: 'Completed' } for 'mark it done'. Pass null to clear a clearable field (description, statutory date, estimate, assignee, team, category). Use for 'mark TSK-123 complete', 'push the deadline to Friday', 'reassign it to Jane' (resolve her code from the roster), 'unassign this task' (assignedUserCode=null). Completing a task does NOT complete its workflow steps — if get_task_context showed open steps, tell the user they remain open. Cannot delete tasks — deletion isn't available through the MCP.",
10068
10080
  inputSchema: UpdateTaskInputSchema,
10069
10081
  annotations: {
10070
10082
  readOnlyHint: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sodiumhq/mcp-pm",
3
- "version": "0.1.0-beta.6257",
3
+ "version": "0.1.0-beta.6299",
4
4
  "description": "Sodium Practice Management MCP server — lets AI assistants interact with your Sodium tenant",
5
5
  "type": "module",
6
6
  "bin": {