@leadbay/mcp 0.34.0 → 0.34.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
@@ -1,5 +1,43 @@
1
1
  # Changelog — @leadbay/mcp
2
2
 
3
+ ## 0.34.1 — 2026-09-07
4
+
5
+ `leadbay_set_lead_status` threw `TypeError: (params.lead_ids ?? []).filter is
6
+ not a function` five times in 29 s on the hosted route (product#4079, Sentry
7
+ MCP-3T): a scheduled agent passed `lead_ids` as something other than an array.
8
+ The same class hit `leadbay_new_lens` on 2026-08-04 (`texts.filter is not a
9
+ function`, `locations` as a string, MCP-3B). Both times the agent retried the
10
+ identical call four or five times, because a raw TypeError names no argument.
11
+
12
+ The CallTool handler in `server.ts` never checked `inputSchema` before
13
+ `tool.execute`; its own comment said enforcement was ours, and it only enforced
14
+ `_triggered_by`. 32 tools declare a top-level `array` or `object` parameter and
15
+ none of them defended against a string: four threw, two silently corrupted
16
+ (`adjust_audience` spread `"tech"` into `["t","e","c","h"]`), the rest forwarded
17
+ the string to the backend.
18
+
19
+ One guard now runs before `execute`, after the `LAST_PROMPT_REQUIRED` check
20
+ (`findShapeMismatch`): for each top-level schema property whose `type` is
21
+ `array` or `object`, a present non-null argument of the wrong JSON shape returns
22
+ `BAD_INPUT` naming the field, the expected shape and the received type
23
+ (`lead_ids must be a JSON array (got string)`), through the same envelope branch
24
+ a tool's own `BAD_INPUT` takes. So PostHog sees `ok:false error_code:BAD_INPUT`
25
+ and Sentry groups the event with the tool's existing `BAD_INPUT` issue
26
+ (`source:business`) instead of a `source:unexpected` stack fingerprint.
27
+
28
+ Deliberately not done: no coercion of a bare string into a one-element array (a
29
+ JSON-stringified array would become one bogus id and fail per lead inside
30
+ `failed[]`); no scalar checks (hosts send `"20"` for a number and tools coerce);
31
+ no `required`, `items`, nested or `additionalProperties` validation; no runtime
32
+ dependency. `null` counts as absent. One visible change on an existing tool:
33
+ `leadbay_report_outreach` with `verification` given as a string answered
34
+ `VERIFICATION_REQUIRED`; it now answers `BAD_INPUT … must be a JSON object (got
35
+ string)`. Tools executed directly outside the MCP server (OpenClaw consumes
36
+ `@leadbay/core` without `buildServer`) keep the raw throw.
37
+
38
+ Test: `packages/mcp/test/unit/input-shape-guard.test.ts` drives `tools/call`
39
+ through `buildServer` with the incident shapes.
40
+
3
41
  ## 0.34.0 — 2026-09-02
4
42
 
5
43
  The OpenAI app directory rejects an app that sells digital goods — "plugins may
package/dist/bin.js CHANGED
@@ -26300,6 +26300,40 @@ function extractTriggeredBy(args) {
26300
26300
  const trimmed = raw.length > 500 ? `${raw.slice(0, 500)}\u2026` : raw;
26301
26301
  return { triggered_by: trimmed, cleaned };
26302
26302
  }
26303
+ function findShapeMismatch(tool, args) {
26304
+ const schema = tool.inputSchema;
26305
+ if (!schema || schema.type !== "object") return void 0;
26306
+ const props = schema.properties;
26307
+ if (!props || typeof props !== "object") return void 0;
26308
+ for (const [key, spec] of Object.entries(props)) {
26309
+ if (key === TRIGGERED_BY_FIELD || !spec || typeof spec !== "object") continue;
26310
+ const declared = spec.type;
26311
+ if (declared !== "array" && declared !== "object") continue;
26312
+ const value = args[key];
26313
+ if (value === void 0 || value === null) continue;
26314
+ const isArray = Array.isArray(value);
26315
+ const got = isArray ? "array" : typeof value;
26316
+ if (declared === "array" && !isArray) {
26317
+ const items = spec.items;
26318
+ const of = typeof items?.type === "string" ? ` of ${items.type}s` : "";
26319
+ return {
26320
+ error: true,
26321
+ code: "BAD_INPUT",
26322
+ message: `${key} must be a JSON array (got ${got})`,
26323
+ hint: `Re-call with ${key} as a JSON array${of}, e.g. ${key}: ["\u2026"]. Do not JSON-stringify the array.`
26324
+ };
26325
+ }
26326
+ if (declared === "object" && (typeof value !== "object" || isArray)) {
26327
+ return {
26328
+ error: true,
26329
+ code: "BAD_INPUT",
26330
+ message: `${key} must be a JSON object (got ${got})`,
26331
+ hint: `Re-call with ${key} as a JSON object, e.g. ${key}: {\u2026}.`
26332
+ };
26333
+ }
26334
+ }
26335
+ return void 0;
26336
+ }
26303
26337
  function toolsListPayload(tools2) {
26304
26338
  return tools2.map((t) => {
26305
26339
  const out = {
@@ -26680,7 +26714,8 @@ ${url}
26680
26714
  isError: true
26681
26715
  };
26682
26716
  }
26683
- const result = await runWithRequestSignal(extra.signal, () => tool.execute(client, args, {
26717
+ const shapeError = findShapeMismatch(tool, args);
26718
+ const result = shapeError ?? await runWithRequestSignal(extra.signal, () => tool.execute(client, args, {
26684
26719
  logger: opts.logger,
26685
26720
  bulkTracker: opts.bulkTracker,
26686
26721
  notificationsInbox: opts.notificationsInbox,
@@ -28366,7 +28401,7 @@ var OAUTH_BASE_URLS = {
28366
28401
  fr: "https://staging.api.leadbay.app"
28367
28402
  }
28368
28403
  };
28369
- var VERSION = "0.34.0";
28404
+ var VERSION = "0.34.1";
28370
28405
  var HELP = `
28371
28406
  leadbay-mcp ${VERSION} \u2014 Leadbay Model Context Protocol server
28372
28407
 
@@ -24319,6 +24319,40 @@ function extractTriggeredBy(args) {
24319
24319
  const trimmed = raw.length > 500 ? `${raw.slice(0, 500)}\u2026` : raw;
24320
24320
  return { triggered_by: trimmed, cleaned };
24321
24321
  }
24322
+ function findShapeMismatch(tool, args) {
24323
+ const schema = tool.inputSchema;
24324
+ if (!schema || schema.type !== "object") return void 0;
24325
+ const props = schema.properties;
24326
+ if (!props || typeof props !== "object") return void 0;
24327
+ for (const [key, spec] of Object.entries(props)) {
24328
+ if (key === TRIGGERED_BY_FIELD || !spec || typeof spec !== "object") continue;
24329
+ const declared = spec.type;
24330
+ if (declared !== "array" && declared !== "object") continue;
24331
+ const value = args[key];
24332
+ if (value === void 0 || value === null) continue;
24333
+ const isArray = Array.isArray(value);
24334
+ const got = isArray ? "array" : typeof value;
24335
+ if (declared === "array" && !isArray) {
24336
+ const items = spec.items;
24337
+ const of = typeof items?.type === "string" ? ` of ${items.type}s` : "";
24338
+ return {
24339
+ error: true,
24340
+ code: "BAD_INPUT",
24341
+ message: `${key} must be a JSON array (got ${got})`,
24342
+ hint: `Re-call with ${key} as a JSON array${of}, e.g. ${key}: ["\u2026"]. Do not JSON-stringify the array.`
24343
+ };
24344
+ }
24345
+ if (declared === "object" && (typeof value !== "object" || isArray)) {
24346
+ return {
24347
+ error: true,
24348
+ code: "BAD_INPUT",
24349
+ message: `${key} must be a JSON object (got ${got})`,
24350
+ hint: `Re-call with ${key} as a JSON object, e.g. ${key}: {\u2026}.`
24351
+ };
24352
+ }
24353
+ }
24354
+ return void 0;
24355
+ }
24322
24356
  function toolsListPayload(tools2) {
24323
24357
  return tools2.map((t) => {
24324
24358
  const out = {
@@ -24699,7 +24733,8 @@ ${url}
24699
24733
  isError: true
24700
24734
  };
24701
24735
  }
24702
- const result = await runWithRequestSignal(extra.signal, () => tool.execute(client, args, {
24736
+ const shapeError = findShapeMismatch(tool, args);
24737
+ const result = shapeError ?? await runWithRequestSignal(extra.signal, () => tool.execute(client, args, {
24703
24738
  logger: opts.logger,
24704
24739
  bulkTracker: opts.bulkTracker,
24705
24740
  notificationsInbox: opts.notificationsInbox,
@@ -25130,7 +25165,7 @@ function parseWriteEnv(env = process.env) {
25130
25165
  }
25131
25166
 
25132
25167
  // src/http-server.ts
25133
- var VERSION = true ? "0.34.0" : "0.0.0-dev";
25168
+ var VERSION = true ? "0.34.1" : "0.0.0-dev";
25134
25169
  var PORT = Number(process.env.PORT ?? 8080);
25135
25170
  var HOST = process.env.HOST ?? "0.0.0.0";
25136
25171
  var logger = {
@@ -25263,6 +25298,7 @@ function buildServerFromClient(client, requestTelemetry, resourcePath) {
25263
25298
  telemetry: requestTelemetry
25264
25299
  });
25265
25300
  }
25301
+ var OPENAI_APPS_CHALLENGE = process.env.OPENAI_APPS_CHALLENGE ?? "vsJ51IZ_3AqM10YZxXQFmB29IGIMDOZGbpbEgRIT4r4";
25266
25302
  var PRM_PREFIX = "/.well-known/oauth-protected-resource";
25267
25303
  var RESOURCE_PATHS = ["/mcp", "/sse", "/fr/mcp", "/fr/sse", "/chatgpt/mcp"];
25268
25304
  function requestOrigin(c) {
@@ -25310,6 +25346,10 @@ function sendChallenge(c, resourcePath, authState) {
25310
25346
  }
25311
25347
  var app = new Hono();
25312
25348
  app.get("/healthz", (c) => c.json({ ok: true, version: VERSION }));
25349
+ app.get(
25350
+ "/.well-known/openai-apps-challenge",
25351
+ (c) => c.text(OPENAI_APPS_CHALLENGE, 200, { "Cache-Control": "no-store" })
25352
+ );
25313
25353
  app.get(PRM_PREFIX, (c) => servePrm(c, "/mcp"));
25314
25354
  app.get(`${PRM_PREFIX}/*`, (c) => {
25315
25355
  const suffix = c.req.path.slice(PRM_PREFIX.length);
@@ -1805,7 +1805,7 @@ var init_installer_gui = __esm({
1805
1805
  init_install_dxt();
1806
1806
  init_install_shared();
1807
1807
  init_oauth();
1808
- VERSION = true ? "0.34.0" : "0.0.0-dev";
1808
+ VERSION = true ? "0.34.1" : "0.0.0-dev";
1809
1809
  MESSAGES = {
1810
1810
  en: {
1811
1811
  installer: {
@@ -1068,7 +1068,7 @@ async function oauthLogin(opts) {
1068
1068
  }
1069
1069
 
1070
1070
  // installer/installer-gui.ts
1071
- var VERSION = true ? "0.34.0" : "0.0.0-dev";
1071
+ var VERSION = true ? "0.34.1" : "0.0.0-dev";
1072
1072
  var MESSAGES = {
1073
1073
  en: {
1074
1074
  installer: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leadbay/mcp",
3
- "version": "0.34.0",
3
+ "version": "0.34.1",
4
4
  "mcpName": "io.github.leadbay/leadbay-mcp",
5
5
  "description": "Model Context Protocol (MCP) server for Leadbay — AI lead discovery, qualification, and enrichment for Claude Desktop, Cursor, and Claude Code.",
6
6
  "type": "module",