Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ dependencies {
implementation 'ch.qos.logback:logback-classic:1.5.17'
implementation 'org.eclipse.jgit:org.eclipse.jgit:6.10.0.202406032230-r'
implementation 'com.brunomnsilva:smartgraph:2.3.0'
implementation('io.modelcontextprotocol.sdk:mcp:0.17.2') {
exclude group: 'com.networknt', module: 'json-schema-validator'
}
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
testImplementation 'org.testfx:testfx-core:4.0.18'
Expand Down
13 changes: 7 additions & 6 deletions src/main/java/com/daniel/jsoneditor/controller/Controller.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
package com.daniel.jsoneditor.controller;

import java.io.File;
import java.util.List;

import com.daniel.jsoneditor.controller.impl.commands.CommandManager;
import com.daniel.jsoneditor.controller.mcp.McpController;
import com.daniel.jsoneditor.controller.settings.SettingsController;
import com.daniel.jsoneditor.model.diff.DiffEntry;
import com.daniel.jsoneditor.model.json.JsonNodeWithPath;
import com.fasterxml.jackson.databind.JsonNode;

import java.io.File;
import java.util.List;


public interface Controller
{
/*
* Other controller methods for nested controllers
*/
SettingsController getSettingsController();

McpController getMcpController();

/**
* Gets the command manager for accessing command history
* @return the command manager instance
Expand Down Expand Up @@ -101,4 +101,5 @@ public interface Controller
*/
List<DiffEntry> calculateJsonDiff();


}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.daniel.jsoneditor.controller.impl;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
Expand All @@ -13,6 +14,7 @@
import com.daniel.jsoneditor.controller.impl.json.VariableHelper;
import com.daniel.jsoneditor.controller.impl.json.impl.JsonFileReaderAndWriterImpl;
import com.daniel.jsoneditor.controller.impl.json.impl.JsonNodeMerger;
import com.daniel.jsoneditor.controller.mcp.McpController;
import com.daniel.jsoneditor.controller.settings.SettingsController;
import com.daniel.jsoneditor.controller.settings.impl.SettingsControllerImpl;
import com.daniel.jsoneditor.model.ReadableModel;
Expand Down Expand Up @@ -64,6 +66,8 @@ public class ControllerImpl implements Controller, Observer

private final CommandFactory commandFactory;

private final McpController mcpController;

public ControllerImpl(WritableModel model, ReadableModel readableModel, Stage stage)
{
this.settingsController = new SettingsControllerImpl();
Expand All @@ -74,6 +78,7 @@ public ControllerImpl(WritableModel model, ReadableModel readableModel, Stage st
this.subjects = new ArrayList<>();
this.view = new ViewImpl(readableModel, this, stage);
this.view.observe(this.readableModel.getForObservation());
this.mcpController = new McpController(model, settingsController);

// Set up callback for unsaved changes notifications from CommandManager
this.commandManager.setUnsavedChangesCallback(this::updateWindowTitle);
Expand All @@ -94,6 +99,12 @@ public SettingsController getSettingsController()
return settingsController;
}

@Override
public McpController getMcpController()
{
return mcpController;
}

@Override
public CommandManager getCommandManager()
{
Expand Down Expand Up @@ -125,7 +136,7 @@ public void jsonAndSchemaSelected(File jsonFile, File schemaFile, File settingsF
{
if (jsonFile != null && schemaFile != null)
{
// grab json from files and validate
// grab Json from files and validate
JsonFileReaderAndWriter reader = new JsonFileReaderAndWriterImpl();
JsonNode json = reader.getJsonFromFile(jsonFile);
JsonSchema schema = reader.getSchemaFromFileResolvingRefs(schemaFile);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,23 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.JsonSchema;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JsonFileReaderAndWriterImpl implements JsonFileReaderAndWriter
{
private final ObjectMapper mapper;
private static final Logger logger = LoggerFactory.getLogger(JsonFileReaderAndWriterImpl.class);

private final ObjectMapper regularMapper;

private final ObjectMapper mapperIgnoringUnknownProperties;

public JsonFileReaderAndWriterImpl()
{
this.mapper = new ObjectMapper();
this.regularMapper = new ObjectMapper();

this.mapperIgnoringUnknownProperties = new ObjectMapper();
this.mapperIgnoringUnknownProperties.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}


Expand All @@ -29,11 +38,11 @@ public JsonNode getJsonFromFile(File file)
{
try
{
return mapper.readTree(file);
return regularMapper.readTree(file);
}
catch (IOException e)
{
e.printStackTrace();
logger.error("Failed to read JSON from file: {}", file.getAbsolutePath(), e);
}
return null;
}
Expand All @@ -46,8 +55,7 @@ public JsonNode getNodeFromString(String content) throws JsonProcessingException
throw new IllegalArgumentException("Content cannot be null");
}

final ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readTree(content);
return regularMapper.readTree(content);
}

@Override
Expand All @@ -57,18 +65,16 @@ public <T> T getJsonFromFile(File file, Class<T> classOfObject, boolean ignoreUn
{
if (ignoreUnknownProperties)
{
ObjectMapper newMapper = new ObjectMapper();
newMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return newMapper.readValue(file, classOfObject);
return mapperIgnoringUnknownProperties.readValue(file, classOfObject);
}
else
{
return mapper.readValue(file, classOfObject);
return regularMapper.readValue(file, classOfObject);
}
}
catch (IOException e)
{
e.printStackTrace();
logger.error("Failed to read JSON from file {} as {}: {}", file.getAbsolutePath(), classOfObject.getSimpleName(), e.getMessage(), e);
}
return null;
}
Expand All @@ -82,10 +88,9 @@ public JsonSchema getSchemaFromFileResolvingRefs(File file)
@Override
public boolean writeJsonToFile(JsonNode json, File file)
{
ObjectMapper mapper = new ObjectMapper();
try
{
mapper.writer(new JsonPrettyPrinter()).writeValue(file, json);
regularMapper.writer(new JsonPrettyPrinter()).writeValue(file, json);
}
catch (IOException e)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.daniel.jsoneditor.controller.mcp;

import java.io.IOException;

import com.daniel.jsoneditor.controller.settings.SettingsController;
import com.daniel.jsoneditor.model.WritableModel;
import com.daniel.jsoneditor.model.mcp.JsonEditorMcpServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class McpController
{
private static final Logger logger = LoggerFactory.getLogger(McpController.class);

private final JsonEditorMcpServer mcpServer;

private final SettingsController settingsController;

public McpController(final WritableModel writableModel, final SettingsController settingsController)
{
this.mcpServer = new JsonEditorMcpServer(writableModel);
this.settingsController = settingsController;
}

/**
* Starts the MCP server.
*/
public void startMcpServer()
{
if (!mcpServer.isRunning())
{
try
{
mcpServer.start(settingsController.getMcpServerPort());
}
catch (IOException e)
{
logger.error("Failed to start MCP server on port {}: {}",
settingsController.getMcpServerPort(), e.getMessage());
}
}
}

/**
* Stops the MCP server if it's running.
*/
public void stopMcpServer()
{
mcpServer.stop();
}

/**
* Checks if the MCP server is currently running.
*
* @return true if the server is running
*/
public boolean isMcpServerRunning()
{
return mcpServer.isRunning();
}

/**
* Gets the current port the server is running on or configured to use.
*
* @return the MCP server port
*/
public int getMcpServerPort()
{
return mcpServer.isRunning() ? mcpServer.getPort() : settingsController.getMcpServerPort();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,12 @@ public interface SettingsController
void setLogGraphRequests(boolean logGraphRequests);

boolean isLogGraphRequests();

void setMcpServerEnabled(boolean enabled);

boolean isMcpServerEnabled();

void setMcpServerPort(int port);

int getMcpServerPort();
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ public class PropertyFileKeys
public final static String PROPERTY_DEBUG_MODE = "debug_mode";

public final static String PROPERTY_LOG_GRAPH_REQUESTS = "log_graph_requests";

public final static String PROPERTY_MCP_SERVER_ENABLED = "autostart_mcp_server";

public final static String PROPERTY_MCP_SERVER_PORT = "mcp_server_port";
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.Properties;

import com.daniel.jsoneditor.controller.settings.SettingsController;
import com.daniel.jsoneditor.model.mcp.JsonEditorMcpServer;


public class SettingsControllerImpl implements SettingsController
Expand Down Expand Up @@ -173,4 +174,42 @@ public boolean isLogGraphRequests()
{
return "true".equalsIgnoreCase(properties.getProperty(PropertyFileKeys.PROPERTY_LOG_GRAPH_REQUESTS));
}

@Override
public void setMcpServerEnabled(boolean enabled)
{
properties.setProperty(PropertyFileKeys.PROPERTY_MCP_SERVER_ENABLED, enabled ? "true" : "false");
PropertiesFileHelper.writePropertiesToFile(properties);
}

@Override
public boolean isMcpServerEnabled()
{
return "true".equalsIgnoreCase(properties.getProperty(PropertyFileKeys.PROPERTY_MCP_SERVER_ENABLED));
}

@Override
public void setMcpServerPort(int port)
{
properties.setProperty(PropertyFileKeys.PROPERTY_MCP_SERVER_PORT, String.valueOf(port));
PropertiesFileHelper.writePropertiesToFile(properties);
}

@Override
public int getMcpServerPort()
{
final String portStr = properties.getProperty(PropertyFileKeys.PROPERTY_MCP_SERVER_PORT);
if (portStr == null)
{
return JsonEditorMcpServer.DEFAULT_PORT;
}
try
{
return Integer.parseInt(portStr);
}
catch (NumberFormatException e)
{
return JsonEditorMcpServer.DEFAULT_PORT;
}
}
}
Loading