@anyshift/mcp-proxy 0.2.0 → 0.2.2
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/README.md +74 -31
- package/dist/__tests__/helpers/testUtils.d.ts +127 -0
- package/dist/__tests__/helpers/testUtils.js +122 -0
- package/dist/__tests__/unit/queryAssistSchema.test.d.ts +1 -0
- package/dist/__tests__/unit/queryAssistSchema.test.js +267 -0
- package/dist/__tests__/unit/truncation.test.d.ts +5 -0
- package/dist/__tests__/unit/truncation.test.js +204 -0
- package/dist/fileWriter/index.d.ts +1 -1
- package/dist/fileWriter/index.js +1 -1
- package/dist/fileWriter/schema.d.ts +38 -11
- package/dist/fileWriter/schema.js +248 -98
- package/dist/fileWriter/writer.js +66 -110
- package/dist/index.js +64 -28
- package/dist/jq/tool.js +15 -0
- package/dist/types/index.d.ts +6 -0
- package/package.json +11 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { generateCompactFilename } from '../utils/filename.js';
|
|
4
|
-
import {
|
|
4
|
+
import { generateQueryAssistSchema } from './schema.js';
|
|
5
5
|
// Default minimum character count to trigger file writing
|
|
6
6
|
const DEFAULT_MIN_CHARS = 1000;
|
|
7
7
|
/**
|
|
@@ -104,6 +104,53 @@ const extractRawText = (response) => {
|
|
|
104
104
|
}
|
|
105
105
|
return null;
|
|
106
106
|
};
|
|
107
|
+
/**
|
|
108
|
+
* Extract the exact content that will be written to file
|
|
109
|
+
* This is used for both character counting AND file writing to ensure consistency
|
|
110
|
+
* @returns Object with contentToWrite and parsedForSchema (if applicable)
|
|
111
|
+
*/
|
|
112
|
+
const extractContentForFile = (responseData) => {
|
|
113
|
+
const rawText = extractRawText(responseData);
|
|
114
|
+
let contentToWrite;
|
|
115
|
+
let parsedForSchema = null;
|
|
116
|
+
if (rawText) {
|
|
117
|
+
try {
|
|
118
|
+
// Try to parse the raw text as JSON for prettier formatting
|
|
119
|
+
let jsonText = rawText;
|
|
120
|
+
// Extract JSON from common response patterns
|
|
121
|
+
const jsonMatch = rawText.match(/:\s*(\{.*\}|\[.*\])$/s);
|
|
122
|
+
if (jsonMatch) {
|
|
123
|
+
jsonText = jsonMatch[1];
|
|
124
|
+
}
|
|
125
|
+
const parsed = JSON.parse(jsonText);
|
|
126
|
+
parsedForSchema = parsed;
|
|
127
|
+
// Remove pagination-related fields before writing
|
|
128
|
+
const { pagination, has_more, next_page, previous_page, page, page_size, total_pages, ...cleanData } = parsed;
|
|
129
|
+
contentToWrite = JSON.stringify(cleanData, null, 2);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// If parsing fails, write the raw text directly
|
|
133
|
+
contentToWrite = rawText;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
// Fallback: extract from display content or stringify the entire response
|
|
138
|
+
if (responseData &&
|
|
139
|
+
typeof responseData === 'object' &&
|
|
140
|
+
'content' in responseData &&
|
|
141
|
+
Array.isArray(responseData.content)) {
|
|
142
|
+
const textContent = responseData.content
|
|
143
|
+
.filter((item) => item.type === 'text')
|
|
144
|
+
.map((item) => item.text)
|
|
145
|
+
.join('\n');
|
|
146
|
+
contentToWrite = textContent;
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
contentToWrite = JSON.stringify(responseData, null, 2);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return { contentToWrite, parsedForSchema };
|
|
153
|
+
};
|
|
107
154
|
/**
|
|
108
155
|
* Centralized response handler with file writing capability
|
|
109
156
|
* @param config - File writer configuration
|
|
@@ -113,8 +160,8 @@ const extractRawText = (response) => {
|
|
|
113
160
|
* @returns Either the original response or a file reference response
|
|
114
161
|
*/
|
|
115
162
|
export async function handleToolResponse(config, toolName, args, responseData) {
|
|
116
|
-
//
|
|
117
|
-
if (toolName === 'execute_jq_query') {
|
|
163
|
+
// Some tools should always return directly to AI (never write to file)
|
|
164
|
+
if (toolName === 'execute_jq_query' || toolName === 'get_label_schema') {
|
|
118
165
|
return responseData;
|
|
119
166
|
}
|
|
120
167
|
// If there's an error, return proper MCP error response (never write errors to file)
|
|
@@ -134,26 +181,11 @@ export async function handleToolResponse(config, toolName, args, responseData) {
|
|
|
134
181
|
if (!config.enabled || !config.outputPath) {
|
|
135
182
|
return responseData;
|
|
136
183
|
}
|
|
184
|
+
// Extract the content that will be written to file
|
|
185
|
+
// This ensures we count the EXACT same content that will be written
|
|
186
|
+
const { contentToWrite, parsedForSchema } = extractContentForFile(responseData);
|
|
137
187
|
// Check character count threshold - if response is too short, return directly
|
|
138
|
-
const
|
|
139
|
-
let contentLength = 0;
|
|
140
|
-
if (rawText) {
|
|
141
|
-
contentLength = rawText.length;
|
|
142
|
-
}
|
|
143
|
-
else if (responseData &&
|
|
144
|
-
typeof responseData === 'object' &&
|
|
145
|
-
'content' in responseData &&
|
|
146
|
-
Array.isArray(responseData.content)) {
|
|
147
|
-
const textContent = responseData.content
|
|
148
|
-
.filter((item) => item.type === 'text')
|
|
149
|
-
.map((item) => item.text)
|
|
150
|
-
.join('\n');
|
|
151
|
-
contentLength = textContent.length;
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
contentLength = JSON.stringify(responseData).length;
|
|
155
|
-
}
|
|
156
|
-
// If content is shorter than threshold, return response directly instead of writing to file
|
|
188
|
+
const contentLength = contentToWrite.length;
|
|
157
189
|
const minChars = config.minCharsForWrite ?? DEFAULT_MIN_CHARS;
|
|
158
190
|
if (contentLength < minChars) {
|
|
159
191
|
return responseData;
|
|
@@ -165,106 +197,30 @@ export async function handleToolResponse(config, toolName, args, responseData) {
|
|
|
165
197
|
const filepath = path.join(config.outputPath, filename);
|
|
166
198
|
// Ensure output directory exists
|
|
167
199
|
await fs.mkdir(config.outputPath, { recursive: true });
|
|
168
|
-
//
|
|
169
|
-
let contentToWrite;
|
|
170
|
-
let parsedForSchema;
|
|
171
|
-
if (rawText) {
|
|
172
|
-
try {
|
|
173
|
-
// Try to parse the raw text as JSON for prettier formatting
|
|
174
|
-
let jsonText = rawText;
|
|
175
|
-
// Extract JSON from common response patterns
|
|
176
|
-
const jsonMatch = rawText.match(/:\s*(\{.*\}|\[.*\])$/s);
|
|
177
|
-
if (jsonMatch) {
|
|
178
|
-
jsonText = jsonMatch[1];
|
|
179
|
-
}
|
|
180
|
-
const parsed = JSON.parse(jsonText);
|
|
181
|
-
parsedForSchema = parsed;
|
|
182
|
-
// Remove pagination-related fields before writing
|
|
183
|
-
const { pagination, has_more, next_page, previous_page, page, page_size, total_pages, ...cleanData } = parsed;
|
|
184
|
-
contentToWrite = JSON.stringify(cleanData, null, 2);
|
|
185
|
-
}
|
|
186
|
-
catch {
|
|
187
|
-
// If parsing fails, write the raw text directly
|
|
188
|
-
contentToWrite = rawText;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
// Fallback: extract from display content or stringify the entire response
|
|
193
|
-
if (responseData &&
|
|
194
|
-
typeof responseData === 'object' &&
|
|
195
|
-
'content' in responseData &&
|
|
196
|
-
Array.isArray(responseData.content)) {
|
|
197
|
-
const textContent = responseData.content
|
|
198
|
-
.filter((item) => item.type === 'text')
|
|
199
|
-
.map((item) => item.text)
|
|
200
|
-
.join('\n');
|
|
201
|
-
contentToWrite = textContent;
|
|
202
|
-
}
|
|
203
|
-
else {
|
|
204
|
-
contentToWrite = JSON.stringify(responseData, null, 2);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
200
|
+
// Write the exact content we counted
|
|
207
201
|
await fs.writeFile(filepath, contentToWrite);
|
|
208
|
-
//
|
|
202
|
+
// Generate query-assist schema if we have valid JSON
|
|
209
203
|
let schemaInfo = '';
|
|
210
|
-
let quickReference = '';
|
|
211
204
|
if (parsedForSchema) {
|
|
212
205
|
// Use the clean data (without pagination) for schema analysis
|
|
213
206
|
const { pagination, has_more, next_page, previous_page, page, page_size, total_pages, ...cleanData } = parsedForSchema;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
else if (schemaObj.type === 'array') {
|
|
224
|
-
quickReference += ` • Structure: Array with ${schemaObj.length} items\n`;
|
|
225
|
-
}
|
|
226
|
-
else if (schemaObj.type === 'object' && schemaObj.properties) {
|
|
227
|
-
const props = schemaObj.properties;
|
|
228
|
-
const keys = Object.keys(props).slice(0, 5).join(', ');
|
|
229
|
-
quickReference += ` • Structure: Object with keys: ${keys}\n`;
|
|
230
|
-
}
|
|
231
|
-
// Always null fields
|
|
232
|
-
if (nullFields.alwaysNull.length > 0) {
|
|
233
|
-
const fieldList = nullFields.alwaysNull.slice(0, 5).join(', ');
|
|
234
|
-
const more = nullFields.alwaysNull.length > 5
|
|
235
|
-
? ` (+${nullFields.alwaysNull.length - 5} more)`
|
|
236
|
-
: '';
|
|
237
|
-
quickReference += ` • Always null: ${fieldList}${more}\n`;
|
|
238
|
-
}
|
|
239
|
-
// Nullable fields
|
|
240
|
-
if (nullFields.nullable.length > 0) {
|
|
241
|
-
const fieldList = nullFields.nullable.slice(0, 5).join(', ');
|
|
242
|
-
const more = nullFields.nullable.length > 5
|
|
243
|
-
? ` (+${nullFields.nullable.length - 5} more)`
|
|
244
|
-
: '';
|
|
245
|
-
quickReference += ` • Sometimes null: ${fieldList}${more}\n`;
|
|
246
|
-
}
|
|
247
|
-
// Suggest exploratory queries
|
|
248
|
-
if (schemaObj._keysAreNumeric) {
|
|
249
|
-
quickReference += ` • Explore: keys, .["0"] | keys, .["0"]\n`;
|
|
250
|
-
}
|
|
251
|
-
else if (schemaObj.type === 'array' && schemaObj.length > 0) {
|
|
252
|
-
quickReference += ` • Explore: length, .[0] | keys, .[0]\n`;
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
quickReference += ` • Explore: keys, type\n`;
|
|
256
|
-
}
|
|
257
|
-
// Full schema
|
|
258
|
-
schemaInfo = `\n\nFull JSON Schema:\n${JSON.stringify(schema, null, 2)}`;
|
|
207
|
+
// Generate compact query-assist schema using config values
|
|
208
|
+
// Pass contentLength to avoid re-stringifying large payloads
|
|
209
|
+
schemaInfo = `\n\n${generateQueryAssistSchema(cleanData, {
|
|
210
|
+
maxDepth: config.schemaMaxDepth ?? 2,
|
|
211
|
+
maxPaths: config.schemaMaxPaths ?? 20,
|
|
212
|
+
maxKeys: config.schemaMaxKeys ?? 50,
|
|
213
|
+
dataSize: contentLength
|
|
214
|
+
})}`;
|
|
259
215
|
}
|
|
260
216
|
// Count lines in the content
|
|
261
217
|
const lineCount = contentToWrite.split('\n').length;
|
|
262
|
-
// Return success message with file path, size, lines,
|
|
218
|
+
// Return success message with file path, size, lines, and schema
|
|
263
219
|
return {
|
|
264
220
|
content: [
|
|
265
221
|
{
|
|
266
222
|
type: 'text',
|
|
267
|
-
text: `📄 File: ${filepath}\nSize: ${contentToWrite.length} characters | Lines: ${lineCount}${
|
|
223
|
+
text: `📄 File: ${filepath}\nSize: ${contentToWrite.length} characters | Lines: ${lineCount}${schemaInfo}`,
|
|
268
224
|
},
|
|
269
225
|
],
|
|
270
226
|
};
|
package/dist/index.js
CHANGED
|
@@ -95,6 +95,24 @@ const ENABLE_JQ = process.env.MCP_PROXY_ENABLE_JQ !== 'false'; // default true
|
|
|
95
95
|
* Timeout in milliseconds for JQ query execution
|
|
96
96
|
*/
|
|
97
97
|
const JQ_TIMEOUT_MS = parseInt(process.env.MCP_PROXY_JQ_TIMEOUT_MS || '30000');
|
|
98
|
+
/**
|
|
99
|
+
* MCP_PROXY_SCHEMA_MAX_DEPTH (OPTIONAL, default: 3)
|
|
100
|
+
* Maximum depth to traverse when generating query-assist schemas
|
|
101
|
+
* Deeper structures will show exploration prompts instead
|
|
102
|
+
*/
|
|
103
|
+
const SCHEMA_MAX_DEPTH = parseInt(process.env.MCP_PROXY_SCHEMA_MAX_DEPTH || '3');
|
|
104
|
+
/**
|
|
105
|
+
* MCP_PROXY_SCHEMA_MAX_PATHS (OPTIONAL, default: 20)
|
|
106
|
+
* Maximum number of paths to show in query-assist schemas
|
|
107
|
+
* Prioritizes non-null, shallow, and interesting paths
|
|
108
|
+
*/
|
|
109
|
+
const SCHEMA_MAX_PATHS = parseInt(process.env.MCP_PROXY_SCHEMA_MAX_PATHS || '20');
|
|
110
|
+
/**
|
|
111
|
+
* MCP_PROXY_SCHEMA_MAX_KEYS (OPTIONAL, default: 50)
|
|
112
|
+
* Maximum number of object keys to analyze per object
|
|
113
|
+
* Objects with more keys will show a key limit warning
|
|
114
|
+
*/
|
|
115
|
+
const SCHEMA_MAX_KEYS = parseInt(process.env.MCP_PROXY_SCHEMA_MAX_KEYS || '50');
|
|
98
116
|
/**
|
|
99
117
|
* MCP_PROXY_ENABLE_LOGGING (OPTIONAL, default: false)
|
|
100
118
|
* Enable debug logging for the proxy
|
|
@@ -228,7 +246,10 @@ async function main() {
|
|
|
228
246
|
enabled: WRITE_TO_FILE,
|
|
229
247
|
outputPath: OUTPUT_PATH,
|
|
230
248
|
minCharsForWrite: MIN_CHARS_FOR_WRITE,
|
|
231
|
-
toolAbbreviations: {} // No service-specific abbreviations (generic proxy)
|
|
249
|
+
toolAbbreviations: {}, // No service-specific abbreviations (generic proxy)
|
|
250
|
+
schemaMaxDepth: SCHEMA_MAX_DEPTH,
|
|
251
|
+
schemaMaxPaths: SCHEMA_MAX_PATHS,
|
|
252
|
+
schemaMaxKeys: SCHEMA_MAX_KEYS
|
|
232
253
|
};
|
|
233
254
|
const fileWriter = createFileWriter(fileWriterConfig);
|
|
234
255
|
// JQ tool configuration
|
|
@@ -263,56 +284,71 @@ async function main() {
|
|
|
263
284
|
console.error(`[mcp-proxy] Tool call: ${toolName}`);
|
|
264
285
|
}
|
|
265
286
|
try {
|
|
287
|
+
let result;
|
|
266
288
|
// Handle JQ tool locally (if enabled)
|
|
267
289
|
if (toolName === 'execute_jq_query' && jqTool) {
|
|
268
290
|
if (ENABLE_LOGGING) {
|
|
269
291
|
console.error('[mcp-proxy] Executing JQ tool locally');
|
|
270
292
|
}
|
|
271
|
-
|
|
293
|
+
result = await jqTool.handler({
|
|
272
294
|
params: { arguments: toolArgs }
|
|
273
295
|
});
|
|
274
296
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
297
|
+
else {
|
|
298
|
+
// Forward all other tools to child MCP (if child exists)
|
|
299
|
+
if (!childClient) {
|
|
300
|
+
return {
|
|
301
|
+
content: [{
|
|
302
|
+
type: 'text',
|
|
303
|
+
text: `Error: Tool ${toolName} not available in standalone mode (no child MCP)`
|
|
304
|
+
}],
|
|
305
|
+
isError: true
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
if (ENABLE_LOGGING) {
|
|
309
|
+
console.error(`[mcp-proxy] Forwarding to child MCP: ${toolName}`);
|
|
310
|
+
}
|
|
311
|
+
result = await childClient.callTool({
|
|
312
|
+
name: toolName,
|
|
313
|
+
arguments: toolArgs
|
|
314
|
+
});
|
|
287
315
|
}
|
|
288
|
-
|
|
289
|
-
name: toolName,
|
|
290
|
-
arguments: toolArgs
|
|
291
|
-
});
|
|
292
|
-
// Apply truncation and file writing to text responses
|
|
316
|
+
// Apply file writing and truncation to text responses
|
|
293
317
|
if (result.content && Array.isArray(result.content) && result.content.length > 0) {
|
|
294
318
|
for (let i = 0; i < result.content.length; i++) {
|
|
295
319
|
const item = result.content[i];
|
|
296
320
|
if (item.type === 'text' && typeof item.text === 'string') {
|
|
321
|
+
const originalText = item.text;
|
|
297
322
|
const originalLength = item.text.length;
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
console.error(`[mcp-proxy] Truncated response: ${originalLength} → ${item.text.length} chars`);
|
|
302
|
-
}
|
|
303
|
-
// Step 2: Apply file writing (if enabled)
|
|
323
|
+
let fileWasWritten = false;
|
|
324
|
+
// Step 1: Try file writing first (if enabled)
|
|
325
|
+
// This writes the FULL original data to file
|
|
304
326
|
if (fileWriterConfig.enabled) {
|
|
305
327
|
const fileResult = await fileWriter.handleResponse(toolName, toolArgs, {
|
|
306
328
|
content: [{ type: 'text', text: item.text }]
|
|
307
329
|
});
|
|
330
|
+
// Check if file was actually written (file reference returned)
|
|
308
331
|
if (fileResult && fileResult.content && Array.isArray(fileResult.content) &&
|
|
309
332
|
fileResult.content.length > 0 && fileResult.content[0].type === 'text') {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
333
|
+
const resultText = fileResult.content[0].text;
|
|
334
|
+
// File reference contains "📄 File:" - this means file was written
|
|
335
|
+
if (resultText.includes('📄 File:')) {
|
|
336
|
+
item.text = resultText;
|
|
337
|
+
fileWasWritten = true;
|
|
338
|
+
if (ENABLE_LOGGING) {
|
|
339
|
+
console.error(`[mcp-proxy] File writing applied for ${toolName} (${originalLength} chars written to file)`);
|
|
340
|
+
}
|
|
313
341
|
}
|
|
314
342
|
}
|
|
315
343
|
}
|
|
344
|
+
// Step 2: Apply truncation only if file was NOT written
|
|
345
|
+
// (File references are small and don't need truncation)
|
|
346
|
+
if (!fileWasWritten) {
|
|
347
|
+
item.text = truncateResponseIfNeeded(truncationConfig, item.text);
|
|
348
|
+
if (item.text.length < originalLength && ENABLE_LOGGING) {
|
|
349
|
+
console.error(`[mcp-proxy] Truncated response: ${originalLength} → ${item.text.length} chars`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
316
352
|
}
|
|
317
353
|
}
|
|
318
354
|
}
|
package/dist/jq/tool.js
CHANGED
|
@@ -73,6 +73,21 @@ export const JQ_TOOL_DEFINITION = {
|
|
|
73
73
|
'\n2. **Incremental filtering**: Start with no filters, add conditions one by one' +
|
|
74
74
|
'\n3. **Alternative null handling**: Use `// empty`, `select(. != null)`, or `try ... catch`' +
|
|
75
75
|
'\n4. **Simplified queries**: Break complex queries into smaller, testable parts' +
|
|
76
|
+
'\n\n## COMMON JQ PATTERNS (Quick Reference):' +
|
|
77
|
+
'\n- **List all keys**: `keys` or `.[] | keys` (for nested)' +
|
|
78
|
+
'\n- **Check type**: `type` or `.field | type`' +
|
|
79
|
+
'\n- **Array length**: `.items | length` or `[.[]] | length`' +
|
|
80
|
+
'\n- **Filter array**: `.items[] | select(.price > 100)` or `select(.field == "value")`' +
|
|
81
|
+
'\n- **Extract field**: `.items[].id` or `.[] | .field`' +
|
|
82
|
+
'\n- **Get unique values**: `.items[].type | unique` or `[.[].field] | unique`' +
|
|
83
|
+
'\n- **Find nulls**: `.items[] | select(.field == null)` or `select(.field)` (non-null only)' +
|
|
84
|
+
'\n- **Count occurrences**: `group_by(.type) | map({type: .[0].type, count: length})`' +
|
|
85
|
+
'\n- **Sort**: `sort_by(.price)` or `sort_by(.price) | reverse` (descending)' +
|
|
86
|
+
'\n- **Map transform**: `[.[] | {id: .id, name: .name}]` (extract subset of fields)' +
|
|
87
|
+
'\n- **First N items**: `.[:5]` (array slice)' +
|
|
88
|
+
'\n- **Limit stream**: `limit(10; .[])` (stream processing)' +
|
|
89
|
+
'\n- **Default values**: `.field // "default"` or `.field // empty`' +
|
|
90
|
+
'\n- **Conditional**: `if .price > 100 then "expensive" else "cheap" end`' +
|
|
76
91
|
'\n\n## COMPREHENSIVE EXAMPLES:' +
|
|
77
92
|
'\n**Debugging sequence for Cypher results**:' +
|
|
78
93
|
'\n- `keys` → ["0", "1", "2", ...] (shows object structure)' +
|
package/dist/types/index.d.ts
CHANGED
|
@@ -10,6 +10,12 @@ export interface FileWriterConfig {
|
|
|
10
10
|
minCharsForWrite?: number;
|
|
11
11
|
/** Custom abbreviations for tool names in filenames */
|
|
12
12
|
toolAbbreviations?: Record<string, string>;
|
|
13
|
+
/** Maximum depth for schema generation (default: 2) */
|
|
14
|
+
schemaMaxDepth?: number;
|
|
15
|
+
/** Maximum paths to show in schema (default: 20) */
|
|
16
|
+
schemaMaxPaths?: number;
|
|
17
|
+
/** Maximum keys to analyze per object (default: 50) */
|
|
18
|
+
schemaMaxKeys?: number;
|
|
13
19
|
}
|
|
14
20
|
/**
|
|
15
21
|
* Configuration for the JQ tool
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anyshift/mcp-proxy",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
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",
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
"zod": "^3.24.2"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
+
"@jest/globals": "^30.2.0",
|
|
21
|
+
"@types/jest": "^30.0.0",
|
|
20
22
|
"@types/node": "^22.0.0",
|
|
23
|
+
"jest": "^30.2.0",
|
|
24
|
+
"ts-jest": "^29.4.5",
|
|
21
25
|
"tsx": "^4.7.0",
|
|
22
26
|
"typescript": "^5.3.3"
|
|
23
27
|
},
|
|
@@ -27,6 +31,11 @@
|
|
|
27
31
|
"scripts": {
|
|
28
32
|
"build": "tsc",
|
|
29
33
|
"start": "node dist/index.js",
|
|
30
|
-
"dev": "tsx src/index.ts"
|
|
34
|
+
"dev": "tsx src/index.ts",
|
|
35
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
36
|
+
"test:unit": "node --experimental-vm-modules node_modules/jest/bin/jest.js --testPathPattern=unit",
|
|
37
|
+
"test:integration": "node --experimental-vm-modules node_modules/jest/bin/jest.js --testPathPattern=integration",
|
|
38
|
+
"test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch",
|
|
39
|
+
"test:coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage"
|
|
31
40
|
}
|
|
32
41
|
}
|