Go to the documentation of this file.
19package org.sleuthkit.autopsy.mcp;
21import com.fasterxml.jackson.databind.ObjectMapper;
22import io.javalin.Javalin;
23import io.javalin.http.Context;
24import static io.javalin.apibuilder.ApiBuilder.*;
25import org.sleuthkit.autopsy.casemodule.Case;
27import java.io.IOException;
28import java.io.InputStream;
29import java.io.OutputStream;
30import java.nio.file.Files;
31import java.nio.file.Path;
32import java.nio.file.StandardOpenOption;
33import java.security.SecureRandom;
34import java.util.Base64;
35import java.util.LinkedHashMap;
37import java.util.Properties;
38import java.util.logging.Level;
39import java.util.logging.Logger;
54 private static final ObjectMapper
MAPPER =
new ObjectMapper();
65 this.protocolHandler =
new McpProtocolHandler();
87 app = Javalin.create(config -> {
88 config.routes.apiBuilder(() -> {
91 String auth = ctx.header(
"Authorization");
92 if (auth ==
null || !auth.equals(
"Bearer " +
authToken)) {
93 ctx.status(401).result(
"Unauthorized");
94 ctx.skipRemainingHandlers();
99 post(
"/mcp", this::handleMcpRequest);
104 app.start(
"127.0.0.1", port);
107 }
catch (IOException ex) {
109 throw new RuntimeException(
"MCP server started but failed to write token file — aborting", ex);
123 String requestBody = ctx.body();
125 ctx.contentType(
"application/json").result(response);
126 }
catch (Exception ex) {
127 logger.log(Level.SEVERE,
"MCP protocol handler threw unexpectedly", ex);
128 String msg = ex.getMessage() !=
null ? ex.getMessage() :
"Internal error";
129 Map<String, Object> errorDetail =
new LinkedHashMap<>();
130 errorDetail.put(
"code", McpProtocolHandler.ERR_INTERNAL_ERROR);
131 errorDetail.put(
"message",
"Internal error");
132 errorDetail.put(
"data", msg);
133 Map<String, Object> envelope =
new LinkedHashMap<>();
134 envelope.put(
"jsonrpc",
"2.0");
135 envelope.put(
"error", errorDetail);
136 envelope.put(
"id",
null);
139 body =
MAPPER.writeValueAsString(envelope);
140 }
catch (Exception jsonEx) {
141 logger.log(Level.SEVERE,
"Failed to serialize MCP error response", jsonEx);
142 body =
"{\"jsonrpc\":\"2.0\",\"error\":{\"code\":" + McpProtocolHandler.ERR_INTERNAL_ERROR +
",\"message\":\"Internal error\"},\"id\":null}";
144 ctx.status(500).contentType(
"application/json").result(body);
156 Properties props =
new Properties();
158 if (Files.exists(configPath)) {
159 try (InputStream in = Files.newInputStream(configPath)) {
161 String portStr = props.getProperty(
PORT_PROPERTY,
"").trim();
162 int port = Integer.parseInt(portStr);
163 if (port > 0 && port <= 65535) {
166 logger.log(Level.WARNING,
"Invalid port in MCP config ({0}), using default {1}",
167 new Object[]{portStr, DEFAULT_PORT});
168 }
catch (IOException | NumberFormatException ex) {
169 logger.log(Level.WARNING,
"Could not read MCP config port, using default", ex);
174 Files.createDirectories(configPath.getParent());
176 try (OutputStream out = Files.newOutputStream(configPath,
177 StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
179 "Autopsy MCP server configuration\n"
180 +
"# Change the port number below if it conflicts with another application.\n"
181 +
"# Restart Autopsy after editing this file.");
183 }
catch (IOException ex) {
184 logger.log(Level.WARNING,
"Could not create MCP config file, using default port", ex);
191 byte[] bytes =
new byte[32];
192 new SecureRandom().nextBytes(bytes);
193 return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
197 String localAppData = System.getenv(
"LOCALAPPDATA");
198 if (localAppData !=
null && !localAppData.isEmpty()) {
199 return Path.of(localAppData,
"autopsy",
"mcp");
201 return Path.of(System.getProperty(
"user.home"),
"AppData",
"Local",
"autopsy",
"mcp");
210 Files.createDirectories(tokenPath.getParent());
212 tokenPath.toFile().deleteOnExit();
218 }
catch (IOException ex) {
219 logger.log(Level.WARNING,
"Failed to delete MCP token file", ex);
SleuthkitCase getSleuthkitCase()
synchronized static Logger getLogger(String name)
int readOrCreateConfigPort()
static final Logger logger
static final ObjectMapper MAPPER
void updateCase(Case openedCase)
final McpProtocolHandler protocolHandler
static final String CONFIG_FILE_NAME
void handleMcpRequest(Context ctx)
static final int DEFAULT_PORT
static final String PORT_PROPERTY
Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a
Creative Commons Attribution-Share Alike 3.0 United States License.