@setluca/mcp 0.2.1 → 0.3.0

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.
Files changed (69) hide show
  1. package/CHANGELOG.md +316 -0
  2. package/LICENSE +21 -0
  3. package/README.md +19 -288
  4. package/dist/annotations.js +33 -18
  5. package/dist/client-address.js +64 -0
  6. package/dist/completions.js +24 -27
  7. package/dist/config.js +89 -38
  8. package/dist/errors.js +94 -13
  9. package/dist/fields.js +68 -0
  10. package/dist/fixed-window.js +46 -0
  11. package/dist/generated/openapi-inputs.js +216 -5820
  12. package/dist/generated/openapi-outputs.js +19014 -14001
  13. package/dist/generated/route-catalog.js +80 -0
  14. package/dist/http.js +103 -41
  15. package/dist/index.js +4 -1
  16. package/dist/oauth-challenge.js +74 -0
  17. package/dist/oauth-resolver.js +58 -61
  18. package/dist/observability.js +30 -15
  19. package/dist/openapi-schema.js +175 -0
  20. package/dist/operations/catalog.js +17 -0
  21. package/dist/operations/groups/analytics.js +20 -10
  22. package/dist/operations/groups/bookings.js +43 -15
  23. package/dist/operations/groups/broadcasts.js +29 -58
  24. package/dist/operations/groups/cadences.js +3 -1
  25. package/dist/operations/groups/callEvents.js +19 -72
  26. package/dist/operations/groups/campaigns.js +28 -32
  27. package/dist/operations/groups/channels.js +23 -0
  28. package/dist/operations/groups/conversations.js +14 -25
  29. package/dist/operations/groups/insights.js +4 -0
  30. package/dist/operations/groups/integrations.js +15 -17
  31. package/dist/operations/groups/knowledge.js +5 -1
  32. package/dist/operations/groups/leads.js +22 -23
  33. package/dist/operations/groups/learning.js +13 -1
  34. package/dist/operations/groups/memory.js +4 -6
  35. package/dist/operations/groups/reports.js +1 -1
  36. package/dist/operations/groups/reviewQueue.js +17 -14
  37. package/dist/operations/groups/safety.js +1 -1
  38. package/dist/operations/groups/voice.js +0 -1
  39. package/dist/operations/groups/webhooks.js +15 -3
  40. package/dist/operations/manifest.js +7 -6
  41. package/dist/operations/registry.js +102 -108
  42. package/dist/operations.js +4 -3
  43. package/dist/optional-field.js +15 -3
  44. package/dist/page-contract.js +74 -0
  45. package/dist/pagination.js +147 -67
  46. package/dist/prompts.js +122 -97
  47. package/dist/rate-limit.js +40 -0
  48. package/dist/remote.js +145 -65
  49. package/dist/resilience.js +107 -52
  50. package/dist/resolvers.js +125 -0
  51. package/dist/scopes.js +70 -17
  52. package/dist/serialization.js +32 -22
  53. package/dist/server.js +148 -132
  54. package/dist/standard-schema.js +215 -0
  55. package/dist/task-tools/analytics.js +145 -86
  56. package/dist/task-tools/bookings.js +168 -81
  57. package/dist/task-tools/cadences.js +30 -45
  58. package/dist/task-tools/calls.js +131 -124
  59. package/dist/task-tools/inbox.js +37 -47
  60. package/dist/task-tools/leads.js +49 -77
  61. package/dist/task-tools/runtime.js +75 -15
  62. package/dist/token-verification.js +42 -0
  63. package/dist/tool-cache.js +3 -2
  64. package/dist/tool.js +151 -96
  65. package/dist/version.js +1 -1
  66. package/package.json +37 -16
  67. package/dist/openapi-zod.js +0 -335
  68. package/dist/page-shape.js +0 -62
  69. package/dist/tsconfig.tsbuildinfo +0 -1
package/CHANGELOG.md CHANGED
@@ -10,6 +10,322 @@ input schema, or changing what a tool does to a workspace.
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [0.3.0] - 2026-10-05
14
+
15
+ This is the first Luca MCP release from its own repository. The package and
16
+ hosted connection now share a release path, with checks against the Luca API
17
+ currently in production. The README starts with what people can do in Luca;
18
+ setup and development details live in the guides.
19
+
20
+ ### Breaking
21
+
22
+ - **Tool names are snake_case throughout.** Operation tools used the camelCase
23
+ of their operation id, so the list mixed `luca_analytics_speedImpact` with
24
+ `luca_book_call`. Every name now matches the task tools' casing. 73 tools
25
+ are renamed; the other 130 keep their names. A client or saved prompt that
26
+ calls an old name gets an unknown-tool error. 203 tools ship: 188 operations
27
+ and 15 task tools.
28
+
29
+ <details>
30
+ <summary>Every renamed tool</summary>
31
+
32
+ | Old name | New name |
33
+ | ------------------------------------------------- | -------------------------------------------------- |
34
+ | `luca_leads_fieldDefinitions_list` | `luca_leads_field_definitions_list` |
35
+ | `luca_leads_fieldDefinitions_upsert` | `luca_leads_field_definitions_upsert` |
36
+ | `luca_leads_importPreview` | `luca_leads_import_preview` |
37
+ | `luca_leads_importApply` | `luca_leads_import_apply` |
38
+ | `luca_reviewQueue_list` | `luca_review_queue_list` |
39
+ | `luca_reviewQueue_explain` | `luca_review_queue_explain` |
40
+ | `luca_reviewQueue_approve` | `luca_review_queue_approve` |
41
+ | `luca_reviewQueue_reject` | `luca_review_queue_reject` |
42
+ | `luca_reviewQueue_restore` | `luca_review_queue_restore` |
43
+ | `luca_reviewQueue_media_retry` | `luca_review_queue_media_retry` |
44
+ | `luca_reviewQueue_sla` | `luca_review_queue_sla` |
45
+ | `luca_reviewQueue_objectionVariant` | `luca_review_queue_objection_variant` |
46
+ | `luca_bookings_managedAvailability` | `luca_bookings_managed_availability` |
47
+ | `luca_bookings_outcomeReasons_list` | `luca_bookings_outcome_reasons_list` |
48
+ | `luca_bookings_needsOutcome` | `luca_bookings_needs_outcome` |
49
+ | `luca_bookings_lifecycleAttention` | `luca_bookings_lifecycle_attention` |
50
+ | `luca_bookings_lifecycleResolution` | `luca_bookings_lifecycle_resolution` |
51
+ | `luca_bookings_providerSync_retry` | `luca_bookings_provider_sync_retry` |
52
+ | `luca_bookings_guestLinks_create` | `luca_bookings_guest_links_create` |
53
+ | `luca_bookings_guestLinks_revoke` | `luca_bookings_guest_links_revoke` |
54
+ | `luca_bookings_outcomeReasons_create` | `luca_bookings_outcome_reasons_create` |
55
+ | `luca_bookings_outcomeReasons_update` | `luca_bookings_outcome_reasons_update` |
56
+ | `luca_callEvents_needingFeedback` | `luca_call_events_needing_feedback` |
57
+ | `luca_callEvents_get` | `luca_call_events_get` |
58
+ | `luca_callEvents_byBooking` | `luca_call_events_by_booking` |
59
+ | `luca_callEvents_byLead` | `luca_call_events_by_lead` |
60
+ | `luca_callEvents_feedback` | `luca_call_events_feedback` |
61
+ | `luca_callEvents_summary_generate` | `luca_call_events_summary_generate` |
62
+ | `luca_callEvents_summary_replace` | `luca_call_events_summary_replace` |
63
+ | `luca_callEvents_pushToCrm` | `luca_call_events_push_to_crm` |
64
+ | `luca_callEvents_undoOutcome` | `luca_call_events_undo_outcome` |
65
+ | `luca_callEvents_backfill` | `luca_call_events_backfill` |
66
+ | `luca_campaigns_providerCapabilities` | `luca_campaigns_provider_capabilities` |
67
+ | `luca_campaigns_commentEvents_list` | `luca_campaigns_comment_events_list` |
68
+ | `luca_campaigns_commentSimulationSuite_run` | `luca_campaigns_comment_simulation_suite_run` |
69
+ | `luca_campaigns_setStatus` | `luca_campaigns_set_status` |
70
+ | `luca_webhooks_testDelivery` | `luca_webhooks_test_delivery` |
71
+ | `luca_webhooks_signatureGuide` | `luca_webhooks_signature_guide` |
72
+ | `luca_webhooks_events_bulkReplay` | `luca_webhooks_events_bulk_replay` |
73
+ | `luca_integrations_crm_oauthUrl` | `luca_integrations_crm_oauth_url` |
74
+ | `luca_integrations_crm_connections_webhooksSetup` | `luca_integrations_crm_connections_webhooks_setup` |
75
+ | `luca_integrations_crm_syncRuns_list` | `luca_integrations_crm_sync_runs_list` |
76
+ | `luca_integrations_crm_syncRuns_create` | `luca_integrations_crm_sync_runs_create` |
77
+ | `luca_coach_nicheProfile` | `luca_coach_niche_profile` |
78
+ | `luca_coach_objectionPlaybook` | `luca_coach_objection_playbook` |
79
+ | `luca_coach_postCallSettings` | `luca_coach_post_call_settings` |
80
+ | `luca_coach_postCallCrmMapping` | `luca_coach_post_call_crm_mapping` |
81
+ | `luca_coach_postCallDealSync` | `luca_coach_post_call_deal_sync` |
82
+ | `luca_coach_auditLog` | `luca_coach_audit_log` |
83
+ | `luca_insights_unreadCount` | `luca_insights_unread_count` |
84
+ | `luca_insights_markRead` | `luca_insights_mark_read` |
85
+ | `luca_insights_markActioned` | `luca_insights_mark_actioned` |
86
+ | `luca_learning_frameworkPerformance` | `luca_learning_framework_performance` |
87
+ | `luca_learning_abTests_list` | `luca_learning_ab_tests_list` |
88
+ | `luca_learning_abTests_get` | `luca_learning_ab_tests_get` |
89
+ | `luca_knowledge_approveSuggestion` | `luca_knowledge_approve_suggestion` |
90
+ | `luca_knowledge_rejectSuggestion` | `luca_knowledge_reject_suggestion` |
91
+ | `luca_memory_setArchived` | `luca_memory_set_archived` |
92
+ | `luca_cadences_rescueStart` | `luca_cadences_rescue_start` |
93
+ | `luca_analytics_speedImpact` | `luca_analytics_speed_impact` |
94
+ | `luca_analytics_callIntelligence` | `luca_analytics_call_intelligence` |
95
+ | `luca_analytics_callIntelligenceCalls` | `luca_analytics_call_intelligence_calls` |
96
+ | `luca_analytics_ghostedLeads` | `luca_analytics_ghosted_leads` |
97
+ | `luca_analytics_trustScore` | `luca_analytics_trust_score` |
98
+ | `luca_analytics_leadScore` | `luca_analytics_lead_score` |
99
+ | `luca_analytics_lossReasons` | `luca_analytics_loss_reasons` |
100
+ | `luca_analytics_contentAttribution` | `luca_analytics_content_attribution` |
101
+ | `luca_analytics_contentPerformance` | `luca_analytics_content_performance` |
102
+ | `luca_analytics_topicPerformance` | `luca_analytics_topic_performance` |
103
+ | `luca_analytics_benchmarkConsent` | `luca_analytics_benchmark_consent` |
104
+ | `luca_analytics_learningInsights` | `luca_analytics_learning_insights` |
105
+ | `luca_analytics_crmSyncHealth` | `luca_analytics_crm_sync_health` |
106
+ | `luca_analytics_objectionDrilldown` | `luca_analytics_objection_drilldown` |
107
+
108
+ </details>
109
+
110
+ - **A tool call with an argument the tool does not define fails.** It used to
111
+ drop the field and run, so a typo such as `limt` silently fell back to the
112
+ default. The error names the field, and no request is sent.
113
+ - **Seven further tools need `confirm: true`:**
114
+ `luca_bookings_lifecycle_resolution`, `luca_bookings_provider_sync_retry`,
115
+ `luca_call_events_summary_replace`, `luca_call_events_push_to_crm`,
116
+ `luca_webhooks_test_delivery`,
117
+ `luca_integrations_crm_connections_webhooks_setup`, and
118
+ `luca_integrations_crm_sync_runs_create`. Each writes to a connected
119
+ calendar or CRM, or posts to an outside URL. 27 operations are now gated,
120
+ which covers every write that can reach past the workspace.
121
+ - **`luca_find_leads` takes `channel` as one of `telegram`, `instagram`,
122
+ `messenger`, or `whatsapp`,** the values `GET /api/leads` accepts. Any other
123
+ value fails before a request is sent.
124
+ - **The analytics task tools return what they could read.**
125
+ `luca_analytics_rollup`, `luca_analytics_magic_monday`, and
126
+ `luca_analytics_deep_dive` used to fail when any one read failed. Each
127
+ section is now optional, and a `failedSections` list names the section and
128
+ the error for every read that failed. The call fails only when every read
129
+ does, or when a read is refused with a 401 or a scope 403.
130
+ - **Six more tools need `confirm: true`:** `luca_leads_import_apply`,
131
+ `luca_leads_consents_grant`, `luca_bookings_cancel`,
132
+ `luca_webhooks_subscriptions_create`, `luca_webhooks_subscriptions_update`,
133
+ and `luca_campaigns_set_status` when it sets `published` (resuming sends). A
134
+ client that called these without it now gets a tool error asking for it.
135
+ - **Two tools are gone.** `luca_integrations_crm_oauthCallback` and
136
+ `luca_integrations_crm_webhooks_record` were provider callbacks that an agent
137
+ had no reason to call.
138
+ - **`openWorldHint` is now `false` for most tools.** It is `true` only for the
139
+ 25 operations, and the task tools built on them, that can reach past the
140
+ workspace: messaging a lead, posting to an outside URL, or writing to a
141
+ connected calendar or CRM.
142
+ - **The remote resolver API changed.** `apiKeyResolver(settings)` and
143
+ `oauthResolver(settings)` take the API base URL, auth header, and timeout as
144
+ one settings object, and a `TokenResolver` returns `Option<LucaConfig>`.
145
+ `ResolvedIdentity` and `chainResolvers` are gone. Pick a resolver by the
146
+ token's shape with the new `looksLikeDeveloperKey`.
147
+ - **`LucaConfig.apiKey` is a `Redacted<string>`.** A custom `TokenResolver`
148
+ wraps the key with `Redacted.make` from `effect/Redacted`, so a logged or
149
+ inspected config prints `<redacted>` instead of the key.
150
+ - **Booking and moving a call need `confirm: true`:** `luca_book_call`,
151
+ `luca_reschedule_call`, `luca_bookings_create`, and `luca_bookings_update`
152
+ now match `luca_bookings_cancel`. A booking moves the lead to booked and
153
+ notifies the coach's webhook subscribers; a move updates the connected
154
+ calendar and the lead's reminder. All four now report `destructiveHint: true`.
155
+ To migrate, show the coach the slot, then pass `confirm: true` once they
156
+ approve it. A call without it returns a tool error and sends nothing.
157
+ - **`luca_close_call_loop` needs `confirm: true` when `attendance` is
158
+ `no_show`.** That report can start recovery that messages the lead. Other
159
+ attendance values run without it.
160
+ - **`provider` on `luca_book_call` and `luca_reschedule_call` is one of
161
+ `manual`, `cal_com`, `calendly`, `google_calendar`, `iclosed`, or
162
+ `gohighlevel`.** Any other value fails before a request is sent. It used to
163
+ be skipped by the availability check without a word.
164
+ - **The hosted server checks developer keys.** A `luca_` key is checked with
165
+ one `GET /api/capabilities` before a server is built, so a made-up key gets
166
+ 401 instead of a tool list. `apiKeyResolver(settings, fetchImpl?, checks?)`
167
+ now resolves asynchronously and can fail with
168
+ `TokenVerificationUnavailable`.
169
+ - **An empty `idempotencyKey` fails before a request is sent** on every task
170
+ tool that takes one. It used to reach the API as a blank header.
171
+ - **`RemoteRateLimiters` has a required `perTokenAddress` limiter,** charged
172
+ per client address on requests that carry a token.
173
+ - **`luca_call_events_feedback` asks for confirmation on a `no_show` report.**
174
+ A no-show can start recovery messaging, so that outcome now needs
175
+ `confirm: true`. Other outcomes need no confirmation, and the tool stays on
176
+ the `luca:draft` scope. `luca_close_call_loop` keeps its own no-show gate.
177
+
178
+ ### Added
179
+
180
+ - Every tool declares the lowest OAuth scope that can call it in
181
+ `_meta.securitySchemes`. On the hosted server, a call that fails because the
182
+ token was revoked or its scopes fall short returns
183
+ `_meta["mcp/www_authenticate"]`, so a client such as ChatGPT can ask the coach
184
+ to reconnect. See [docs/remote.md](./docs/remote.md#per-tool-scopes-and-re-authorization).
185
+ - Two prompts, `luca-plan-campaign` and `luca-plan-broadcast`, which take a
186
+ campaign or broadcast through a dry run or preview before anything goes live.
187
+ - Server instructions, returned on `initialize`, naming the starting tools, the
188
+ confirm rule, and how to treat lead-authored text.
189
+ - Cancelling a tool call now aborts the request it has in flight.
190
+ - `luca_book_call` takes an optional `bookingTypeId` and sends it with the
191
+ booking. The API requires one for a `google_calendar` booking, so that
192
+ provider could not be booked through the tool before.
193
+ - Two tools: `luca_learning_ab_tests_list` and `luca_learning_ab_tests_get`
194
+ read the coach's A/B tests and per-variant results. Creating, running, and
195
+ adopting a test stay in the Luca app.
196
+ - Four channel tools under the new `channels:read` API-key scope, granted from
197
+ `luca:read` up: `luca_channels_list` and `luca_channels_get` read the coach's
198
+ connected accounts with their status and health, `luca_channels_catalog`
199
+ says which channels can be connected yet, and `luca_channels_features` says
200
+ what each provider supports. Connecting, pausing, and repairing an account
201
+ stay in the Luca app.
202
+ - The 15 task tools declare an `outputSchema`, so a client can read their
203
+ `structuredContent` without guessing its shape.
204
+ - `createRemoteResponder` in `@setluca/mcp/remote` returns the remote handler as
205
+ an Effect, for a host that runs its own Effect program around each request.
206
+ `createRemoteHandler` still returns a plain fetch handler.
207
+ - `createLocalRateLimits` in `@setluca/mcp/remote` builds the in-memory
208
+ limiters, and `createRemoteHandler` and `createRemoteResponder` take them as
209
+ an optional `rateLimits`, so a host can keep one set across requests.
210
+
211
+ ### Changed
212
+
213
+ - A tool call stops after 45 seconds in total, retries and waits included, and
214
+ each attempt still stops after 30. A write that times out says its outcome is
215
+ unknown and names the idempotency key that makes a second send safe. The
216
+ timeout error carries the last 429 or 5xx the call saw.
217
+ - A 429, 502, 503, or 504 retries even when its body is HTML or plain text, and
218
+ honors `Retry-After`. It used to fail as a decode error.
219
+ - A paged list that stops early keeps the pages it fetched and leads with a
220
+ `warning` that says the list is incomplete and how to fetch the rest. It
221
+ stops when a page fails or can't be read, when the server repeats a cursor
222
+ or sends an empty page with one, or after 120 seconds in total. A later page
223
+ refused with 401, or 403 for a missing scope, fails the whole call instead,
224
+ so the client gets the challenge that sends it back through sign-in.
225
+ - Lead-authored text is marked untrusted per operation instead of only on
226
+ reads, so a write that returns lead text is framed too. 51 operations are
227
+ framed, among them every booking route that returns the invitee's
228
+ cancellation reason (`luca_bookings_list`, `luca_bookings_get`,
229
+ `luca_bookings_create`, `luca_bookings_update`, `luca_bookings_cancel`,
230
+ `luca_bookings_lifecycle_attention`, and
231
+ `luca_bookings_lifecycle_resolution`), `luca_bookings_needs_outcome`,
232
+ `luca_analytics_ghosted_leads`, and the five lead writes that echo the lead
233
+ record back: `luca_leads_create`, `luca_leads_update`,
234
+ `luca_leads_import_preview`, `luca_leads_import_apply`, and
235
+ `luca_leads_identities_attach`. Those five now list `full_content` as their
236
+ data sensitivity. `luca_book_call`, `luca_reschedule_call`, and
237
+ `luca_analytics_deep_dive` are framed as well.
238
+ - A task tool that returns partial results fails the whole call when the API
239
+ answers 401, or 403 with `scope_required` or `needs_scope`, so the agent
240
+ sees a credentials problem instead of a missing section. This covers the
241
+ three analytics task tools, `luca_close_call_loop`, the explanations of
242
+ `luca_triage_inbox`, and the guest links of `luca_reschedule_call`. Other failures still come back as a section error.
243
+ - `luca_book_call` and `luca_reschedule_call` skip the availability check when
244
+ the call carries an `idempotencyKey`, so a retry of a booking that went
245
+ through reaches the API's replay instead of reporting the slot taken.
246
+ `luca_book_call` sends `provider` with the booking; `luca_reschedule_call`
247
+ uses it only for the availability check and keeps the booking's provider.
248
+ - `luca_book_call` with a `bookingTypeId` checks the slot against that booking
249
+ type's own schedule instead of the calendar provider's free time. An
250
+ availability check refused with 401, or 403 for a missing scope, fails the
251
+ call instead of booking unchecked.
252
+ - `luca_close_call_loop` says what filing starts: no-show recovery, a call
253
+ summary that can sync to the CRM, and a CRM deal-stage update.
254
+ - The workflow prompts point at the task tools, such as
255
+ `luca_morning_report` and `luca_draft_reply`, instead of the raw operations.
256
+ - Completion suggestions match the typed prefix in any case.
257
+ - `luca-rescue-silent-leads` applies its `days` window to the conversation
258
+ list step, not only to the heading.
259
+ - `luca_analytics_deep_dive` no longer requires `question`.
260
+ - `luca_close_call_loop` reports a failed call-event lookup as
261
+ `feedbackError`. It used to read as "no call event for this booking".
262
+ - A failed resource read answers with a JSON-RPC error: resource-not-found for
263
+ a missing record, an invalid request carrying the same
264
+ `mcp/www_authenticate` challenge a tool error carries for a refused token or
265
+ missing scope, and an internal error for anything else.
266
+ - An API response that is not JSON is reported by its length. The error no
267
+ longer quotes the body, text no Luca schema shaped.
268
+ - Calls to the Luca API refuse to follow redirects, so a key or token is never
269
+ sent to a host the config did not name.
270
+ - The hosted server:
271
+ - checks an OAuth token within 5 seconds and answers 503 if it cannot;
272
+ - treats only a 401 from the token check as a refused token, so a misrouted
273
+ or overloaded API no longer sends every client back through sign-in;
274
+ - limits each bearer token to 60 requests a minute, so coaches who
275
+ share an address no longer share a budget, and also limits requests with
276
+ a token to 600 a minute per address, so made-up tokens run out too;
277
+ - limits requests without a token to 600 a minute per address;
278
+ - refuses a token over 4,096 characters, or an OAuth token that is not
279
+ three base64url segments, with 401 `invalid_token` and no API call;
280
+ - leaves `scope` out of an `invalid_token` challenge, so a client that
281
+ reconnects asks for the scope it had rather than a lower one;
282
+ - answers 429 with "Too many requests";
283
+ - reads the client address from `CF-Connecting-IP` only and ignores
284
+ `X-Forwarded-For`, which any client can set;
285
+ - answers 500 to every request when a production deploy is missing any of
286
+ its three rate-limit bindings, or when `MCP_PUBLIC_ORIGIN` is not an
287
+ http(s) origin with no path;
288
+ - serves its discovery documents to any origin, CORS preflight included.
289
+ - The package no longer depends on Zod directly.
290
+ - Tool input schemas now advertise closed objects
291
+ (`additionalProperties: false`) and keep each field's format, title, and
292
+ description.
293
+ - The docs describe Luca as a DM sales platform.
294
+ - The hosted Worker reads `LUCA_AUTH_HEADER` and `LUCA_REQUEST_TIMEOUT_MS` with
295
+ the stdio parser. `bearer` now works there, and an unknown value answers 500
296
+ instead of sending keys in a header the API ignores.
297
+ - The `WWW-Authenticate` challenge on a tool error now carries
298
+ `realm="luca-mcp"`, like the transport's 401.
299
+ - A tool call missing a path parameter fails as a tool input error before any
300
+ request is sent.
301
+ - Prompts no longer use em dashes, and every workflow prompt ends with the same
302
+ safety rules.
303
+ - A task tool's read-only, confirm, and untrusted-content flags now come from
304
+ the operations it composes, so a task tool cannot claim to be safer than the
305
+ routes it calls.
306
+ - Cancelling a resource read aborts the request it has in flight.
307
+ - A write that needs an idempotency key and gets none now mints a fresh key
308
+ each time the call runs, rather than once per built request.
309
+ - A cancelled tool call is logged with `outcome: "interrupted"`.
310
+ - Each tool's `x-luca-redacted-fields` list now comes from the API's
311
+ `sensitive()` markers at generation time instead of a hand-written list, and
312
+ `redaction:check` is gone. The lists did not change.
313
+ - **`luca_reschedule_call` checks the booking's own schedule.** It reads the
314
+ booking first and checks the new time against the slots of its booking type,
315
+ or the provider calendar when the booking has no booking type or cannot be
316
+ read.
317
+ - **More routes frame lead-written text as untrusted.** CRM rows, broadcast
318
+ recipients, campaign enrollments, and integration sync records now carry the
319
+ untrusted-content marker and `full_content` data sensitivity.
320
+ - **`luca_rescue_silent_leads` rejects empty lead ids.**
321
+
322
+ ### Fixed
323
+
324
+ - The hosted server logs `mcp.token_verification_unavailable`, with the error
325
+ message and no token, when the token verifier cannot answer and the request
326
+ gets a 503.
327
+ - Resource registration shares one read path for fixed and templated URIs.
328
+
13
329
  ## [0.2.1] - 2026-08-22
14
330
 
15
331
  ### Added
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Luca
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.