@anyshift/mcp-tools-common
Version:
Reusable JQ tool and file writing utilities for MCP servers
276 lines (263 loc) • 8.97 kB
text/typescript
import { z } from 'zod';
/**
* Configuration for the file writer module
*/
interface FileWriterConfig {
/** Whether file writing is enabled */
enabled: boolean;
/** Directory where files should be written */
outputPath: string;
/** Minimum character count to trigger file writing (default: 1000) */
minCharsForWrite?: number;
/** Custom abbreviations for tool names in filenames */
toolAbbreviations?: Record<string, string>;
}
/**
* Configuration for the JQ tool
*/
interface JqConfig {
/** Paths where JQ is allowed to read files */
allowedPaths: string[];
/** Timeout for JQ query execution in milliseconds (default: 30000) */
timeoutMs?: number;
}
/**
* Result from file writer operations
*/
interface FileWriterResult {
content: Array<{
type: string;
text: string;
}>;
}
/**
* Schema analysis result for a JSON structure
*/
interface JsonSchema {
type: string;
properties?: Record<string, unknown>;
items?: unknown;
length?: number;
_hasNulls?: boolean;
_keysAreNumeric?: boolean;
_accessPattern?: string;
}
/**
* Nullable fields extracted from schema
*/
interface NullableFields {
/** Fields that are always null */
alwaysNull: string[];
/** Fields that can be null (mixed types) */
nullable: string[];
}
/**
* Analyze JSON structure and generate enhanced schema
* @param obj - The object to analyze
* @param path - Current path in the object (for debugging)
* @returns Schema representation of the object
*/
declare function analyzeJsonSchema(obj: unknown, path?: string): JsonSchema;
/**
* Extract nullable and always-null fields from schema
* @param schema - The schema to analyze
* @param basePath - Base path for field names
* @returns Object containing arrays of always-null and nullable field paths
*/
declare function extractNullableFields(schema: unknown, basePath?: string): NullableFields;
/**
* Create a file writer instance with the given configuration
* @param config - File writer configuration
* @returns Object with handleResponse method
*/
declare function createFileWriter(config: FileWriterConfig): {
/**
* Handle tool response - writes to file if conditions are met
* @param toolName - Name of the tool that generated the response
* @param args - Arguments passed to the tool
* @param responseData - The response data to potentially write to file
* @returns Either the original response or a file reference response
*/
handleResponse: (toolName: string, args: Record<string, unknown>, responseData: unknown) => Promise<FileWriterResult | unknown>;
};
/**
* Centralized response handler with file writing capability
* @param config - File writer configuration
* @param toolName - Name of the tool that generated the response
* @param args - Arguments passed to the tool
* @param responseData - The response data to potentially write to file
* @returns Either the original response or a file reference response
*/
declare function handleToolResponse(config: FileWriterConfig, toolName: string, args: Record<string, unknown>, responseData: unknown): Promise<FileWriterResult | unknown>;
/**
* Zod schema for JQ query execution
*/
declare const ExecuteJqQuerySchema: z.ZodObject<{
jq_query: z.ZodString;
file_path: z.ZodString;
description: z.ZodOptional<z.ZodString>;
}, "strip", z.ZodTypeAny, {
jq_query: string;
file_path: string;
description?: string | undefined;
}, {
jq_query: string;
file_path: string;
description?: string | undefined;
}>;
/**
* Tool definition for JQ query execution with enhanced prompts
* This includes schema-first development hints for better LLM usage
*/
declare const JQ_TOOL_DEFINITION: {
name: string;
description: string;
inputSchema: {
type: string;
properties: {
jq_query: {
type: string;
description: string;
};
file_path: {
type: string;
description: string;
};
description: {
type: string;
description: string;
};
};
required: string[];
};
};
/**
* Execute a JQ query on a JSON file
* @param config - JQ configuration
* @param jqQuery - The JQ query to execute
* @param filePath - Absolute path to the JSON file
* @returns Promise with the query result
*/
declare function executeJqQuery(config: JqConfig, jqQuery: string, filePath: string): Promise<{
content: Array<{
type: 'text';
text: string;
}>;
}>;
/**
* Create a JQ tool instance with the given configuration
* @param config - JQ configuration
* @returns Object with handler and toolDefinition
*/
declare function createJqTool(config: JqConfig): {
/**
* Tool definition for MCP server registration
*/
toolDefinition: {
name: string;
description: string;
inputSchema: {
type: string;
properties: {
jq_query: {
type: string;
description: string;
};
file_path: {
type: string;
description: string;
};
description: {
type: string;
description: string;
};
};
required: string[];
};
};
/**
* Handler for JQ query execution requests
* @param request - MCP request containing jq_query and file_path
* @returns Promise with the query result
*/
handler: (request: {
params: {
arguments: Record<string, unknown>;
};
}) => Promise<{
content: Array<{
type: "text";
text: string;
}>;
}>;
};
/**
* Generate LLM-friendly compact filename
* @param toolName - Name of the tool that generated the data
* @param args - Arguments passed to the tool
* @param toolAbbreviations - Optional custom abbreviations for tool names
* @returns Compact filename like "1697834567123_met_qry_a3b4c5.json"
*/
declare const generateCompactFilename: (toolName: string, args: Record<string, unknown>, toolAbbreviations?: Record<string, string>) => string;
/**
* Validate that a file path is within allowed directories
* @param filePath - The file path to validate
* @param allowedPaths - Array of allowed directory paths
* @returns The validated real path (with symlinks resolved)
* @throws Error if path is invalid, doesn't exist, or is outside allowed directories
*/
declare const validatePathWithinAllowedDirs: (filePath: string, allowedPaths: string[]) => string;
/**
* Configuration for response truncation
*/
interface TruncationConfig {
/**
* Maximum number of tokens allowed in response
* Common values: 10000 (Datadog), 15000 (Anyshift)
*/
maxTokens: number;
/**
* Enable detailed JSON logging to stderr
* Useful for debugging and monitoring truncation behavior
* Default: false
*/
enableLogging?: boolean;
/**
* Custom prefix for truncation notice message
* Default: "RESPONSE TRUNCATED"
*/
messagePrefix?: string;
/**
* Characters per token ratio for estimation
* Default: 4 (standard approximation)
*/
charsPerToken?: number;
}
/**
* Estimate token count using chars/token ratio
* Uses a simple approximation where 1 token ≈ 4 characters
*
* @param text - The text to estimate tokens for
* @param charsPerToken - Characters per token ratio (default: 4)
* @returns Estimated token count
*/
declare function estimateTokens(text: string, charsPerToken?: number): number;
/**
* Check if content would be truncated without actually truncating
*
* @param content - The content to check
* @param maxTokens - Maximum token limit
* @param charsPerToken - Characters per token ratio (default: 4)
* @returns True if content exceeds token limit
*/
declare function wouldBeTruncated(content: string, maxTokens: number, charsPerToken?: number): boolean;
/**
* Truncate response content if it exceeds token limit
* Optionally logs detailed metrics to stderr for monitoring
*
* @param config - Truncation configuration
* @param content - The content to potentially truncate
* @returns Original content if under limit, or truncated content with notice
*/
declare function truncateResponseIfNeeded(config: TruncationConfig, content: string): string;
export { ExecuteJqQuerySchema, type FileWriterConfig, type FileWriterResult, JQ_TOOL_DEFINITION, type JqConfig, type JsonSchema, type NullableFields, type TruncationConfig, analyzeJsonSchema, createFileWriter, createJqTool, estimateTokens, executeJqQuery, extractNullableFields, generateCompactFilename, handleToolResponse, truncateResponseIfNeeded, validatePathWithinAllowedDirs, wouldBeTruncated };