@setluca/mcp 0.5.0 → 0.5.1
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/CHANGELOG.md +29 -0
- package/dist/annotations.js +6 -5
- package/dist/operations/groups/bookings.js +7 -0
- package/dist/operations/groups/broadcasts.js +4 -0
- package/dist/operations/groups/cadences.js +1 -0
- package/dist/operations/groups/callEvents.js +6 -0
- package/dist/operations/groups/conversations.js +1 -0
- package/dist/operations/groups/insights.js +4 -0
- package/dist/operations/groups/integrations.js +9 -4
- package/dist/operations/groups/knowledge.js +2 -0
- package/dist/operations/groups/leads.js +12 -0
- package/dist/operations/groups/learning.js +1 -0
- package/dist/operations/groups/memory.js +1 -0
- package/dist/operations/groups/reviewQueue.js +7 -1
- package/dist/operations/groups/webhooks.js +1 -0
- package/dist/operations/registry.js +3 -1
- package/dist/task-tools/bookings.js +1 -1
- package/dist/tool.js +3 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,35 @@ input schema, or changing what a tool does to a workspace.
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [0.5.1] - 2026-10-22
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Tool annotations now match what each tool does, as the October 2026 audit
|
|
18
|
+
for the OpenAI app directory listed them. All 203 tools it covered match its
|
|
19
|
+
corrected values, and the 21 tools added in 0.5.0 follow the same rules.
|
|
20
|
+
- `idempotentHint` is true for 33 writes that a repeat with the same input
|
|
21
|
+
cannot move further, such as `luca_leads_notes_delete`,
|
|
22
|
+
`luca_insights_dismiss`, `luca_broadcasts_cancel`, and
|
|
23
|
+
`luca_pause_cadence`. It was true only for reads before.
|
|
24
|
+
- `openWorldHint` is true for writes the coach's webhook subscribers hear
|
|
25
|
+
about (`luca_leads_create`, `luca_leads_update`,
|
|
26
|
+
`luca_leads_identities_attach`, `luca_review_queue_reject`,
|
|
27
|
+
`luca_review_queue_bulk_reject`, `luca_broadcasts_pause`) and for the call
|
|
28
|
+
summaries that later reach the CRM (`luca_call_events_summary_generate`,
|
|
29
|
+
`luca_call_events_backfill`). None of them needs `confirm: true`.
|
|
30
|
+
- `luca_leads_import_preview` is read-only. It saves nothing.
|
|
31
|
+
- `luca_integrations_crm_connections_schema` is no longer read-only, because
|
|
32
|
+
it refreshes Luca's cached copy of the CRM schema.
|
|
33
|
+
- `luca_review_queue_objection_variant` is no longer destructive. It adds a
|
|
34
|
+
learning event and leaves the draft as it was.
|
|
35
|
+
- `luca_integrations_crm_sync_runs_create` is titled "Preview a CRM sync" and
|
|
36
|
+
no longer asks for `confirm: true` or claims to reach the CRM. The API
|
|
37
|
+
refuses a live sync, so the tool only records a dry-run preview.
|
|
38
|
+
- `luca_reschedule_call` says that a Cal.com or Google Calendar move waits for
|
|
39
|
+
the provider: until it confirms, the booking reads `pending_provider` and
|
|
40
|
+
keeps its old time.
|
|
41
|
+
|
|
13
42
|
## [0.5.0] - 2026-10-22
|
|
14
43
|
|
|
15
44
|
### Breaking
|
package/dist/annotations.js
CHANGED
|
@@ -10,6 +10,7 @@ export function operationAnnotationSource(operation) {
|
|
|
10
10
|
title: operation.title,
|
|
11
11
|
readOnly: operation.readOnly,
|
|
12
12
|
mutatesExisting: operation.mutatesExisting,
|
|
13
|
+
idempotent: operation.idempotent,
|
|
13
14
|
...optionalField("confirm", operation.confirm),
|
|
14
15
|
openWorld: operation.openWorld,
|
|
15
16
|
};
|
|
@@ -34,11 +35,11 @@ export function toolAnnotations(source) {
|
|
|
34
35
|
// existed before the call.
|
|
35
36
|
destructiveHint: !source.readOnly &&
|
|
36
37
|
(source.confirm !== undefined || source.mutatesExisting),
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
idempotentHint: source.
|
|
38
|
+
// Stated per operation: every read, plus the writes a repeat cannot move
|
|
39
|
+
// further, such as a delete, a dismiss, or a whole-value replace. It never
|
|
40
|
+
// leans on the idempotency key, which the HTTP layer mints fresh when the
|
|
41
|
+
// caller leaves it out.
|
|
42
|
+
idempotentHint: source.idempotent,
|
|
42
43
|
// Every call goes to the Luca API, but most only touch the coach's own
|
|
43
44
|
// workspace. The hint is true only when the call can reach past it: a
|
|
44
45
|
// lead's inbox, an outside URL, or a connected calendar or CRM.
|
|
@@ -169,6 +169,7 @@ export const bookingsOperations = [
|
|
|
169
169
|
op({
|
|
170
170
|
id: "bookings.guestLinks.revoke",
|
|
171
171
|
title: "Revoke a booking's guest links",
|
|
172
|
+
idempotent: true,
|
|
172
173
|
mutatesExisting: true,
|
|
173
174
|
description: "Kill every outstanding guest link on a booking at once.",
|
|
174
175
|
}),
|
|
@@ -197,11 +198,13 @@ export const bookingsOperations = [
|
|
|
197
198
|
op({
|
|
198
199
|
id: "bookings.types.delete",
|
|
199
200
|
title: "Delete a booking type",
|
|
201
|
+
idempotent: true,
|
|
200
202
|
description: "Remove a booking type and get back the types that are left. Calls already booked against it are not cancelled.",
|
|
201
203
|
}),
|
|
202
204
|
op({
|
|
203
205
|
id: "bookings.types.schedule.replace",
|
|
204
206
|
title: "Replace a booking type schedule",
|
|
207
|
+
idempotent: true,
|
|
205
208
|
mutatesExisting: true,
|
|
206
209
|
description: "Replace a booking type's schedule outright. Send every rule and override you want to keep \u2014 anything left out is dropped, which is also how you clear a day. Read the current schedule first unless you mean to start over.",
|
|
207
210
|
}),
|
|
@@ -213,12 +216,14 @@ export const bookingsOperations = [
|
|
|
213
216
|
op({
|
|
214
217
|
id: "bookings.types.attendees.remove",
|
|
215
218
|
title: "Remove a booking type attendee",
|
|
219
|
+
idempotent: true,
|
|
216
220
|
mutatesExisting: true,
|
|
217
221
|
description: "Remove a standing attendee from one booking type and get back the ones that are left. Calls already booked keep the invitees they were created with.",
|
|
218
222
|
}),
|
|
219
223
|
op({
|
|
220
224
|
id: "bookings.calendars.select",
|
|
221
225
|
title: "Select a booking calendar",
|
|
226
|
+
idempotent: true,
|
|
222
227
|
mutatesExisting: true,
|
|
223
228
|
// Selection checks that the calendar exists in the connected Google account.
|
|
224
229
|
openWorld: true,
|
|
@@ -227,6 +232,7 @@ export const bookingsOperations = [
|
|
|
227
232
|
op({
|
|
228
233
|
id: "bookings.calendars.remove",
|
|
229
234
|
title: "Remove a booking calendar",
|
|
235
|
+
idempotent: true,
|
|
230
236
|
mutatesExisting: true,
|
|
231
237
|
description: "Stop using a calendar for conflict checks or as the booking destination. The calendar itself is left alone in Google; only Luca's selection changes.",
|
|
232
238
|
}),
|
|
@@ -238,6 +244,7 @@ export const bookingsOperations = [
|
|
|
238
244
|
op({
|
|
239
245
|
id: "bookings.recovery.stop",
|
|
240
246
|
title: "Stop a no-show recovery",
|
|
247
|
+
idempotent: true,
|
|
241
248
|
mutatesExisting: true,
|
|
242
249
|
description: "Stop a booking's no-show recovery so no further follow-up is scheduled. A follow-up already being sent may still arrive. Send recovery.id as recoveryCaseId and recovery.outcomeVersion from luca_bookings_recovery_get; a recovery that changed since that read returns 409 booking_recovery_changed.",
|
|
243
250
|
}),
|
|
@@ -51,6 +51,9 @@ export const broadcastsOperations = [
|
|
|
51
51
|
op({
|
|
52
52
|
id: "broadcasts.pause",
|
|
53
53
|
title: "Pause a broadcast",
|
|
54
|
+
idempotent: true,
|
|
55
|
+
// Sends broadcast.paused to the coach's webhook subscribers.
|
|
56
|
+
openWorld: true,
|
|
54
57
|
mutatesExisting: true,
|
|
55
58
|
// Recipient snapshot, see the note above this list.
|
|
56
59
|
untrustedContent: true,
|
|
@@ -69,6 +72,7 @@ export const broadcastsOperations = [
|
|
|
69
72
|
op({
|
|
70
73
|
id: "broadcasts.cancel",
|
|
71
74
|
title: "Cancel a broadcast",
|
|
75
|
+
idempotent: true,
|
|
72
76
|
mutatesExisting: true,
|
|
73
77
|
// Recipient snapshot, see the note above this list.
|
|
74
78
|
untrustedContent: true,
|
|
@@ -45,6 +45,8 @@ export const callEventsOperations = [
|
|
|
45
45
|
op({
|
|
46
46
|
id: "callEvents.summary.generate",
|
|
47
47
|
title: "Generate a call summary",
|
|
48
|
+
// The summary is pushed to the coach's connected CRM after its review window.
|
|
49
|
+
openWorld: true,
|
|
48
50
|
mutatesExisting: true,
|
|
49
51
|
untrustedContent: true,
|
|
50
52
|
description: "Queue a fresh summary and return straight away. Poll the call event for the result.",
|
|
@@ -75,6 +77,7 @@ export const callEventsOperations = [
|
|
|
75
77
|
op({
|
|
76
78
|
id: "callEvents.undoOutcome",
|
|
77
79
|
title: "Undo a call's outcome",
|
|
80
|
+
idempotent: true,
|
|
78
81
|
mutatesExisting: true,
|
|
79
82
|
untrustedContent: true,
|
|
80
83
|
description: "Clear a mistaken attendance mark and put the call back in the queue. The window is 48 hours from when the outcome was set.",
|
|
@@ -82,6 +85,9 @@ export const callEventsOperations = [
|
|
|
82
85
|
op({
|
|
83
86
|
id: "callEvents.backfill",
|
|
84
87
|
title: "Backfill a booking's call event",
|
|
88
|
+
idempotent: true,
|
|
89
|
+
// The summary it queues later syncs to the coach's connected CRM.
|
|
90
|
+
openWorld: true,
|
|
85
91
|
untrustedContent: true,
|
|
86
92
|
description: "Create a call event for a booking that predates automatic creation, then queue its summary. Safe to call twice: an existing call event comes back as is.",
|
|
87
93
|
}),
|
|
@@ -8,18 +8,21 @@ export const insightsOperations = [
|
|
|
8
8
|
op({
|
|
9
9
|
id: "insights.markRead",
|
|
10
10
|
title: "Mark an insight read",
|
|
11
|
+
idempotent: true,
|
|
11
12
|
mutatesExisting: true,
|
|
12
13
|
description: "Mark one insight read, so it stops coming back at the top of the feed.",
|
|
13
14
|
}),
|
|
14
15
|
op({
|
|
15
16
|
id: "insights.dismiss",
|
|
16
17
|
title: "Dismiss an insight",
|
|
18
|
+
idempotent: true,
|
|
17
19
|
mutatesExisting: true,
|
|
18
20
|
description: "Dismiss one insight the coach does not want to act on. Use this rather than leaving it unread forever.",
|
|
19
21
|
}),
|
|
20
22
|
op({
|
|
21
23
|
id: "insights.markActioned",
|
|
22
24
|
title: "Mark an insight actioned",
|
|
25
|
+
idempotent: true,
|
|
23
26
|
mutatesExisting: true,
|
|
24
27
|
description: "Record that the thing an insight asked for was actually done. Use it after doing the work, not instead of it.",
|
|
25
28
|
}),
|
|
@@ -31,6 +34,7 @@ export const insightsOperations = [
|
|
|
31
34
|
op({
|
|
32
35
|
id: "insights.settings.update",
|
|
33
36
|
title: "Update alert delivery settings",
|
|
37
|
+
idempotent: true,
|
|
34
38
|
mutatesExisting: true,
|
|
35
39
|
description: "Merge-patch the coach's alert-delivery settings. Send only what you are changing; anything left out keeps its current value.",
|
|
36
40
|
}),
|
|
@@ -20,12 +20,16 @@ export const integrationsOperations = [
|
|
|
20
20
|
op({
|
|
21
21
|
id: "integrations.crm.connections.create",
|
|
22
22
|
title: "Create a CRM connection",
|
|
23
|
+
idempotent: true,
|
|
23
24
|
mutatesExisting: true,
|
|
24
25
|
description: "Create or update a workspace-scoped CRM connection.",
|
|
25
26
|
}),
|
|
26
27
|
op({
|
|
27
28
|
id: "integrations.crm.connections.schema",
|
|
28
29
|
title: "Read a CRM's field schema",
|
|
30
|
+
// Refreshes Luca's cached copy of the CRM schema, the same copy every time.
|
|
31
|
+
readOnly: false,
|
|
32
|
+
idempotent: true,
|
|
29
33
|
openWorld: true,
|
|
30
34
|
// sampleRecords are literal contact and deal rows from the coach's CRM, which leads and other people filled in.
|
|
31
35
|
untrustedContent: true,
|
|
@@ -48,11 +52,13 @@ export const integrationsOperations = [
|
|
|
48
52
|
op({
|
|
49
53
|
id: "integrations.crm.mappings.suggest",
|
|
50
54
|
title: "Suggest CRM field mappings",
|
|
55
|
+
idempotent: true,
|
|
51
56
|
description: "Suggest CRM field mappings.",
|
|
52
57
|
}),
|
|
53
58
|
op({
|
|
54
59
|
id: "integrations.crm.mappings.confirm",
|
|
55
60
|
title: "Confirm CRM field mappings",
|
|
61
|
+
idempotent: true,
|
|
56
62
|
mutatesExisting: true,
|
|
57
63
|
description: "Confirm CRM field mappings.",
|
|
58
64
|
}),
|
|
@@ -65,12 +71,10 @@ export const integrationsOperations = [
|
|
|
65
71
|
}),
|
|
66
72
|
op({
|
|
67
73
|
id: "integrations.crm.syncRuns.create",
|
|
68
|
-
title: "
|
|
69
|
-
openWorld: true,
|
|
70
|
-
confirm: "always",
|
|
74
|
+
title: "Preview a CRM sync",
|
|
71
75
|
// Mutations carry CRM record values: names, emails, notes.
|
|
72
76
|
untrustedContent: true,
|
|
73
|
-
description: "
|
|
77
|
+
description: "Record a dry-run preview of the changes a CRM sync would make, as a new sync run. Nothing is written to the CRM: the API refuses a live sync.",
|
|
74
78
|
}),
|
|
75
79
|
op({
|
|
76
80
|
id: "integrations.events.list",
|
|
@@ -82,6 +86,7 @@ export const integrationsOperations = [
|
|
|
82
86
|
op({
|
|
83
87
|
id: "integrations.crm.connections.disconnect",
|
|
84
88
|
title: "Disconnect a CRM",
|
|
89
|
+
idempotent: true,
|
|
85
90
|
mutatesExisting: true,
|
|
86
91
|
// Stops every sync with that CRM until the coach signs in to it again.
|
|
87
92
|
confirm: "always",
|
|
@@ -15,6 +15,7 @@ export const knowledgeOperations = [
|
|
|
15
15
|
op({
|
|
16
16
|
id: "knowledge.rejectSuggestion",
|
|
17
17
|
title: "Reject a knowledge suggestion",
|
|
18
|
+
idempotent: true,
|
|
18
19
|
mutatesExisting: true,
|
|
19
20
|
description: "Reject a profile suggestion so it stops being offered. The profile is left as it stands.",
|
|
20
21
|
}),
|
|
@@ -33,6 +34,7 @@ export const knowledgeOperations = [
|
|
|
33
34
|
op({
|
|
34
35
|
id: "knowledge.sources.disconnect",
|
|
35
36
|
title: "Disconnect a knowledge source",
|
|
37
|
+
idempotent: true,
|
|
36
38
|
mutatesExisting: true,
|
|
37
39
|
description: "Delete a knowledge source. Its pending suggestions go with it; values the coach already approved stay in the profile, because approving copies them there.",
|
|
38
40
|
}),
|
|
@@ -18,12 +18,17 @@ export const leadsOperations = [
|
|
|
18
18
|
op({
|
|
19
19
|
id: "leads.create",
|
|
20
20
|
title: "Create or upsert a lead",
|
|
21
|
+
// Sends lead.created to the coach's webhook subscribers.
|
|
22
|
+
openWorld: true,
|
|
21
23
|
mutatesExisting: true,
|
|
22
24
|
description: "Create or upsert a lead identity. Body requires channel and externalUserId.",
|
|
23
25
|
}),
|
|
24
26
|
op({
|
|
25
27
|
id: "leads.update",
|
|
26
28
|
title: "Update a lead",
|
|
29
|
+
// Sends lead.updated to the coach's webhook subscribers, and a tag change
|
|
30
|
+
// can resume a campaign step that messages the lead.
|
|
31
|
+
openWorld: true,
|
|
27
32
|
description: "Update a lead's display name, stage, score, tags, or custom fields.",
|
|
28
33
|
}),
|
|
29
34
|
op({
|
|
@@ -33,6 +38,7 @@ export const leadsOperations = [
|
|
|
33
38
|
}),
|
|
34
39
|
op({
|
|
35
40
|
id: "leads.fieldDefinitions.upsert",
|
|
41
|
+
idempotent: true,
|
|
36
42
|
// Echoes the coach's own field definition back.
|
|
37
43
|
untrustedContent: false,
|
|
38
44
|
title: "Define a custom lead field",
|
|
@@ -42,6 +48,8 @@ export const leadsOperations = [
|
|
|
42
48
|
op({
|
|
43
49
|
id: "leads.importPreview",
|
|
44
50
|
title: "Preview a lead import",
|
|
51
|
+
// A POST only so the CSV fits in a body. It checks the mapping and saves nothing.
|
|
52
|
+
readOnly: true,
|
|
45
53
|
description: "Preview a mapped CSV lead import before applying it.",
|
|
46
54
|
}),
|
|
47
55
|
op({
|
|
@@ -55,6 +63,8 @@ export const leadsOperations = [
|
|
|
55
63
|
op({
|
|
56
64
|
id: "leads.identities.attach",
|
|
57
65
|
title: "Attach a channel identity to a lead",
|
|
66
|
+
// Sends lead.updated to the coach's webhook subscribers.
|
|
67
|
+
openWorld: true,
|
|
58
68
|
mutatesExisting: true,
|
|
59
69
|
description: "Attach a verified or claimed identity to a lead.",
|
|
60
70
|
}),
|
|
@@ -71,6 +81,7 @@ export const leadsOperations = [
|
|
|
71
81
|
}),
|
|
72
82
|
op({
|
|
73
83
|
id: "leads.consents.revoke",
|
|
84
|
+
idempotent: true,
|
|
74
85
|
// Echoes the consent record the coach revoked.
|
|
75
86
|
untrustedContent: false,
|
|
76
87
|
title: "Revoke a lead's consent",
|
|
@@ -92,6 +103,7 @@ export const leadsOperations = [
|
|
|
92
103
|
}),
|
|
93
104
|
op({
|
|
94
105
|
id: "leads.notes.delete",
|
|
106
|
+
idempotent: true,
|
|
95
107
|
// Answers with ids and status flags only; no lead-authored text.
|
|
96
108
|
untrustedContent: false,
|
|
97
109
|
title: "Delete a lead note",
|
|
@@ -22,6 +22,8 @@ export const reviewQueueOperations = [
|
|
|
22
22
|
}),
|
|
23
23
|
op({
|
|
24
24
|
id: "reviewQueue.reject",
|
|
25
|
+
// Sends review.rejected to the coach's webhook subscribers.
|
|
26
|
+
openWorld: true,
|
|
25
27
|
// Answers with ids and status flags only; no lead-authored text.
|
|
26
28
|
untrustedContent: false,
|
|
27
29
|
title: "Reject a queued draft",
|
|
@@ -54,7 +56,6 @@ export const reviewQueueOperations = [
|
|
|
54
56
|
// Answers with ids and status flags only; no lead-authored text.
|
|
55
57
|
untrustedContent: false,
|
|
56
58
|
title: "Record an objection variant choice",
|
|
57
|
-
mutatesExisting: true,
|
|
58
59
|
description: "Record which objection-specialist variant was picked for one queued draft. It is how the objection tests learn which handling works, so record the variant the coach actually chose.",
|
|
59
60
|
}),
|
|
60
61
|
op({
|
|
@@ -76,6 +77,7 @@ export const reviewQueueOperations = [
|
|
|
76
77
|
}),
|
|
77
78
|
op({
|
|
78
79
|
id: "reviewQueue.drafting.cancel",
|
|
80
|
+
idempotent: true,
|
|
79
81
|
// Answers with a turn id and state only; no lead-authored text.
|
|
80
82
|
untrustedContent: false,
|
|
81
83
|
title: "Cancel a draft in progress",
|
|
@@ -95,6 +97,7 @@ export const reviewQueueOperations = [
|
|
|
95
97
|
}),
|
|
96
98
|
op({
|
|
97
99
|
id: "reviewQueue.drafting.dismiss",
|
|
100
|
+
idempotent: true,
|
|
98
101
|
// Answers with no body.
|
|
99
102
|
untrustedContent: false,
|
|
100
103
|
title: "Dismiss a stopped draft",
|
|
@@ -124,6 +127,9 @@ export const reviewQueueOperations = [
|
|
|
124
127
|
}),
|
|
125
128
|
op({
|
|
126
129
|
id: "reviewQueue.bulkReject",
|
|
130
|
+
idempotent: true,
|
|
131
|
+
// Sends review.rejected for each draft to the coach's webhook subscribers.
|
|
132
|
+
openWorld: true,
|
|
127
133
|
// Answers with one outcome per id; no lead-authored text.
|
|
128
134
|
untrustedContent: false,
|
|
129
135
|
title: "Reject several drafts",
|
|
@@ -29,6 +29,7 @@ export const webhooksOperations = [
|
|
|
29
29
|
op({
|
|
30
30
|
id: "webhooks.subscriptions.delete",
|
|
31
31
|
title: "Delete a webhook subscription",
|
|
32
|
+
idempotent: true,
|
|
32
33
|
// Deleting by id is naturally idempotent (the route matches its policy
|
|
33
34
|
// entry, which carries no requireIdempotency), so no Idempotency-Key is
|
|
34
35
|
// sent and it stays out of the confirm-gated tier that re-triggers live
|
|
@@ -286,6 +286,7 @@ export function op(input) {
|
|
|
286
286
|
const pageContract = pageContractFor(method, path);
|
|
287
287
|
const group = operationGroupForId(input.id);
|
|
288
288
|
const untrustedContent = input.untrustedContent ?? HashSet.has(UNTRUSTED_CONTENT_GROUPS, group);
|
|
289
|
+
const readOnly = input.readOnly ?? method === "GET";
|
|
289
290
|
const operation = {
|
|
290
291
|
id: input.id,
|
|
291
292
|
group,
|
|
@@ -295,7 +296,8 @@ export function op(input) {
|
|
|
295
296
|
path,
|
|
296
297
|
scopes,
|
|
297
298
|
mutatesExisting: mutatesExistingRoute,
|
|
298
|
-
readOnly
|
|
299
|
+
readOnly,
|
|
300
|
+
idempotent: input.idempotent ?? readOnly,
|
|
299
301
|
idempotencyRequired,
|
|
300
302
|
...optionalField("confirm", input.confirm),
|
|
301
303
|
openWorld: input.openWorld ?? false,
|
|
@@ -239,7 +239,7 @@ const DEFAULT_GUEST_LINK_HOURS = 168;
|
|
|
239
239
|
const rescheduleCall = taskTool({
|
|
240
240
|
name: "luca_reschedule_call",
|
|
241
241
|
title: "Move a call to a new time",
|
|
242
|
-
description:
|
|
242
|
+
description: "Move a booked call to a new time and hand back a fresh reschedule link for the lead. Checks the new slot is open first, against the schedule of its booking type when it has one, and returns the open slots instead of moving the call when it is taken. A call with an idempotencyKey skips that check so a retry gets the original result back. A manual booking moves in Luca only. A Cal.com or Google Calendar booking moves on that calendar too, but only once the provider confirms: until then the booking's operation.state reads pending_provider and it keeps its old time, so read the booking again before telling the coach the call moved. A Calendly, GoHighLevel or iClosed booking is refused so the coach can move it with the provider, and so is a provider booking with no calendar event, such as one recorded with luca_bookings_create. Moving the call re-arms the lead's reminder for the new time, so pass confirm: true only after the coach approves the move. Example: call with { bookingId, slotAt: \"2026-07-16T15:00:00Z\", confirm: true } after the lead asked to push the call. No message about the move is sent: send the lead the link yourself, or draft the message through the review queue.",
|
|
243
243
|
inputSchema: {
|
|
244
244
|
...WORKSPACE_INPUT_FIELDS,
|
|
245
245
|
bookingId: NonEmptyString.annotate({ description: "The booking to move." }),
|
package/dist/tool.js
CHANGED
|
@@ -95,6 +95,9 @@ function buildFromTaskTool(tool) {
|
|
|
95
95
|
// A task tool overwrites or removes existing state when any write it
|
|
96
96
|
// composes does.
|
|
97
97
|
mutatesExisting: composed.some((operation) => operation.mutatesExisting),
|
|
98
|
+
// Repeating a task tool has no further effect only when that holds for
|
|
99
|
+
// every operation it composes.
|
|
100
|
+
idempotent: composed.every((operation) => operation.idempotent),
|
|
98
101
|
...optionalField("confirm", confirm),
|
|
99
102
|
untrustedContent,
|
|
100
103
|
// A task tool reaches as far as the furthest operation it composes.
|
package/dist/version.js
CHANGED
|
@@ -9,6 +9,6 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Bump this in the same commit as package.json and server.json.
|
|
11
11
|
*/
|
|
12
|
-
export const LUCA_MCP_VERSION = "0.5.
|
|
12
|
+
export const LUCA_MCP_VERSION = "0.5.1";
|
|
13
13
|
/** The user-agent every outbound Luca API call identifies itself with. */
|
|
14
14
|
export const LUCA_MCP_USER_AGENT = `luca-mcp/${LUCA_MCP_VERSION}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@setluca/mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Connect SetLuca to Claude, ChatGPT, Cursor, or any MCP client to triage the review queue, look up leads, draft replies, and check campaigns.",
|
|
6
6
|
"keywords": [
|