|
14 | 14 | McpSchema$CallToolRequest |
15 | 15 | McpSchema$ClientCapabilities |
16 | 16 | McpSchema$Content |
| 17 | + McpSchema$GetPromptRequest |
| 18 | + McpSchema$Prompt |
| 19 | + McpSchema$PromptArgument |
| 20 | + McpSchema$PromptMessage |
17 | 21 | McpSchema$Root |
18 | 22 | McpSchema$TextContent |
19 | 23 | McpSchema$Tool |
|
72 | 76 | :tools (get-in db [:mcp-clients mcp-name :tools]) |
73 | 77 | :status status}) |
74 | 78 |
|
| 79 | +(defn ^:private list-server-tools [^ObjectMapper obj-mapper ^McpSyncClient client] |
| 80 | + (mapv (fn [^McpSchema$Tool tool-client] |
| 81 | + {:name (.name tool-client) |
| 82 | + :description (.description tool-client) |
| 83 | + ;; We convert to json to then read so we have a clojure map |
| 84 | + ;; TODO avoid this converting to clojure map directly |
| 85 | + :parameters (json/parse-string (.writeValueAsString obj-mapper (.inputSchema tool-client)) true)}) |
| 86 | + (.tools (.listTools client)))) |
| 87 | + |
| 88 | +(defn ^:private list-server-prompts [^McpSyncClient client] |
| 89 | + (mapv (fn [^McpSchema$Prompt prompt-client] |
| 90 | + {:name (.name prompt-client) |
| 91 | + :description (.description prompt-client) |
| 92 | + :arguments (mapv (fn [^McpSchema$PromptArgument content] |
| 93 | + {:name (.name content) |
| 94 | + :description (.description content) |
| 95 | + :required (.required content)}) |
| 96 | + (.arguments prompt-client))}) |
| 97 | + (.prompts (.listPrompts client)))) |
| 98 | + |
75 | 99 | (defn initialize-servers-async! [{:keys [on-server-updated]} db* config] |
76 | 100 | (let [workspaces (:workspace-folders @db*) |
77 | 101 | db @db* |
|
89 | 113 | (doseq [{:keys [name uri]} workspaces] |
90 | 114 | (.addRoot client (McpSchema$Root. uri name))) |
91 | 115 | (.initialize client) |
92 | | - (let [tools (mapv (fn [^McpSchema$Tool tool-client] |
93 | | - {:name (.name tool-client) |
94 | | - :description (.description tool-client) |
95 | | - ;; We convert to json to then read so we have a clojure map |
96 | | - ;; TODO avoid this converting to clojure map directly |
97 | | - :parameters (json/parse-string (.writeValueAsString obj-mapper (.inputSchema tool-client)) true)}) |
98 | | - (.tools (.listTools client)))] |
99 | | - (swap! db* assoc-in [:mcp-clients name :tools] tools)) |
| 116 | + (swap! db* assoc-in [:mcp-clients name :tools] (list-server-tools obj-mapper client)) |
| 117 | + (swap! db* assoc-in [:mcp-clients name :prompts] (list-server-prompts client)) |
100 | 118 | (on-server-updated (->server name server-config :running @db*))) |
101 | 119 | (catch Exception e |
102 | 120 | (logger/warn logger-tag (format "Could not initialize MCP server %s. Error: %s" name (.getMessage e))) |
|
118 | 136 | (let [result (.callTool ^McpSyncClient mcp-client |
119 | 137 | (McpSchema$CallToolRequest. name arguments))] |
120 | 138 | (logger/debug logger-tag "ToolCall result: " result) |
121 | | - {:contents (map (fn [content] |
| 139 | + {:error (.isError result) |
| 140 | + :contents (map (fn [content] |
122 | 141 | (case (.type ^McpSchema$Content content) |
123 | 142 | "text" {:type :text |
124 | | - :error (.isError result) |
125 | 143 | :content (.text ^McpSchema$TextContent content)} |
126 | 144 | nil)) |
127 | 145 | (.content result))}) |
128 | 146 | (catch Exception e |
129 | | - {:contents [{:type :text |
130 | | - :error true |
| 147 | + {:error true |
| 148 | + :contents [{:type :text |
131 | 149 | :content (.getMessage e)}]})))) |
132 | 150 |
|
| 151 | +(defn get-prompt! [^String name ^Map arguments db] |
| 152 | + (let [mcp-client (->> (vals (:mcp-clients db)) |
| 153 | + (keep (fn [{:keys [client prompts]}] |
| 154 | + (when (some #(= name (:name %)) prompts) |
| 155 | + client))) |
| 156 | + first) |
| 157 | + prompt (.getPrompt ^McpSyncClient mcp-client (McpSchema$GetPromptRequest. name arguments))] |
| 158 | + {:description (.description prompt) |
| 159 | + :messages (mapv (fn [^McpSchema$PromptMessage message] |
| 160 | + {:role (.role message) |
| 161 | + :content (.content message)}) |
| 162 | + (.messages prompt))})) |
| 163 | + |
133 | 164 | (defn shutdown! [db*] |
134 | 165 | (doseq [[_name {:keys [_client]}] (:mcp-clients @db*)] |
135 | 166 | ;; TODO NoClassDefFound being thrown for some reason |
136 | 167 | #_(.closeGracefully ^McpSyncClient client)) |
137 | 168 | (swap! db* assoc :mcp-clients {})) |
| 169 | + |
| 170 | +(comment |
| 171 | + (def db* (atom user/*db*)) |
| 172 | + (user/with-workspace-root "/home/greg/dev/eca/eca" |
| 173 | + (initialize-servers-async! {:on-server-updated println} |
| 174 | + db* |
| 175 | + {:mcpTimeoutSeconds 10 |
| 176 | + :mcpServers {"fetch" {:command "docker" :args ["run" "-i" "--rm" "mcp/fetch"]}}})) |
| 177 | + (:prompts (second (first (:mcp-clients @db*)))) |
| 178 | + (get-prompt! "fetch" {"url" "https://eca.dev"} @db*)) |
0 commit comments