@fidacy/mcp 0.4.0 → 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 +12 -0
- package/dist/core.js +1 -1
- package/dist/index.js +25 -10
- package/dist/lib.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
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
|
+
|
|
9
|
+
## 0.4.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Say what actually goes wrong. The lapse warning used to talk about proof and auditors, which is not what an operator is afraid of. What genuinely switches off when the allowance runs out is the set of checks that ask WHO is spending: agent identity verification, revoked/stolen key detection, and behavioural anomaly detection all run server-side. Local rules keep blocking the wrong payee and the over-cap amount, but a compromised or prompt-injected agent operating inside your own allowlist and under your own cap now pays out unchallenged — real money, irreversible. The warnings now name that, and the trial-expiry warnings name the operational cost of a halted agent plus the trap of "fixing" it by removing the firewall entirely.
|
|
14
|
+
|
|
3
15
|
## 0.4.0
|
|
4
16
|
|
|
5
17
|
### Minor 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.
|
|
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.
|
|
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.
|
|
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");
|
|
@@ -1088,11 +1088,11 @@ function protectionWarning(decisionsUsed2, keyOverride) {
|
|
|
1088
1088
|
return null;
|
|
1089
1089
|
// working and nothing is expiring soon: silence is the respect.
|
|
1090
1090
|
case "protected_low":
|
|
1091
|
-
return `\u26A0 ${BRAND}: ${left} free decision${left === 1 ? "" : "s"} left
|
|
1091
|
+
return `\u26A0 ${BRAND}: ${left} free decision${left === 1 ? "" : "s"} left, then this agent's payments STOP DEAD. Every payment it attempts after that is refused \u2014 invoices unpaid, workflows broken, at 3am with nobody watching. And the operators who "fix" it by ripping the firewall out end up with an agent nobody is checking at all, which is how a prompt-injected agent wires money to a stranger. Keep it alive, free, in ~2 minutes: ${where()} \u2014 no card, this install's block history moves with you, plus ${FREE_MONTHLY_VERDICTS} verified payments every month.`;
|
|
1092
1092
|
case "halted":
|
|
1093
|
-
return `\u26D4 ${BRAND}
|
|
1093
|
+
return `\u26D4 ${BRAND} IS NOT ACTIVATED \u2014 YOUR AGENT CAN NO LONGER PAY ANYTHING. Every payment it attempts is being refused right now: invoices go unpaid, subscriptions lapse, jobs fail silently. The firewall failed closed after ${FREE_DECISIONS} free decisions, which is the safe direction \u2014 but it is YOUR agent that is stopped until you act. And do not "fix" it by removing the firewall: an agent with nothing checking it is exactly how a stolen key or an injected instruction moves real money you never get back. Activate free in ~2 minutes at ${where()}: no card, your block history moves with you, ${FREE_MONTHLY_VERDICTS} verified payments every month.`;
|
|
1094
1094
|
case "unproven":
|
|
1095
|
-
return `\
|
|
1095
|
+
return `\u26D4 ${BRAND}: YOUR AGENT IS EXPOSED RIGHT NOW. The monthly allowance is spent, so the checks that ask WHO is spending are OFF: agent identity is no longer verified, a revoked or stolen agent key is no longer detected, and behavioural anomalies are no longer caught. Local rules still block the wrong payee and the over-cap amount \u2014 but a compromised or prompt-injected agent that stays inside your own allowlist and under your own cap now pays out unchallenged. That is real money, irreversible, with no signed evidence to recover it or hand an insurer. Turn the checks back on: https://app.fidacy.com/billing \u2014 pay-as-you-go, no plan, from $0.04 per payment checked. One prevented payment pays for years of it.`;
|
|
1096
1096
|
case "protected_keyed":
|
|
1097
1097
|
return null;
|
|
1098
1098
|
}
|
|
@@ -1105,7 +1105,7 @@ function protectionBootLine(decisionsUsed2) {
|
|
|
1105
1105
|
case "protected_low":
|
|
1106
1106
|
return ` \u26A0 ${Math.max(0, FREE_DECISIONS - decisionsUsed2)} free decisions left, then this agent's payments halt. Free key: ${where()}`;
|
|
1107
1107
|
case "unproven":
|
|
1108
|
-
return ` \
|
|
1108
|
+
return ` \u26D4 EXPOSED \u2014 identity, revocation and anomaly checks are OFF (allowance spent). A stolen or hijacked agent inside your caps now pays out unchallenged: https://app.fidacy.com/billing`;
|
|
1109
1109
|
case "protected_trial":
|
|
1110
1110
|
return ` ${BRAND} active \u2014 ${decisionsUsed2}/${FREE_DECISIONS} free decisions used.`;
|
|
1111
1111
|
case "protected_keyed":
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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)",
|