revclamp-mcp 1.20.7 → 1.20.9
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/README.md +2 -2
- package/index.mjs +215 -6
- package/local_engine.mjs +659 -90
- package/mcp-config.json +12 -1
- package/package.json +8 -2
- package/server.json +13 -13
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ Add `revclamp-mcp` to your `claude_desktop_config.json` or `.cursor/mcp.json`. B
|
|
|
41
41
|
"mcpServers": {
|
|
42
42
|
"revclamp": {
|
|
43
43
|
"command": "npx",
|
|
44
|
-
"args": ["-y", "github:
|
|
44
|
+
"args": ["-y", "github:lhphong09t2/revclamp-mcp"]
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
}
|
|
@@ -59,7 +59,7 @@ curl -sS -X POST https://api.revclamp.com/api/mcp \
|
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
61
|
# Run a <60s local CLI smoke check (verifies all 9 tool schemas & executes a local_engine.mjs sample audit with zero API key)
|
|
62
|
-
npx -y github:
|
|
62
|
+
npx -y github:lhphong09t2/revclamp-mcp#v1.20.9 --self-test
|
|
63
63
|
|
|
64
64
|
# List all 9 MCP tools and JSON schemas (runs locally in-process)
|
|
65
65
|
node index.mjs --list-tools
|
package/index.mjs
CHANGED
|
@@ -20,6 +20,7 @@ import { fileURLToPath } from "node:url";
|
|
|
20
20
|
import {
|
|
21
21
|
LOCAL_MCP_TOOLS,
|
|
22
22
|
MCP_PROTOCOL_VERSION,
|
|
23
|
+
normalizeMcpToolArgs,
|
|
23
24
|
executeLocalMcpTool,
|
|
24
25
|
executeLocalMcpJsonRpc,
|
|
25
26
|
} from "./local_engine.mjs";
|
|
@@ -27,11 +28,12 @@ import {
|
|
|
27
28
|
export {
|
|
28
29
|
LOCAL_MCP_TOOLS,
|
|
29
30
|
MCP_PROTOCOL_VERSION,
|
|
31
|
+
normalizeMcpToolArgs,
|
|
30
32
|
executeLocalMcpTool,
|
|
31
33
|
executeLocalMcpJsonRpc,
|
|
32
34
|
};
|
|
33
35
|
|
|
34
|
-
export const PACKAGE_VERSION = "1.20.
|
|
36
|
+
export const PACKAGE_VERSION = "1.20.9";
|
|
35
37
|
export const DEFAULT_MCP_ENDPOINT = "https://api.revclamp.com/api/mcp";
|
|
36
38
|
export const ALLOWED_MCP_ORIGINS = Object.freeze(
|
|
37
39
|
new Set(["https://api.revclamp.com", "https://revclamp.com"])
|
|
@@ -41,7 +43,7 @@ export const MAX_STDIO_LINE_BYTES = MAX_PAYLOAD_BYTES;
|
|
|
41
43
|
export const REQUEST_TIMEOUT_MS = 10_000;
|
|
42
44
|
|
|
43
45
|
const LIVE_SECRET_GUARD_RE =
|
|
44
|
-
/(?:[sr]k_live_[A-Za-z0-9_-]{4,}|whsec_live_[A-Za-z0-9_-]{4,}|whsec_(?!test_|synth_|sandbox_)[A-Za-z0-9_-]{10,}|shpss_live_[A-Za-z0-9_-]{4,}|shpss_[0-9a-fA-F]{16,}|shp(?:at|ua|ca|pa)_[A-Za-z0-9_-]{8,}|lsq_[A-Za-z0-9_-]{4,}|r[gc]_live_[A-Za-z0-9_-]{4,}|AKIA[0-9A-Z]{12,}|-----BEGIN [A-Z ]*PRIVATE KEY-----)/i;
|
|
46
|
+
/(?:[sr]k_live_[A-Za-z0-9_-]{4,}|whsec_live_[A-Za-z0-9_-]{4,}|whsec_(?!test_|synth_|sandbox_|demo_|sample_|mock_|example_|fake_|dummy_)[A-Za-z0-9_-]{10,}|shpss_live_[A-Za-z0-9_-]{4,}|shpss_[0-9a-fA-F]{16,}|shp(?:at|ua|ca|pa)_[A-Za-z0-9_-]{8,}|lsq_[A-Za-z0-9_-]{4,}|r[gc]_live_[A-Za-z0-9_-]{4,}|AKIA[0-9A-Z]{12,}|-----BEGIN [A-Z ]*PRIVATE KEY-----)/i;
|
|
45
47
|
|
|
46
48
|
export function toSafeRpcId(rawId) {
|
|
47
49
|
if (typeof rawId === "number" && Number.isFinite(rawId)) {
|
|
@@ -314,6 +316,37 @@ export async function forwardMcpJsonRpc(
|
|
|
314
316
|
},
|
|
315
317
|
};
|
|
316
318
|
}
|
|
319
|
+
if (methodStr === "tools/call" && rpcRequest.params.arguments !== undefined) {
|
|
320
|
+
const normRes = normalizeMcpToolArgs(
|
|
321
|
+
String(rpcRequest.params?.name || "").trim(),
|
|
322
|
+
rpcRequest.params.arguments
|
|
323
|
+
);
|
|
324
|
+
if (!normRes.ok) {
|
|
325
|
+
if (isNotification) {
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
jsonrpc: "2.0",
|
|
330
|
+
id: safeId,
|
|
331
|
+
error: {
|
|
332
|
+
code: -32602,
|
|
333
|
+
message: normRes.message,
|
|
334
|
+
data: {
|
|
335
|
+
error: normRes.error,
|
|
336
|
+
reason: normRes.error,
|
|
337
|
+
...(normRes.canonicalKey ? { canonicalKey: normRes.canonicalKey } : {}),
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
rpcRequest = {
|
|
343
|
+
...rpcRequest,
|
|
344
|
+
params: {
|
|
345
|
+
...rpcRequest.params,
|
|
346
|
+
arguments: normRes.args,
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
}
|
|
317
350
|
}
|
|
318
351
|
|
|
319
352
|
try {
|
|
@@ -841,18 +874,191 @@ export async function runCli(argv = process.argv.slice(2), options = {}) {
|
|
|
841
874
|
effectiveOptions.apiUrl ||
|
|
842
875
|
DEFAULT_MCP_ENDPOINT
|
|
843
876
|
);
|
|
844
|
-
const
|
|
845
|
-
|
|
877
|
+
const expectedToolNames = [
|
|
878
|
+
"revclamp_audit_revenue_leakage",
|
|
879
|
+
"revclamp_evaluate_ghost_usage",
|
|
880
|
+
"revclamp_stage_stripe_dispute_ce3",
|
|
881
|
+
"revclamp_validate_eu_vat_vies",
|
|
882
|
+
"revclamp_reconcile_b2b_net_terms",
|
|
883
|
+
"revclamp_verify_webhook_signature",
|
|
884
|
+
"revclamp_calculate_roi_and_plan_fit",
|
|
885
|
+
"revclamp_simulate_webhook_replay",
|
|
886
|
+
"revclamp_lint_dispute_evidence",
|
|
887
|
+
];
|
|
888
|
+
const selfTestFixtures = {
|
|
889
|
+
revclamp_audit_revenue_leakage: {
|
|
890
|
+
args: { demo: true },
|
|
891
|
+
verify: (r) => Boolean(r && r.ok === true && r.ghostEventsDetected > 0),
|
|
892
|
+
},
|
|
893
|
+
revclamp_evaluate_ghost_usage: {
|
|
894
|
+
args: {
|
|
895
|
+
customerId: "cus_selftest_01",
|
|
896
|
+
subscriptions: {
|
|
897
|
+
has_more: false,
|
|
898
|
+
data: [
|
|
899
|
+
{
|
|
900
|
+
customer: "cus_selftest_01",
|
|
901
|
+
status: "canceled",
|
|
902
|
+
current_period_end: 1750000000,
|
|
903
|
+
},
|
|
904
|
+
],
|
|
905
|
+
},
|
|
906
|
+
usageEvent: {
|
|
907
|
+
customer_id: "cus_selftest_01",
|
|
908
|
+
occurred_at_sec: 1760000000,
|
|
909
|
+
},
|
|
910
|
+
},
|
|
911
|
+
verify: (r) =>
|
|
912
|
+
Boolean(
|
|
913
|
+
r &&
|
|
914
|
+
r.ok === true &&
|
|
915
|
+
r.isGhostUsage === true &&
|
|
916
|
+
r.decision === "DISPATCH_SIGNED_REVOKE_WEBHOOK"
|
|
917
|
+
),
|
|
918
|
+
},
|
|
919
|
+
revclamp_stage_stripe_dispute_ce3: {
|
|
920
|
+
args: {
|
|
921
|
+
disputeId: "du_selftest_01",
|
|
922
|
+
customerId: "cus_selftest_01",
|
|
923
|
+
demo: true,
|
|
924
|
+
},
|
|
925
|
+
verify: (r) =>
|
|
926
|
+
Boolean(
|
|
927
|
+
r &&
|
|
928
|
+
r.ok === true &&
|
|
929
|
+
r.submit === false &&
|
|
930
|
+
typeof r.evidence?.access_activity_log === "string" &&
|
|
931
|
+
r.utf16CodeUnits <= 18000
|
|
932
|
+
),
|
|
933
|
+
},
|
|
934
|
+
revclamp_validate_eu_vat_vies: {
|
|
935
|
+
args: {
|
|
936
|
+
sellerCountry: "DE",
|
|
937
|
+
buyerVatId: "XI123456789",
|
|
938
|
+
viesMode: "FORMAT_ONLY",
|
|
939
|
+
},
|
|
940
|
+
verify: (r) =>
|
|
941
|
+
Boolean(
|
|
942
|
+
r &&
|
|
943
|
+
r.ok === true &&
|
|
944
|
+
r.formatValid === true &&
|
|
945
|
+
r.crossBorderSupply === true &&
|
|
946
|
+
r.memberState === "XI"
|
|
947
|
+
),
|
|
948
|
+
},
|
|
949
|
+
revclamp_reconcile_b2b_net_terms: {
|
|
950
|
+
args: {
|
|
951
|
+
orderId: "ord_selftest_01",
|
|
952
|
+
totalOrderCents: 100000,
|
|
953
|
+
paidAmountCents: 30000,
|
|
954
|
+
requiredDepositPercent: 30,
|
|
955
|
+
paymentTermsDays: 30,
|
|
956
|
+
daysElapsedSinceInvoice: 5,
|
|
957
|
+
},
|
|
958
|
+
verify: (r) =>
|
|
959
|
+
Boolean(
|
|
960
|
+
r &&
|
|
961
|
+
r.ok === true &&
|
|
962
|
+
r.fulfillmentAllowed === true &&
|
|
963
|
+
r.doubleEntryBalanced === true &&
|
|
964
|
+
r.remainingBalanceCents === 70000
|
|
965
|
+
),
|
|
966
|
+
},
|
|
967
|
+
revclamp_verify_webhook_signature: {
|
|
968
|
+
args: {
|
|
969
|
+
provider: "stripe",
|
|
970
|
+
secret: "whsec_test_selftest_secret_2026",
|
|
971
|
+
rawPayload: '{"id":"evt_selftest_01","type":"customer.subscription.deleted"}',
|
|
972
|
+
demo: true,
|
|
973
|
+
},
|
|
974
|
+
verify: (r) =>
|
|
975
|
+
Boolean(
|
|
976
|
+
r &&
|
|
977
|
+
r.ok === true &&
|
|
978
|
+
typeof r.verification?.generatedHeader === "string" &&
|
|
979
|
+
r.verification.generatedHeader.includes("v1=")
|
|
980
|
+
),
|
|
981
|
+
},
|
|
982
|
+
revclamp_calculate_roi_and_plan_fit: {
|
|
983
|
+
args: {
|
|
984
|
+
monthlyWonDisputeUsd: 10000,
|
|
985
|
+
monthlyGhostBleedUsd: 600,
|
|
986
|
+
contingencyTakeRatePercent: 25,
|
|
987
|
+
monthlyUsageEvents: 250000,
|
|
988
|
+
},
|
|
989
|
+
verify: (r) =>
|
|
990
|
+
Boolean(
|
|
991
|
+
r &&
|
|
992
|
+
r.ok === true &&
|
|
993
|
+
r.zeroTakeRateGuaranteed === true &&
|
|
994
|
+
r.annualNetMarginSavedCents > 0 &&
|
|
995
|
+
r.recommendedTier === "growth_59"
|
|
996
|
+
),
|
|
997
|
+
},
|
|
998
|
+
revclamp_simulate_webhook_replay: {
|
|
999
|
+
args: {
|
|
1000
|
+
scenarioPreset: "out_of_order_stale_update_after_cancel",
|
|
1001
|
+
demo: true,
|
|
1002
|
+
},
|
|
1003
|
+
verify: (r) =>
|
|
1004
|
+
Boolean(
|
|
1005
|
+
r &&
|
|
1006
|
+
r.ok === true &&
|
|
1007
|
+
r.preventedStateCorruption === true &&
|
|
1008
|
+
r.finalStatus === "canceled"
|
|
1009
|
+
),
|
|
1010
|
+
},
|
|
1011
|
+
revclamp_lint_dispute_evidence: {
|
|
1012
|
+
args: {
|
|
1013
|
+
preset: "valid_ce3_non_destructive",
|
|
1014
|
+
demo: true,
|
|
1015
|
+
},
|
|
1016
|
+
verify: (r) =>
|
|
1017
|
+
Boolean(
|
|
1018
|
+
r &&
|
|
1019
|
+
r.ok === true &&
|
|
1020
|
+
r.within18kCeiling === true &&
|
|
1021
|
+
r.withinCeiling === true &&
|
|
1022
|
+
r.overallVerdict === "PASS_CE3_READY"
|
|
1023
|
+
),
|
|
1024
|
+
},
|
|
1025
|
+
};
|
|
1026
|
+
|
|
1027
|
+
const toolsList = Array.isArray(LOCAL_MCP_TOOLS) ? LOCAL_MCP_TOOLS : [];
|
|
1028
|
+
const toolsCount = toolsList.length;
|
|
1029
|
+
const exactCatalogMatch =
|
|
1030
|
+
toolsCount === expectedToolNames.length &&
|
|
1031
|
+
expectedToolNames.every((tName, idx) => toolsList[idx]?.name === tName);
|
|
1032
|
+
|
|
1033
|
+
const toolResults = [];
|
|
1034
|
+
let passedCount = 0;
|
|
1035
|
+
for (const toolDef of toolsList) {
|
|
1036
|
+
const tName = String(toolDef?.name || "");
|
|
1037
|
+
const spec = selfTestFixtures[tName];
|
|
1038
|
+
let itemOk = false;
|
|
1039
|
+
if (spec) {
|
|
1040
|
+
try {
|
|
1041
|
+
const out = executeLocalMcpTool(tName, spec.args);
|
|
1042
|
+
itemOk = Boolean(spec.verify(out));
|
|
1043
|
+
} catch {
|
|
1044
|
+
itemOk = false;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
if (itemOk) passedCount += 1;
|
|
1048
|
+
toolResults.push({ tool: tName, ok: itemOk });
|
|
1049
|
+
}
|
|
1050
|
+
|
|
846
1051
|
const localEngineVerified =
|
|
847
|
-
Boolean(
|
|
848
|
-
toolsCount === 9;
|
|
1052
|
+
Boolean(exactCatalogMatch) && toolsCount === 9 && passedCount === 9;
|
|
849
1053
|
console.log(
|
|
850
1054
|
JSON.stringify({
|
|
851
1055
|
ok: localEngineVerified,
|
|
852
1056
|
version: PACKAGE_VERSION,
|
|
853
1057
|
toolsCount,
|
|
1058
|
+
passedCount,
|
|
854
1059
|
localEngineVerified,
|
|
855
1060
|
endpoint: validated,
|
|
1061
|
+
toolResults,
|
|
856
1062
|
})
|
|
857
1063
|
);
|
|
858
1064
|
return localEngineVerified ? 0 : 1;
|
|
@@ -1040,6 +1246,9 @@ export async function runCli(argv = process.argv.slice(2), options = {}) {
|
|
|
1040
1246
|
"valid_ce3_non_destructive",
|
|
1041
1247
|
"utf16_overflow_19k",
|
|
1042
1248
|
"missing_device_and_overwrite_risk",
|
|
1249
|
+
"ce3_24k_truncation_trap",
|
|
1250
|
+
"ce3_clean_compacted_14k",
|
|
1251
|
+
"ce3_missing_device_binding_19k",
|
|
1043
1252
|
]);
|
|
1044
1253
|
const preset = positional[0] || "valid_ce3_non_destructive";
|
|
1045
1254
|
if (!allowedPresets.has(preset)) {
|