@anyshift/mcp-proxy 0.6.1 → 0.6.3

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 (2) hide show
  1. package/dist/index.js +58 -4
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -417,6 +417,51 @@ async function main() {
417
417
  console.debug('[mcp-proxy] Standalone mode - no child MCP');
418
418
  }
419
419
  // ------------------------------------------------------------------------
420
+ // 2.5. STORE ORIGINAL TOOL SCHEMAS FOR SMART PARAMETER SANITIZATION
421
+ // ------------------------------------------------------------------------
422
+ // Create a map of original tool schemas (before proxy param injection)
423
+ // This allows us to detect if description/isRetryAttempt/originalToolId were
424
+ // originally part of the child tool's schema (to handle parameter name collisions)
425
+ const originalToolSchemas = new Map();
426
+ if (childToolsResponse.tools) {
427
+ for (const tool of childToolsResponse.tools) {
428
+ const originalParams = new Set();
429
+ if (tool.inputSchema?.properties) {
430
+ for (const paramName of Object.keys(tool.inputSchema.properties)) {
431
+ originalParams.add(paramName);
432
+ }
433
+ }
434
+ originalToolSchemas.set(tool.name, originalParams);
435
+ }
436
+ }
437
+ /**
438
+ * Sanitize tool arguments before forwarding to child MCP.
439
+ * Removes proxy parameters (description, isRetryAttempt, originalToolId)
440
+ * ONLY if they weren't originally part of the child tool's schema.
441
+ * This handles parameter name collisions gracefully.
442
+ */
443
+ function sanitizeToolArgs(toolName, args) {
444
+ const originalParams = originalToolSchemas.get(toolName);
445
+ if (!originalParams) {
446
+ // If we don't have schema info, pass through all args (safer than stripping)
447
+ return args;
448
+ }
449
+ const sanitized = {};
450
+ const proxyParamNames = Object.keys(PROXY_PARAMS);
451
+ for (const [key, value] of Object.entries(args)) {
452
+ // Keep the parameter if:
453
+ // 1. It's NOT a proxy parameter, OR
454
+ // 2. It WAS in the original tool's schema (collision case)
455
+ if (!proxyParamNames.includes(key) || originalParams.has(key)) {
456
+ sanitized[key] = value;
457
+ }
458
+ else if (ENABLE_LOGGING) {
459
+ console.debug(`[mcp-proxy] Stripping proxy parameter '${key}' from ${toolName} call`);
460
+ }
461
+ }
462
+ return sanitized;
463
+ }
464
+ // ------------------------------------------------------------------------
420
465
  // 3. CREATE PROXY SERVER
421
466
  // ------------------------------------------------------------------------
422
467
  const server = new Server({
@@ -641,12 +686,18 @@ async function main() {
641
686
  if (ENABLE_LOGGING) {
642
687
  console.debug(`[mcp-proxy] Forwarding to child MCP: ${toolName}`);
643
688
  }
689
+ // Sanitize arguments: remove proxy parameters that weren't in original tool schema
690
+ const sanitizedArgs = sanitizeToolArgs(toolName, toolArgs);
644
691
  result = await childClient.callTool({
645
692
  name: toolName,
646
- arguments: toolArgs
693
+ arguments: sanitizedArgs
647
694
  });
648
695
  // Check if child MCP returned an error
649
696
  const childReturnedError = !!result.isError;
697
+ // Preserve _meta from child response (e.g. parsed_commands) for passthrough
698
+ const childMeta = result._meta && typeof result._meta === 'object' && Object.keys(result._meta).length > 0
699
+ ? result._meta
700
+ : undefined;
650
701
  // Process result through file writer to get unified response
651
702
  if (result.content && Array.isArray(result.content) && result.content.length > 0) {
652
703
  // Extract text content and embedded resources from all content items
@@ -709,7 +760,8 @@ async function main() {
709
760
  type: 'text',
710
761
  text: JSON.stringify(createErrorResponse(tool_id, contentStr, toolArgs), null, 2)
711
762
  }],
712
- isError: true
763
+ isError: true,
764
+ ...(childMeta ? { _meta: childMeta } : {})
713
765
  };
714
766
  }
715
767
  // Get unified response from file writer
@@ -750,7 +802,8 @@ async function main() {
750
802
  type: 'text',
751
803
  text: JSON.stringify(unifiedResponse, null, 2)
752
804
  }],
753
- isError: !!unifiedResponse.error
805
+ isError: !!unifiedResponse.error,
806
+ ...(childMeta ? { _meta: childMeta } : {})
754
807
  };
755
808
  }
756
809
  }
@@ -761,7 +814,8 @@ async function main() {
761
814
  type: 'text',
762
815
  text: JSON.stringify(createContentResponse(tool_id, result, toolArgs), null, 2)
763
816
  }],
764
- isError: childReturnedError
817
+ isError: childReturnedError,
818
+ ...(childMeta ? { _meta: childMeta } : {})
765
819
  };
766
820
  }
767
821
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anyshift/mcp-proxy",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "Generic MCP proxy that adds truncation, file writing, and JQ capabilities to any MCP server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -19,6 +19,7 @@
19
19
  },
20
20
  "devDependencies": {
21
21
  "@jest/globals": "^30.2.0",
22
+ "@types/glob": "^9.0.0",
22
23
  "@types/jest": "^30.0.0",
23
24
  "@types/node": "^22.0.0",
24
25
  "jest": "^30.2.0",