fix(mcp): bootstrap lsp when cli is unavailable
Keep the built-in lsp MCP registered even when the submodule CLI artifact is missing. The fallback command initializes the lsp-tools-mcp submodule, prefers the source CLI without dirtying the checkout with dist output, and keeps npm build as a last resort when Bun cannot run the source entrypoint. Plan: plans/fix-lsp-mcp-missing-cli.md
This commit is contained in:
@@ -1,13 +1,11 @@
|
||||
/// <reference types="bun-types" />
|
||||
|
||||
import { afterEach, describe, expect, it, mock } from "bun:test"
|
||||
import { afterEach, describe, expect, it } from "bun:test"
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"
|
||||
import { tmpdir } from "node:os"
|
||||
import { join } from "node:path"
|
||||
import { clearPluginConfigFileDetectionCache } from "../../../shared/jsonc-parser"
|
||||
|
||||
const originalCwd = process.cwd()
|
||||
const originalOpenCodeConfigDir = process.env.OPENCODE_CONFIG_DIR
|
||||
const temporaryDirectories: string[] = []
|
||||
|
||||
function createTemporaryDirectory(prefix: string): string {
|
||||
@@ -16,16 +14,14 @@ function createTemporaryDirectory(prefix: string): string {
|
||||
return directory
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore()
|
||||
clearPluginConfigFileDetectionCache()
|
||||
process.chdir(originalCwd)
|
||||
function createLspDistCli(workspaceDirectory: string): void {
|
||||
const lspDistDirectory = join(workspaceDirectory, "packages", "lsp-tools-mcp", "dist")
|
||||
mkdirSync(lspDistDirectory, { recursive: true })
|
||||
writeFileSync(join(lspDistDirectory, "cli.js"), "#!/usr/bin/env node\n", "utf-8")
|
||||
}
|
||||
|
||||
if (originalOpenCodeConfigDir === undefined) {
|
||||
delete process.env.OPENCODE_CONFIG_DIR
|
||||
} else {
|
||||
process.env.OPENCODE_CONFIG_DIR = originalOpenCodeConfigDir
|
||||
}
|
||||
afterEach(() => {
|
||||
clearPluginConfigFileDetectionCache()
|
||||
|
||||
for (const directory of temporaryDirectories.splice(0)) {
|
||||
rmSync(directory, { recursive: true, force: true })
|
||||
@@ -39,36 +35,49 @@ describe("getInstalledLspServers", () => {
|
||||
const workspaceDirectory = createTemporaryDirectory("omo-tools-lsp-workspace-")
|
||||
const projectConfigDirectory = join(workspaceDirectory, ".opencode")
|
||||
mkdirSync(projectConfigDirectory, { recursive: true })
|
||||
createLspDistCli(workspaceDirectory)
|
||||
writeFileSync(
|
||||
join(projectConfigDirectory, "oh-my-openagent.json"),
|
||||
JSON.stringify({ disabled_mcps: ["lsp"] }),
|
||||
"utf-8",
|
||||
)
|
||||
process.env.OPENCODE_CONFIG_DIR = userConfigDirectory
|
||||
process.chdir(workspaceDirectory)
|
||||
clearPluginConfigFileDetectionCache()
|
||||
|
||||
const { getInstalledLspServers } = await import(`./tools-lsp?t=${Date.now()}-disabled`)
|
||||
|
||||
// when
|
||||
const servers = getInstalledLspServers()
|
||||
const servers = getInstalledLspServers({ configDirectory: userConfigDirectory, cwd: workspaceDirectory })
|
||||
|
||||
// then
|
||||
expect(servers).toEqual([])
|
||||
})
|
||||
|
||||
it("returns bundled lsp server info when lsp MCP uses bootstrap fallback", async () => {
|
||||
// given
|
||||
const userConfigDirectory = createTemporaryDirectory("omo-tools-lsp-user-")
|
||||
const workspaceDirectory = createTemporaryDirectory("omo-tools-lsp-bootstrap-")
|
||||
clearPluginConfigFileDetectionCache()
|
||||
|
||||
const { getInstalledLspServers } = await import(`./tools-lsp?t=${Date.now()}-bootstrap`)
|
||||
|
||||
// when
|
||||
const servers = getInstalledLspServers({ configDirectory: userConfigDirectory, cwd: workspaceDirectory })
|
||||
|
||||
// then
|
||||
expect(servers).toEqual([{ id: "lsp-tools-mcp", extensions: ["*"] }])
|
||||
})
|
||||
|
||||
it("returns bundled lsp server info when MCP is enabled", async () => {
|
||||
// given
|
||||
const userConfigDirectory = createTemporaryDirectory("omo-tools-lsp-user-")
|
||||
const workspaceDirectory = createTemporaryDirectory("omo-tools-lsp-enabled-")
|
||||
process.env.OPENCODE_CONFIG_DIR = userConfigDirectory
|
||||
process.chdir(workspaceDirectory)
|
||||
createLspDistCli(workspaceDirectory)
|
||||
clearPluginConfigFileDetectionCache()
|
||||
|
||||
const { getInstalledLspServers } = await import(`./tools-lsp?t=${Date.now()}-enabled`)
|
||||
|
||||
// when
|
||||
const servers = getInstalledLspServers()
|
||||
const servers = getInstalledLspServers({ configDirectory: userConfigDirectory, cwd: workspaceDirectory })
|
||||
|
||||
// then
|
||||
expect(servers).toEqual([{ id: "lsp-tools-mcp", extensions: ["*"] }])
|
||||
|
||||
@@ -7,7 +7,10 @@ type OmoConfigForDoctor = {
|
||||
disabled_mcps?: string[]
|
||||
}
|
||||
|
||||
const PROJECT_CONFIG_DIR = join(process.cwd(), ".opencode")
|
||||
type InstalledLspServersOptions = {
|
||||
readonly configDirectory?: string
|
||||
readonly cwd?: string
|
||||
}
|
||||
|
||||
function readOmoConfig(configDirectory: string): OmoConfigForDoctor | null {
|
||||
const detected = detectPluginConfigFile(configDirectory)
|
||||
@@ -23,10 +26,11 @@ function readOmoConfig(configDirectory: string): OmoConfigForDoctor | null {
|
||||
}
|
||||
}
|
||||
|
||||
function isLspMcpDisabled(): boolean {
|
||||
const userConfigDirectory = getOpenCodeConfigDir({ binary: "opencode" })
|
||||
function isLspMcpDisabled(options: InstalledLspServersOptions): boolean {
|
||||
const userConfigDirectory = options.configDirectory ?? getOpenCodeConfigDir({ binary: "opencode" })
|
||||
const projectConfigDirectory = join(options.cwd ?? process.cwd(), ".opencode")
|
||||
const userConfig = readOmoConfig(userConfigDirectory)
|
||||
const projectConfig = readOmoConfig(PROJECT_CONFIG_DIR)
|
||||
const projectConfig = readOmoConfig(projectConfigDirectory)
|
||||
|
||||
const disabledMcps = new Set<string>([
|
||||
...(userConfig?.disabled_mcps ?? []),
|
||||
@@ -36,12 +40,12 @@ function isLspMcpDisabled(): boolean {
|
||||
return disabledMcps.has("lsp")
|
||||
}
|
||||
|
||||
export function getInstalledLspServers(): Array<{ id: string; extensions: string[] }> {
|
||||
if (isLspMcpDisabled()) {
|
||||
export function getInstalledLspServers(options: InstalledLspServersOptions = {}): Array<{ id: string; extensions: string[] }> {
|
||||
if (isLspMcpDisabled(options)) {
|
||||
return []
|
||||
}
|
||||
|
||||
const lspMcpConfig = createLspMcpConfig()
|
||||
const lspMcpConfig = createLspMcpConfig({ cwd: options.cwd })
|
||||
|
||||
return lspMcpConfig.enabled ? [{ id: "lsp-tools-mcp", extensions: ["*"] }] : []
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user