Skip to content

Commit 40b88ac

Browse files
authored
Merge pull request #17 from rhblind/14-server-advertises-mcp-capabilities-it-doesnt-implement
fix(capabilities): Remove unimplemented capabilities to prevent client from hanging
2 parents 1947617 + 9df7846 commit 40b88ac

4 files changed

Lines changed: 48 additions & 51 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
### Fixed
11+
- `mcp-server-capabilities`: the server advertised `tools.listChanged`, `resources.subscribe`, `resources.listChanged`, and `prompts.listChanged` despite never emitting any of the corresponding notifications (issue #14). Clients relying on these could wait for notifications that never arrived. Capabilities now advertise only `tools` as an empty object (`{"tools":{}}`), indicating tools exist without optional sub-capabilities. The unused `resources/list`, `resources/read`, and `prompts/list` stub handlers have been removed; requests for these methods now correctly return JSON-RPC error `-32601` (method not found)
12+
813
## [0.6.0] - 2026-03-31
914

1015
### Fixed

mcp-server.el

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,14 @@ Defaults to `user-emacs-directory'. Users can customize this with:
141141
:group 'mcp-server)
142142

143143
(defvar mcp-server-capabilities
144-
'((tools . ((listChanged . t)))
145-
(resources . ((subscribe . t) (listChanged . t)))
146-
(prompts . ((listChanged . t))))
147-
"Capabilities supported by this MCP server.")
144+
`((tools . ,(make-hash-table :test 'equal)))
145+
"Capabilities supported by this MCP server.
146+
Only advertise capabilities that are actually implemented. Empty
147+
hash-table for `tools' serializes to `{}', indicating tools exist
148+
without optional sub-capabilities. Add `listChanged' back when a
149+
notification is wired up on tool register/unregister/filter changes.
150+
Add `resources' and `prompts' entries only when the corresponding
151+
handlers and notifications are implemented.")
148152

149153
;;; Logging
150154

@@ -293,17 +297,6 @@ Uses `catch'/`throw' for early exit after successful response send."
293297
(mcp-server--debug "tools/call handler returned: %S" result)
294298
result))
295299

296-
;; Resources (future implementation)
297-
((string= method "resources/list")
298-
(mcp-server--handle-resources-list id params client-id))
299-
300-
((string= method "resources/read")
301-
(mcp-server--handle-resources-read id params client-id))
302-
303-
;; Prompts (future implementation)
304-
((string= method "prompts/list")
305-
(mcp-server--handle-prompts-list id params client-id))
306-
307300
;; Notifications
308301
((string= method "notifications/initialized")
309302
(mcp-server--handle-initialized))
@@ -399,31 +392,6 @@ Uses `catch'/`throw' for early exit after successful response send."
399392
(text . "Tool execution failed"))))
400393
(isError . t))))))
401394

402-
(defun mcp-server--handle-resources-list (id params client-id)
403-
"Handle resources/list request with ID and PARAMS from CLIENT-ID.
404-
Returns empty list as resources feature is planned for future implementation."
405-
(mcp-server--debug "Resources list request from %s: %s" client-id params)
406-
407-
(mcp-server--send-response
408-
client-id id
409-
'((resources . []))))
410-
411-
(defun mcp-server--handle-resources-read (id params client-id)
412-
"Handle resources/read request with ID and PARAMS from CLIENT-ID.
413-
Returns error as resources feature is planned for future implementation."
414-
(mcp-server--debug "Resources read request from %s: %s" client-id params)
415-
416-
(mcp-server--send-error client-id id -32002 "Resource not found" params))
417-
418-
(defun mcp-server--handle-prompts-list (id params client-id)
419-
"Handle prompts/list request with ID and PARAMS from CLIENT-ID.
420-
Returns empty list as prompts feature is planned for future implementation."
421-
(mcp-server--debug "Prompts list request from %s: %s" client-id params)
422-
423-
(mcp-server--send-response
424-
client-id id
425-
'((prompts . []))))
426-
427395
(defun mcp-server--send-error (client-id id code message &optional data)
428396
"Send error response to CLIENT-ID with ID, CODE, MESSAGE and optional DATA."
429397
(let ((error-response `((jsonrpc . "2.0")

test/unit/test-mcp-basic.el

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,11 @@
133133
;;; Configuration Pattern Tests
134134

135135
(ert-deftest mcp-test-server-capabilities-structure ()
136-
"Test server capabilities structure."
137-
(let ((capabilities '((tools . ((listChanged . t)))
138-
(resources . ((subscribe . t) (listChanged . t)))
139-
(prompts . ((listChanged . t))))))
140-
(should (alist-get 'tools capabilities))
141-
(should (alist-get 'resources capabilities))
142-
(should (alist-get 'prompts capabilities))))
136+
"Minimum viable capabilities advertise tools only (issue #14)."
137+
(let ((capabilities `((tools . ,(make-hash-table :test 'equal)))))
138+
(should (assq 'tools capabilities))
139+
(should-not (assq 'resources capabilities))
140+
(should-not (assq 'prompts capabilities))))
143141

144142
(ert-deftest mcp-test-client-info-structure ()
145143
"Test client info structure."

test/unit/test-mcp-server-full.el

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,37 @@
3838
(should (boundp 'mcp-server-default-transport)))
3939

4040
(ert-deftest mcp-test-server-capabilities ()
41-
"Test that server capabilities are defined."
41+
"Server must only advertise capabilities it actually implements.
42+
Issue #14: advertising resources/prompts/listChanged we don't emit
43+
causes clients to wait forever or otherwise misbehave."
4244
(should (boundp 'mcp-server-capabilities))
43-
(should (alist-get 'tools mcp-server-capabilities))
44-
(should (alist-get 'resources mcp-server-capabilities))
45-
(should (alist-get 'prompts mcp-server-capabilities)))
45+
;; Tools is implemented, must be advertised
46+
(should (assq 'tools mcp-server-capabilities))
47+
;; Resources not implemented (tracked in #12) - must not be advertised
48+
(should-not (assq 'resources mcp-server-capabilities))
49+
;; Prompts not implemented - must not be advertised
50+
(should-not (assq 'prompts mcp-server-capabilities))
51+
;; tools.listChanged notification is never emitted - must not be claimed
52+
(let ((tools-cap (alist-get 'tools mcp-server-capabilities)))
53+
(should (or (null tools-cap)
54+
(and (hash-table-p tools-cap)
55+
(zerop (hash-table-count tools-cap)))))))
56+
57+
(ert-deftest mcp-test-server-capabilities-serialize-empty-tools-object ()
58+
"Capabilities must serialize as {\"tools\":{}} for MCP spec compliance.
59+
Empty object indicates tools exist without optional sub-capabilities."
60+
(let* ((converted (mcp-server-transport--alist-to-json mcp-server-capabilities))
61+
(json-str (json-serialize converted)))
62+
(should (string= json-str "{\"tools\":{}}"))))
63+
64+
(ert-deftest mcp-test-no-unimplemented-method-handlers ()
65+
"Handlers for unsupported methods must not exist.
66+
Issue #14: resources and prompts handlers were stubs that lied about
67+
support; removing them lets the method router return -32601 (method
68+
not found) which is the correct response."
69+
(should-not (fboundp 'mcp-server--handle-resources-list))
70+
(should-not (fboundp 'mcp-server--handle-resources-read))
71+
(should-not (fboundp 'mcp-server--handle-prompts-list)))
4672

4773
;;; Server Function Tests
4874

0 commit comments

Comments
 (0)