shabaaspay-mcp-server 1.0.1

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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/dist/api/client.d.ts +60 -0
  3. package/dist/api/client.js +214 -0
  4. package/dist/config/index.d.ts +54 -0
  5. package/dist/config/index.js +79 -0
  6. package/dist/enricher/action-suggester.d.ts +2 -0
  7. package/dist/enricher/action-suggester.js +26 -0
  8. package/dist/enricher/index.d.ts +2 -0
  9. package/dist/enricher/index.js +166 -0
  10. package/dist/enricher/status-analyzer.d.ts +6 -0
  11. package/dist/enricher/status-analyzer.js +71 -0
  12. package/dist/enricher/summary-generator.d.ts +8 -0
  13. package/dist/enricher/summary-generator.js +45 -0
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.js +28 -0
  16. package/dist/security/auth.d.ts +4 -0
  17. package/dist/security/auth.js +30 -0
  18. package/dist/security/policy.d.ts +18 -0
  19. package/dist/security/policy.js +35 -0
  20. package/dist/security/rate-limiter.d.ts +13 -0
  21. package/dist/security/rate-limiter.js +55 -0
  22. package/dist/security/validator.d.ts +6 -0
  23. package/dist/security/validator.js +22 -0
  24. package/dist/server/http-server.d.ts +28 -0
  25. package/dist/server/http-server.js +524 -0
  26. package/dist/server/stdio-server.d.ts +13 -0
  27. package/dist/server/stdio-server.js +114 -0
  28. package/dist/server-http.d.ts +2 -0
  29. package/dist/server-http.js +27 -0
  30. package/dist/tools/auth.d.ts +17 -0
  31. package/dist/tools/auth.js +51 -0
  32. package/dist/tools/index.d.ts +159 -0
  33. package/dist/tools/index.js +14 -0
  34. package/dist/tools/payment-agreements.d.ts +68 -0
  35. package/dist/tools/payment-agreements.js +92 -0
  36. package/dist/tools/payment-initiations.d.ts +84 -0
  37. package/dist/tools/payment-initiations.js +162 -0
  38. package/dist/tools/response-helpers.d.ts +4 -0
  39. package/dist/tools/response-helpers.js +32 -0
  40. package/dist/types/index.d.ts +184 -0
  41. package/dist/types/index.js +80 -0
  42. package/dist/worker.d.ts +15 -0
  43. package/dist/worker.js +767 -0
  44. package/package.json +64 -0
  45. package/readme.md +113 -0
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzeStatus = analyzeStatus;
4
+ function parseExtendedStatusStatus(extendedStatus) {
5
+ if (!extendedStatus)
6
+ return undefined;
7
+ const parts = extendedStatus
8
+ .split("-")
9
+ .map((p) => p.trim())
10
+ .filter(Boolean);
11
+ if (parts.length < 4)
12
+ return undefined;
13
+ return parts[3]?.toLowerCase();
14
+ }
15
+ function analyzeStatus(status, extendedStatus) {
16
+ const warnings = [];
17
+ const normalizedStatus = (status || "").toLowerCase().trim();
18
+ const extStatus = parseExtendedStatusStatus(extendedStatus);
19
+ if (!normalizedStatus && extStatus) {
20
+ warnings.push("Status was empty so status was inferred from extended status");
21
+ }
22
+ const effectiveStatus = normalizedStatus || extStatus || "unknown";
23
+ if (extStatus && normalizedStatus && extStatus !== normalizedStatus) {
24
+ warnings.push("Status differs from extended status. Treat as requiring manual verification");
25
+ }
26
+ switch (effectiveStatus) {
27
+ case "active":
28
+ return {
29
+ displayStatus: "Active",
30
+ canInitiatePayment: true,
31
+ warnings: warnings.length ? warnings : undefined,
32
+ };
33
+ case "created":
34
+ case "pending":
35
+ warnings.push("Payer must authorise the PayTo agreement in their banking app before any debit can occur");
36
+ return {
37
+ displayStatus: effectiveStatus === "created" ? "Created" : "Pending authorisation",
38
+ canInitiatePayment: false,
39
+ warnings,
40
+ };
41
+ case "suspended":
42
+ warnings.push("Agreement is suspended. Do not initiate payments until it returns to active");
43
+ return {
44
+ displayStatus: "Suspended",
45
+ canInitiatePayment: false,
46
+ warnings,
47
+ };
48
+ case "cancelled":
49
+ case "canceled":
50
+ warnings.push("Agreement is cancelled. A new agreement is required to take payments");
51
+ return {
52
+ displayStatus: "Cancelled",
53
+ canInitiatePayment: false,
54
+ warnings,
55
+ };
56
+ case "expired":
57
+ warnings.push("Agreement is expired. A new agreement is required to take payments");
58
+ return {
59
+ displayStatus: "Expired",
60
+ canInitiatePayment: false,
61
+ warnings,
62
+ };
63
+ default:
64
+ warnings.push(`Unknown status: ${effectiveStatus}. Please verify before proceeding`);
65
+ return {
66
+ displayStatus: effectiveStatus,
67
+ canInitiatePayment: false,
68
+ warnings,
69
+ };
70
+ }
71
+ }
@@ -0,0 +1,8 @@
1
+ import { PaymentAgreement } from "../types/index.js";
2
+ type StatusAnalysis = {
3
+ displayStatus: string;
4
+ canInitiatePayment: boolean;
5
+ warnings?: string[];
6
+ };
7
+ export declare function generateSummary(agreement: PaymentAgreement, statusAnalysis: StatusAnalysis, business?: any): string;
8
+ export {};
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateSummary = generateSummary;
4
+ function generateSummary(agreement, statusAnalysis, business) {
5
+ const parts = [];
6
+ if (business?.meaning) {
7
+ parts.push(business.meaning);
8
+ }
9
+ else {
10
+ parts.push(`Payment agreement status is ${statusAnalysis.displayStatus || agreement.status}`);
11
+ }
12
+ const maxAmount = agreement.maximum_amount ? Number(agreement.maximum_amount) : undefined;
13
+ if (typeof maxAmount === "number" && !Number.isNaN(maxAmount)) {
14
+ const freq = business?.cadence?.frequency ? String(business.cadence.frequency).toLowerCase() : undefined;
15
+ parts.push(freq ? `Maximum debit is AUD ${maxAmount} per ${freq}` : `Maximum debit is AUD ${maxAmount}`);
16
+ }
17
+ const createdAt = safeDate(agreement.created_at);
18
+ if (createdAt)
19
+ parts.push(`Created on ${formatDate(createdAt)}`);
20
+ const endAt = safeDate(agreement.end_date);
21
+ if (endAt)
22
+ parts.push(`Ends on ${formatDate(endAt)}`);
23
+ if (business?.timing?.isExpiringSoon) {
24
+ parts.push("Agreement is expiring soon");
25
+ }
26
+ if (statusAnalysis.warnings && statusAnalysis.warnings.length > 0) {
27
+ parts.push(`Warnings: ${statusAnalysis.warnings.join("; ")}`);
28
+ }
29
+ return parts.join(". ") + ".";
30
+ }
31
+ function safeDate(value) {
32
+ if (!value)
33
+ return null;
34
+ const d = new Date(value);
35
+ if (Number.isNaN(d.getTime()))
36
+ return null;
37
+ return d;
38
+ }
39
+ function formatDate(date) {
40
+ return date.toLocaleDateString("en-AU", {
41
+ year: "numeric",
42
+ month: "short",
43
+ day: "2-digit",
44
+ });
45
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const index_js_1 = require("./config/index.js");
5
+ const stdio_server_js_1 = require("./server/stdio-server.js");
6
+ async function main() {
7
+ try {
8
+ // Load configuration
9
+ const config = (0, index_js_1.loadConfig)();
10
+ // Create and start server
11
+ const server = new stdio_server_js_1.StdioMcpServer(config);
12
+ await server.start();
13
+ // Handle shutdown
14
+ process.on('SIGINT', () => {
15
+ console.error('\n[STDIO] Shutting down gracefully...');
16
+ process.exit(0);
17
+ });
18
+ process.on('SIGTERM', () => {
19
+ console.error('\n[STDIO] Shutting down gracefully...');
20
+ process.exit(0);
21
+ });
22
+ }
23
+ catch (error) {
24
+ console.error('[STDIO] Fatal error:', error);
25
+ process.exit(1);
26
+ }
27
+ }
28
+ main();
@@ -0,0 +1,4 @@
1
+ import { Config } from '../config/index.js';
2
+ export declare function validateApiKey(providedKey: string, config: Config): boolean;
3
+ export declare function extractBearerToken(authHeader?: string): string | null;
4
+ export declare function generateApiKey(): string;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateApiKey = validateApiKey;
4
+ exports.extractBearerToken = extractBearerToken;
5
+ exports.generateApiKey = generateApiKey;
6
+ function validateApiKey(providedKey, config) {
7
+ // If no MCP HTTP API key is configured, HTTP mode is effectively unauthenticated.
8
+ // For production deployments, you should set MCP_HTTP_API_KEY.
9
+ if (!config.mcpHttpApiKey)
10
+ return true;
11
+ return providedKey === config.mcpHttpApiKey;
12
+ }
13
+ function extractBearerToken(authHeader) {
14
+ if (!authHeader)
15
+ return null;
16
+ // Only accept raw tokens (no "Bearer" prefix)
17
+ if (/^Bearer\s+/i.test(authHeader)) {
18
+ return null;
19
+ }
20
+ return authHeader.trim() || null;
21
+ }
22
+ function generateApiKey() {
23
+ // For demo purposes - in production, use proper key generation
24
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
25
+ let key = '';
26
+ for (let i = 0; i < 32; i++) {
27
+ key += chars.charAt(Math.floor(Math.random() * chars.length));
28
+ }
29
+ return key;
30
+ }
@@ -0,0 +1,18 @@
1
+ export interface ClientPolicy {
2
+ client_id: string;
3
+ status: 'active' | 'suspended';
4
+ allowed_tools: string[];
5
+ environment: 'sandbox' | 'production';
6
+ admin?: boolean;
7
+ }
8
+ type PolicyLookupResult = {
9
+ policy: ClientPolicy;
10
+ rejection?: undefined;
11
+ } | {
12
+ policy?: undefined;
13
+ rejection: string;
14
+ };
15
+ export declare function lookupClientPolicy(token: string, environment: 'sandbox' | 'production', policyJson?: string): PolicyLookupResult;
16
+ export declare function isToolAllowed(policy: ClientPolicy, toolName: string): boolean;
17
+ export declare function isAdmin(policy: ClientPolicy): boolean;
18
+ export {};
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.lookupClientPolicy = lookupClientPolicy;
4
+ exports.isToolAllowed = isToolAllowed;
5
+ exports.isAdmin = isAdmin;
6
+ function parsePolicyTable(policyJson) {
7
+ if (!policyJson)
8
+ return {};
9
+ try {
10
+ const parsed = JSON.parse(policyJson);
11
+ if (typeof parsed === 'object' && parsed)
12
+ return parsed;
13
+ }
14
+ catch {
15
+ // ignore parse errors, return empty
16
+ }
17
+ return {};
18
+ }
19
+ function lookupClientPolicy(token, environment, policyJson) {
20
+ const table = parsePolicyTable(policyJson);
21
+ const policy = table[token];
22
+ if (!policy)
23
+ return { rejection: 'unknown_client' };
24
+ if (policy.status !== 'active')
25
+ return { rejection: 'suspended' };
26
+ if (policy.environment !== environment)
27
+ return { rejection: 'environment_mismatch' };
28
+ return { policy };
29
+ }
30
+ function isToolAllowed(policy, toolName) {
31
+ return policy.allowed_tools.includes('*') || policy.allowed_tools.includes(toolName);
32
+ }
33
+ function isAdmin(policy) {
34
+ return !!policy.admin;
35
+ }
@@ -0,0 +1,13 @@
1
+ export declare class RateLimiter {
2
+ private limits;
3
+ private perMinuteLimit;
4
+ private perHourLimit;
5
+ constructor(perMinuteLimit: number, perHourLimit: number);
6
+ checkLimit(identifier: string, timeWindow: 'minute' | 'hour'): {
7
+ allowed: boolean;
8
+ limit: number;
9
+ remaining: number;
10
+ reset: number;
11
+ };
12
+ private cleanup;
13
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RateLimiter = void 0;
4
+ class RateLimiter {
5
+ limits = new Map();
6
+ perMinuteLimit;
7
+ perHourLimit;
8
+ constructor(perMinuteLimit, perHourLimit) {
9
+ this.perMinuteLimit = perMinuteLimit;
10
+ this.perHourLimit = perHourLimit;
11
+ // Cleanup old entries every 5 minutes
12
+ setInterval(() => this.cleanup(), 5 * 60 * 1000);
13
+ }
14
+ checkLimit(identifier, timeWindow) {
15
+ const now = Date.now();
16
+ const key = `${identifier}:${timeWindow}`;
17
+ const limit = timeWindow === 'minute' ? this.perMinuteLimit : this.perHourLimit;
18
+ const windowMs = timeWindow === 'minute' ? 60 * 1000 : 60 * 60 * 1000;
19
+ let entry = this.limits.get(key);
20
+ // Create or reset entry
21
+ if (!entry || now >= entry.resetTime) {
22
+ entry = {
23
+ count: 0,
24
+ resetTime: now + windowMs,
25
+ };
26
+ this.limits.set(key, entry);
27
+ }
28
+ // Check if limit exceeded
29
+ if (entry.count >= limit) {
30
+ return {
31
+ allowed: false,
32
+ limit,
33
+ remaining: 0,
34
+ reset: entry.resetTime,
35
+ };
36
+ }
37
+ // Increment counter
38
+ entry.count++;
39
+ return {
40
+ allowed: true,
41
+ limit,
42
+ remaining: limit - entry.count,
43
+ reset: entry.resetTime,
44
+ };
45
+ }
46
+ cleanup() {
47
+ const now = Date.now();
48
+ for (const [key, entry] of this.limits.entries()) {
49
+ if (now >= entry.resetTime) {
50
+ this.limits.delete(key);
51
+ }
52
+ }
53
+ }
54
+ }
55
+ exports.RateLimiter = RateLimiter;
@@ -0,0 +1,6 @@
1
+ import { ZodSchema } from 'zod';
2
+ export declare function validateInput<T>(schema: ZodSchema<T>, data: unknown): {
3
+ success: boolean;
4
+ data?: T;
5
+ errors?: string[];
6
+ };
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateInput = validateInput;
4
+ const zod_1 = require("zod");
5
+ function validateInput(schema, data) {
6
+ try {
7
+ const validated = schema.parse(data);
8
+ return { success: true, data: validated };
9
+ }
10
+ catch (error) {
11
+ if (error instanceof zod_1.z.ZodError) {
12
+ return {
13
+ success: false,
14
+ errors: error.errors.map(e => `${e.path.join('.')}: ${e.message}`),
15
+ };
16
+ }
17
+ return {
18
+ success: false,
19
+ errors: ['Unknown validation error'],
20
+ };
21
+ }
22
+ }
@@ -0,0 +1,28 @@
1
+ import { Config } from '../config/index.js';
2
+ export declare class HttpMcpServer {
3
+ private server;
4
+ private tools;
5
+ private rateLimiter;
6
+ private config;
7
+ private mcpServer;
8
+ private mcpTransport;
9
+ constructor(config: Config);
10
+ private handleRequest;
11
+ private healthResponse;
12
+ private handleMcpTransport;
13
+ private parseBody;
14
+ private authenticate;
15
+ private authenticateHeaders;
16
+ private getClientIdentifier;
17
+ private getClientIdentifierFromHeaders;
18
+ private routeRequest;
19
+ private corsHeaders;
20
+ private corsResponse;
21
+ private rateLimitResponse;
22
+ private errorResponse;
23
+ private sendResponse;
24
+ private logStructured;
25
+ private setupMcpHandlers;
26
+ start(): Promise<void>;
27
+ stop(): Promise<void>;
28
+ }