From 4ac555546afb1029b54e6d142ac08922a0a1e486 Mon Sep 17 00:00:00 2001 From: shahondin1624 Date: Sat, 14 Feb 2026 11:17:18 +0100 Subject: [PATCH] Add examples and bundler --- .gitignore | 1 + LICENSE | 18 +++ README.md | 51 +++++++ pom.xml | 144 ++++++++++++++++++ src/main/java/mcp/app/McpLauncher.java | 49 ++++++ .../java/mcp/app/tools/CalculatorTool.java | 57 +++++++ .../mcp/app/tools/CurrentDateTimeTool.java | 60 ++++++++ src/main/java/mcp/app/tools/EchoTool.java | 55 +++++++ src/main/resources/simplelogger.properties | 1 + 9 files changed, 436 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 pom.xml create mode 100644 src/main/java/mcp/app/McpLauncher.java create mode 100644 src/main/java/mcp/app/tools/CalculatorTool.java create mode 100644 src/main/java/mcp/app/tools/CurrentDateTimeTool.java create mode 100644 src/main/java/mcp/app/tools/EchoTool.java create mode 100644 src/main/resources/simplelogger.properties diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a785e39 --- /dev/null +++ b/LICENSE @@ -0,0 +1,18 @@ +MIT License + +Copyright (c) 2026 shahondin1624 + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c609a3e --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +### Building the project + +To build the project and generate the fat JAR and binaries, run: +```bash +mvn clean package +``` + +### Generated Artifacts + +After a successful build, you can find the following artifacts in the `target/` directory: +- `mcp-server-app-all.jar`: Executable fat JAR containing all dependencies. +- `mcp-server-app`: Linux executable wrapper script. +- `mcp-server-app.exe`: Windows executable (console application). + +### Running the server + +Using the fat JAR: +```bash +java -jar target/mcp-server-app-all.jar +``` + +Using the Linux executable: +```bash +./target/mcp-server-app +``` + +The server will start on port `8080`. + +### MCP Configuration +This server is available using +```json +{ + "mcpServers": { + "test": { + "command": "java", + "args": ["-jar", "/path/to/mcp-server-app-all.jar"] + } + } +} +``` +or via HTTP: +```json +{ + "mcpServers": { + "test": { + "url": "http://127.0.0.1:8080/mcp" + } + } +} +``` +as mcp config. \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f04d4ac --- /dev/null +++ b/pom.xml @@ -0,0 +1,144 @@ + + + 4.0.0 + + mcp.app + mcp-server-app + 1.0.0 + + + 21 + 21 + UTF-8 + + + + + io.github.mcp-java + mcp-server-lib + 1.0.0 + + + + + org.slf4j + slf4j-simple + 2.0.9 + + + net.objecthunter + exp4j + 0.4.8 + + + de.shahondin1624 + knowledge-graph + 1.0-SNAPSHOT + compile + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + mcp.app.McpLauncher + + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.1 + + + package + + shade + + + false + false + mcp-server-app-all + + + mcp.app.McpLauncher + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + com.akathist.maven.plugins.launch4j + launch4j-maven-plugin + 2.5.2 + + + l4j-clui + package + + launch4j + + + console + ${project.build.directory}/mcp-server-app-all.jar + ${project.build.directory}/mcp-server-app.exe + http://java.com/download + + mcp.app.McpLauncher + + + 21 + 64 + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.1.0 + + + package + + run + + + + #!/bin/sh +# Linux wrapper script for mcp-server-app +java -jar "$(dirname "$0")/mcp-server-app-all.jar" "$@" + + + + + + + + + + diff --git a/src/main/java/mcp/app/McpLauncher.java b/src/main/java/mcp/app/McpLauncher.java new file mode 100644 index 0000000..dfc3548 --- /dev/null +++ b/src/main/java/mcp/app/McpLauncher.java @@ -0,0 +1,49 @@ +package mcp.app; + +import de.shahondin1624.knowledgegraph.DatabaseLauncher; +import mcp.server.McpServlet; +import mcp.server.ToolRegistrationServlet; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.server.Server; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class McpLauncher { + private static final Logger logger = LoggerFactory.getLogger(McpLauncher.class); + + public static void main(String[] args) throws Exception { + logger.info("Starting MCP App Launcher..."); + new DatabaseLauncher().start(); + + // Scan both library tools and app tools + Set classpaths = Stream.concat( + Stream.of("mcp.tools", "mcp.app.tools", "de.shahondin1624.knowledgegraph.tooling"), + args.length > 0 ? Stream.of(args[0].split(",")) : Stream.empty() + ).collect(Collectors.toSet()); + + logger.debug("Configured classpaths: {}", classpaths); + + McpServlet mcpServlet = new McpServlet(classpaths); + mcpServlet.setServerName("mcp-app-server"); + + Server server = new Server(8080); + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/"); + server.setHandler(context); + + ServletHolder mcpHolder = new ServletHolder(mcpServlet); + context.addServlet(mcpHolder, "/mcp/*"); + + ServletHolder registrationHolder = new ServletHolder(new ToolRegistrationServlet(mcpServlet.getToolRegistry())); + context.addServlet(registrationHolder, "/mcp/register"); + + server.start(); + logger.info("MCP App Server started on port 8080"); + server.join(); + } +} diff --git a/src/main/java/mcp/app/tools/CalculatorTool.java b/src/main/java/mcp/app/tools/CalculatorTool.java new file mode 100644 index 0000000..204bc44 --- /dev/null +++ b/src/main/java/mcp/app/tools/CalculatorTool.java @@ -0,0 +1,57 @@ +package mcp.app.tools; + +import io.modelcontextprotocol.spec.McpSchema; +import mcp.tools.DefaultMcpTool; +import mcp.tools.McpValidatedTool; +import mcp.tools.helper.SchemaBuilder; +import net.objecthunter.exp4j.Expression; +import net.objecthunter.exp4j.ExpressionBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +@DefaultMcpTool +public class CalculatorTool extends McpValidatedTool { + private static final Logger logger = LoggerFactory.getLogger(CalculatorTool.class); + + @Override + public String name() { + return "calculator"; + } + + @Override + public String title() { + return "Calculator Tool"; + } + + @Override + public String description() { + return "Evaluates a mathematical expression. Supports basic operators (+, -, *, /), functions (sin, cos, tan, log, exp), and parentheses."; + } + + @Override + public McpSchema.JsonSchema inputSchema() { + return new SchemaBuilder() + .addProperty("expression", "string", "The mathematical expression to evaluate") + .required("expression") + .build(); + } + + @Override + public Map outputSchema() { + return new SchemaBuilder() + .returns("number", "The result of the evaluation") + .buildMap(); + } + + @Override + public McpSchema.CallToolResult callValidated(McpSchema.CallToolRequest request, Map arguments) { + String expressionStr = (String) arguments.get("expression"); + logger.debug("Evaluating expression: {}", expressionStr); + + Expression expression = new ExpressionBuilder(expressionStr).build(); + double result = expression.evaluate(); + return success(String.valueOf(result)); + } +} diff --git a/src/main/java/mcp/app/tools/CurrentDateTimeTool.java b/src/main/java/mcp/app/tools/CurrentDateTimeTool.java new file mode 100644 index 0000000..9bf02eb --- /dev/null +++ b/src/main/java/mcp/app/tools/CurrentDateTimeTool.java @@ -0,0 +1,60 @@ +package mcp.app.tools; + +import io.modelcontextprotocol.spec.McpSchema; +import mcp.tools.DefaultMcpTool; +import mcp.tools.McpValidatedTool; +import mcp.tools.helper.SchemaBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Map; + +/** + * A simple tool that returns the current date and time. + */ +@DefaultMcpTool +public class CurrentDateTimeTool extends McpValidatedTool { + private static final Logger logger = LoggerFactory.getLogger(CurrentDateTimeTool.class); + + @Override + public String name() { + return "current_date_time"; + } + + @Override + public String title() { + return "Current Date and Time Tool"; + } + + @Override + public String description() { + return "Returns the current date and time on the server"; + } + + @Override + public McpSchema.JsonSchema inputSchema() { + return new SchemaBuilder() + .build(); + } + + @Override + public Map outputSchema() { + return new SchemaBuilder() + .returns("string", "The current date and time in ISO-8601 format") + .buildMap(); + } + + @Override + protected boolean isIdempotent() { + return false; + } + + @Override + public McpSchema.CallToolResult callValidated(McpSchema.CallToolRequest request, Map arguments) { + String now = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); + logger.info("CurrentDateTimeTool called, returning: {}", now); + return success(now); + } +} diff --git a/src/main/java/mcp/app/tools/EchoTool.java b/src/main/java/mcp/app/tools/EchoTool.java new file mode 100644 index 0000000..d061a6f --- /dev/null +++ b/src/main/java/mcp/app/tools/EchoTool.java @@ -0,0 +1,55 @@ +package mcp.app.tools; + +import io.modelcontextprotocol.spec.McpSchema; +import mcp.tools.DefaultMcpTool; +import mcp.tools.McpValidatedTool; +import mcp.tools.helper.SchemaBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +/** + * A simple tool that echoes back the input message. + */ +@DefaultMcpTool() +public class EchoTool extends McpValidatedTool { + private static final Logger logger = LoggerFactory.getLogger(EchoTool.class); + + @Override + public String name() { + return "echo"; + } + + @Override + public String title() { + return "Echo Tool"; + } + + @Override + public String description() { + return "Echoes back the input message"; + } + + @Override + public McpSchema.JsonSchema inputSchema() { + return new SchemaBuilder() + .addProperty("message", "string", "The message to echo") + .required("message") + .build(); + } + + @Override + public Map outputSchema() { + return new SchemaBuilder() + .returns("string", "The echoed message") + .buildMap(); + } + + @Override + public McpSchema.CallToolResult callValidated(McpSchema.CallToolRequest request, Map arguments) { + String message = (String) arguments.get("message"); + logger.debug("EchoTool called with message: {}", message); + return success(message); + } +} diff --git a/src/main/resources/simplelogger.properties b/src/main/resources/simplelogger.properties new file mode 100644 index 0000000..d8dceb6 --- /dev/null +++ b/src/main/resources/simplelogger.properties @@ -0,0 +1 @@ +org.slf4j.simpleLogger.defaultLogLevel=info