capsulemcp 1.6.2 → 1.6.3

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 CHANGED
@@ -48,7 +48,7 @@ For most individual users the install is a single JSON snippet pasted into Claud
48
48
 
49
49
  3. Restart Claude Desktop. The Capsule tools appear in the tool picker.
50
50
 
51
- That's it. The first launch fetches the package from npm (a few seconds); subsequent launches are instant from the npx cache. To pin a specific version, use `"capsulemcp@1.6.2"` in `args`. If you're tracking a fork or an unreleased branch, use the GitHub-ref form instead: `"github:soil-dev/capsulemcp#v1.6.2"` — same arguments, just installs from a git clone rather than the npm registry. See [INSTALL.md](INSTALL.md) for the Claude Code path, manual install, and troubleshooting.
51
+ That's it. The first launch fetches the package from npm (a few seconds); subsequent launches are instant from the npx cache. To pin a specific version, use `"capsulemcp@1.6.3"` in `args`. If you're tracking a fork or an unreleased branch, use the GitHub-ref form instead: `"github:soil-dev/capsulemcp#v1.6.3"` — same arguments, just installs from a git clone rather than the npm registry. See [INSTALL.md](INSTALL.md) for the Claude Code path, manual install, and troubleshooting.
52
52
 
53
53
  ## Tools
54
54
 
package/dist/http.js CHANGED
@@ -1760,16 +1760,20 @@ var updatePartySchema = z7.object({
1760
1760
  title: z7.string().optional(),
1761
1761
  jobTitle: z7.string().optional(),
1762
1762
  name: z7.string().optional(),
1763
+ organisationId: positiveId.nullable().optional().describe(
1764
+ "For PERSON parties: link to an organisation by id, or `null` to unlink (the person becomes an orphan / standalone record). Discover org IDs via search_parties / filter_parties with type=organisation. For ORGANISATION parties: silently ignored by Capsule's API \u2014 organisations don't have a parent organisation in the data model. Empirically verified in v1.6.3 wire-trace; no client-side type guard since the no-op is harmless."
1765
+ ),
1763
1766
  fields: z7.array(CustomFieldWriteSchema).optional().describe(fieldsArrayDescriptor("get_party")),
1764
1767
  ...PartyWriteBaseSchema
1765
1768
  });
1766
1769
  async function updateParty(input) {
1767
- const { id, ownerId, fields, ...rest } = input;
1770
+ const { id, ownerId, organisationId, fields, ...rest } = input;
1768
1771
  const body = {};
1769
1772
  for (const [k, v] of Object.entries(rest)) {
1770
1773
  if (v !== void 0) body[k] = v;
1771
1774
  }
1772
1775
  setRef(body, "owner", ownerId);
1776
+ setNullableRef(body, "organisation", organisationId);
1773
1777
  const mappedFields = mapFieldsForBody(fields);
1774
1778
  if (mappedFields !== void 0) body["fields"] = mappedFields;
1775
1779
  return capsulePut(`/parties/${id}`, { party: body });
@@ -2038,6 +2042,9 @@ async function createOpportunity(input) {
2038
2042
  var updateOpportunitySchema = z8.object({
2039
2043
  id: positiveId,
2040
2044
  name: z8.string().min(1).optional(),
2045
+ partyId: positiveId.optional().describe(
2046
+ "Reassign the opportunity to a different primary party. Capsule requires every opportunity to have a party \u2014 passing `null` is rejected with 422 'party is required' (use Capsule's web UI if you need to dissolve the link entirely). Discover ids via search_parties / filter_parties. No defensive read-modify-write needed: this connector verified empirically (v1.6.3 wire-trace) that `party` is a standalone PUT field on /opportunities and does not interact with the asymmetric owner/team semantic from NOTES-ON-CAPSULE-API.md \xA727."
2047
+ ),
2041
2048
  milestoneId: positiveId.optional().describe(
2042
2049
  "Move the opportunity to this milestone. Side effects depend on the target: closing milestones (Won/Lost) auto-set `closedOn` to today and `probability` to the milestone default (100/0), preserving `lastOpenMilestone` as the previous open stage; moving back to an open milestone clears `closedOn` and re-applies the milestone's default probability (Won/Lost is reversible \u2014 no separate reopen tool). WARNING: Capsule does NOT validate that the new milestone belongs to the opportunity's current pipeline. Passing a milestoneId from a different pipeline silently relocates the opportunity across pipelines, and `lastOpenMilestone` may then reference a milestone in the previous pipeline. Verify against the opportunity's current pipeline (read the opp first, list its pipeline's milestones via list_milestones) before passing a cross-pipeline id. NOTE: changing `milestoneId` can fire **pipeline / milestone-reached automations** that mutate `owner` / `team` on the destination milestone (same shape as `create_opportunity` \u2014 see its `milestoneId` description for the owner-clearing automation caveat). If a milestone-change-and-owner-set in the same call lands with `owner: null`, follow up with a second `update_opportunity` (or `batch_update_opportunity`) carrying both `ownerId` and `teamId` \u2014 milestone-reached triggers only fire on the transition, so a subsequent PUT preserves your values."
2043
2050
  ),
@@ -2059,11 +2066,12 @@ var updateOpportunitySchema = z8.object({
2059
2066
  fields: z8.array(CustomFieldWriteSchema).optional().describe(fieldsArrayDescriptor("get_opportunity"))
2060
2067
  });
2061
2068
  async function updateOpportunity(input) {
2062
- const { id, milestoneId, ownerId, teamId, lostReasonId, fields, ...rest } = input;
2069
+ const { id, partyId, milestoneId, ownerId, teamId, lostReasonId, fields, ...rest } = input;
2063
2070
  const body = {};
2064
2071
  for (const [k, v] of Object.entries(rest)) {
2065
2072
  if (v !== void 0) body[k] = v;
2066
2073
  }
2074
+ setRef(body, "party", partyId);
2067
2075
  setRef(body, "milestone", milestoneId);
2068
2076
  let resolvedTeamId = teamId;
2069
2077
  if (ownerId !== void 0 && teamId === void 0) {
@@ -2172,6 +2180,9 @@ var updateProjectSchema = z9.object({
2172
2180
  name: z9.string().min(1).optional(),
2173
2181
  description: z9.string().optional(),
2174
2182
  status: z9.enum(["OPEN", "CLOSED"]).optional(),
2183
+ partyId: positiveId.optional().describe(
2184
+ "Reassign the project to a different primary party. Capsule requires every project to have a party \u2014 passing `null` is rejected with 422 'party is required' (verified empirically in v1.6.3 wire-trace). Discover ids via search_parties / filter_parties."
2185
+ ),
2175
2186
  ownerId: positiveId.nullable().optional().describe(
2176
2187
  "Reassign owner: pass a user ID to set, or `null` to unassign (matches the 'Unassign' option in Capsule's web UI). When you supply `ownerId` and omit `teamId` and/or `stageId`, the connector fetches the project's current omitted fields and includes them in the PUT body \u2014 this preserves them across the owner change (without it, Capsule's PUT would clear team; stage carry is defensive against the symmetric clear). Supply `teamId` and/or `stageId` explicitly on the same call to change them instead. `teamId: null` clears the team as part of an owner change. Constraints (Capsule enforces, 422 on violation): owner must be a member of the team if both are set; a project must always have at least one of {owner, team} set (cannot clear both)."
2177
2188
  ),
@@ -2187,11 +2198,12 @@ var updateProjectSchema = z9.object({
2187
2198
  )
2188
2199
  });
2189
2200
  async function updateProject(input) {
2190
- const { id, ownerId, teamId, stageId, fields, ...rest } = input;
2201
+ const { id, partyId, ownerId, teamId, stageId, fields, ...rest } = input;
2191
2202
  const body = {};
2192
2203
  for (const [k, v] of Object.entries(rest)) {
2193
2204
  if (v !== void 0) body[k] = v;
2194
2205
  }
2206
+ setRef(body, "party", partyId);
2195
2207
  let resolvedTeamId = teamId;
2196
2208
  let resolvedStageId = stageId;
2197
2209
  if (ownerId !== void 0 && (teamId === void 0 || stageId === void 0)) {
@@ -2303,15 +2315,33 @@ var updateTaskSchema = z10.object({
2303
2315
  ),
2304
2316
  ownerId: positiveId.optional().describe(
2305
2317
  "Reassign owner to user ID. Once set, this connector cannot clear an owner back to null \u2014 use Capsule's web UI for that."
2318
+ ),
2319
+ partyId: positiveId.nullable().optional().describe(
2320
+ "Re-link the task to a party by id, or `null` to orphan it. Mutually exclusive with `opportunityId` / `projectId` \u2014 Capsule enforces 'task can be related to at most one entity' server-side (422 if two parent-refs are set at once, verified in v1.6.3 wire-trace). To swap parent type atomically, pass the old one as `null` and the new one as an id in the same call."
2321
+ ),
2322
+ opportunityId: positiveId.nullable().optional().describe(
2323
+ "Re-link the task to an opportunity by id, or `null` to orphan it. Mutually exclusive with `partyId` / `projectId` \u2014 see `partyId` for the XOR semantic."
2324
+ ),
2325
+ projectId: positiveId.nullable().optional().describe(
2326
+ "Re-link the task to a project (kase) by id, or `null` to orphan it. Mutually exclusive with `partyId` / `opportunityId` \u2014 see `partyId` for the XOR semantic."
2306
2327
  )
2307
2328
  });
2308
2329
  async function updateTask(input) {
2309
- const { id, ownerId, ...rest } = input;
2330
+ const { id, ownerId, partyId, opportunityId, projectId, ...rest } = input;
2331
+ const setCount = [partyId, opportunityId, projectId].filter((v) => typeof v === "number").length;
2332
+ if (setCount > 1) {
2333
+ throw new Error(
2334
+ "update_task: provide at most one of partyId, opportunityId, or projectId (Capsule rejects multi-parent tasks with 422 'task can be related to at most one entity')"
2335
+ );
2336
+ }
2310
2337
  const body = {};
2311
2338
  for (const [k, v] of Object.entries(rest)) {
2312
2339
  if (v !== void 0) body[k] = v;
2313
2340
  }
2314
2341
  setRef(body, "owner", ownerId);
2342
+ setNullableRef(body, "party", partyId);
2343
+ setNullableRef(body, "opportunity", opportunityId);
2344
+ setNullableRef(body, "kase", projectId);
2315
2345
  return capsulePut(`/tasks/${id}`, { task: body });
2316
2346
  }
2317
2347
  var completeTaskSchema = z10.object({
@@ -3080,7 +3110,7 @@ function createCapsuleMcpServer(opts) {
3080
3110
  const server = new McpServer(
3081
3111
  {
3082
3112
  name: "capsulemcp",
3083
- version: "1.6.2",
3113
+ version: "1.6.3",
3084
3114
  description: "Read and (optionally) modify Capsule CRM data \u2014 parties, opportunities, projects, tasks, timeline entries, pipelines, tags.",
3085
3115
  websiteUrl: "https://github.com/soil-dev/capsulemcp",
3086
3116
  icons: ICONS
@@ -3183,7 +3213,7 @@ function createCapsuleMcpServer(opts) {
3183
3213
  registerTool(
3184
3214
  server,
3185
3215
  "update_party",
3186
- "Update top-level fields on an existing party (about, firstName/lastName/name/title/jobTitle, ownerId). Only the fields you provide are changed. Child arrays (emailAddresses / phoneNumbers / addresses / websites) on this tool are APPEND-ONLY: items are merged into the existing list, not replaced. For surgical changes \u2014 replacing one email, removing one phone number, fixing the type on one address \u2014 use the dedicated atomic tools: add_party_email_address / remove_party_email_address_by_id (and the phone/address/website equivalents).",
3216
+ "Update top-level fields on an existing party (about, firstName/lastName/name/title/jobTitle, ownerId, organisationId). For PERSON parties, organisationId links to an organisation or null unlinks the person from its organisation; for ORGANISATION parties Capsule silently ignores organisationId. Only the fields you provide are changed. Child arrays (emailAddresses / phoneNumbers / addresses / websites) on this tool are APPEND-ONLY: items are merged into the existing list, not replaced. For surgical changes \u2014 replacing one email, removing one phone number, fixing the type on one address \u2014 use the dedicated atomic tools: add_party_email_address / remove_party_email_address_by_id (and the phone/address/website equivalents).",
3187
3217
  updatePartySchema,
3188
3218
  updateParty
3189
3219
  );
@@ -3318,7 +3348,7 @@ function createCapsuleMcpServer(opts) {
3318
3348
  registerTool(
3319
3349
  server,
3320
3350
  "update_opportunity",
3321
- "Update fields on an existing opportunity. Only the fields you provide are changed. Closed (Won/Lost) opportunities ARE editable \u2014 Capsule does not enforce closed-record immutability, so `value`, `description`, etc. can be changed on a Won opp without warning. If the workflow needs historical revenue numbers to be stable, enforce that caller-side.",
3351
+ "Update fields on an existing opportunity, including the parent-reference field `partyId` to reassign the opp to a different primary party. Only the fields you provide are changed. Closed (Won/Lost) opportunities ARE editable \u2014 Capsule does not enforce closed-record immutability, so `value`, `description`, etc. can be changed on a Won opp without warning. If the workflow needs historical revenue numbers to be stable, enforce that caller-side. Capsule requires every opportunity to have a party \u2014 passing `partyId: null` is rejected with 422 'party is required'.",
3322
3352
  updateOpportunitySchema,
3323
3353
  updateOpportunity
3324
3354
  );
@@ -3383,7 +3413,7 @@ function createCapsuleMcpServer(opts) {
3383
3413
  registerTool(
3384
3414
  server,
3385
3415
  "update_project",
3386
- "Update fields on an existing project. Only the fields you provide are changed. Use status='CLOSED' to close a project. CLOSED projects remain fully editable \u2014 Capsule does not enforce closed-record immutability. Stage moves and description edits on a CLOSED project are accepted without warning.",
3416
+ "Update fields on an existing project, including the parent-reference field `partyId` to reassign the project to a different primary party. Only the fields you provide are changed. Use status='CLOSED' to close a project. CLOSED projects remain fully editable \u2014 Capsule does not enforce closed-record immutability. Stage moves and description edits on a CLOSED project are accepted without warning. Capsule requires every project to have a party \u2014 passing `partyId: null` is rejected with 422 'party is required'.",
3387
3417
  updateProjectSchema,
3388
3418
  updateProject
3389
3419
  );
@@ -3462,7 +3492,7 @@ function createCapsuleMcpServer(opts) {
3462
3492
  registerTool(
3463
3493
  server,
3464
3494
  "update_task",
3465
- "Update fields on an existing task: `description`, `dueOn`, `dueTime`, `detail`, `status` (OPEN or COMPLETED), and `ownerId`. Only the fields you provide are changed. To mark a task done, prefer the dedicated `complete_task` tool \u2014 it's idempotent (a no-op success on an already-completed task) and semantically clearer than `update_task status=COMPLETED`. Capsule rejects directly setting status=PENDING (which exists only internally for track-driven tasks); use OPEN or COMPLETED. Completed tasks remain fully editable \u2014 Capsule does not enforce closed-record immutability.",
3495
+ "Update fields on an existing task: `description`, `dueOn`, `dueTime`, `detail`, `status` (OPEN or COMPLETED), `ownerId`, and the parent-reference fields `partyId`, `opportunityId`, `projectId`. Pass a parent id to re-link the task, or null on a parent field to orphan/unlink it; at most one parent id may be set in a single call, though null+id swaps are allowed. Only the fields you provide are changed. To mark a task done, prefer the dedicated `complete_task` tool \u2014 it's idempotent (a no-op success on an already-completed task) and semantically clearer than `update_task status=COMPLETED`. Capsule rejects directly setting status=PENDING (which exists only internally for track-driven tasks); use OPEN or COMPLETED. Completed tasks remain fully editable \u2014 Capsule does not enforce closed-record immutability.",
3466
3496
  updateTaskSchema,
3467
3497
  updateTask
3468
3498
  );
package/dist/index.js CHANGED
@@ -1257,16 +1257,20 @@ var updatePartySchema = z6.object({
1257
1257
  title: z6.string().optional(),
1258
1258
  jobTitle: z6.string().optional(),
1259
1259
  name: z6.string().optional(),
1260
+ organisationId: positiveId.nullable().optional().describe(
1261
+ "For PERSON parties: link to an organisation by id, or `null` to unlink (the person becomes an orphan / standalone record). Discover org IDs via search_parties / filter_parties with type=organisation. For ORGANISATION parties: silently ignored by Capsule's API \u2014 organisations don't have a parent organisation in the data model. Empirically verified in v1.6.3 wire-trace; no client-side type guard since the no-op is harmless."
1262
+ ),
1260
1263
  fields: z6.array(CustomFieldWriteSchema).optional().describe(fieldsArrayDescriptor("get_party")),
1261
1264
  ...PartyWriteBaseSchema
1262
1265
  });
1263
1266
  async function updateParty(input) {
1264
- const { id, ownerId, fields, ...rest } = input;
1267
+ const { id, ownerId, organisationId, fields, ...rest } = input;
1265
1268
  const body = {};
1266
1269
  for (const [k, v] of Object.entries(rest)) {
1267
1270
  if (v !== void 0) body[k] = v;
1268
1271
  }
1269
1272
  setRef(body, "owner", ownerId);
1273
+ setNullableRef(body, "organisation", organisationId);
1270
1274
  const mappedFields = mapFieldsForBody(fields);
1271
1275
  if (mappedFields !== void 0) body["fields"] = mappedFields;
1272
1276
  return capsulePut(`/parties/${id}`, { party: body });
@@ -1535,6 +1539,9 @@ async function createOpportunity(input) {
1535
1539
  var updateOpportunitySchema = z7.object({
1536
1540
  id: positiveId,
1537
1541
  name: z7.string().min(1).optional(),
1542
+ partyId: positiveId.optional().describe(
1543
+ "Reassign the opportunity to a different primary party. Capsule requires every opportunity to have a party \u2014 passing `null` is rejected with 422 'party is required' (use Capsule's web UI if you need to dissolve the link entirely). Discover ids via search_parties / filter_parties. No defensive read-modify-write needed: this connector verified empirically (v1.6.3 wire-trace) that `party` is a standalone PUT field on /opportunities and does not interact with the asymmetric owner/team semantic from NOTES-ON-CAPSULE-API.md \xA727."
1544
+ ),
1538
1545
  milestoneId: positiveId.optional().describe(
1539
1546
  "Move the opportunity to this milestone. Side effects depend on the target: closing milestones (Won/Lost) auto-set `closedOn` to today and `probability` to the milestone default (100/0), preserving `lastOpenMilestone` as the previous open stage; moving back to an open milestone clears `closedOn` and re-applies the milestone's default probability (Won/Lost is reversible \u2014 no separate reopen tool). WARNING: Capsule does NOT validate that the new milestone belongs to the opportunity's current pipeline. Passing a milestoneId from a different pipeline silently relocates the opportunity across pipelines, and `lastOpenMilestone` may then reference a milestone in the previous pipeline. Verify against the opportunity's current pipeline (read the opp first, list its pipeline's milestones via list_milestones) before passing a cross-pipeline id. NOTE: changing `milestoneId` can fire **pipeline / milestone-reached automations** that mutate `owner` / `team` on the destination milestone (same shape as `create_opportunity` \u2014 see its `milestoneId` description for the owner-clearing automation caveat). If a milestone-change-and-owner-set in the same call lands with `owner: null`, follow up with a second `update_opportunity` (or `batch_update_opportunity`) carrying both `ownerId` and `teamId` \u2014 milestone-reached triggers only fire on the transition, so a subsequent PUT preserves your values."
1540
1547
  ),
@@ -1556,11 +1563,12 @@ var updateOpportunitySchema = z7.object({
1556
1563
  fields: z7.array(CustomFieldWriteSchema).optional().describe(fieldsArrayDescriptor("get_opportunity"))
1557
1564
  });
1558
1565
  async function updateOpportunity(input) {
1559
- const { id, milestoneId, ownerId, teamId, lostReasonId, fields, ...rest } = input;
1566
+ const { id, partyId, milestoneId, ownerId, teamId, lostReasonId, fields, ...rest } = input;
1560
1567
  const body = {};
1561
1568
  for (const [k, v] of Object.entries(rest)) {
1562
1569
  if (v !== void 0) body[k] = v;
1563
1570
  }
1571
+ setRef(body, "party", partyId);
1564
1572
  setRef(body, "milestone", milestoneId);
1565
1573
  let resolvedTeamId = teamId;
1566
1574
  if (ownerId !== void 0 && teamId === void 0) {
@@ -1669,6 +1677,9 @@ var updateProjectSchema = z8.object({
1669
1677
  name: z8.string().min(1).optional(),
1670
1678
  description: z8.string().optional(),
1671
1679
  status: z8.enum(["OPEN", "CLOSED"]).optional(),
1680
+ partyId: positiveId.optional().describe(
1681
+ "Reassign the project to a different primary party. Capsule requires every project to have a party \u2014 passing `null` is rejected with 422 'party is required' (verified empirically in v1.6.3 wire-trace). Discover ids via search_parties / filter_parties."
1682
+ ),
1672
1683
  ownerId: positiveId.nullable().optional().describe(
1673
1684
  "Reassign owner: pass a user ID to set, or `null` to unassign (matches the 'Unassign' option in Capsule's web UI). When you supply `ownerId` and omit `teamId` and/or `stageId`, the connector fetches the project's current omitted fields and includes them in the PUT body \u2014 this preserves them across the owner change (without it, Capsule's PUT would clear team; stage carry is defensive against the symmetric clear). Supply `teamId` and/or `stageId` explicitly on the same call to change them instead. `teamId: null` clears the team as part of an owner change. Constraints (Capsule enforces, 422 on violation): owner must be a member of the team if both are set; a project must always have at least one of {owner, team} set (cannot clear both)."
1674
1685
  ),
@@ -1684,11 +1695,12 @@ var updateProjectSchema = z8.object({
1684
1695
  )
1685
1696
  });
1686
1697
  async function updateProject(input) {
1687
- const { id, ownerId, teamId, stageId, fields, ...rest } = input;
1698
+ const { id, partyId, ownerId, teamId, stageId, fields, ...rest } = input;
1688
1699
  const body = {};
1689
1700
  for (const [k, v] of Object.entries(rest)) {
1690
1701
  if (v !== void 0) body[k] = v;
1691
1702
  }
1703
+ setRef(body, "party", partyId);
1692
1704
  let resolvedTeamId = teamId;
1693
1705
  let resolvedStageId = stageId;
1694
1706
  if (ownerId !== void 0 && (teamId === void 0 || stageId === void 0)) {
@@ -1800,15 +1812,33 @@ var updateTaskSchema = z9.object({
1800
1812
  ),
1801
1813
  ownerId: positiveId.optional().describe(
1802
1814
  "Reassign owner to user ID. Once set, this connector cannot clear an owner back to null \u2014 use Capsule's web UI for that."
1815
+ ),
1816
+ partyId: positiveId.nullable().optional().describe(
1817
+ "Re-link the task to a party by id, or `null` to orphan it. Mutually exclusive with `opportunityId` / `projectId` \u2014 Capsule enforces 'task can be related to at most one entity' server-side (422 if two parent-refs are set at once, verified in v1.6.3 wire-trace). To swap parent type atomically, pass the old one as `null` and the new one as an id in the same call."
1818
+ ),
1819
+ opportunityId: positiveId.nullable().optional().describe(
1820
+ "Re-link the task to an opportunity by id, or `null` to orphan it. Mutually exclusive with `partyId` / `projectId` \u2014 see `partyId` for the XOR semantic."
1821
+ ),
1822
+ projectId: positiveId.nullable().optional().describe(
1823
+ "Re-link the task to a project (kase) by id, or `null` to orphan it. Mutually exclusive with `partyId` / `opportunityId` \u2014 see `partyId` for the XOR semantic."
1803
1824
  )
1804
1825
  });
1805
1826
  async function updateTask(input) {
1806
- const { id, ownerId, ...rest } = input;
1827
+ const { id, ownerId, partyId, opportunityId, projectId, ...rest } = input;
1828
+ const setCount = [partyId, opportunityId, projectId].filter((v) => typeof v === "number").length;
1829
+ if (setCount > 1) {
1830
+ throw new Error(
1831
+ "update_task: provide at most one of partyId, opportunityId, or projectId (Capsule rejects multi-parent tasks with 422 'task can be related to at most one entity')"
1832
+ );
1833
+ }
1807
1834
  const body = {};
1808
1835
  for (const [k, v] of Object.entries(rest)) {
1809
1836
  if (v !== void 0) body[k] = v;
1810
1837
  }
1811
1838
  setRef(body, "owner", ownerId);
1839
+ setNullableRef(body, "party", partyId);
1840
+ setNullableRef(body, "opportunity", opportunityId);
1841
+ setNullableRef(body, "kase", projectId);
1812
1842
  return capsulePut(`/tasks/${id}`, { task: body });
1813
1843
  }
1814
1844
  var completeTaskSchema = z9.object({
@@ -2577,7 +2607,7 @@ function createCapsuleMcpServer(opts) {
2577
2607
  const server2 = new McpServer(
2578
2608
  {
2579
2609
  name: "capsulemcp",
2580
- version: "1.6.2",
2610
+ version: "1.6.3",
2581
2611
  description: "Read and (optionally) modify Capsule CRM data \u2014 parties, opportunities, projects, tasks, timeline entries, pipelines, tags.",
2582
2612
  websiteUrl: "https://github.com/soil-dev/capsulemcp",
2583
2613
  icons: ICONS
@@ -2680,7 +2710,7 @@ function createCapsuleMcpServer(opts) {
2680
2710
  registerTool(
2681
2711
  server2,
2682
2712
  "update_party",
2683
- "Update top-level fields on an existing party (about, firstName/lastName/name/title/jobTitle, ownerId). Only the fields you provide are changed. Child arrays (emailAddresses / phoneNumbers / addresses / websites) on this tool are APPEND-ONLY: items are merged into the existing list, not replaced. For surgical changes \u2014 replacing one email, removing one phone number, fixing the type on one address \u2014 use the dedicated atomic tools: add_party_email_address / remove_party_email_address_by_id (and the phone/address/website equivalents).",
2713
+ "Update top-level fields on an existing party (about, firstName/lastName/name/title/jobTitle, ownerId, organisationId). For PERSON parties, organisationId links to an organisation or null unlinks the person from its organisation; for ORGANISATION parties Capsule silently ignores organisationId. Only the fields you provide are changed. Child arrays (emailAddresses / phoneNumbers / addresses / websites) on this tool are APPEND-ONLY: items are merged into the existing list, not replaced. For surgical changes \u2014 replacing one email, removing one phone number, fixing the type on one address \u2014 use the dedicated atomic tools: add_party_email_address / remove_party_email_address_by_id (and the phone/address/website equivalents).",
2684
2714
  updatePartySchema,
2685
2715
  updateParty
2686
2716
  );
@@ -2815,7 +2845,7 @@ function createCapsuleMcpServer(opts) {
2815
2845
  registerTool(
2816
2846
  server2,
2817
2847
  "update_opportunity",
2818
- "Update fields on an existing opportunity. Only the fields you provide are changed. Closed (Won/Lost) opportunities ARE editable \u2014 Capsule does not enforce closed-record immutability, so `value`, `description`, etc. can be changed on a Won opp without warning. If the workflow needs historical revenue numbers to be stable, enforce that caller-side.",
2848
+ "Update fields on an existing opportunity, including the parent-reference field `partyId` to reassign the opp to a different primary party. Only the fields you provide are changed. Closed (Won/Lost) opportunities ARE editable \u2014 Capsule does not enforce closed-record immutability, so `value`, `description`, etc. can be changed on a Won opp without warning. If the workflow needs historical revenue numbers to be stable, enforce that caller-side. Capsule requires every opportunity to have a party \u2014 passing `partyId: null` is rejected with 422 'party is required'.",
2819
2849
  updateOpportunitySchema,
2820
2850
  updateOpportunity
2821
2851
  );
@@ -2880,7 +2910,7 @@ function createCapsuleMcpServer(opts) {
2880
2910
  registerTool(
2881
2911
  server2,
2882
2912
  "update_project",
2883
- "Update fields on an existing project. Only the fields you provide are changed. Use status='CLOSED' to close a project. CLOSED projects remain fully editable \u2014 Capsule does not enforce closed-record immutability. Stage moves and description edits on a CLOSED project are accepted without warning.",
2913
+ "Update fields on an existing project, including the parent-reference field `partyId` to reassign the project to a different primary party. Only the fields you provide are changed. Use status='CLOSED' to close a project. CLOSED projects remain fully editable \u2014 Capsule does not enforce closed-record immutability. Stage moves and description edits on a CLOSED project are accepted without warning. Capsule requires every project to have a party \u2014 passing `partyId: null` is rejected with 422 'party is required'.",
2884
2914
  updateProjectSchema,
2885
2915
  updateProject
2886
2916
  );
@@ -2959,7 +2989,7 @@ function createCapsuleMcpServer(opts) {
2959
2989
  registerTool(
2960
2990
  server2,
2961
2991
  "update_task",
2962
- "Update fields on an existing task: `description`, `dueOn`, `dueTime`, `detail`, `status` (OPEN or COMPLETED), and `ownerId`. Only the fields you provide are changed. To mark a task done, prefer the dedicated `complete_task` tool \u2014 it's idempotent (a no-op success on an already-completed task) and semantically clearer than `update_task status=COMPLETED`. Capsule rejects directly setting status=PENDING (which exists only internally for track-driven tasks); use OPEN or COMPLETED. Completed tasks remain fully editable \u2014 Capsule does not enforce closed-record immutability.",
2992
+ "Update fields on an existing task: `description`, `dueOn`, `dueTime`, `detail`, `status` (OPEN or COMPLETED), `ownerId`, and the parent-reference fields `partyId`, `opportunityId`, `projectId`. Pass a parent id to re-link the task, or null on a parent field to orphan/unlink it; at most one parent id may be set in a single call, though null+id swaps are allowed. Only the fields you provide are changed. To mark a task done, prefer the dedicated `complete_task` tool \u2014 it's idempotent (a no-op success on an already-completed task) and semantically clearer than `update_task status=COMPLETED`. Capsule rejects directly setting status=PENDING (which exists only internally for track-driven tasks); use OPEN or COMPLETED. Completed tasks remain fully editable \u2014 Capsule does not enforce closed-record immutability.",
2963
2993
  updateTaskSchema,
2964
2994
  updateTask
2965
2995
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capsulemcp",
3
- "version": "1.6.2",
3
+ "version": "1.6.3",
4
4
  "description": "Model Context Protocol server for Capsule CRM. Lets Claude (Desktop, Code, or web Projects via Custom Connector) read and write your CRM in plain English. Covers contacts, opportunities, projects, tasks, timeline activity, structured filters, saved filters with sort, workflow tracks, file attachments, audit, and batch fetches.",
5
5
  "keywords": [
6
6
  "mcp",