paygate-mcp 10.2.0 → 10.3.0

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/dist/server.js CHANGED
@@ -126,6 +126,7 @@ const config_profiles_1 = require("./config-profiles");
126
126
  const scheduled_reports_1 = require("./scheduled-reports");
127
127
  const approval_workflows_1 = require("./approval-workflows");
128
128
  const gateway_hooks_1 = require("./gateway-hooks");
129
+ const dynamic_discovery_1 = require("./dynamic-discovery");
129
130
  /** Max request body size: 1MB */
130
131
  const MAX_BODY_SIZE = 1_048_576;
131
132
  /**
@@ -495,6 +496,8 @@ class PayGateServer {
495
496
  scheduledReports;
496
497
  approvalWorkflows;
497
498
  gatewayHooks;
499
+ /** Cached backend tool list for dynamic discovery mode. */
500
+ cachedBackendTools = null;
498
501
  /** The active request handler — either proxy or router */
499
502
  get handler() {
500
503
  return (this.router || this.proxy);
@@ -1996,6 +1999,130 @@ class PayGateServer {
1996
1999
  }
1997
2000
  return;
1998
2001
  }
2002
+ // ─── Dynamic Discovery Mode ──────────────────────────────────────────────
2003
+ // When discoveryMode === 'dynamic', intercept tools/list and meta-tool calls.
2004
+ if (this.config.discoveryMode === 'dynamic') {
2005
+ // Intercept tools/list → return meta-tools instead of backend tools
2006
+ if (request.method === 'tools/list') {
2007
+ // Cache backend tools on first tools/list (lazy initialization)
2008
+ if (!this.cachedBackendTools) {
2009
+ try {
2010
+ const backendResponse = await this.handler.handleRequest(request, apiKey, clientIp, scopedTokenTools, countryCode);
2011
+ const backendResult = backendResponse.result;
2012
+ this.cachedBackendTools = (backendResult?.tools || []).map(t => ({
2013
+ name: t.name,
2014
+ description: t.description,
2015
+ inputSchema: t.inputSchema,
2016
+ }));
2017
+ }
2018
+ catch {
2019
+ this.cachedBackendTools = [];
2020
+ }
2021
+ }
2022
+ const metaTools = (0, dynamic_discovery_1.getMetaTools)();
2023
+ const rateLimitHeaders = this.buildRateLimitHeaders(apiKey, request);
2024
+ const accept = req.headers['accept'] || '';
2025
+ const wantsSse = accept.includes('text/event-stream');
2026
+ const metaResponse = {
2027
+ jsonrpc: '2.0',
2028
+ id: request.id,
2029
+ result: { tools: metaTools },
2030
+ };
2031
+ if (wantsSse) {
2032
+ (0, session_1.writeSseHeaders)(res, { 'Mcp-Session-Id': sessionId, ...rateLimitHeaders });
2033
+ (0, session_1.writeSseEvent)(res, metaResponse, 'message');
2034
+ res.end();
2035
+ }
2036
+ else {
2037
+ res.writeHead(200, {
2038
+ 'Content-Type': 'application/json',
2039
+ 'Mcp-Session-Id': sessionId,
2040
+ ...rateLimitHeaders,
2041
+ });
2042
+ res.end(JSON.stringify(metaResponse));
2043
+ }
2044
+ return;
2045
+ }
2046
+ // Intercept tools/call for meta-tools
2047
+ if (request.method === 'tools/call') {
2048
+ const metaToolName = request.params?.name || '';
2049
+ if (metaToolName === 'paygate_call_tool') {
2050
+ // Unwrap paygate_call_tool → rewrite as a regular tools/call
2051
+ const metaArgs = request.params?.arguments || {};
2052
+ const realToolName = String(metaArgs.name || '');
2053
+ const realArgs = metaArgs.arguments || {};
2054
+ if (!realToolName) {
2055
+ const rateLimitHeaders = this.buildRateLimitHeaders(apiKey, request);
2056
+ const accept = req.headers['accept'] || '';
2057
+ const wantsSse = accept.includes('text/event-stream');
2058
+ const errResp = {
2059
+ jsonrpc: '2.0',
2060
+ id: request.id,
2061
+ error: { code: -32602, message: 'paygate_call_tool requires "name" argument' },
2062
+ };
2063
+ if (wantsSse) {
2064
+ (0, session_1.writeSseHeaders)(res, { 'Mcp-Session-Id': sessionId, ...rateLimitHeaders });
2065
+ (0, session_1.writeSseEvent)(res, errResp, 'message');
2066
+ res.end();
2067
+ }
2068
+ else {
2069
+ res.writeHead(200, { 'Content-Type': 'application/json', 'Mcp-Session-Id': sessionId, ...rateLimitHeaders });
2070
+ res.end(JSON.stringify(errResp));
2071
+ }
2072
+ return;
2073
+ }
2074
+ // Rewrite the request to call the real tool
2075
+ request = {
2076
+ ...request,
2077
+ params: { name: realToolName, arguments: realArgs },
2078
+ };
2079
+ }
2080
+ else if (metaToolName === 'paygate_list_tools' || metaToolName === 'paygate_search_tools') {
2081
+ // Handle list/search meta-tools locally (no credits charged, no gating)
2082
+ if (!this.cachedBackendTools) {
2083
+ try {
2084
+ const listReq = { jsonrpc: '2.0', id: 'discovery-init', method: 'tools/list', params: {} };
2085
+ const backendResponse = await this.handler.handleRequest(listReq, null, clientIp);
2086
+ const backendResult = backendResponse.result;
2087
+ this.cachedBackendTools = (backendResult?.tools || []).map(t => ({
2088
+ name: t.name,
2089
+ description: t.description,
2090
+ inputSchema: t.inputSchema,
2091
+ }));
2092
+ }
2093
+ catch {
2094
+ this.cachedBackendTools = [];
2095
+ }
2096
+ }
2097
+ const metaArgs = request.params?.arguments || {};
2098
+ const discoveryConfig = {
2099
+ defaultCreditsPerCall: this.config.defaultCreditsPerCall,
2100
+ toolPricing: this.config.toolPricing,
2101
+ globalRateLimitPerMin: this.config.globalRateLimitPerMin,
2102
+ };
2103
+ const metaResult = (0, dynamic_discovery_1.handleMetaToolCall)(metaToolName, metaArgs, this.cachedBackendTools, discoveryConfig);
2104
+ const rateLimitHeaders = this.buildRateLimitHeaders(apiKey, request);
2105
+ const accept = req.headers['accept'] || '';
2106
+ const wantsSse = accept.includes('text/event-stream');
2107
+ const metaResponse = {
2108
+ jsonrpc: '2.0',
2109
+ id: request.id,
2110
+ result: metaResult || { content: [{ type: 'text', text: '{}' }] },
2111
+ };
2112
+ if (wantsSse) {
2113
+ (0, session_1.writeSseHeaders)(res, { 'Mcp-Session-Id': sessionId, ...rateLimitHeaders });
2114
+ (0, session_1.writeSseEvent)(res, metaResponse, 'message');
2115
+ res.end();
2116
+ }
2117
+ else {
2118
+ res.writeHead(200, { 'Content-Type': 'application/json', 'Mcp-Session-Id': sessionId, ...rateLimitHeaders });
2119
+ res.end(JSON.stringify(metaResponse));
2120
+ }
2121
+ return;
2122
+ }
2123
+ // If not a meta-tool, fall through to normal tools/call handling
2124
+ }
2125
+ }
1999
2126
  // Plugin: beforeToolCall — let plugins modify the request before forwarding
2000
2127
  let pluginRequest = request;
2001
2128
  if (this.plugins.count > 0 && request.method === 'tools/call') {