@llms-sdk/diff-server
Version:
LLMS SDK Diff server for file diff viewing and collaboration
193 lines (176 loc) • 5.02 kB
text/typescript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
import { LlmsSdkDiffServer } from "./llms-sdk-diff-server.js";
import { openTool } from "./mcp/open.js";
import { closeTool } from "./mcp/close.js";
import { highlightTool } from "./mcp/highlight.js";
import { refreshTool } from "./mcp/refresh.js";
/**
* Main MCP server entry point for LLMS SDK Diff
* Handles stdio transport and coordinates with internal file server
*/
class LlmsSdkDiffMCPServer {
private server: Server;
private diffServer: LlmsSdkDiffServer;
constructor() {
this.server = new Server(
{
name: "llms-sdk-diff-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
},
);
this.diffServer = new LlmsSdkDiffServer();
this.setupTools();
this.setupErrorHandling();
}
private setupTools() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "open",
description: "Open a file in the specified panel with optional git diff",
inputSchema: {
type: "object",
properties: {
absolutePath: {
type: "string",
description: "Absolute path to file",
},
panel: {
type: "number",
enum: [0, 1],
description: "Panel index (0=left, 1=right)",
},
branch: {
type: "string",
description: "Optional: branch/commit/tag to diff against",
},
},
required: ["absolutePath", "panel"],
},
},
{
name: "close",
description: "Close a file from all panels",
inputSchema: {
type: "object",
properties: {
absolutePath: {
type: "string",
description: "Absolute path to file",
},
},
required: ["absolutePath"],
},
},
{
name: "highlight",
description: "Highlight specific lines in a file (content mode only)",
inputSchema: {
type: "object",
properties: {
absolutePath: {
type: "string",
description: "Absolute path to file",
},
startLine: {
type: "number",
description: "Start line number (1-indexed)",
},
endLine: {
type: "number",
description: "End line number (1-indexed, optional)",
},
},
required: ["absolutePath", "startLine"],
},
},
{
name: "refresh",
description: "Refresh all watched files and recalculate diffs",
inputSchema: {
type: "object",
properties: {},
additionalProperties: false,
},
},
],
};
});
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "open":
return await openTool(args, this.diffServer);
case "close":
return await closeTool(args, this.diffServer);
case "highlight":
return await highlightTool(args, this.diffServer);
case "refresh":
return await refreshTool(args, this.diffServer);
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
}
} catch (error) {
console.error(`Error in tool ${name}:`, error);
throw error instanceof McpError
? error
: new McpError(
ErrorCode.InternalError,
`Tool ${name} failed: ${error instanceof Error ? error.message : String(error)}`,
);
}
});
}
private setupErrorHandling() {
this.server.onerror = (error) => {
console.error("[MCP Server Error]", error);
};
process.on("SIGINT", async () => {
console.log("Shutting down LLMS SDK Diff MCP server...");
await this.cleanup();
process.exit(0);
});
process.on("SIGTERM", async () => {
console.log("Shutting down LLMS SDK Diff MCP server...");
await this.cleanup();
process.exit(0);
});
}
private async cleanup() {
try {
await this.diffServer.stop();
console.log("LLMS SDK Diff server stopped");
} catch (error) {
console.error("Error during cleanup:", error);
}
}
async start() {
// Start the internal LLMS SDK Diff server
await this.diffServer.start();
// Start MCP server with stdio transport
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error("LLMS SDK Diff server started and listening on stdio");
}
}
// Start the server if this file is run directly
if (import.meta.url === `file://${process.argv[1]}`) {
const server = new LlmsSdkDiffMCPServer();
server.start().catch((error) => {
console.error("Failed to start LLMS SDK Diff server:", error);
process.exit(1);
});
}
export { LlmsSdkDiffMCPServer };