@fidacy/mcp 0.4.1 → 0.4.2

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,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Critical for Claude Code users: the firewall's messages now actually reach the model. MCP hosts disagree about which half of a tool result the model sees. Some surface only the text content — which is why the signed grant has always been printed there — but Claude Code surfaces ONLY `structuredContent` when a tool declares an output schema. Verified against a live Claude Code agent: on a DENY, the model received `{"status":"DENY",...}` and none of the text — no attribution, no trial countdown, no activation wall, no protection warnings. Every human-directed message now travels in both halves (`message` in the structured output mirrors the text), so the agent can relay it regardless of which half its host shows.
8
+
3
9
  ## 0.4.1
4
10
 
5
11
  ### Patch Changes
package/dist/core.js CHANGED
@@ -486,7 +486,7 @@ function resolveMandateRules(cfg) {
486
486
  }
487
487
 
488
488
  // src/telemetry.ts
489
- var CLIENT_VERSION = true ? "0.4.1" : "dev";
489
+ var CLIENT_VERSION = true ? "0.4.2" : "dev";
490
490
  function bandOf(amount) {
491
491
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
492
492
  if (amount < 10) return "lt10";
package/dist/index.js CHANGED
@@ -518,7 +518,7 @@ function resolveMandateRules(cfg) {
518
518
  }
519
519
 
520
520
  // src/telemetry.ts
521
- var CLIENT_VERSION = true ? "0.4.1" : "dev";
521
+ var CLIENT_VERSION = true ? "0.4.2" : "dev";
522
522
  function bandOf(amount) {
523
523
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
524
524
  if (amount < 10) return "lt10";
@@ -880,7 +880,7 @@ async function findArtifacts(sha2562, cfg) {
880
880
  }
881
881
 
882
882
  // src/provision.ts
883
- var CLIENT_VERSION2 = true ? "0.4.1" : "dev";
883
+ var CLIENT_VERSION2 = true ? "0.4.2" : "dev";
884
884
  function provisionEnabled() {
885
885
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
886
886
  return !(v === "1" || v === "true" || v === "yes");
@@ -1381,7 +1381,7 @@ function renderAlertLine(alerts) {
1381
1381
  var state = ensureState();
1382
1382
  var core = makeCore();
1383
1383
  var subject = process.env.FIDACY_SUBJECT ?? "agent:demo";
1384
- var SERVER_VERSION = true ? "0.4.1" : "dev";
1384
+ var SERVER_VERSION = true ? "0.4.2" : "dev";
1385
1385
  var bootClaim = claimUrl();
1386
1386
  var server = new McpServer(
1387
1387
  // The commercial identity. `name` is the stable slug hosts key on; `title` is
@@ -1415,15 +1415,30 @@ server.registerTool(
1415
1415
  status: z.enum(["ALLOW", "DENY"]),
1416
1416
  decisionId: z.string(),
1417
1417
  grant: z.string().optional(),
1418
- violatedRule: z.string().optional()
1418
+ violatedRule: z.string().optional(),
1419
+ /**
1420
+ * The full human-directed message, duplicated from the text content ON
1421
+ * PURPOSE. Hosts disagree about which half of a tool result the model
1422
+ * sees: some surface only the text (which is why the grant is printed
1423
+ * there), and Claude Code surfaces ONLY structuredContent when an
1424
+ * outputSchema is declared — verified live 2026-07-25, where the model
1425
+ * received exactly {"status":"DENY",...} and none of the text. Anything
1426
+ * that must reach the model must therefore travel in BOTH halves.
1427
+ */
1428
+ message: z.string()
1419
1429
  },
1420
1430
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }
1421
1431
  },
1422
1432
  async (args) => {
1423
1433
  const gate = activationGate();
1424
1434
  if (gate) {
1425
- const out2 = { status: "DENY", decisionId: "activation_required", violatedRule: "activation_required" };
1426
1435
  const halted = protectionWarning(decisionsUsed()) ?? gate.message;
1436
+ const out2 = {
1437
+ status: "DENY",
1438
+ decisionId: "activation_required",
1439
+ violatedRule: "activation_required",
1440
+ message: halted
1441
+ };
1427
1442
  return { content: [{ type: "text", text: halted }], structuredContent: out2 };
1428
1443
  }
1429
1444
  const req = args;
@@ -1434,7 +1449,6 @@ server.registerTool(
1434
1449
  } catch {
1435
1450
  }
1436
1451
  const d = await core.decide(req, subject);
1437
- const out = { status: d.status, decisionId: d.decisionId, grant: d.grant, violatedRule: d.violatedRule };
1438
1452
  const sentinelLine = d.status === "ALLOW" ? renderAlertLine(sentinelAlerts) : "";
1439
1453
  const base = d.status === "ALLOW" ? `ALLOW (decision ${d.decisionId})${req.invoiceRef ? ` for invoice ${req.invoiceRef}` : ""}.${sentinelLine} To settle, call execute_payment with the SAME payee, amount, currency, and idempotencyKey, and set "grant" to EXACTLY this signed value:
1440
1454
  ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}. No grant issued, this payment cannot proceed. The denial itself is recorded in the tamper-evident, hash-chained audit: call get_audit_proof with decisionId ${d.decisionId} for the proof of what was blocked.${d.violatedRule?.startsWith("payee_not_in_allowlist") ? ` If the user trusts this payee, add "${req.payee}" to mandate.payees in ~/.fidacy/config.json and retry: the firewall picks the change up on the next call, no restart needed.` : d.violatedRule?.startsWith("category_not_allowed") ? ` If the user considers "${req.category}" a legitimate purpose for this agent, add it to mandate.categories in ~/.fidacy/config.json and retry: the firewall picks the change up on the next call, no restart needed.` : d.violatedRule?.startsWith("currency_not_allowed") ? ` The mandate is single-currency and set to another one (mandate.currency in ~/.fidacy/config.json, default USD). If this agent legitimately pays in ${req.currency}, update it there and retry: hot-reloaded, no restart.` : ""}`;
@@ -1443,6 +1457,7 @@ ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}
1443
1457
  const countdown = trialCountdownLine();
1444
1458
  const lapse = isHostedLapsed() ? protectionWarning(decisionsUsed()) : "";
1445
1459
  const human = [badge, base, nudge, countdown, lapse].filter(Boolean).join(" ");
1460
+ const out = { status: d.status, decisionId: d.decisionId, grant: d.grant, violatedRule: d.violatedRule, message: human };
1446
1461
  return { content: [{ type: "text", text: human }], structuredContent: out };
1447
1462
  }
1448
1463
  );
package/dist/lib.js CHANGED
@@ -641,7 +641,7 @@ function resolveMandateRules(cfg) {
641
641
  }
642
642
 
643
643
  // src/telemetry.ts
644
- var CLIENT_VERSION = true ? "0.4.1" : "dev";
644
+ var CLIENT_VERSION = true ? "0.4.2" : "dev";
645
645
  function bandOf(amount) {
646
646
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
647
647
  if (amount < 10) return "lt10";
@@ -829,7 +829,7 @@ function requestUpgrade() {
829
829
  }
830
830
 
831
831
  // src/provision.ts
832
- var CLIENT_VERSION2 = true ? "0.4.1" : "dev";
832
+ var CLIENT_VERSION2 = true ? "0.4.2" : "dev";
833
833
  function provisionEnabled() {
834
834
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
835
835
  return !(v === "1" || v === "true" || v === "yes");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fidacy/mcp",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Fidacy action firewall for AI agents. Mandate-gated payment authorization as an MCP server.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Fidacy (ZeepCode Group Technology LLC) <hello@fidacy.com> (https://fidacy.com)",