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 +1 -1
- package/dist/http.js +39 -9
- package/dist/index.js +39 -9
- package/package.json +1 -1
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.
|
|
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.
|
|
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 `
|
|
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.
|
|
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 `
|
|
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.
|
|
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",
|