revclamp-mcp 1.20.7 → 1.20.8

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 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:revclamp/revclamp-mcp"]
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:revclamp/revclamp-mcp#v1.20.7 --self-test
62
+ npx -y github:lhphong09t2/revclamp-mcp#v1.20.8 --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.7";
36
+ export const PACKAGE_VERSION = "1.20.8";
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 {
package/local_engine.mjs CHANGED
@@ -26,7 +26,79 @@ export const SUPPORTED_MCP_PROTOCOL_VERSIONS = Object.freeze([
26
26
  ]);
27
27
 
28
28
  const LOCAL_LIVE_SECRET_GUARD_RE =
29
- /(?:[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;
29
+ /(?:[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;
30
+
31
+ const LOCAL_MCP_TOOL_ARG_ALIAS_GROUPS = Object.freeze({
32
+ revclamp_verify_webhook_signature: Object.freeze([
33
+ Object.freeze({ canonical: "rawPayload", aliases: Object.freeze(["payload", "body"]) }),
34
+ Object.freeze({
35
+ canonical: "signatureHeader",
36
+ aliases: Object.freeze(["signature", "header", "stripeSignature", "shopifyHmac"]),
37
+ }),
38
+ Object.freeze({
39
+ canonical: "secret",
40
+ aliases: Object.freeze(["webhookSecret", "signingSecret"]),
41
+ }),
42
+ Object.freeze({ canonical: "timestampSec", aliases: Object.freeze(["timestamp"]) }),
43
+ Object.freeze({
44
+ canonical: "toleranceSec",
45
+ aliases: Object.freeze(["toleranceSeconds", "tolerance"]),
46
+ }),
47
+ ]),
48
+ revclamp_simulate_webhook_replay: Object.freeze([
49
+ Object.freeze({ canonical: "scenarioPreset", aliases: Object.freeze(["preset"]) }),
50
+ Object.freeze({
51
+ canonical: "idempotencyWindowSec",
52
+ aliases: Object.freeze(["windowSec", "dedupWindowSec"]),
53
+ }),
54
+ ]),
55
+ });
56
+
57
+ export function normalizeMcpToolArgs(toolName, rawArgs) {
58
+ if (rawArgs === null || typeof rawArgs !== "object" || Array.isArray(rawArgs)) {
59
+ return {
60
+ ok: false,
61
+ error: "INVALID_ARGUMENTS_OBJECT",
62
+ message: "Invalid params: arguments must be a JSON object",
63
+ };
64
+ }
65
+ const normalized = { ...rawArgs };
66
+ const groups = Object.hasOwn(LOCAL_MCP_TOOL_ARG_ALIAS_GROUPS, toolName)
67
+ ? LOCAL_MCP_TOOL_ARG_ALIAS_GROUPS[toolName]
68
+ : null;
69
+ if (!groups) {
70
+ return { ok: true, args: normalized };
71
+ }
72
+ for (const group of groups) {
73
+ const allKeys = [group.canonical, ...group.aliases];
74
+ const presentKeys = allKeys.filter(
75
+ (k) => Object.hasOwn(rawArgs, k) && rawArgs[k] !== undefined
76
+ );
77
+ if (presentKeys.length > 1) {
78
+ const firstKey = presentKeys[0];
79
+ const firstVal = rawArgs[firstKey];
80
+ for (let i = 1; i < presentKeys.length; i += 1) {
81
+ const otherKey = presentKeys[i];
82
+ if (!Object.is(firstVal, rawArgs[otherKey])) {
83
+ return {
84
+ ok: false,
85
+ error: "CONFLICTING_PARAMETER_ALIASES",
86
+ canonicalKey: group.canonical,
87
+ conflictKey: otherKey,
88
+ message: `Invalid params: conflicting parameter aliases for '${group.canonical}' ('${firstKey}' vs '${otherKey}')`,
89
+ };
90
+ }
91
+ }
92
+ }
93
+ if (presentKeys.length >= 1) {
94
+ normalized[group.canonical] = rawArgs[presentKeys[0]];
95
+ for (const aliasKey of group.aliases) {
96
+ delete normalized[aliasKey];
97
+ }
98
+ }
99
+ }
100
+ return { ok: true, args: normalized };
101
+ }
30
102
 
31
103
  export function scanLocalMcpPayloadForSecrets(node, depth = 0) {
32
104
  if (depth > 8) {
@@ -381,7 +453,28 @@ export const LOCAL_MCP_TOOLS = Object.freeze([
381
453
  maximum: 2592000,
382
454
  default: 259200,
383
455
  },
384
- events: { type: "array" },
456
+ events: {
457
+ type: "array",
458
+ items: {
459
+ type: "object",
460
+ properties: {
461
+ eventId: { type: "string" },
462
+ eventType: { type: "string" },
463
+ createdSec: { type: "integer" },
464
+ subscriptionStatus: { type: "string" },
465
+ tier: { type: "string" },
466
+ currentPeriodEndSec: { type: "integer" },
467
+ id: { type: "string" },
468
+ type: { type: "string" },
469
+ created: { type: "integer" },
470
+ status: { type: "string" },
471
+ target_status: { type: "string" },
472
+ arrivedAtSec: { type: "integer" },
473
+ arrived_at_sec: { type: "integer" },
474
+ },
475
+ additionalProperties: false,
476
+ },
477
+ },
385
478
  demo: { type: "boolean" },
386
479
  },
387
480
  },
@@ -1148,14 +1241,33 @@ function evaluateLocalVisaCe3Qualification(disputeReason, disputedTransaction, p
1148
1241
  }
1149
1242
 
1150
1243
  export function executeLocalMcpTool(toolName, rawArgs = {}) {
1151
- const args = rawArgs && typeof rawArgs === "object" && !Array.isArray(rawArgs) ? rawArgs : {};
1152
- const secretScan = scanLocalMcpPayloadForSecrets(args);
1244
+ const inputArgs =
1245
+ rawArgs === undefined
1246
+ ? {}
1247
+ : rawArgs && typeof rawArgs === "object" && !Array.isArray(rawArgs)
1248
+ ? rawArgs
1249
+ : null;
1250
+ if (!inputArgs) {
1251
+ const err = new Error("Invalid params: arguments must be a JSON object");
1252
+ err.code = -32602;
1253
+ err.reason = "INVALID_ARGUMENTS_OBJECT";
1254
+ throw err;
1255
+ }
1256
+ const secretScan = scanLocalMcpPayloadForSecrets(inputArgs);
1153
1257
  if (!secretScan.ok) {
1154
1258
  const err = new Error(`Invalid params: ${secretScan.reason}`);
1155
1259
  err.code = -32602;
1156
1260
  err.reason = secretScan.reason;
1157
1261
  throw err;
1158
1262
  }
1263
+ const normalizedRes = normalizeMcpToolArgs(toolName, inputArgs);
1264
+ if (!normalizedRes.ok) {
1265
+ const err = new Error(normalizedRes.message);
1266
+ err.code = -32602;
1267
+ err.reason = normalizedRes.error;
1268
+ throw err;
1269
+ }
1270
+ const args = normalizedRes.args;
1159
1271
 
1160
1272
  if (toolName === "revclamp_calculate_roi_and_plan_fit") {
1161
1273
  const monthlyWonDisputeUsd = Number(args.monthlyWonDisputeUsd ?? 10_000);
@@ -2611,7 +2723,7 @@ function toSafeLocalRpcId(rawId) {
2611
2723
  return null;
2612
2724
  }
2613
2725
 
2614
- export function executeLocalMcpJsonRpc(rpcRequest, packageVersion = "1.20.7") {
2726
+ export function executeLocalMcpJsonRpc(rpcRequest, packageVersion = "1.20.8") {
2615
2727
  if (!rpcRequest || typeof rpcRequest !== "object" || Array.isArray(rpcRequest)) {
2616
2728
  return {
2617
2729
  jsonrpc: "2.0",
@@ -2794,7 +2906,18 @@ export function executeLocalMcpJsonRpc(rpcRequest, packageVersion = "1.20.7") {
2794
2906
 
2795
2907
  if (method === "tools/call") {
2796
2908
  const toolName = String(rpcRequest?.params?.name || "").trim();
2797
- const toolArgs = rpcRequest?.params?.arguments ?? {};
2909
+ const rawToolArgs =
2910
+ rpcRequest?.params?.arguments === undefined ? {} : rpcRequest.params.arguments;
2911
+ if (!rawToolArgs || typeof rawToolArgs !== "object" || Array.isArray(rawToolArgs)) {
2912
+ return {
2913
+ jsonrpc: "2.0",
2914
+ id: safeId,
2915
+ error: {
2916
+ code: -32602,
2917
+ message: "Invalid params: arguments must be a JSON object",
2918
+ },
2919
+ };
2920
+ }
2798
2921
  const toolSpec = LOCAL_MCP_TOOLS.find((t) => t.name === toolName);
2799
2922
  if (!toolSpec) {
2800
2923
  return {
@@ -2807,6 +2930,24 @@ export function executeLocalMcpJsonRpc(rpcRequest, packageVersion = "1.20.7") {
2807
2930
  };
2808
2931
  }
2809
2932
 
2933
+ const normalizedRes = normalizeMcpToolArgs(toolName, rawToolArgs);
2934
+ if (!normalizedRes.ok) {
2935
+ return {
2936
+ jsonrpc: "2.0",
2937
+ id: safeId,
2938
+ error: {
2939
+ code: -32602,
2940
+ message: normalizedRes.message,
2941
+ data: {
2942
+ error: normalizedRes.error,
2943
+ reason: normalizedRes.error,
2944
+ ...(normalizedRes.canonicalKey ? { canonicalKey: normalizedRes.canonicalKey } : {}),
2945
+ },
2946
+ },
2947
+ };
2948
+ }
2949
+ const toolArgs = normalizedRes.args;
2950
+
2810
2951
  const exampleCallMap = {
2811
2952
  revclamp_verify_webhook_signature: {
2812
2953
  provider: "stripe",
package/mcp-config.json CHANGED
@@ -26,6 +26,6 @@
26
26
  },
27
27
  "manifest": {
28
28
  "name": "revclamp-mcp",
29
- "version": "1.20.7"
29
+ "version": "1.20.8"
30
30
  }
31
31
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "revclamp-mcp",
3
3
  "mcpName": "io.github.revclamp/revclamp-mcp",
4
- "version": "1.20.7",
4
+ "version": "1.20.8",
5
5
  "description": "Model Context Protocol (MCP) server and CLI for Stripe 2025-03-31.basil entitlement checks, Visa CE 3.0 dispute staging (submit: false), EU/XI VIES VAT validation, Shopify Plus B2B Net-15/30/60 reconciliation, webhook HMAC-SHA256 verification, 0% take-rate ROI calculation, 72-hour webhook replay & out-of-order state-machine simulation, and Stripe dispute evidence 18,000 UTF-16 code unit & CE 3.0 completeness linting.",
6
6
  "type": "module",
7
7
  "main": "./index.mjs",
@@ -36,7 +36,10 @@
36
36
  "homepage": "https://revclamp.com",
37
37
  "repository": {
38
38
  "type": "git",
39
- "url": "https://github.com/revclamp/revclamp-mcp.git"
39
+ "url": "https://github.com/lhphong09t2/revclamp-mcp.git"
40
+ },
41
+ "bugs": {
42
+ "url": "https://github.com/lhphong09t2/revclamp-mcp/issues"
40
43
  },
41
44
  "license": "MIT",
42
45
  "engines": {
package/server.json CHANGED
@@ -4,16 +4,16 @@
4
4
  "description": "Zero-take-rate Stripe 2025-03-31.basil, Visa CE 3.0 & EU/UK VIES VAT guardrails MCP server.",
5
5
  "status": "active",
6
6
  "repository": {
7
- "url": "https://github.com/revclamp/revclamp-mcp",
7
+ "url": "https://github.com/lhphong09t2/revclamp-mcp",
8
8
  "source": "github"
9
9
  },
10
- "version": "1.20.7",
10
+ "version": "1.20.8",
11
11
  "packages": [
12
12
  {
13
13
  "registry_type": "npm",
14
14
  "registry_base_url": "https://registry.npmjs.org",
15
15
  "identifier": "revclamp-mcp",
16
- "version": "1.20.7",
16
+ "version": "1.20.8",
17
17
  "transport": {
18
18
  "type": "stdio"
19
19
  },