Skip to content

Commit 80752b2

Browse files
authored
Fix IDE tool response handling (#274)
1 parent 7b8b709 commit 80752b2

12 files changed

Lines changed: 280 additions & 135 deletions

lua/claudecode/server/init.lua

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,6 @@ function M.register_handlers()
282282
capabilities = {
283283
logging = vim.empty_dict(), -- Ensure this is an object {} not an array []
284284
prompts = { listChanged = true },
285-
resources = { subscribe = true, listChanged = true },
286285
tools = { listChanged = true },
287286
},
288287
serverInfo = {

lua/claudecode/tools/check_document_dirty.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ local function handler(params)
3636
text = vim.json.encode({
3737
success = false,
3838
message = "Document not open: " .. params.filePath,
39-
}, { indent = 2 }),
39+
}),
4040
},
4141
},
4242
}
@@ -55,7 +55,7 @@ local function handler(params)
5555
filePath = params.filePath,
5656
isDirty = is_dirty,
5757
isUntitled = is_untitled,
58-
}, { indent = 2 }),
58+
}),
5959
},
6060
},
6161
}

lua/claudecode/tools/get_current_selection.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ local schema = {
1414
---@param error_context string A description of what failed for error messages
1515
---@return string The JSON-encoded string
1616
local function safe_json_encode(data, error_context)
17-
local ok, encoded = pcall(vim.json.encode, data, { indent = 2 })
17+
local ok, encoded = pcall(vim.json.encode, data)
1818
if not ok then
1919
error({
2020
code = -32000,

lua/claudecode/tools/get_diagnostics.lua

Lines changed: 88 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
--- Tool implementation for getting diagnostics.
22

3-
-- NOTE: Its important we don't tip off Claude that we're dealing with Neovim LSP diagnostics because it may adjust
4-
-- line and col numbers by 1 on its own (since it knows nvim LSP diagnostics are 0-indexed). By calling these
5-
-- "editor diagnostics" and converting to 1-indexed ourselves we (hopefully) avoid incorrect line and column numbers
6-
-- in Claude's responses.
73
local schema = {
84
description = "Get language diagnostics (errors, warnings) from the editor",
95
inputSchema = {
@@ -19,14 +15,81 @@ local schema = {
1915
},
2016
}
2117

18+
local severity_names = {
19+
[1] = "Error",
20+
[2] = "Warning",
21+
[3] = "Info",
22+
[4] = "Hint",
23+
}
24+
25+
local function starts_with(str, prefix)
26+
return type(str) == "string" and str:sub(1, #prefix) == prefix
27+
end
28+
29+
local function path_to_uri(path)
30+
if vim.uri_from_fname then
31+
return vim.uri_from_fname(path)
32+
end
33+
return "file://" .. path
34+
end
35+
36+
local function severity_name(severity)
37+
if type(severity) == "string" then
38+
return severity
39+
end
40+
return severity_names[severity] or "Info"
41+
end
42+
43+
local function diagnostic_range(diagnostic)
44+
local start_line = diagnostic.lnum or 0
45+
local start_col = diagnostic.col or 0
46+
local end_line = diagnostic.end_lnum or start_line
47+
local end_col = diagnostic.end_col or (start_col + 1)
48+
49+
return {
50+
start = { line = start_line, character = start_col },
51+
["end"] = { line = end_line, character = end_col },
52+
}
53+
end
54+
55+
local function get_diagnostic_path(diagnostic, fallback_path)
56+
if diagnostic.bufnr then
57+
local file_path = vim.api.nvim_buf_get_name(diagnostic.bufnr)
58+
if file_path and file_path ~= "" then
59+
return file_path
60+
end
61+
end
62+
return fallback_path
63+
end
64+
65+
local function append_diagnostic(files, file_by_uri, file_path, diagnostic)
66+
if not file_path or file_path == "" then
67+
return
68+
end
69+
70+
local uri = path_to_uri(file_path)
71+
local file = file_by_uri[uri]
72+
if not file then
73+
file = { uri = uri, diagnostics = {} }
74+
file_by_uri[uri] = file
75+
table.insert(files, file)
76+
end
77+
78+
table.insert(file.diagnostics, {
79+
message = diagnostic.message or "",
80+
severity = severity_name(diagnostic.severity),
81+
range = diagnostic_range(diagnostic),
82+
source = diagnostic.source,
83+
code = diagnostic.code and tostring(diagnostic.code) or nil,
84+
})
85+
end
86+
2287
---Handles the getDiagnostics tool invocation.
2388
---Retrieves diagnostics from Neovim's diagnostic system.
2489
---@param params table The input parameters for the tool
2590
---@return table diagnostics MCP-compliant response with diagnostics data
2691
local function handler(params)
2792
if not vim.lsp or not vim.diagnostic or not vim.diagnostic.get then
28-
-- Returning an empty list or a specific status could be an alternative.
29-
-- For now, let's align with the error pattern for consistency if the feature is unavailable.
3093
error({
3194
code = -32000,
3295
message = "Feature unavailable",
@@ -38,58 +101,44 @@ local function handler(params)
38101

39102
logger.debug("getDiagnostics handler called with params: " .. vim.inspect(params))
40103

41-
-- Extract the uri parameter
42104
local diagnostics
105+
local fallback_path
43106

44107
if not params.uri then
45-
-- Get diagnostics for all buffers
46108
logger.debug("Getting diagnostics for all open buffers")
47109
diagnostics = vim.diagnostic.get(nil)
48110
else
49111
local uri = params.uri
50-
local filepath = vim.startswith(uri, "file://") and vim.uri_to_fname(uri) or uri
112+
fallback_path = starts_with(uri, "file://") and vim.uri_to_fname(uri) or uri
51113

52-
-- Get buffer number for the specific file
53-
local bufnr = vim.fn.bufnr(filepath)
114+
local bufnr = vim.fn.bufnr(fallback_path)
54115
if bufnr == -1 then
55-
-- File is not open in any buffer, throw an error
56-
logger.debug("File buffer must be open to get diagnostics: " .. filepath)
116+
logger.debug("File buffer must be open to get diagnostics: " .. fallback_path)
57117
error({
58118
code = -32001,
59119
message = "File not open",
60-
data = "File must be open to retrieve diagnostics: " .. filepath,
120+
data = "File must be open to retrieve diagnostics: " .. fallback_path,
61121
})
62-
else
63-
-- Get diagnostics for the specific buffer
64-
logger.debug("Getting diagnostics for bufnr: " .. bufnr)
65-
diagnostics = vim.diagnostic.get(bufnr)
66122
end
123+
124+
logger.debug("Getting diagnostics for bufnr: " .. bufnr)
125+
diagnostics = vim.diagnostic.get(bufnr)
67126
end
68127

69-
local formatted_diagnostics = {}
70-
for _, diagnostic in ipairs(diagnostics) do
71-
local file_path = vim.api.nvim_buf_get_name(diagnostic.bufnr)
72-
-- Ensure we only include diagnostics with valid file paths
73-
if file_path and file_path ~= "" then
74-
table.insert(formatted_diagnostics, {
75-
type = "text",
76-
-- json encode this
77-
text = vim.json.encode({
78-
-- Use the file path and diagnostic information
79-
filePath = file_path,
80-
-- Convert line and column to 1-indexed
81-
line = diagnostic.lnum + 1,
82-
character = diagnostic.col + 1,
83-
severity = diagnostic.severity, -- e.g., vim.diagnostic.severity.ERROR
84-
message = diagnostic.message,
85-
source = diagnostic.source,
86-
}),
87-
})
88-
end
128+
local files = {}
129+
local file_by_uri = {}
130+
131+
for _, diagnostic in ipairs(diagnostics or {}) do
132+
append_diagnostic(files, file_by_uri, get_diagnostic_path(diagnostic, fallback_path), diagnostic)
89133
end
90134

91135
return {
92-
content = formatted_diagnostics,
136+
content = {
137+
{
138+
type = "text",
139+
text = vim.json.encode(files),
140+
},
141+
},
93142
}
94143
end
95144

lua/claudecode/tools/get_latest_selection.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ local function handler(params)
3030
text = vim.json.encode({
3131
success = false,
3232
message = "No selection available",
33-
}, { indent = 2 }),
33+
}),
3434
},
3535
},
3636
}
@@ -41,7 +41,7 @@ local function handler(params)
4141
content = {
4242
{
4343
type = "text",
44-
text = vim.json.encode(selection, { indent = 2 }),
44+
text = vim.json.encode(selection),
4545
},
4646
},
4747
}

lua/claudecode/tools/get_open_editors.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ local function handler(params)
105105
content = {
106106
{
107107
type = "text",
108-
text = vim.json.encode({ tabs = tabs }, { indent = 2 }),
108+
text = vim.json.encode({ tabs = tabs }),
109109
},
110110
},
111111
}

lua/claudecode/tools/get_workspace_folders.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ local function handler(params)
5555
success = true,
5656
folders = folders,
5757
rootPath = cwd,
58-
}, { indent = 2 }),
58+
}),
5959
},
6060
},
6161
}

0 commit comments

Comments
 (0)