Skip to content

Commit e420e71

Browse files
committed
Support enable/disable tool servers
1 parent 1e65b92 commit e420e71

File tree

7 files changed

+130
-27
lines changed

7 files changed

+130
-27
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Unreleased
44

5+
- Support enable/disable tool servers.
6+
- Bump mcp java sdk to 0.11.0.
7+
58
## 0.13.1
69

710
- Improve ollama model listing getting capabilities, avoiding change ollama config for different models.

deps.edn

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
org.clojure/core.async {:mvn/version "1.8.741"}
44
org.babashka/cli {:mvn/version "0.8.65"}
55
com.github.clojure-lsp/lsp4clj {:mvn/version "1.13.1"}
6-
io.modelcontextprotocol.sdk/mcp {:mvn/version "0.10.0"}
6+
io.modelcontextprotocol.sdk/mcp {:mvn/version "0.11.0"}
77
borkdude/dynaload {:mvn/version "0.3.5"}
88
babashka/fs {:mvn/version "0.5.26"}
99
hato/hato {:mvn/version "1.0.0"}

docs/protocol.md

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -845,14 +845,11 @@ A server notification about a tool status update like a MCP or native tool.
845845
This is useful for clients present to user the list of configured tools/MCPs,
846846
their status and available tools and actions.
847847

848-
_Request:_
848+
_Notification:_
849849

850850
* method: `tool/serverUpdated`
851851
* params: `ToolServerUpdatedParams` defined as follows:
852852

853-
_Response:_
854-
855-
856853
```typescript
857854
type ToolServerUpdatedParams = EcaServerUpdatedParams | MCPServerUpdatedParams;
858855

@@ -921,6 +918,44 @@ interface ServerTool {
921918
}
922919
```
923920

921+
### Stop MCP server (➡️)
922+
923+
A client notification for server to stop a MCP server, stopping the process.
924+
Updates its status via `tool/serverUpdated` notification.
925+
926+
_Notification:_
927+
928+
* method: `mcp/stopServer`
929+
* params: `MCPStopServerParams` defined as follows:
930+
931+
```typescript
932+
interface MCPStopServerParams {
933+
/**
934+
* The MCP server name.
935+
*/
936+
name: string;
937+
}
938+
```
939+
940+
### Start MCP server (➡️)
941+
942+
A client notification for server to start a stopped MCP server, starting the process again.
943+
Updates its status via `tool/serverUpdated` notification.
944+
945+
_Notification:_
946+
947+
* method: `mcp/startServer`
948+
* params: `MCPStartServerParams` defined as follows:
949+
950+
```typescript
951+
interface MCPStartServerParams {
952+
/**
953+
* The server name.
954+
*/
955+
name: string;
956+
}
957+
```
958+
924959
### Add MCP (↩️)
925960

926961
Soon

src/eca/features/tools.clj

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,29 @@
6969
(update :tools #(mapv with-tool-status %)))))}
7070
db*
7171
config)))
72+
73+
(defn stop-server! [name db* messenger config]
74+
(let [disabled-tools (set (get-in config [:disabledTools] []))
75+
with-tool-status (fn [tool]
76+
(assoc-some tool :disabled (contains? disabled-tools (:name tool))))]
77+
(f.mcp/stop-server!
78+
name
79+
db*
80+
config
81+
{:on-server-updated (fn [server]
82+
(messenger/tool-server-updated messenger (-> server
83+
(assoc :type :mcp)
84+
(update :tools #(mapv with-tool-status %)))))})))
85+
86+
(defn start-server! [name db* messenger config]
87+
(let [disabled-tools (set (get-in config [:disabledTools] []))
88+
with-tool-status (fn [tool]
89+
(assoc-some tool :disabled (contains? disabled-tools (:name tool))))]
90+
(f.mcp/start-server!
91+
name
92+
db*
93+
config
94+
{:on-server-updated (fn [server]
95+
(messenger/tool-server-updated messenger (-> server
96+
(assoc :type :mcp)
97+
(update :tools #(mapv with-tool-status %)))))})))

src/eca/features/tools/mcp.clj

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -102,29 +102,52 @@
102102
(.arguments prompt-client))})
103103
(.prompts (.listPrompts client))))
104104

105-
(defn initialize-servers-async! [{:keys [on-server-updated]} db* config]
106-
(let [workspaces (:workspace-folders @db*)
107-
db @db*
105+
(defn ^:private initialize-server! [name db* config on-server-updated]
106+
(let [db @db*
107+
workspaces (:workspace-folders @db*)
108+
server-config (get-in config [:mcpServers (keyword name)])
108109
obj-mapper (ObjectMapper.)]
109-
(doseq [[name server-config] (:mcpServers config)]
110-
(when-not (get-in db [:mcp-clients name])
111-
(if (get server-config :disabled false)
112-
(on-server-updated (->server name server-config :disabled db))
113-
(future
114-
(try
115-
(let [transport (->transport server-config workspaces)
116-
client (->client transport config)]
117-
(on-server-updated (->server name server-config :starting db))
118-
(swap! db* assoc-in [:mcp-clients name] {:client client})
119-
(doseq [{:keys [name uri]} workspaces]
120-
(.addRoot client (McpSchema$Root. uri name)))
121-
(.initialize client)
122-
(swap! db* assoc-in [:mcp-clients name :tools] (list-server-tools obj-mapper client))
123-
(swap! db* assoc-in [:mcp-clients name :prompts] (list-server-prompts client))
124-
(on-server-updated (->server name server-config :running @db*)))
125-
(catch Exception e
126-
(logger/warn logger-tag (format "Could not initialize MCP server %s. Error: %s" name (.getMessage e)))
127-
(on-server-updated (->server name server-config :failed db))))))))))
110+
(try
111+
(let [transport (->transport server-config workspaces)
112+
client (->client transport config)]
113+
(on-server-updated (->server name server-config :starting db))
114+
(swap! db* assoc-in [:mcp-clients name] {:client client})
115+
(doseq [{:keys [name uri]} workspaces]
116+
(.addRoot client (McpSchema$Root. uri name)))
117+
(.initialize client)
118+
(swap! db* assoc-in [:mcp-clients name :tools] (list-server-tools obj-mapper client))
119+
(swap! db* assoc-in [:mcp-clients name :prompts] (list-server-prompts client))
120+
(on-server-updated (->server name server-config :running @db*)))
121+
(catch Exception e
122+
(logger/warn logger-tag (format "Could not initialize MCP server %s. Error: %s" name (.getMessage e)))
123+
(on-server-updated (->server name server-config :failed db))))))
124+
125+
(defn initialize-servers-async! [{:keys [on-server-updated]} db* config]
126+
(let [db @db*]
127+
(doseq [[name-kwd server-config] (:mcpServers config)]
128+
(let [name (name name-kwd)]
129+
(when-not (get-in db [:mcp-clients name])
130+
(if (get server-config :disabled false)
131+
(on-server-updated (->server name server-config :disabled db))
132+
(future
133+
(initialize-server! name db* config on-server-updated))))))))
134+
135+
(defn stop-server! [name db* config {:keys [on-server-updated]}]
136+
(when-let [{:keys [client]} (get-in @db* [:mcp-clients name])]
137+
(let [server-config (get-in config [:mcpServers (keyword name)])]
138+
(on-server-updated (->server name server-config :stopping @db*))
139+
(.closeGracefully ^McpSyncClient client)
140+
(swap! db* update :mcp-clients dissoc name)
141+
(on-server-updated (->server name server-config :stopped @db*))
142+
(logger/info logger-tag (format "Stopped MCP server %s" name)))))
143+
144+
(defn start-server! [name db* config {:keys [on-server-updated]}]
145+
(when-let [server-config (get-in config [:mcpServers (keyword name)])]
146+
(if (get server-config :disabled false)
147+
(logger/warn logger-tag (format "MCP server %s is disabled and cannot be started" name))
148+
(do
149+
(initialize-server! name db* config on-server-updated)
150+
(logger/info logger-tag (format "Started MCP server %s" name))))))
128151

129152
(defn all-tools [db]
130153
(into []

src/eca/handlers.clj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,13 @@
9898
:eca/chat-delete
9999
(f.chat/delete-chat params db*)
100100
{}))
101+
102+
(defn mcp-stop-server [{:keys [db* messenger config]} params]
103+
(logger/logging-task
104+
:eca/mcp-stop-server
105+
(f.tools/stop-server! (:name params) db* messenger config)))
106+
107+
(defn mcp-start-server [{:keys [db* messenger config]} params]
108+
(logger/logging-task
109+
:eca/mcp-start-server
110+
(f.tools/start-server! (:name params) db* messenger config)))

src/eca/server.clj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@
6262
(defmethod lsp.server/receive-request "chat/delete" [_ components params]
6363
(handlers/chat-delete (with-config components) params))
6464

65+
(defmethod lsp.server/receive-notification "mcp/stopServer" [_ components params]
66+
(handlers/mcp-stop-server (with-config components) params))
67+
68+
(defmethod lsp.server/receive-notification "mcp/startServer" [_ components params]
69+
(handlers/mcp-start-server (with-config components) params))
70+
6571
(defn ^:private monitor-server-logs [log-ch]
6672
;; NOTE: if this were moved to `initialize`, after timbre has been configured,
6773
;; the server's startup logs and traces would appear in the regular log file

0 commit comments

Comments
 (0)