Skip to content

Feat: Add more emacs mcps: buffer and project query tools and xref support#277

Merged
tninja merged 6 commits intomainfrom
kang_feat
Apr 5, 2026
Merged

Feat: Add more emacs mcps: buffer and project query tools and xref support#277
tninja merged 6 commits intomainfrom
kang_feat

Conversation

@tninja
Copy link
Copy Markdown
Owner

@tninja tninja commented Apr 5, 2026

Referencing https://github.com/acmorrow/claude-code-ide-extras, port 4 mcps from there.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5aa52baeb4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ai-code-mcp-server.el
Comment on lines +516 to +517
(group (ai-code-mcp--display-path
(xref-location-group location)))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve full paths for external xref hits

Wrapping xref-location-group with ai-code-mcp--display-path truncates any location outside the active project to file-name-nondirectory, so references/definitions from different external files with the same basename become indistinguishable (for example, two different index.js files). Before this change, the formatter kept the full group path, so this is a regression in result accuracy whenever the backend returns matches outside the project root.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds additional MCP tools to ai-code-mcp-server.el to expose more Emacs editor/project navigation capabilities (ported from claude-code-ide-extras), plus accompanying ERT coverage.

Changes:

  • Add MCP tools for buffer line-range querying, project file listing, and project buffer listing.
  • Add MCP tool to find xref definitions at a specific file location (line/column).
  • Extend ERT tests to cover the new tools and ensure they appear in the tools list.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
ai-code-mcp-server.el Registers new MCP tools and implements buffer/project/xref helpers, plus tweaks xref formatting and result formatting.
test/test_ai-code-mcp-server.el Updates tool-list expectations and adds tests for the new MCP tools.

Comment thread ai-code-mcp-server.el
@@ -14,6 +14,7 @@
(require 'cl-lib)
(require 'imenu)
(require 'project)
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ai-code-mcp-server.el uses several seq-* functions (e.g., seq-remove, seq-filter) but does not (require 'seq). Many other modules in this repo explicitly require seq when using these functions; adding the require here avoids load-order dependent runtime failures and byte-compile warnings when this file is loaded standalone.

Suggested change
(require 'project)
(require 'project)
(require 'seq)

Copilot uses AI. Check for mistakes.
Comment thread ai-code-mcp-server.el Outdated
Comment on lines +238 to +239
(string-trim-right
(buffer-substring-no-properties start-pos (point))))))))))
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ai-code-mcp-buffer-query uses string-trim-right on the extracted substring. This will strip all trailing whitespace from the last returned line (not just the final newline), which can corrupt code/text where trailing spaces are significant. Prefer only removing a single trailing newline (or return the exact substring unchanged) to preserve buffer contents faithfully.

Suggested change
(string-trim-right
(buffer-substring-no-properties start-pos (point))))))))))
(let ((text (buffer-substring-no-properties start-pos (point))))
(if (string-suffix-p "\n" text)
(substring text 0 -1)
text)))))))))

Copilot uses AI. Check for mistakes.
Comment thread ai-code-mcp-server.el Outdated
Comment on lines +227 to +237
(when (or (and start-line (not num-lines))
(and num-lines (not start-line)))
(error "Arguments start_line and num_lines must both be provided or both be omitted"))
(with-current-buffer buffer
(save-excursion
(if (not start-line)
(buffer-substring-no-properties (point-min) (point-max))
(goto-char (point-min))
(forward-line (1- start-line))
(let ((start-pos (point)))
(forward-line num-lines)
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ai-code-mcp-buffer-query claims start_line is 1-based, but it doesn’t validate start_line >= 1 and num_lines >= 1. Passing 0/negative values will produce surprising results (or errors) due to (forward-line (1- start-line)) / (forward-line num-lines). Add explicit validation with a clear error message (or normalize inputs) before moving point.

Copilot uses AI. Check for mistakes.
Comment thread ai-code-mcp-server.el Outdated
Comment on lines +241 to +249
(defun ai-code-mcp-get-project-files ()
"Return regular files in the current project as relative paths."
(let ((project-dir (ai-code-mcp--project-directory)))
(if (not (and project-dir (file-directory-p project-dir)))
nil
(mapcar #'ai-code-mcp--display-path
(seq-filter
#'file-regular-p
(directory-files-recursively project-dir ".*" t))))))
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ai-code-mcp-get-project-files walks the entire project directory via directory-files-recursively ".*", which will include files like .git/* and can be very slow on large repos. Prefer project-current + project-files (with a recursive fallback like ai-code-session-link--project-files uses) so the result respects project backends/ignores and avoids scanning VCS internals.

Suggested change
(defun ai-code-mcp-get-project-files ()
"Return regular files in the current project as relative paths."
(let ((project-dir (ai-code-mcp--project-directory)))
(if (not (and project-dir (file-directory-p project-dir)))
nil
(mapcar #'ai-code-mcp--display-path
(seq-filter
#'file-regular-p
(directory-files-recursively project-dir ".*" t))))))
(defun ai-code-mcp--project-files (project-dir)
"Return regular files for PROJECT-DIR using project APIs when available.
Fall back to a recursive directory walk that skips VCS internals."
(let* ((default-directory (file-name-as-directory project-dir))
(project (project-current nil))
(project-root default-directory))
(cond
((and project (fboundp 'project-files))
(seq-filter
#'file-regular-p
(mapcar (lambda (file)
(if (file-name-absolute-p file)
file
(expand-file-name file project-root)))
(project-files project))))
(t
(cl-labels
((collect-files (dir)
(apply
#'append
(mapcar
(lambda (entry)
(cond
((member entry '("." "..")) nil)
((string-prefix-p "." entry) nil)
(t
(let ((path (expand-file-name entry dir)))
(cond
((file-directory-p path)
(collect-files path))
((file-regular-p path)
(list path))
(t nil))))))
(directory-files dir nil nil t))))
(collect-files project-root))))))
(defun ai-code-mcp-get-project-files ()
"Return regular files in the current project as relative paths."
(let ((project-dir (ai-code-mcp--project-directory)))
(if (not (and project-dir (file-directory-p project-dir)))
nil
(mapcar #'ai-code-mcp--display-path
(ai-code-mcp--project-files project-dir)))))

Copilot uses AI. Check for mistakes.
Comment thread ai-code-mcp-server.el
Comment on lines 513 to 518
(defun ai-code-mcp--format-xref-item (item)
"Return a human-readable line for xref ITEM."
(let* ((location (xref-item-location item))
(group (xref-location-group location))
(group (ai-code-mcp--display-path
(xref-location-group location)))
(marker (xref-location-marker location))
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ai-code-mcp--format-xref-item now runs xref-location-group through ai-code-mcp--display-path, but ai-code-mcp--display-path uses a simple string-prefix-p check against project-dir. That can misclassify paths like /tmp/proj2/... as being under /tmp/proj and produce misleading relative paths with ... Consider switching the containment check to file-in-directory-p (or ensure project-dir is normalized with a trailing slash) before calling file-relative-name.

Copilot uses AI. Check for mistakes.
Comment thread test/test_ai-code-mcp-server.el Outdated
Comment on lines +308 to +336
(ert-deftest ai-code-test-mcp-xref-find-definitions-at-point-uses-location-context ()
"Definitions-at-point should resolve via the xref backend at a file location."
(let* ((project-dir (make-temp-file "ai-code-mcp-xref-defs-" t))
(file-path (expand-file-name "defs.el" project-dir))
(buffer (generate-new-buffer " *ai-code-mcp-xref-defs*")))
(unwind-protect
(progn
(with-temp-file file-path
(insert "(defun alpha ()\n")
(insert " (beta))\n\n")
(insert "(defun beta ()\n")
(insert " t)\n"))
(cl-letf (((symbol-function 'xref-find-backend)
(lambda () 'mock-backend))
((symbol-function 'xref-backend-identifier-at-point)
(lambda (_backend) "beta"))
((symbol-function 'xref-backend-definitions)
(lambda (_backend identifier)
(list (xref-make
(format "%s definition" identifier)
(xref-make-file-location file-path 4 0))))))
(should (equal '("defs.el:4: beta definition")
(ai-code-mcp-xref-find-definitions-at-point
file-path
2
3)))))
(when (buffer-live-p buffer)
(kill-buffer buffer))
(delete-directory project-dir t))))
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In ai-code-test-mcp-xref-find-definitions-at-point-uses-location-context, the local buffer created with generate-new-buffer is never used, while the file-visiting buffer created by ai-code-mcp-xref-find-definitions-at-point (via find-file-noselect) is not cleaned up. This can leak buffers across the test suite; remove the unused buffer and explicitly kill the file-visiting buffer (or bind/kill it via ai-code-mcp--file-buffer).

Copilot uses AI. Check for mistakes.
@tninja
Copy link
Copy Markdown
Owner Author

tninja commented Apr 5, 2026

feedbacks are addressed.

@tninja tninja merged commit 48d0a97 into main Apr 5, 2026
0 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants