@rapay/mcp-server 1.1.4
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 +277 -0
- package/dist/audit.d.ts +47 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +149 -0
- package/dist/audit.js.map +1 -0
- package/dist/handlers.d.ts +45 -0
- package/dist/handlers.d.ts.map +1 -0
- package/dist/handlers.js +495 -0
- package/dist/handlers.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +111 -0
- package/dist/index.js.map +1 -0
- package/dist/sanitize.d.ts +41 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/sanitize.js +172 -0
- package/dist/sanitize.js.map +1 -0
- package/dist/tools.d.ts +41 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +209 -0
- package/dist/tools.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ra Pay MCP Server - Response Sanitization
|
|
3
|
+
*
|
|
4
|
+
* Security layer to prevent prompt injection attacks.
|
|
5
|
+
* Filters out system markers and malicious patterns from CLI output.
|
|
6
|
+
* Includes Unicode NFC normalization to prevent homoglyph attacks.
|
|
7
|
+
*
|
|
8
|
+
* @see MCP-SERVER-PLAN.md - Security Requirements
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Sanitize MCP response to prevent injection attacks
|
|
12
|
+
*
|
|
13
|
+
* @param response - Raw CLI output string
|
|
14
|
+
* @returns Sanitized string safe for MCP response
|
|
15
|
+
*/
|
|
16
|
+
export declare function sanitizeResponse(response: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Sanitize error messages
|
|
19
|
+
* More aggressive filtering for error output which may contain stack traces
|
|
20
|
+
*
|
|
21
|
+
* @param error - Error message or stack trace
|
|
22
|
+
* @returns Sanitized error message
|
|
23
|
+
*/
|
|
24
|
+
export declare function sanitizeError(error: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Validate that a string doesn't contain injection attempts
|
|
27
|
+
* Returns true if safe, false if suspicious
|
|
28
|
+
*
|
|
29
|
+
* @param input - String to validate
|
|
30
|
+
* @returns Boolean indicating if input is safe
|
|
31
|
+
*/
|
|
32
|
+
export declare function isInputSafe(input: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Sanitize JSON output specifically
|
|
35
|
+
* Ensures JSON structure is preserved while removing dangerous content
|
|
36
|
+
*
|
|
37
|
+
* @param jsonString - JSON string from CLI
|
|
38
|
+
* @returns Sanitized JSON string
|
|
39
|
+
*/
|
|
40
|
+
export declare function sanitizeJsonResponse(jsonString: string): string;
|
|
41
|
+
//# sourceMappingURL=sanitize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAuEH;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAyBzD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAYnD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAWlD;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAa/D"}
|
package/dist/sanitize.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ra Pay MCP Server - Response Sanitization
|
|
3
|
+
*
|
|
4
|
+
* Security layer to prevent prompt injection attacks.
|
|
5
|
+
* Filters out system markers and malicious patterns from CLI output.
|
|
6
|
+
* Includes Unicode NFC normalization to prevent homoglyph attacks.
|
|
7
|
+
*
|
|
8
|
+
* @see MCP-SERVER-PLAN.md - Security Requirements
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Normalize Unicode text to NFC (Canonical Composition) form.
|
|
12
|
+
* This prevents homoglyph attacks using decomposed characters.
|
|
13
|
+
* Example: "é" (U+00E9) vs "e" + "́" (U+0065 + U+0301) are normalized to same form.
|
|
14
|
+
*
|
|
15
|
+
* Note: This doesn't catch cross-script homoglyphs (Cyrillic а vs Latin a)
|
|
16
|
+
* but handles decomposed character attacks which are more common.
|
|
17
|
+
*/
|
|
18
|
+
function normalizeUnicode(text) {
|
|
19
|
+
return text.normalize("NFC");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Patterns that could be used for prompt injection
|
|
23
|
+
* These are filtered from all CLI output before returning to MCP clients
|
|
24
|
+
*/
|
|
25
|
+
const INJECTION_PATTERNS = [
|
|
26
|
+
/\[SYSTEM\]/gi,
|
|
27
|
+
/\[USER\]/gi,
|
|
28
|
+
/\[ASSISTANT\]/gi,
|
|
29
|
+
/\[INST\]/gi,
|
|
30
|
+
/\[\/INST\]/gi,
|
|
31
|
+
/<\|im_start\|>/gi,
|
|
32
|
+
/<\|im_end\|>/gi,
|
|
33
|
+
/<\|system\|>/gi,
|
|
34
|
+
/<\|user\|>/gi,
|
|
35
|
+
/<\|assistant\|>/gi,
|
|
36
|
+
/<<SYS>>/gi,
|
|
37
|
+
/<<\/SYS>>/gi,
|
|
38
|
+
/Human:/gi,
|
|
39
|
+
/Assistant:/gi,
|
|
40
|
+
];
|
|
41
|
+
/**
|
|
42
|
+
* ANSI escape sequence pattern
|
|
43
|
+
* Matches all ANSI terminal formatting codes
|
|
44
|
+
*/
|
|
45
|
+
const ANSI_PATTERN = /\x1b\[[0-9;]*[a-zA-Z]/g;
|
|
46
|
+
/**
|
|
47
|
+
* Control character pattern (except newlines and tabs)
|
|
48
|
+
*/
|
|
49
|
+
const CONTROL_CHARS_PATTERN = /[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/g;
|
|
50
|
+
/**
|
|
51
|
+
* Unicode Tags pattern (U+E0000-U+E007F)
|
|
52
|
+
* These are invisible characters that can be used for:
|
|
53
|
+
* - Hidden text attacks (invisible instructions)
|
|
54
|
+
* - Watermarking attacks
|
|
55
|
+
* - Bypass detection systems
|
|
56
|
+
*
|
|
57
|
+
* Range: U+E0000 (TAG NULL) through U+E007F (TAG DELETE)
|
|
58
|
+
* These map to ASCII 0x00-0x7F but render invisibly
|
|
59
|
+
*/
|
|
60
|
+
const UNICODE_TAGS_PATTERN = /[\u{E0000}-\u{E007F}]/gu;
|
|
61
|
+
/**
|
|
62
|
+
* Additional invisible Unicode characters that could be abused
|
|
63
|
+
* - Zero Width Space (U+200B)
|
|
64
|
+
* - Zero Width Non-Joiner (U+200C)
|
|
65
|
+
* - Zero Width Joiner (U+200D)
|
|
66
|
+
* - Word Joiner (U+2060)
|
|
67
|
+
* - Left-to-Right Mark (U+200E)
|
|
68
|
+
* - Right-to-Left Mark (U+200F)
|
|
69
|
+
* - Invisible Separator (U+2063)
|
|
70
|
+
* - Invisible Plus (U+2064)
|
|
71
|
+
*/
|
|
72
|
+
const INVISIBLE_CHARS_PATTERN = /[\u200B-\u200F\u2060-\u2064\uFEFF]/g;
|
|
73
|
+
/**
|
|
74
|
+
* Sanitize MCP response to prevent injection attacks
|
|
75
|
+
*
|
|
76
|
+
* @param response - Raw CLI output string
|
|
77
|
+
* @returns Sanitized string safe for MCP response
|
|
78
|
+
*/
|
|
79
|
+
export function sanitizeResponse(response) {
|
|
80
|
+
// Step 0: Normalize Unicode to NFC form (prevents decomposed homoglyph attacks)
|
|
81
|
+
let sanitized = normalizeUnicode(response);
|
|
82
|
+
// Step 1: Remove ANSI escape sequences (terminal colors/formatting)
|
|
83
|
+
sanitized = sanitized.replace(ANSI_PATTERN, "");
|
|
84
|
+
// Step 2: Remove prompt injection patterns
|
|
85
|
+
for (const pattern of INJECTION_PATTERNS) {
|
|
86
|
+
sanitized = sanitized.replace(pattern, "");
|
|
87
|
+
}
|
|
88
|
+
// Step 3: Remove control characters (keep \n and \t)
|
|
89
|
+
sanitized = sanitized.replace(CONTROL_CHARS_PATTERN, "");
|
|
90
|
+
// Step 4: Remove Unicode Tags (invisible hidden text)
|
|
91
|
+
sanitized = sanitized.replace(UNICODE_TAGS_PATTERN, "");
|
|
92
|
+
// Step 5: Remove other invisible Unicode characters
|
|
93
|
+
sanitized = sanitized.replace(INVISIBLE_CHARS_PATTERN, "");
|
|
94
|
+
// Step 6: Trim whitespace
|
|
95
|
+
sanitized = sanitized.trim();
|
|
96
|
+
return sanitized;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Sanitize error messages
|
|
100
|
+
* More aggressive filtering for error output which may contain stack traces
|
|
101
|
+
*
|
|
102
|
+
* @param error - Error message or stack trace
|
|
103
|
+
* @returns Sanitized error message
|
|
104
|
+
*/
|
|
105
|
+
export function sanitizeError(error) {
|
|
106
|
+
let sanitized = sanitizeResponse(error);
|
|
107
|
+
// Remove file paths that might leak system info
|
|
108
|
+
sanitized = sanitized.replace(/[A-Z]:\\[^\s]+/gi, "[path]"); // Windows paths
|
|
109
|
+
sanitized = sanitized.replace(/\/(?:home|Users|var|tmp)[^\s]+/gi, "[path]"); // Unix paths
|
|
110
|
+
// Remove potential stack traces
|
|
111
|
+
sanitized = sanitized.replace(/at .+:\d+:\d+/g, ""); // JS stack traces
|
|
112
|
+
sanitized = sanitized.replace(/\s+at .+\(.+\)/g, ""); // Node.js stack traces
|
|
113
|
+
return sanitized.trim();
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Validate that a string doesn't contain injection attempts
|
|
117
|
+
* Returns true if safe, false if suspicious
|
|
118
|
+
*
|
|
119
|
+
* @param input - String to validate
|
|
120
|
+
* @returns Boolean indicating if input is safe
|
|
121
|
+
*/
|
|
122
|
+
export function isInputSafe(input) {
|
|
123
|
+
// Normalize Unicode before checking (prevents decomposed homoglyph attacks)
|
|
124
|
+
const normalized = normalizeUnicode(input);
|
|
125
|
+
for (const pattern of INJECTION_PATTERNS) {
|
|
126
|
+
pattern.lastIndex = 0; // Reset stateful global regex before each test
|
|
127
|
+
if (pattern.test(normalized)) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Sanitize JSON output specifically
|
|
135
|
+
* Ensures JSON structure is preserved while removing dangerous content
|
|
136
|
+
*
|
|
137
|
+
* @param jsonString - JSON string from CLI
|
|
138
|
+
* @returns Sanitized JSON string
|
|
139
|
+
*/
|
|
140
|
+
export function sanitizeJsonResponse(jsonString) {
|
|
141
|
+
try {
|
|
142
|
+
// Parse and re-stringify to ensure valid JSON
|
|
143
|
+
const parsed = JSON.parse(jsonString);
|
|
144
|
+
// Recursively sanitize string values
|
|
145
|
+
const sanitized = sanitizeObject(parsed);
|
|
146
|
+
return JSON.stringify(sanitized, null, 2);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// If not valid JSON, sanitize as regular string
|
|
150
|
+
return sanitizeResponse(jsonString);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Recursively sanitize object string values
|
|
155
|
+
*/
|
|
156
|
+
function sanitizeObject(obj) {
|
|
157
|
+
if (typeof obj === "string") {
|
|
158
|
+
return sanitizeResponse(obj);
|
|
159
|
+
}
|
|
160
|
+
if (Array.isArray(obj)) {
|
|
161
|
+
return obj.map(sanitizeObject);
|
|
162
|
+
}
|
|
163
|
+
if (obj !== null && typeof obj === "object") {
|
|
164
|
+
const result = {};
|
|
165
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
166
|
+
result[sanitizeResponse(key)] = sanitizeObject(value);
|
|
167
|
+
}
|
|
168
|
+
return result;
|
|
169
|
+
}
|
|
170
|
+
return obj;
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=sanitize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ra Pay MCP Server - Tool Definitions
|
|
3
|
+
*
|
|
4
|
+
* 6 MVP Tools (Perplexity-approved):
|
|
5
|
+
* - 3 Payment Operations (SENSITIVE)
|
|
6
|
+
* - 3 Query Operations (Read-only)
|
|
7
|
+
*
|
|
8
|
+
* @see MCP-SERVER-PLAN.md for full specification
|
|
9
|
+
*/
|
|
10
|
+
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
11
|
+
/**
|
|
12
|
+
* All 6 MVP tools combined
|
|
13
|
+
*/
|
|
14
|
+
export declare const TOOLS: Tool[];
|
|
15
|
+
/**
|
|
16
|
+
* Tool names that require user confirmation (SENSITIVE operations)
|
|
17
|
+
*/
|
|
18
|
+
export declare const SENSITIVE_TOOLS: Set<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Check if a tool is a sensitive payment operation
|
|
21
|
+
*/
|
|
22
|
+
export declare function isSensitiveTool(toolName: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Compute the integrity hash of the tool definitions
|
|
25
|
+
* Hash is based on tool names and their input schemas (deterministic)
|
|
26
|
+
*/
|
|
27
|
+
export declare function computeToolHash(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Verify tool definitions haven't been tampered with
|
|
30
|
+
* Returns true if hash matches expected value
|
|
31
|
+
*/
|
|
32
|
+
export declare function verifyToolIntegrity(): {
|
|
33
|
+
valid: boolean;
|
|
34
|
+
hash: string;
|
|
35
|
+
expected: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Log tool integrity status (for debugging)
|
|
39
|
+
*/
|
|
40
|
+
export declare function logToolIntegrity(): void;
|
|
41
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAmI/D;;GAEG;AACH,eAAO,MAAM,KAAK,EAAE,IAAI,EAAuC,CAAC;AAEhE;;GAEG;AACH,eAAO,MAAM,eAAe,aAAoD,CAAC;AAEjF;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEzD;AAoBD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAWxC;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CASxF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAYvC"}
|
package/dist/tools.js
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ra Pay MCP Server - Tool Definitions
|
|
3
|
+
*
|
|
4
|
+
* 6 MVP Tools (Perplexity-approved):
|
|
5
|
+
* - 3 Payment Operations (SENSITIVE)
|
|
6
|
+
* - 3 Query Operations (Read-only)
|
|
7
|
+
*
|
|
8
|
+
* @see MCP-SERVER-PLAN.md for full specification
|
|
9
|
+
*/
|
|
10
|
+
import { createHash } from "crypto";
|
|
11
|
+
/**
|
|
12
|
+
* Payment operations - marked SENSITIVE for MCP client approval UI
|
|
13
|
+
*/
|
|
14
|
+
const PAYMENT_TOOLS = [
|
|
15
|
+
{
|
|
16
|
+
name: "ra_send",
|
|
17
|
+
description: "SENSITIVE: Send a payment. TWO-STEP PROCESS REQUIRED: " +
|
|
18
|
+
"Step 1: Call with user_confirmed=false to get fee preview (NO payment is made). " +
|
|
19
|
+
"Step 2: Show the fee_breakdown from the response to the user and ask them to confirm. " +
|
|
20
|
+
"Step 3: ONLY after user explicitly approves the fee, call again with user_confirmed=true. " +
|
|
21
|
+
"A 5% Ra Pay application fee applies. The preview shows exact fees before any money moves. " +
|
|
22
|
+
"COMPLIANCE SCREENING REQUIRED: Before processing, you MUST verify the business_purpose does NOT contain: " +
|
|
23
|
+
"(1) P2P patterns: friend, family, roommate, reimbursement for personal expenses, splitting bills, payback, loan, gift, birthday, holiday " +
|
|
24
|
+
"(2) Money laundering red flags: gift cards, prepaid cards, cryptocurrency purchase, wire transfer " +
|
|
25
|
+
"(3) Vague purposes: 'for services', 'payment', 'transfer' without specifics " +
|
|
26
|
+
"(4) Gibberish: repetitive characters (aaaa), random letters (adlkfjal), or repeated words (test test test). " +
|
|
27
|
+
"If detected, WARN the user that Ra Pay is for business transactions only and ask them to provide a legitimate business purpose.",
|
|
28
|
+
inputSchema: {
|
|
29
|
+
type: "object",
|
|
30
|
+
properties: {
|
|
31
|
+
amount: {
|
|
32
|
+
type: "number",
|
|
33
|
+
description: "Amount to send in USD cents (e.g., 10000 = $100.00). Minimum: 100 cents ($1.00)",
|
|
34
|
+
},
|
|
35
|
+
recipient_id: {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "Stripe connected account ID (format: acct_xxx). Must be a valid Stripe account.",
|
|
38
|
+
pattern: "^acct_[a-zA-Z0-9]+$",
|
|
39
|
+
},
|
|
40
|
+
business_purpose: {
|
|
41
|
+
type: "string",
|
|
42
|
+
description: "Business purpose for this payment (10-200 characters). Required for compliance. " +
|
|
43
|
+
"Must describe actual goods or services rendered (e.g., 'Logo design work', 'API consulting'). " +
|
|
44
|
+
"REJECT purposes containing: friend, family, roommate, reimbursement, loan, gift, birthday, " +
|
|
45
|
+
"gift card, prepaid card, splitting bills, payback, personal expense, crypto, bitcoin. " +
|
|
46
|
+
"ALSO REJECT gibberish: repetitive chars (aaaa), random letters, repeated words (test test test). " +
|
|
47
|
+
"Ra Pay is a B2B payment platform - NOT for peer-to-peer transfers. " +
|
|
48
|
+
"Valid examples: 'Freelance development work - Invoice #123', 'API consulting services - March 2026'. " +
|
|
49
|
+
"Invalid examples: 'Lunch reimbursement', 'Gift for friend', 'Gift cards for team', 'aaaaaaaaaa', 'asdfghjkl'.",
|
|
50
|
+
minLength: 10,
|
|
51
|
+
maxLength: 200,
|
|
52
|
+
},
|
|
53
|
+
user_confirmed: {
|
|
54
|
+
type: "boolean",
|
|
55
|
+
description: "REQUIRED. Must be false on first call to get fee preview. " +
|
|
56
|
+
"Only set to true AFTER showing user the fee_breakdown and getting their explicit approval.",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
required: ["amount", "recipient_id", "business_purpose", "user_confirmed"],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "ra_subscribe",
|
|
64
|
+
description: "SENSITIVE: Create a subscription for a customer. Sets up recurring payments using a Stripe price. " +
|
|
65
|
+
"Requires explicit user confirmation. Use 'ra create-price' to create prices first.",
|
|
66
|
+
inputSchema: {
|
|
67
|
+
type: "object",
|
|
68
|
+
properties: {
|
|
69
|
+
price_id: {
|
|
70
|
+
type: "string",
|
|
71
|
+
description: "Stripe price ID (format: price_xxx). Create prices with 'ra create-price' command.",
|
|
72
|
+
pattern: "^price_[a-zA-Z0-9]+$",
|
|
73
|
+
},
|
|
74
|
+
customer_email: {
|
|
75
|
+
type: "string",
|
|
76
|
+
description: "Customer email address for the subscription",
|
|
77
|
+
format: "email",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
required: ["price_id", "customer_email"],
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "ra_refund",
|
|
85
|
+
description: "SENSITIVE: Open Stripe Dashboard to process refunds. " +
|
|
86
|
+
"For security, refunds are processed through the Stripe Dashboard interface. " +
|
|
87
|
+
"This opens your browser to the refund management page.",
|
|
88
|
+
inputSchema: {
|
|
89
|
+
type: "object",
|
|
90
|
+
properties: {},
|
|
91
|
+
required: [],
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
/**
|
|
96
|
+
* Query operations - Read-only, no SENSITIVE marker needed
|
|
97
|
+
*/
|
|
98
|
+
const QUERY_TOOLS = [
|
|
99
|
+
{
|
|
100
|
+
name: "ra_balance",
|
|
101
|
+
description: "Check available balance in your Ra Pay account. " +
|
|
102
|
+
"Returns current balance, pending amounts, and payout schedule.",
|
|
103
|
+
inputSchema: {
|
|
104
|
+
type: "object",
|
|
105
|
+
properties: {},
|
|
106
|
+
required: [],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: "ra_history",
|
|
111
|
+
description: "Get transaction history from your Ra Pay account. " +
|
|
112
|
+
"Returns recent payments, subscriptions, and refunds with timestamps and status.",
|
|
113
|
+
inputSchema: {
|
|
114
|
+
type: "object",
|
|
115
|
+
properties: {
|
|
116
|
+
limit: {
|
|
117
|
+
type: "number",
|
|
118
|
+
description: "Number of transactions to retrieve (default: 10, max: 100)",
|
|
119
|
+
minimum: 1,
|
|
120
|
+
maximum: 100,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
required: [],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: "ra_whoami",
|
|
128
|
+
description: "Check account status and identity. " +
|
|
129
|
+
"Returns user ID, Stripe account status, verification status, and account tier.",
|
|
130
|
+
inputSchema: {
|
|
131
|
+
type: "object",
|
|
132
|
+
properties: {},
|
|
133
|
+
required: [],
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
/**
|
|
138
|
+
* All 6 MVP tools combined
|
|
139
|
+
*/
|
|
140
|
+
export const TOOLS = [...PAYMENT_TOOLS, ...QUERY_TOOLS];
|
|
141
|
+
/**
|
|
142
|
+
* Tool names that require user confirmation (SENSITIVE operations)
|
|
143
|
+
*/
|
|
144
|
+
export const SENSITIVE_TOOLS = new Set(["ra_send", "ra_subscribe", "ra_refund"]);
|
|
145
|
+
/**
|
|
146
|
+
* Check if a tool is a sensitive payment operation
|
|
147
|
+
*/
|
|
148
|
+
export function isSensitiveTool(toolName) {
|
|
149
|
+
return SENSITIVE_TOOLS.has(toolName);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Tool Integrity Verification
|
|
153
|
+
*
|
|
154
|
+
* Generates a SHA-256 hash of the tool definitions to detect tampering.
|
|
155
|
+
* This hash should match the expected value from the official release.
|
|
156
|
+
*/
|
|
157
|
+
/**
|
|
158
|
+
* Expected hash of tool definitions (update when tools change)
|
|
159
|
+
* This is the SHA-256 hash of the sorted tool names and their input schemas.
|
|
160
|
+
*
|
|
161
|
+
* To regenerate after modifying tools:
|
|
162
|
+
* 1. Run the MCP server with RAPAY_DEBUG=1
|
|
163
|
+
* 2. The computed hash will be logged
|
|
164
|
+
* 3. Update this constant with the new hash
|
|
165
|
+
*/
|
|
166
|
+
const EXPECTED_TOOL_HASH = "pending-initial-release";
|
|
167
|
+
/**
|
|
168
|
+
* Compute the integrity hash of the tool definitions
|
|
169
|
+
* Hash is based on tool names and their input schemas (deterministic)
|
|
170
|
+
*/
|
|
171
|
+
export function computeToolHash() {
|
|
172
|
+
// Create a deterministic representation of tools
|
|
173
|
+
const toolData = TOOLS.map((tool) => ({
|
|
174
|
+
name: tool.name,
|
|
175
|
+
inputSchema: tool.inputSchema,
|
|
176
|
+
}))
|
|
177
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
178
|
+
.map((t) => JSON.stringify(t))
|
|
179
|
+
.join("|");
|
|
180
|
+
return createHash("sha256").update(toolData).digest("hex").substring(0, 16);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Verify tool definitions haven't been tampered with
|
|
184
|
+
* Returns true if hash matches expected value
|
|
185
|
+
*/
|
|
186
|
+
export function verifyToolIntegrity() {
|
|
187
|
+
const computedHash = computeToolHash();
|
|
188
|
+
const isValid = EXPECTED_TOOL_HASH === "pending-initial-release" || computedHash === EXPECTED_TOOL_HASH;
|
|
189
|
+
return {
|
|
190
|
+
valid: isValid,
|
|
191
|
+
hash: computedHash,
|
|
192
|
+
expected: EXPECTED_TOOL_HASH,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Log tool integrity status (for debugging)
|
|
197
|
+
*/
|
|
198
|
+
export function logToolIntegrity() {
|
|
199
|
+
const { valid, hash, expected } = verifyToolIntegrity();
|
|
200
|
+
if (process.env.RAPAY_DEBUG) {
|
|
201
|
+
console.error(`[TOOL_INTEGRITY] Computed hash: ${hash}`);
|
|
202
|
+
console.error(`[TOOL_INTEGRITY] Expected hash: ${expected}`);
|
|
203
|
+
}
|
|
204
|
+
if (!valid) {
|
|
205
|
+
console.error(`[SECURITY WARNING] Tool definitions may have been modified!`);
|
|
206
|
+
console.error(`[SECURITY WARNING] Expected: ${expected}, Got: ${hash}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rapay/mcp-server",
|
|
3
|
+
"version": "1.1.4",
|
|
4
|
+
"description": "Ra Pay MCP Server for Claude Desktop and Claude Code - AI Agent Payment Infrastructure",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"rapay-mcp-server": "dist/index.js",
|
|
10
|
+
"@rapay/mcp-server": "dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "tsc --watch",
|
|
15
|
+
"start": "node dist/index.js",
|
|
16
|
+
"lint": "eslint src/",
|
|
17
|
+
"test": "vitest",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"mcp",
|
|
22
|
+
"model-context-protocol",
|
|
23
|
+
"rapay",
|
|
24
|
+
"payments",
|
|
25
|
+
"stripe",
|
|
26
|
+
"ai-agents",
|
|
27
|
+
"claude",
|
|
28
|
+
"automation"
|
|
29
|
+
],
|
|
30
|
+
"author": "Ra Pay",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/Ra-Pay-AI/rapay"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@rapay/cli": "^1.3.0"
|
|
41
|
+
},
|
|
42
|
+
"peerDependenciesMeta": {
|
|
43
|
+
"@rapay/cli": {
|
|
44
|
+
"optional": true
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
52
|
+
"cross-spawn": "^7.0.6"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/node": "^20.10.0",
|
|
56
|
+
"@types/cross-spawn": "^6.0.6",
|
|
57
|
+
"typescript": "^5.3.0",
|
|
58
|
+
"vitest": "^1.0.0",
|
|
59
|
+
"eslint": "^8.55.0",
|
|
60
|
+
"@typescript-eslint/eslint-plugin": "^6.13.0",
|
|
61
|
+
"@typescript-eslint/parser": "^6.13.0"
|
|
62
|
+
},
|
|
63
|
+
"files": [
|
|
64
|
+
"dist/",
|
|
65
|
+
"README.md"
|
|
66
|
+
]
|
|
67
|
+
}
|