70 lines
2.2 KiB
TypeScript
70 lines
2.2 KiB
TypeScript
/**
|
|
* Wires the language server, the headless editor and the MCP server together.
|
|
*
|
|
* Build order matters: the language server initializes first (so tool
|
|
* registration can consult its capabilities), then the editor services, then
|
|
* the MCP transport, then tools and resources are registered.
|
|
*/
|
|
import { createServer } from "node:http";
|
|
import { NodeHttpServer } from "@effect/platform-node";
|
|
import { Effect, Layer } from "effect";
|
|
import { McpProtocol, McpServer } from "effect/unstable/ai";
|
|
import { HttpRouter } from "effect/unstable/http";
|
|
import { Diagnostics } from "./lsp/Diagnostics.ts";
|
|
import * as Documents from "./lsp/Documents.ts";
|
|
import * as LanguageServer from "./lsp/LanguageServer.ts";
|
|
import * as Resources from "./Resources.ts";
|
|
import * as Tools from "./Tools.ts";
|
|
|
|
export type Transport =
|
|
| { readonly _tag: "stdio" }
|
|
| { readonly _tag: "http"; readonly port: number };
|
|
|
|
export interface Options {
|
|
readonly transport: Transport;
|
|
readonly server: LanguageServer.Options;
|
|
readonly documents: Documents.Options;
|
|
}
|
|
|
|
const info = {
|
|
name: "lsp-mcp",
|
|
version: "0.1.2",
|
|
protocols: [
|
|
McpProtocol.v2025_11_25,
|
|
McpProtocol.v2025_06_18,
|
|
McpProtocol.v2025_03_26,
|
|
McpProtocol.v2024_11_05,
|
|
],
|
|
} as const;
|
|
|
|
const transport = (transport: Transport) =>
|
|
transport._tag === "stdio"
|
|
? McpServer.layerStdio(info)
|
|
: HttpRouter.serve(McpServer.layerHttp({ ...info, path: "/mcp" }), {
|
|
disableLogger: true,
|
|
}).pipe(
|
|
Layer.provide(
|
|
NodeHttpServer.layer(createServer, { port: transport.port }),
|
|
),
|
|
);
|
|
|
|
/** Registers the tools the connected server can serve. */
|
|
export const tools = Layer.effectDiscard(
|
|
Effect.gen(function* () {
|
|
const { capabilities } = yield* LanguageServer.LanguageServer;
|
|
yield* McpServer.registerToolkit(Tools.supportedBy(capabilities));
|
|
}),
|
|
).pipe(Layer.provide(Tools.layer));
|
|
|
|
export const layer = (options: Options) =>
|
|
Layer.mergeAll(tools, Resources.layer).pipe(
|
|
Layer.provide(transport(options.transport)),
|
|
Layer.provide(
|
|
Layer.mergeAll(
|
|
Documents.Documents.layer(options.documents),
|
|
Diagnostics.layer,
|
|
),
|
|
),
|
|
Layer.provide(LanguageServer.LanguageServer.layer(options.server)),
|
|
);
|