mcp-json
Version:
MCP server for JSON file operations - read, analyze, search, find, relate, validate, and write JSON files
979 lines • 40.4 kB
JavaScript
#!/usr/bin/env node
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
const ajv_1 = __importDefault(require("ajv"));
const ajv_formats_1 = __importDefault(require("ajv-formats"));
const JSONPath = __importStar(require("jsonpath"));
const glob_1 = require("glob");
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const lodash_1 = __importDefault(require("lodash"));
class JSONMCPServer {
constructor() {
this.server = new index_js_1.Server({
name: 'mcp-json',
version: '1.0.0',
}, {
capabilities: {
tools: {},
},
});
this.ajv = new ajv_1.default({ allErrors: true, verbose: true });
(0, ajv_formats_1.default)(this.ajv);
this.workingDirectory = process.cwd();
this.setupToolHandlers();
}
setupToolHandlers() {
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
tools: [
{
name: 'find_json_files',
description: 'Find JSON files in the current directory and subdirectories',
inputSchema: {
type: 'object',
properties: {
directory: {
type: 'string',
description: 'Directory to search (defaults to current directory)',
},
pattern: {
type: 'string',
description: 'Glob pattern for JSON files (defaults to **/*.json)',
},
},
},
},
{
name: 'read_json_file',
description: 'Read and parse a JSON file',
inputSchema: {
type: 'object',
properties: {
filePath: {
type: 'string',
description: 'Path to the JSON file to read',
},
},
required: ['filePath'],
},
},
{
name: 'analyze_json_file',
description: 'Analyze a JSON file structure and content',
inputSchema: {
type: 'object',
properties: {
filePath: {
type: 'string',
description: 'Path to the JSON file to analyze',
},
},
required: ['filePath'],
},
},
{
name: 'search_json_content',
description: 'Search for specific content within JSON files',
inputSchema: {
type: 'object',
properties: {
searchTerm: {
type: 'string',
description: 'Term to search for in JSON content',
},
directory: {
type: 'string',
description: 'Directory to search (defaults to current directory)',
},
jsonPath: {
type: 'string',
description: 'JSONPath expression to search within specific paths',
},
},
required: ['searchTerm'],
},
},
{
name: 'write_json_file',
description: 'Write data to a JSON file',
inputSchema: {
type: 'object',
properties: {
filePath: {
type: 'string',
description: 'Path where to write the JSON file',
},
data: {
type: 'object',
description: 'JavaScript object to convert to JSON',
},
indent: {
type: 'number',
description: 'Number of spaces for indentation (defaults to 2)',
},
},
required: ['filePath', 'data'],
},
},
{
name: 'transform_json',
description: 'Transform JSON content using various operations',
inputSchema: {
type: 'object',
properties: {
filePath: {
type: 'string',
description: 'Path to the JSON file to transform',
},
outputPath: {
type: 'string',
description: 'Path where to save the transformed JSON',
},
operation: {
type: 'string',
enum: ['filter_keys', 'rename_keys', 'add_properties', 'remove_properties', 'flatten', 'unflatten'],
description: 'Type of transformation to perform',
},
parameters: {
type: 'object',
description: 'Parameters for the transformation operation',
},
},
required: ['filePath', 'outputPath', 'operation'],
},
},
{
name: 'relate_json_files',
description: 'Find relationships between JSON files based on content or structure',
inputSchema: {
type: 'object',
properties: {
directory: {
type: 'string',
description: 'Directory to analyze (defaults to current directory)',
},
relationType: {
type: 'string',
enum: ['common_keys', 'shared_values', 'similar_structure', 'reference_links'],
description: 'Type of relationship to analyze',
},
},
required: ['relationType'],
},
},
{
name: 'validate_json',
description: 'Validate JSON file structure and content against a schema',
inputSchema: {
type: 'object',
properties: {
filePath: {
type: 'string',
description: 'Path to the JSON file to validate',
},
schema: {
type: 'object',
description: 'JSON Schema to validate against (optional)',
},
},
required: ['filePath'],
},
},
{
name: 'query_json',
description: 'Query JSON files using JSONPath expressions',
inputSchema: {
type: 'object',
properties: {
filePath: {
type: 'string',
description: 'Path to the JSON file to query',
},
jsonPath: {
type: 'string',
description: 'JSONPath expression to execute',
},
},
required: ['filePath', 'jsonPath'],
},
},
{
name: 'merge_json_files',
description: 'Merge multiple JSON files into one',
inputSchema: {
type: 'object',
properties: {
filePaths: {
type: 'array',
items: { type: 'string' },
description: 'Array of JSON file paths to merge',
},
outputPath: {
type: 'string',
description: 'Path where to save the merged JSON',
},
strategy: {
type: 'string',
enum: ['merge', 'concat', 'deep_merge'],
description: 'Merge strategy to use',
},
},
required: ['filePaths', 'outputPath', 'strategy'],
},
},
],
}));
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'find_json_files':
return await this.findJSONFiles(args);
case 'read_json_file':
return await this.readJSONFile(args);
case 'analyze_json_file':
return await this.analyzeJSONFile(args);
case 'search_json_content':
return await this.searchJSONContent(args);
case 'write_json_file':
return await this.writeJSONFile(args);
case 'transform_json':
return await this.transformJSON(args);
case 'relate_json_files':
return await this.relateJSONFiles(args);
case 'validate_json':
return await this.validateJSON(args);
case 'query_json':
return await this.queryJSON(args);
case 'merge_json_files':
return await this.mergeJSONFiles(args);
default:
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
}
}
catch (error) {
if (error instanceof types_js_1.McpError) {
throw error;
}
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Error executing ${name}: ${error}`);
}
});
}
async findJSONFiles(args) {
const directory = args.directory || this.workingDirectory;
const pattern = args.pattern || '**/*.json';
try {
const fullPattern = path_1.default.resolve(directory, pattern);
const files = await (0, glob_1.glob)(fullPattern);
return {
content: [
{
type: 'text',
text: JSON.stringify({
directory,
pattern,
found: files.length,
files: files.map(f => path_1.default.relative(this.workingDirectory, f))
}, null, 2)
}
]
};
}
catch (error) {
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to find JSON files: ${error}`);
}
}
async readJSONFile(args) {
const { filePath } = args;
try {
const fullPath = path_1.default.resolve(this.workingDirectory, filePath);
const content = await fs_extra_1.default.readFile(fullPath, 'utf8');
const parsedContent = JSON.parse(content);
return {
content: [
{
type: 'text',
text: JSON.stringify({
filePath,
content: parsedContent,
size: content.length,
lines: content.split('\n').length
}, null, 2)
}
]
};
}
catch (error) {
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to read JSON file: ${error}`);
}
}
async analyzeJSONFile(args) {
const { filePath } = args;
try {
const fullPath = path_1.default.resolve(this.workingDirectory, filePath);
const content = await fs_extra_1.default.readFile(fullPath, 'utf8');
const parsedContent = JSON.parse(content);
const analysis = this.analyzeJSONStructure(parsedContent, content);
return {
content: [
{
type: 'text',
text: JSON.stringify({
filePath,
analysis,
summary: {
totalKeys: analysis.keyCount,
maxDepth: analysis.depth,
fileSize: analysis.size,
structure: analysis.valueTypes
}
}, null, 2)
}
]
};
}
catch (error) {
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to analyze JSON file: ${error}`);
}
}
analyzeJSONStructure(obj, rawContent) {
const analysis = {
keyCount: 0,
keys: [],
valueTypes: {},
depth: 0,
arrayCount: 0,
objectCount: 0,
paths: [],
size: rawContent.length
};
const traverse = (node, path = '$', depth = 0) => {
analysis.depth = Math.max(analysis.depth, depth);
analysis.paths.push(path);
if (Array.isArray(node)) {
analysis.arrayCount++;
analysis.valueTypes['array'] = (analysis.valueTypes['array'] || 0) + 1;
node.forEach((item, index) => {
traverse(item, `${path}[${index}]`, depth + 1);
});
}
else if (node !== null && typeof node === 'object') {
analysis.objectCount++;
analysis.valueTypes['object'] = (analysis.valueTypes['object'] || 0) + 1;
Object.keys(node).forEach(key => {
analysis.keyCount++;
analysis.keys.push(key);
const newPath = path === '$' ? `$.${key}` : `${path}.${key}`;
traverse(node[key], newPath, depth + 1);
});
}
else {
const type = typeof node;
analysis.valueTypes[type] = (analysis.valueTypes[type] || 0) + 1;
}
};
traverse(obj);
analysis.keys = [...new Set(analysis.keys)]; // Remove duplicates
return analysis;
}
async searchJSONContent(args) {
const { searchTerm, directory, jsonPath } = args;
const searchDir = directory || this.workingDirectory;
try {
const files = await (0, glob_1.glob)(path_1.default.resolve(searchDir, '**/*.json'));
const results = [];
for (const file of files) {
const content = await fs_extra_1.default.readFile(file, 'utf8');
const parsedContent = JSON.parse(content);
let matches = [];
if (jsonPath) {
// Search within specific JSONPath
try {
const pathResults = JSONPath.query(parsedContent, jsonPath);
matches = this.searchInValue(pathResults, searchTerm);
}
catch (error) {
// Invalid JSONPath, skip
}
}
else {
// Search in entire JSON
matches = this.searchInValue(parsedContent, searchTerm);
}
if (matches.length > 0) {
results.push({
file: path_1.default.relative(this.workingDirectory, file),
matches: matches.length,
matchedPaths: matches
});
}
}
return {
content: [
{
type: 'text',
text: JSON.stringify({
searchTerm,
directory: searchDir,
jsonPath,
totalFiles: files.length,
matchedFiles: results.length,
results
}, null, 2)
}
]
};
}
catch (error) {
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to search JSON content: ${error}`);
}
}
searchInValue(obj, searchTerm, path = '$') {
const matches = [];
const search = (node, currentPath) => {
if (typeof node === 'string' && node.toLowerCase().includes(searchTerm.toLowerCase())) {
matches.push(currentPath);
}
else if (Array.isArray(node)) {
node.forEach((item, index) => {
search(item, `${currentPath}[${index}]`);
});
}
else if (node !== null && typeof node === 'object') {
Object.keys(node).forEach(key => {
const newPath = currentPath === '$' ? `$.${key}` : `${currentPath}.${key}`;
if (key.toLowerCase().includes(searchTerm.toLowerCase())) {
matches.push(newPath);
}
search(node[key], newPath);
});
}
};
search(obj, path);
return matches;
}
async writeJSONFile(args) {
const { filePath, data, indent = 2 } = args;
try {
const fullPath = path_1.default.resolve(this.workingDirectory, filePath);
await fs_extra_1.default.ensureDir(path_1.default.dirname(fullPath));
const jsonString = JSON.stringify(data, null, indent);
await fs_extra_1.default.writeFile(fullPath, jsonString, 'utf8');
return {
content: [
{
type: 'text',
text: JSON.stringify({
filePath,
status: 'success',
size: jsonString.length,
lines: jsonString.split('\n').length
}, null, 2)
}
]
};
}
catch (error) {
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to write JSON file: ${error}`);
}
}
async transformJSON(args) {
const { filePath, outputPath, operation, parameters = {} } = args;
try {
const fullPath = path_1.default.resolve(this.workingDirectory, filePath);
const content = await fs_extra_1.default.readFile(fullPath, 'utf8');
const parsedContent = JSON.parse(content);
let transformedData;
switch (operation) {
case 'filter_keys':
transformedData = this.filterKeys(parsedContent, parameters.keysToKeep || []);
break;
case 'rename_keys':
transformedData = this.renameKeys(parsedContent, parameters.keyMapping || {});
break;
case 'add_properties':
transformedData = this.addProperties(parsedContent, parameters.properties || {});
break;
case 'remove_properties':
transformedData = this.removeProperties(parsedContent, parameters.keysToRemove || []);
break;
case 'flatten':
transformedData = this.flattenObject(parsedContent, parameters.separator || '.');
break;
case 'unflatten':
transformedData = this.unflattenObject(parsedContent, parameters.separator || '.');
break;
default:
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Unknown operation: ${operation}`);
}
const outputFullPath = path_1.default.resolve(this.workingDirectory, outputPath);
await fs_extra_1.default.ensureDir(path_1.default.dirname(outputFullPath));
await fs_extra_1.default.writeFile(outputFullPath, JSON.stringify(transformedData, null, 2), 'utf8');
return {
content: [
{
type: 'text',
text: JSON.stringify({
inputFile: filePath,
outputFile: outputPath,
operation,
parameters,
status: 'success'
}, null, 2)
}
]
};
}
catch (error) {
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to transform JSON: ${error}`);
}
}
filterKeys(obj, keysToKeep) {
if (Array.isArray(obj)) {
return obj.map(item => this.filterKeys(item, keysToKeep));
}
else if (obj !== null && typeof obj === 'object') {
const filtered = {};
keysToKeep.forEach(key => {
if (key in obj) {
filtered[key] = this.filterKeys(obj[key], keysToKeep);
}
});
return filtered;
}
return obj;
}
renameKeys(obj, keyMapping) {
if (Array.isArray(obj)) {
return obj.map(item => this.renameKeys(item, keyMapping));
}
else if (obj !== null && typeof obj === 'object') {
const renamed = {};
Object.keys(obj).forEach(key => {
const newKey = keyMapping[key] || key;
renamed[newKey] = this.renameKeys(obj[key], keyMapping);
});
return renamed;
}
return obj;
}
addProperties(obj, properties) {
if (Array.isArray(obj)) {
return obj.map(item => this.addProperties(item, properties));
}
else if (obj !== null && typeof obj === 'object') {
return { ...obj, ...properties };
}
return obj;
}
removeProperties(obj, keysToRemove) {
if (Array.isArray(obj)) {
return obj.map(item => this.removeProperties(item, keysToRemove));
}
else if (obj !== null && typeof obj === 'object') {
const filtered = {};
Object.keys(obj).forEach(key => {
if (!keysToRemove.includes(key)) {
filtered[key] = this.removeProperties(obj[key], keysToRemove);
}
});
return filtered;
}
return obj;
}
flattenObject(obj, separator) {
const flatten = (current, prefix = '') => {
const flattened = {};
Object.keys(current).forEach(key => {
const newKey = prefix ? `${prefix}${separator}${key}` : key;
if (current[key] !== null && typeof current[key] === 'object' && !Array.isArray(current[key])) {
Object.assign(flattened, flatten(current[key], newKey));
}
else {
flattened[newKey] = current[key];
}
});
return flattened;
};
return flatten(obj);
}
unflattenObject(obj, separator) {
const result = {};
Object.keys(obj).forEach(key => {
const keys = key.split(separator);
let current = result;
keys.forEach((k, index) => {
if (index === keys.length - 1) {
current[k] = obj[key];
}
else {
if (!(k in current)) {
current[k] = {};
}
current = current[k];
}
});
});
return result;
}
async relateJSONFiles(args) {
const { directory, relationType } = args;
const searchDir = directory || this.workingDirectory;
try {
const files = await (0, glob_1.glob)(path_1.default.resolve(searchDir, '**/*.json'));
const relationships = [];
for (let i = 0; i < files.length; i++) {
for (let j = i + 1; j < files.length; j++) {
const content1 = await fs_extra_1.default.readFile(files[i], 'utf8');
const content2 = await fs_extra_1.default.readFile(files[j], 'utf8');
const parsed1 = JSON.parse(content1);
const parsed2 = JSON.parse(content2);
const relationship = this.findRelationship(parsed1, parsed2, relationType);
if (relationship) {
relationships.push({
file1: path_1.default.relative(this.workingDirectory, files[i]),
file2: path_1.default.relative(this.workingDirectory, files[j]),
relationship: relationType,
similarity: relationship.similarity,
details: relationship.details
});
}
}
}
return {
content: [
{
type: 'text',
text: JSON.stringify({
directory: searchDir,
relationType,
totalFiles: files.length,
relationships: relationships.length,
results: relationships
}, null, 2)
}
]
};
}
catch (error) {
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to relate JSON files: ${error}`);
}
}
findRelationship(obj1, obj2, type) {
switch (type) {
case 'common_keys':
return this.findCommonKeys(obj1, obj2);
case 'shared_values':
return this.findSharedValues(obj1, obj2);
case 'similar_structure':
return this.findSimilarStructure(obj1, obj2);
case 'reference_links':
return this.findReferenceLinks(obj1, obj2);
default:
return null;
}
}
findCommonKeys(obj1, obj2) {
const keys1 = this.getAllKeys(obj1);
const keys2 = this.getAllKeys(obj2);
const commonKeys = keys1.filter(key => keys2.includes(key));
if (commonKeys.length === 0)
return null;
const similarity = commonKeys.length / Math.max(keys1.length, keys2.length);
return {
similarity,
details: `Common keys: ${commonKeys.join(', ')}`
};
}
findSharedValues(obj1, obj2) {
const values1 = this.getAllValues(obj1);
const values2 = this.getAllValues(obj2);
const sharedValues = values1.filter(value => values2.includes(value));
if (sharedValues.length === 0)
return null;
const similarity = sharedValues.length / Math.max(values1.length, values2.length);
return {
similarity,
details: `Shared values: ${sharedValues.slice(0, 5).join(', ')}${sharedValues.length > 5 ? '...' : ''}`
};
}
findSimilarStructure(obj1, obj2) {
const structure1 = this.getStructure(obj1);
const structure2 = this.getStructure(obj2);
const similarity = this.calculateSimilarity(structure1, structure2);
if (similarity < 0.3)
return null;
return {
similarity,
details: `Structure similarity: ${Math.round(similarity * 100)}%`
};
}
findReferenceLinks(obj1, obj2) {
const references = this.findCrossReferences(obj1, obj2);
if (references.length === 0)
return null;
return {
similarity: 1.0,
details: `Cross-references found: ${references.join(', ')}`
};
}
getAllKeys(obj) {
const keys = [];
const traverse = (node) => {
if (Array.isArray(node)) {
node.forEach(item => traverse(item));
}
else if (node !== null && typeof node === 'object') {
Object.keys(node).forEach(key => {
keys.push(key);
traverse(node[key]);
});
}
};
traverse(obj);
return [...new Set(keys)]; // Remove duplicates
}
getAllValues(obj) {
const values = [];
const traverse = (node) => {
if (Array.isArray(node)) {
node.forEach(item => traverse(item));
}
else if (node !== null && typeof node === 'object') {
Object.values(node).forEach(value => traverse(value));
}
else if (typeof node === 'string') {
values.push(node);
}
};
traverse(obj);
return values;
}
getStructure(obj) {
const traverse = (node) => {
if (Array.isArray(node)) {
return `[${node.map(item => traverse(item)).join(',')}]`;
}
else if (node !== null && typeof node === 'object') {
const keys = Object.keys(node).sort();
return `{${keys.map(key => `${key}:${traverse(node[key])}`).join(',')}}`;
}
else {
return typeof node;
}
};
return traverse(obj);
}
calculateSimilarity(str1, str2) {
const longer = str1.length > str2.length ? str1 : str2;
const shorter = str1.length > str2.length ? str2 : str1;
if (longer.length === 0)
return 1.0;
const editDistance = this.levenshteinDistance(longer, shorter);
return (longer.length - editDistance) / longer.length;
}
levenshteinDistance(str1, str2) {
const matrix = Array(str2.length + 1).fill(null).map(() => Array(str1.length + 1).fill(null));
for (let i = 0; i <= str1.length; i++)
matrix[0][i] = i;
for (let j = 0; j <= str2.length; j++)
matrix[j][0] = j;
for (let j = 1; j <= str2.length; j++) {
for (let i = 1; i <= str1.length; i++) {
const substitutionCost = str1[i - 1] === str2[j - 1] ? 0 : 1;
matrix[j][i] = Math.min(matrix[j][i - 1] + 1, // deletion
matrix[j - 1][i] + 1, // insertion
matrix[j - 1][i - 1] + substitutionCost // substitution
);
}
}
return matrix[str2.length][str1.length];
}
findCrossReferences(obj1, obj2) {
const values1 = this.getAllValues(obj1);
const values2 = this.getAllValues(obj2);
const references = [];
values1.forEach(value => {
if (values2.includes(value) && value.length > 3) {
references.push(value);
}
});
return [...new Set(references)];
}
async validateJSON(args) {
const { filePath, schema } = args;
try {
const fullPath = path_1.default.resolve(this.workingDirectory, filePath);
const content = await fs_extra_1.default.readFile(fullPath, 'utf8');
// First, validate JSON syntax
let parsedContent;
try {
parsedContent = JSON.parse(content);
}
catch (parseError) {
return {
content: [
{
type: 'text',
text: JSON.stringify({
filePath,
valid: false,
syntaxError: parseError instanceof Error ? parseError.message : String(parseError),
type: 'syntax'
}, null, 2)
}
]
};
}
// If schema is provided, validate against schema
if (schema) {
const validate = this.ajv.compile(schema);
const valid = validate(parsedContent);
return {
content: [
{
type: 'text',
text: JSON.stringify({
filePath,
valid,
errors: validate.errors || [],
type: 'schema'
}, null, 2)
}
]
};
}
// Basic validation passed
return {
content: [
{
type: 'text',
text: JSON.stringify({
filePath,
valid: true,
type: 'syntax'
}, null, 2)
}
]
};
}
catch (error) {
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to validate JSON: ${error}`);
}
}
async queryJSON(args) {
const { filePath, jsonPath } = args;
try {
const fullPath = path_1.default.resolve(this.workingDirectory, filePath);
const content = await fs_extra_1.default.readFile(fullPath, 'utf8');
const parsedContent = JSON.parse(content);
const results = JSONPath.query(parsedContent, jsonPath);
return {
content: [
{
type: 'text',
text: JSON.stringify({
filePath,
jsonPath,
results,
count: results.length
}, null, 2)
}
]
};
}
catch (error) {
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to query JSON: ${error}`);
}
}
async mergeJSONFiles(args) {
const { filePaths, outputPath, strategy } = args;
try {
const contents = await Promise.all(filePaths.map(async (filePath) => {
const fullPath = path_1.default.resolve(this.workingDirectory, filePath);
const content = await fs_extra_1.default.readFile(fullPath, 'utf8');
return JSON.parse(content);
}));
let mergedData;
switch (strategy) {
case 'merge':
mergedData = Object.assign({}, ...contents);
break;
case 'concat':
mergedData = contents.flat();
break;
case 'deep_merge':
mergedData = lodash_1.default.merge({}, ...contents);
break;
default:
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Unknown merge strategy: ${strategy}`);
}
const outputFullPath = path_1.default.resolve(this.workingDirectory, outputPath);
await fs_extra_1.default.ensureDir(path_1.default.dirname(outputFullPath));
await fs_extra_1.default.writeFile(outputFullPath, JSON.stringify(mergedData, null, 2), 'utf8');
return {
content: [
{
type: 'text',
text: JSON.stringify({
inputFiles: filePaths,
outputFile: outputPath,
strategy,
status: 'success'
}, null, 2)
}
]
};
}
catch (error) {
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to merge JSON files: ${error}`);
}
}
async run() {
const transport = new stdio_js_1.StdioServerTransport();
await this.server.connect(transport);
console.error('MCP JSON Server running on stdio');
}
}
const server = new JSONMCPServer();
server.run().catch(console.error);
//# sourceMappingURL=index.js.map