@anyshift/mcp-proxy 0.4.0 → 0.4.2-dev0

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.
@@ -5,6 +5,18 @@ import { generateQueryAssistSchema, generateJsonlQueryAssistSchema } from './sch
5
5
  import { parseDynatraceDqlResponse, isDynatraceDqlTool, } from './dynatrace.js';
6
6
  // Default minimum character count to trigger file writing
7
7
  const DEFAULT_MIN_CHARS = 1000;
8
+ /**
9
+ * Generate the message to include when data is written to file, guiding the LLM on how to use the result.
10
+ * The message conditionally includes the timeseries tool based on configuration.
11
+ */
12
+ function getFileWrittenMessage(enableTimeseries) {
13
+ const toolsList = enableTimeseries
14
+ ? '"execute_jq_query" tool (for JSON/JSONL data extraction and transformation) or "detect_timeseries_anomalies" tool (for time series analysis)'
15
+ : '"execute_jq_query" tool (for JSON/JSONL data extraction and transformation)';
16
+ return `To read this file, use the ${toolsList}.
17
+
18
+ IMPORTANT for supporting facts: This tool_id CANNOT be used as the proxy_tool_id in your output's supporting_facts evidence. You must read the file using one of the tools above and use THAT tool's tool_id as the proxy_tool_id to support facts in your output.`;
19
+ }
8
20
  /**
9
21
  * Detect whether content is JSON, JSONL, or plain text
10
22
  */
@@ -251,6 +263,7 @@ async function handleDynatraceDqlResponse(config, tool_id, parsedDql) {
251
263
  wroteToFile: true,
252
264
  filePath: filepath,
253
265
  fileSchema,
266
+ message: getFileWrittenMessage(config.enableTimeseries ?? false),
254
267
  };
255
268
  }
256
269
  catch (error) {
@@ -388,6 +401,7 @@ export async function handleToolResponse(config, toolName, args, responseData) {
388
401
  wroteToFile: true,
389
402
  filePath: filepath,
390
403
  fileSchema,
404
+ message: getFileWrittenMessage(config.enableTimeseries ?? false),
391
405
  };
392
406
  }
393
407
  catch (error) {
package/dist/index.js CHANGED
@@ -324,7 +324,8 @@ async function main() {
324
324
  toolAbbreviations: {}, // No service-specific abbreviations (generic proxy)
325
325
  schemaMaxDepth: SCHEMA_MAX_DEPTH,
326
326
  schemaMaxPaths: SCHEMA_MAX_PATHS,
327
- schemaMaxKeys: SCHEMA_MAX_KEYS
327
+ schemaMaxKeys: SCHEMA_MAX_KEYS,
328
+ enableTimeseries: ENABLE_TIMESERIES
328
329
  };
329
330
  const fileWriter = createFileWriter(fileWriterConfig);
330
331
  // JQ tool configuration
@@ -452,26 +453,72 @@ async function main() {
452
453
  const childReturnedError = !!result.isError;
453
454
  // Process result through file writer to get unified response
454
455
  if (result.content && Array.isArray(result.content) && result.content.length > 0) {
455
- const item = result.content[0];
456
- if (item.type === 'text' && typeof item.text === 'string') {
457
- const originalLength = item.text.length;
456
+ // Extract text content and embedded resources from all content items
457
+ const textContents = [];
458
+ const resources = [];
459
+ for (const item of result.content) {
460
+ if (item.type === 'text' && typeof item.text === 'string') {
461
+ textContents.push(item.text);
462
+ }
463
+ else if (item.type === 'resource' && item.resource) {
464
+ // Handle EmbeddedResource - extract content from resource field
465
+ const resource = item.resource;
466
+ const resourceObj = {};
467
+ if (resource.uri)
468
+ resourceObj.uri = resource.uri;
469
+ if (resource.mimeType)
470
+ resourceObj.mimeType = resource.mimeType;
471
+ // Content can be in 'text' (string) or 'blob' (base64 binary)
472
+ if (resource.text) {
473
+ resourceObj.content = resource.text;
474
+ }
475
+ else if (resource.blob) {
476
+ // For binary content, keep as base64 or decode based on mimeType
477
+ resourceObj.content = typeof resource.blob === 'string'
478
+ ? resource.blob
479
+ : Buffer.from(resource.blob).toString('base64');
480
+ }
481
+ resources.push(resourceObj);
482
+ }
483
+ }
484
+ // Build combined content object
485
+ let combinedContent;
486
+ if (resources.length > 0) {
487
+ // Has resources - combine text and resources into single object
488
+ combinedContent = {
489
+ text: textContents.length === 1 ? textContents[0] : textContents,
490
+ resources: resources.length === 1 ? resources[0] : resources
491
+ };
492
+ if (ENABLE_LOGGING) {
493
+ console.debug(`[mcp-proxy] Combined ${textContents.length} text items and ${resources.length} resources`);
494
+ }
495
+ }
496
+ else if (textContents.length > 0) {
497
+ // Text only - use first text content (original behavior)
498
+ combinedContent = textContents[0];
499
+ }
500
+ if (combinedContent !== undefined) {
501
+ const contentStr = typeof combinedContent === 'string'
502
+ ? combinedContent
503
+ : JSON.stringify(combinedContent);
504
+ const originalLength = contentStr.length;
458
505
  // If child returned error, pass through directly without file writing
459
506
  if (childReturnedError) {
460
507
  const tool_id = generateToolId(toolName, toolArgs, fileWriterConfig.toolAbbreviations);
461
508
  if (ENABLE_LOGGING) {
462
- console.debug(`[mcp-proxy] Child MCP returned error for ${toolName}: ${item.text.substring(0, 100)}...`);
509
+ console.debug(`[mcp-proxy] Child MCP returned error for ${toolName}: ${contentStr.substring(0, 100)}...`);
463
510
  }
464
511
  return {
465
512
  content: [{
466
513
  type: 'text',
467
- text: JSON.stringify(createErrorResponse(tool_id, item.text, toolArgs), null, 2)
514
+ text: JSON.stringify(createErrorResponse(tool_id, contentStr, toolArgs), null, 2)
468
515
  }],
469
516
  isError: true
470
517
  };
471
518
  }
472
519
  // Get unified response from file writer
473
520
  const unifiedResponse = await fileWriter.handleResponse(toolName, toolArgs, {
474
- content: [{ type: 'text', text: item.text }]
521
+ content: [{ type: 'text', text: contentStr }]
475
522
  });
476
523
  if (ENABLE_LOGGING) {
477
524
  if (unifiedResponse.wroteToFile) {
@@ -483,13 +530,13 @@ async function main() {
483
530
  }
484
531
  // If not written to file, apply truncation to outputContent
485
532
  if (!unifiedResponse.wroteToFile && unifiedResponse.outputContent) {
486
- const contentStr = typeof unifiedResponse.outputContent === 'string'
533
+ const outputStr = typeof unifiedResponse.outputContent === 'string'
487
534
  ? unifiedResponse.outputContent
488
535
  : JSON.stringify(unifiedResponse.outputContent);
489
- const truncated = truncateResponseIfNeeded(truncationConfig, contentStr);
490
- if (truncated.length < contentStr.length) {
536
+ const truncated = truncateResponseIfNeeded(truncationConfig, outputStr);
537
+ if (truncated.length < outputStr.length) {
491
538
  if (ENABLE_LOGGING) {
492
- console.debug(`[mcp-proxy] Truncated response: ${contentStr.length} → ${truncated.length} chars`);
539
+ console.debug(`[mcp-proxy] Truncated response: ${outputStr.length} → ${truncated.length} chars`);
493
540
  }
494
541
  // Re-parse if it was JSON, otherwise keep as string
495
542
  try {
@@ -34,6 +34,8 @@ export interface FileWriterConfig {
34
34
  schemaMaxPaths?: number;
35
35
  /** Maximum keys to analyze per object (default: 50) */
36
36
  schemaMaxKeys?: number;
37
+ /** Whether the timeseries anomaly detection tool is enabled (affects file-written message) */
38
+ enableTimeseries?: boolean;
37
39
  }
38
40
  /**
39
41
  * Configuration for the JQ tool
@@ -74,4 +76,6 @@ export interface UnifiedToolResponse {
74
76
  isRetryAttempt?: boolean;
75
77
  /** The original tool_id this call is retrying */
76
78
  originalToolId?: string;
79
+ /** Informational message for the LLM (e.g., guidance on how to use the file) */
80
+ message?: string;
77
81
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anyshift/mcp-proxy",
3
- "version": "0.4.0",
3
+ "version": "0.4.2-dev0",
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",