Skip to content

Commit e9b8587

Browse files
committed
Add mcp/listServers request
1 parent fd32d63 commit e9b8587

File tree

8 files changed

+144
-7
lines changed

8 files changed

+144
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Allow comments on `json` configs.
66
- Improve MCP tool call feedback.
77
- Add support for env vars in mcp configs.
8+
- Add `mcp/listServers` request.
89

910
## 0.0.4
1011

docs/protocol.md

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,9 +645,75 @@ Soon
645645

646646
Soon
647647

648+
## Configuration
649+
648650
### List MCPs (↩️)
649651

650-
Soon
652+
A client request listing configured MCP servers configured by server and its status.
653+
654+
_Request:_
655+
656+
* method: `mcp/listServers`
657+
* params: `ListMCPServersParams` defined as follows:
658+
659+
```typescript
660+
interface ListMCPServersParams {}
661+
```
662+
663+
_Response:_
664+
665+
```typescript
666+
interface ListMCPServersResponse {
667+
/**
668+
* The list of servers.
669+
*/
670+
servers: MCPServer[];
671+
}
672+
673+
interface MCPServer {
674+
/**
675+
* The server name.
676+
*/
677+
name: string;
678+
679+
/**
680+
* The command to start this server.
681+
*/
682+
command: string;
683+
684+
/**
685+
* The arguments to start this server.
686+
*/
687+
args: string[];
688+
689+
/**
690+
* The status of the server.
691+
*/
692+
status: 'running' | 'starting' | 'stopped' | 'disabled';
693+
694+
/**
695+
* The tools supported by this mcp server if not disabled.
696+
*/
697+
tools?: MCPServerTool[];
698+
}
699+
700+
interface MCPServerTool {
701+
/**
702+
* The MCP server tool name.
703+
*/
704+
name: string;
705+
706+
/**
707+
* The MCP server tool description.
708+
*/
709+
description: string;
710+
711+
/**
712+
* The MCP server tool parameters.
713+
*/
714+
parameters: any;
715+
}
716+
```
651717

652718
### Add MCP (↩️)
653719

src/eca/config.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
:anthropicApiKey nil
2121
:rules []
2222
:mcpTimeoutSeconds 60
23-
:mcpServers []
23+
:mcpServers {}
2424
:ollama {:host "http://localhost"
2525
:port 11434
2626
:useTools false}

src/eca/features/chat.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
:state :running
7070
:text "Finding MCPs"}})
7171
(f.mcp/cache-tools! db*))
72-
(f.mcp/list-tools @db*))
72+
(f.mcp/all-tools @db*))
7373

7474
(defn prompt
7575
[{:keys [message model behavior contexts chat-id request-id]}

src/eca/features/mcp.clj

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,12 @@
6767
(get server-config :disabled false))
6868
(let [transport (->transport server-config workspaces)
6969
client (->client transport config)]
70-
(swap! db* assoc-in [:mcp-clients name :client] client)
70+
(swap! db* assoc-in [:mcp-clients name] {:client client
71+
:status :starting})
7172
(doseq [{:keys [name uri]} workspaces]
7273
(.addRoot client (McpSchema$Root. uri name)))
73-
(.initialize client)))
74+
(.initialize client)
75+
(swap! db* update-in [:mcp-clients name] dissoc :status)))
7476
(catch Exception e
7577
(logger/warn logger-tag (format "Could not initialize MCP server %s. Error: %s" name (.getMessage e)))
7678
(on-error name e))))))
@@ -87,12 +89,12 @@
8789
:mcp-name name
8890
:mcp-client client
8991
:description (.description tool-client)
90-
;; We convert to json to then read so we have the clojrue map
92+
;; We convert to json to then read so we have the clojure map
9193
;; TODO avoid this converting to clojure map directly
9294
:parameters (json/parse-string (.writeValueAsString obj-mapper (.inputSchema tool-client)) true)}]
9395
(swap! db* assoc-in [:mcp-tools (:name tool)] tool)))))))
9496

95-
(defn list-tools [db]
97+
(defn all-tools [db]
9698
(vals (:mcp-tools db)))
9799

98100
(defn call-tool! [^String name ^Map arguments db]
@@ -114,3 +116,26 @@
114116
(swap! db* assoc
115117
:mcp-clients {}
116118
:mcp-tools {}))
119+
120+
(defn all-servers [db config]
121+
(reduce
122+
(fn [servers [name server]]
123+
(let [{:keys [status client]} (get-in db [:mcp-clients name])
124+
tools (->> (vals (:mcp-tools db))
125+
(filterv #(= name (:mcp-name %)))
126+
(map (fn [mcp-tool]
127+
{:name (:name mcp-tool)
128+
:description (:description mcp-tool)
129+
:parameters (:parameters mcp-tool)})))]
130+
(conj servers (cond-> {:name (clojure.core/name name)
131+
:command (:command server)
132+
:args (:args server)
133+
:status (or status
134+
(if client
135+
(if (.isInitialized ^McpSyncClient client)
136+
:running
137+
:stopped)
138+
:disabled))}
139+
(seq tools) (assoc :tools tools)))))
140+
[]
141+
(:mcpServers config)))

src/eca/handlers.clj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,8 @@
6464
(logger/logging-task
6565
:eca/chat-query-context
6666
(f.chat/query-context params db*)))
67+
68+
(defn mcp-list-servers [{:keys [db*]} _params]
69+
(logger/logging-task
70+
:eca/mcp-list-servers
71+
{:servers (f.mcp/all-servers @db* (config/all @db*))}))

src/eca/server.clj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
(defmethod lsp.server/receive-request "chat/queryContext" [_ components params]
4646
(handlers/chat-query-context components params))
4747

48+
(defmethod lsp.server/receive-request "mcp/listServers" [_ components params]
49+
(handlers/mcp-list-servers components params))
50+
4851
(defn ^:private monitor-server-logs [log-ch]
4952
;; NOTE: if this were moved to `initialize`, after timbre has been configured,
5053
;; the server's startup logs and traces would appear in the regular log file

test/eca/features/mcp_test.clj

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
(ns eca.features.mcp-test
2+
(:require
3+
[clojure.test :refer [deftest is testing]]
4+
[eca.features.mcp :as f.mcp]
5+
[matcher-combinators.test :refer [match?]]))
6+
7+
(deftest all-servers-test
8+
(testing "disabled servers"
9+
(is (match? [{:name "clojureMCP"
10+
:command "clojure"
11+
:args ["-X:mcp" ":port" "7888"]
12+
:status :disabled}]
13+
(f.mcp/all-servers
14+
{}
15+
{:mcpServers
16+
{:clojureMCP {:command "clojure"
17+
:args ["-X:mcp" ":port" "7888"]
18+
:disabled true}}}))))
19+
(testing "running servers"
20+
(is (match? [{:name "clojureMCP"
21+
:command "clojure"
22+
:args ["-X:mcp" ":port" "7888"]
23+
:status :running
24+
:tools [{:name "eval"
25+
:description "Evaluate Clojure code"
26+
:parameters {:type "object"
27+
:properties {:code {:type "string"}}}}]}]
28+
(f.mcp/all-servers
29+
{:mcp-clients {:clojureMCP {:status :running}}
30+
:mcp-tools {"eval" {:name "eval"
31+
:mcp-name :clojureMCP
32+
:description "Evaluate Clojure code"
33+
:parameters {:type "object"
34+
:properties {:code {:type "string"}}}}}}
35+
{:mcpServers
36+
{:clojureMCP {:command "clojure"
37+
:args ["-X:mcp" ":port" "7888"]}}})))))

0 commit comments

Comments
 (0)