diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1fac4d5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,43 @@
+.gradle
+build/
+!gradle/wrapper/gradle-wrapper.jar
+!**/src/main/**/build/
+!**/src/test/**/build/
+.kotlin
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..a2b11dd
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,79 @@
+
+
+ 4.0.0
+
+ de.shahondin1624
+ knowledge-graph
+ 1.0-SNAPSHOT
+
+
+ 25
+ 25
+ UTF-8
+ 2026.01.4
+
+
+
+
+ org.neo4j
+ neo4j
+ ${neo4j.version}
+
+
+ org.neo4j
+ neo4j-bolt
+ ${neo4j.version}
+
+
+ io.github.mcp-java
+ mcp-server-lib
+ 1.0.0
+ compile
+
+
+ org.slf4j
+ slf4j-api
+ 2.0.9
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.5.1
+
+
+ package
+
+ shade
+
+
+ false
+
+
+ de.shahondin1624.knowledgegraph.Main
+
+
+
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/de/shahondin1624/knowledgegraph/DatabaseLauncher.java b/src/main/java/de/shahondin1624/knowledgegraph/DatabaseLauncher.java
new file mode 100644
index 0000000..ddcb4e4
--- /dev/null
+++ b/src/main/java/de/shahondin1624/knowledgegraph/DatabaseLauncher.java
@@ -0,0 +1,135 @@
+package de.shahondin1624.knowledgegraph;
+
+import org.neo4j.configuration.GraphDatabaseSettings;
+import org.neo4j.configuration.connectors.BoltConnector;
+import org.neo4j.configuration.helpers.SocketAddress;
+import org.neo4j.io.ByteUnit;
+import org.neo4j.dbms.api.DatabaseManagementService;
+import org.neo4j.dbms.api.DatabaseManagementServiceBuilder;
+import org.neo4j.graphdb.GraphDatabaseService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.*;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.stream.Stream;
+
+import static org.neo4j.configuration.GraphDatabaseSettings.DEFAULT_DATABASE_NAME;
+
+public class DatabaseLauncher {
+ private static final Logger logger = LoggerFactory.getLogger(DatabaseLauncher.class);
+ private final Path databaseDirectory;
+ private GraphDatabaseService graphDb;
+
+ public DatabaseLauncher() {
+ this(Paths.get(System.getProperty("user.home"), ".graphdb"));
+ }
+
+ public DatabaseLauncher(final Path databaseDirectory) {
+ this.databaseDirectory = databaseDirectory;
+ }
+
+ public void start() throws IOException, URISyntaxException {
+ logger.info("Starting database from directory: {}", databaseDirectory);
+ if (!Files.exists(databaseDirectory)) {
+ logger.info("Database directory does not exist, extracting template...");
+ extractDatabaseTemplate();
+ }
+ spinUpDatabase();
+ }
+
+ private void extractDatabaseTemplate() throws IOException, URISyntaxException {
+ final URL resourceUrl = getClass().getResource("/graphdb-template");
+ if (resourceUrl == null) {
+ logger.warn("No database template found at /graphdb-template, creating empty directory");
+ // If no template is provided, just create the directory
+ Files.createDirectories(databaseDirectory);
+ return;
+ }
+
+ URI resourceUri = resourceUrl.toURI();
+ logger.debug("Extracting database template from URI: {}", resourceUri);
+
+ if ("jar".equals(resourceUri.getScheme())) {
+ try (final FileSystem fileSystem = FileSystems.newFileSystem(resourceUri, Collections.emptyMap())) {
+ final Path source = fileSystem.getPath("/graphdb-template");
+ copyDirectory(source, databaseDirectory);
+ }
+ } else {
+ Path source = Paths.get(resourceUri);
+ copyDirectory(source, databaseDirectory);
+ }
+ logger.info("Database template extracted successfully to {}", databaseDirectory);
+ }
+
+ private void copyDirectory(final Path source, final Path target) throws IOException {
+ try (final Stream stream = Files.walk(source)) {
+ stream.forEach(path -> {
+ try {
+ String relativePathStr = source.relativize(path).toString();
+ Path destination = target.resolve(relativePathStr);
+ if (Files.isDirectory(path)) {
+ if (!Files.exists(destination)) {
+ Files.createDirectories(destination);
+ }
+ } else {
+ Files.copy(path, destination, StandardCopyOption.REPLACE_EXISTING);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to copy database template", e);
+ }
+ });
+ }
+ }
+
+ public void spinUpDatabase() {
+ logger.info("Spinning up Neo4j database...");
+ final Path configFile = databaseDirectory.resolve("neo4j.conf");
+ final DatabaseManagementServiceBuilder builder = new DatabaseManagementServiceBuilder(databaseDirectory);
+
+ if (Files.exists(configFile)) {
+ logger.info("Loading database configuration from {}", configFile);
+ builder.loadPropertiesFromFile(configFile);
+ } else {
+ logger.info("No configuration file found, using programmatic defaults");
+ configureProgrammatically(builder);
+ }
+
+ final DatabaseManagementService managementService = builder.build();
+ graphDb = managementService.database(DEFAULT_DATABASE_NAME);
+ GraphDatabaseProvider.setGraphDb(graphDb);
+ registerShutdownHook(managementService);
+ logger.info("Neo4j database '{}' is ready", DEFAULT_DATABASE_NAME);
+ }
+
+ private void configureProgrammatically(final DatabaseManagementServiceBuilder builder) {
+ builder.setConfig(GraphDatabaseSettings.pagecache_memory, ByteUnit.mebiBytes(512))
+ .setConfig(GraphDatabaseSettings.transaction_timeout, Duration.ofSeconds(60))
+ .setConfig(GraphDatabaseSettings.preallocate_logical_logs, true)
+ .setConfig(BoltConnector.enabled, true)
+ .setConfig(BoltConnector.listen_address, new SocketAddress("0.0.0.0", 7687))
+ .setConfig(BoltConnector.encryption_level, BoltConnector.EncryptionLevel.DISABLED)
+ .setConfig(GraphDatabaseSettings.auth_enabled, false);
+ }
+
+ private void registerShutdownHook(final DatabaseManagementService managementService) {
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ logger.info("Shutting down Neo4j database...");
+ managementService.shutdown();
+ logger.info("Neo4j database shut down successfully");
+ }));
+ }
+
+ public GraphDatabaseService getGraphDb() {
+ return graphDb;
+ }
+
+ public Path getDatabaseDirectory() {
+ return databaseDirectory;
+ }
+}
diff --git a/src/main/java/de/shahondin1624/knowledgegraph/GraphDatabaseProvider.java b/src/main/java/de/shahondin1624/knowledgegraph/GraphDatabaseProvider.java
new file mode 100644
index 0000000..c262222
--- /dev/null
+++ b/src/main/java/de/shahondin1624/knowledgegraph/GraphDatabaseProvider.java
@@ -0,0 +1,25 @@
+package de.shahondin1624.knowledgegraph;
+
+import org.neo4j.graphdb.GraphDatabaseService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Static provider for the GraphDatabaseService to be accessed by MCP tools.
+ */
+public class GraphDatabaseProvider {
+ private static final Logger logger = LoggerFactory.getLogger(GraphDatabaseProvider.class);
+ private static GraphDatabaseService graphDb;
+
+ public static synchronized void setGraphDb(final GraphDatabaseService db) {
+ logger.info("Registering GraphDatabaseService with provider");
+ graphDb = db;
+ }
+
+ public static synchronized GraphDatabaseService getGraphDb() {
+ if (graphDb == null) {
+ throw new IllegalStateException("GraphDatabaseService has not been initialized yet.");
+ }
+ return graphDb;
+ }
+}
diff --git a/src/main/java/de/shahondin1624/knowledgegraph/tooling/CreateNodeTool.java b/src/main/java/de/shahondin1624/knowledgegraph/tooling/CreateNodeTool.java
new file mode 100644
index 0000000..a45b0c4
--- /dev/null
+++ b/src/main/java/de/shahondin1624/knowledgegraph/tooling/CreateNodeTool.java
@@ -0,0 +1,85 @@
+package de.shahondin1624.knowledgegraph.tooling;
+
+import de.shahondin1624.knowledgegraph.GraphDatabaseProvider;
+import de.shahondin1624.knowledgegraph.util.DbHelper;
+import io.modelcontextprotocol.spec.McpSchema;
+import mcp.tools.DefaultMcpTool;
+import mcp.tools.McpValidatedTool;
+import mcp.tools.helper.SchemaBuilder;
+import mcp.util.Result;
+import org.neo4j.graphdb.Label;
+import org.neo4j.graphdb.Node;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * MCP tool for creating a new node with optional labels.
+ */
+@DefaultMcpTool
+public class CreateNodeTool extends McpValidatedTool {
+ private static final Logger logger = LoggerFactory.getLogger(CreateNodeTool.class);
+
+ @Override
+ public String name() {
+ return "create_node";
+ }
+
+ @Override
+ public String title() {
+ return "Create Node";
+ }
+
+ @Override
+ public String description() {
+ return "Creates a new node with the specified labels.";
+ }
+
+ @Override
+ public McpSchema.JsonSchema inputSchema() {
+ return new SchemaBuilder()
+ .addProperty("labels", "array", "Optional list of labels for the node")
+ .build();
+ }
+
+ @Override
+ protected boolean isIdempotent() {
+ return false;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public McpSchema.CallToolResult callValidated(McpSchema.CallToolRequest request, Map arguments) throws Exception {
+ List labelNames = (List) arguments.get("labels");
+ logger.debug("Creating node with labels: {}", labelNames);
+
+ final Result result = getStringExceptionResult(labelNames);
+
+ if (result.isOk()) {
+ String elementId = result.unwrap();
+ logger.info("Successfully created node with ID: {}", elementId);
+ return success("Node created with Element ID: " + elementId);
+ } else {
+ logger.error("Failed to create node: {}", result.err().unwrap().getMessage());
+ return error("Failed to create node: " + result.err().unwrap().getMessage());
+ }
+ }
+
+ private static Result getStringExceptionResult(List labelNames) {
+ DbHelper dbHelper = new DbHelper(GraphDatabaseProvider.getGraphDb());
+ return dbHelper.wrapInTx(tx -> {
+ Node node;
+ if (labelNames == null || labelNames.isEmpty()) {
+ node = tx.createNode();
+ } else {
+ Label[] labels = labelNames.stream()
+ .map(Label::label)
+ .toArray(Label[]::new);
+ node = tx.createNode(labels);
+ }
+ return node.getElementId();
+ });
+ }
+}
diff --git a/src/main/java/de/shahondin1624/knowledgegraph/tooling/ExecuteCypherTool.java b/src/main/java/de/shahondin1624/knowledgegraph/tooling/ExecuteCypherTool.java
new file mode 100644
index 0000000..216a112
--- /dev/null
+++ b/src/main/java/de/shahondin1624/knowledgegraph/tooling/ExecuteCypherTool.java
@@ -0,0 +1,84 @@
+package de.shahondin1624.knowledgegraph.tooling;
+
+import de.shahondin1624.knowledgegraph.GraphDatabaseProvider;
+import de.shahondin1624.knowledgegraph.util.DbHelper;
+import io.modelcontextprotocol.spec.McpSchema;
+import mcp.tools.DefaultMcpTool;
+import mcp.tools.McpValidatedTool;
+import mcp.tools.helper.SchemaBuilder;
+import org.neo4j.graphdb.Result;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * MCP tool for executing Cypher queries.
+ */
+@DefaultMcpTool
+public class ExecuteCypherTool extends McpValidatedTool {
+ private static final Logger logger = LoggerFactory.getLogger(ExecuteCypherTool.class);
+
+ @Override
+ public String name() {
+ return "execute_cypher";
+ }
+
+ @Override
+ public String title() {
+ return "Execute Cypher Query";
+ }
+
+ @Override
+ public String description() {
+ return "Executes a Cypher query on the Neo4j database and returns the result as a list of maps.";
+ }
+
+ @Override
+ public McpSchema.JsonSchema inputSchema() {
+ return new SchemaBuilder()
+ .addProperty("query", "string", "The Cypher query to execute")
+ .addProperty("parameters", "object", "Optional query parameters")
+ .required("query")
+ .build();
+ }
+
+ @Override
+ protected boolean isReadOnly() {
+ return false;
+ }
+
+ @Override
+ protected boolean isIdempotent() {
+ return false;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public McpSchema.CallToolResult callValidated(McpSchema.CallToolRequest request, Map arguments) throws Exception {
+ final String query = (String) arguments.get("query");
+ final Map parameters = (Map) arguments.get("parameters");
+ logger.debug("Executing Cypher query: {} with parameters: {}", query, parameters);
+
+ final DbHelper dbHelper = new DbHelper(GraphDatabaseProvider.getGraphDb());
+ mcp.util.Result>, Exception> result = dbHelper.wrapInTx(tx -> {
+ Result neoResult = tx.execute(query, parameters != null ? parameters : Map.of());
+ List