@setluca/mcp 0.3.0 → 0.3.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 CHANGED
@@ -10,6 +10,21 @@ input schema, or changing what a tool does to a workspace.
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [0.3.1] - 2026-10-06
14
+
15
+ ### Fixed
16
+
17
+ - Restore authenticated remote MCP discovery and tool calls on Cloudflare.
18
+ Token checks and tool requests now use a direct service binding to Luca's API,
19
+ avoiding the public API's WAF challenge. Both paths use Cloudflare's supported
20
+ manual redirect mode and still reject redirected responses without forwarding
21
+ credentials.
22
+
23
+ ### Changed
24
+
25
+ - The remote tool catalog includes explicit safety annotations for directory
26
+ review. The protocol suite checks the published `tools/list` shape.
27
+
13
28
  ## [0.3.0] - 2026-10-05
14
29
 
15
30
  This is the first Luca MCP release from its own repository. The package and
package/dist/http.js CHANGED
@@ -136,17 +136,18 @@ function errorBody(text) {
136
136
  */
137
137
  const describeUrl = (url) => `${url.origin}${url.pathname}`;
138
138
  /** One trip to the API, with no retry and no timeout of its own. */
139
- function sendOnce(config, input, requestIdempotencyKey) {
139
+ function sendOnce(config, input, requestIdempotencyKey, fetchImpl) {
140
140
  return Effect.gen(function* () {
141
141
  const url = yield* buildUrl(config, input);
142
142
  const init = buildRequestInit(config, input, requestIdempotencyKey);
143
143
  const response = yield* Effect.tryPromise({
144
144
  try: (signal) =>
145
145
  // fallow-ignore-next-line security-sink -- the origin is the configured Luca api base url and the path comes from the generated operation manifest with its params encoded, so a tool argument can never move the destination host
146
- fetch(url, {
146
+ fetchImpl(url, {
147
147
  ...init,
148
- // A redirect would carry the API key to a host we never named.
149
- redirect: "error",
148
+ // Cloudflare supports manual redirects, not `error`. A 3xx response
149
+ // falls through as an HTTP error without forwarding the API key.
150
+ redirect: "manual",
150
151
  signal,
151
152
  }),
152
153
  catch: (cause) => new LucaNetworkError({
@@ -190,12 +191,12 @@ function sendOnce(config, input, requestIdempotencyKey) {
190
191
  }));
191
192
  });
192
193
  }
193
- export function createLucaApi(config) {
194
+ export function createLucaApi(config, fetchImpl = fetch) {
194
195
  const timeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
195
196
  return {
196
197
  // The key each attempt carries is minted by `resilient`, which also decides
197
198
  // whether a failure may be replayed under it.
198
- request: (input) => resilient(input, timeoutMs, (key) => sendOnce(config, input, key)),
199
+ request: (input) => resilient(input, timeoutMs, (key) => sendOnce(config, input, key, fetchImpl)),
199
200
  };
200
201
  }
201
202
  export const LucaApiLive = Layer.effect(LucaApi, Effect.gen(function* () {
package/dist/remote.js CHANGED
@@ -168,7 +168,7 @@ export function createRemoteResponder(options) {
168
168
  return yield* Effect.fail(invalidToken(options.resourceMetadataUrl));
169
169
  }
170
170
  const handler = createMcpHandler(() => createLucaServer({
171
- lucaLayer: Layer.succeed(LucaApi, createLucaApi(config.value)),
171
+ lucaLayer: Layer.succeed(LucaApi, createLucaApi(config.value, options.apiFetch)),
172
172
  ...optionalField("toolset", options.toolset),
173
173
  ...optionalField("resourceMetadataUrl", options.resourceMetadataUrl),
174
174
  }), { ...optionalField("onerror", options.onError) });
@@ -16,8 +16,9 @@ export const sha256Hex = (value) => Effect.tryPromise(() => crypto.subtle.digest
16
16
  * One bounded call to the Luca API that carries a token, with `read` turning
17
17
  * the response into the verdict. Timeout but no retry: this is on the auth
18
18
  * path of every request, and the client retries a 503 on its own schedule. A
19
- * redirect is an error, because it would carry the token to a host we never
20
- * named.
19
+ * redirect is refused, because it would carry the token to a host we never
20
+ * named. Cloudflare Workers does not support `redirect: "error"`, so fetch
21
+ * uses `manual` and we reject 3xx responses before the resolver reads them.
21
22
  */
22
23
  export function callApi(settings, fetchImpl, request, read) {
23
24
  const controller = new AbortController();
@@ -25,11 +26,15 @@ export function callApi(settings, fetchImpl, request, read) {
25
26
  return Effect.tryPromise({
26
27
  try: () => fetchImpl(`${settings.apiBaseUrl}${request.path}`, {
27
28
  ...request.init,
28
- redirect: "error",
29
+ redirect: "manual",
29
30
  signal: controller.signal,
30
31
  }),
31
32
  catch: unavailable("Could not reach token verification"),
32
- }).pipe(Effect.flatMap(read), Effect.timeoutOrElse({
33
+ }).pipe(Effect.flatMap((response) => response.status >= 300 && response.status < 400
34
+ ? Effect.fail(new TokenVerificationUnavailable({
35
+ message: "Token verification redirected",
36
+ }))
37
+ : read(response)), Effect.timeoutOrElse({
33
38
  duration: Duration.millis(timeoutMs),
34
39
  orElse: () => Effect.fail(new TokenVerificationUnavailable({
35
40
  message: `Token verification did not answer within ${timeoutMs}ms`,
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.3.0";
12
+ export const LUCA_MCP_VERSION = "0.3.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.3.0",
3
+ "version": "0.3.1",
4
4
  "private": false,
5
5
  "description": "Let your AI assistant help with Luca conversations, leads, replies, campaigns, and bookings.",
6
6
  "keywords": [