Skip to content

Commit a4d94af

Browse files
committed
chore: improve server tests
- Use test defined tools and prompts - move tools specific tests to tools-test namespace
1 parent 239e7da commit a4d94af

File tree

2 files changed

+148
-109
lines changed

2 files changed

+148
-109
lines changed
Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,45 @@
11
(ns mcp-clj.mcp-server.tools-test
22
(:require
3-
[clojure.test :refer [deftest is testing]]
4-
[mcp-clj.mcp-server.tools :as tools]))
3+
[clojure.test :refer [deftest is testing use-fixtures]]
4+
[mcp-clj.mcp-server.core :as mcp]
5+
[mcp-clj.mcp-server.tools]))
56

6-
(deftest list-tools-test
7-
(testing "list tools"
8-
(let [result (tools/list-tools {})]
9-
(is (vector? (:tools result)))
10-
(is (pos? (count (:tools result))))
11-
(is (= "clj-eval" (-> result :tools first :name))))))
7+
(def ^:private ^:dynamic *server*)
128

13-
(deftest call-tool-test
14-
(testing "clj-eval tool"
15-
(testing "successful evaluation"
16-
(let [result (tools/call-tool
17-
{:name "clj-eval"
18-
:arguments {:code "(+ 1 2)"}})]
19-
(is (= [{:type "text"
20-
:text "3"}]
21-
(:content result)))
22-
(is (not (:isError result)))))
9+
(defn with-server
10+
"Test fixture for server lifecycle"
11+
[f]
12+
(let [server (mcp/create-server {:port 0 :threads 2 :queue-size 10})]
13+
(try
14+
(binding [*server* server]
15+
(f))
16+
(finally
17+
((:stop server))))))
2318

24-
(testing "evaluation error"
25-
(let [result (tools/call-tool
26-
{:name "clj-eval"
27-
:arguments {:code "(/ 1 0)"}})]
28-
(is (:isError result))
29-
(is (= 1 (count (:content result))))
30-
(is (= "text" (-> result :content first :type)))
31-
(is (string? (-> result :content first :text))))))
19+
(use-fixtures :each with-server)
3220

33-
(testing "unknown tool"
34-
(let [result (tools/call-tool {:name "unknown" :arguments {}})]
35-
(is (:isError result))
36-
(is (= [{:type "text"
37-
:text "Tool not found: unknown"}]
38-
(:content result))))))
21+
(deftest clj-eval-test
22+
(testing "clj eval"
23+
(let [{:keys [implementation]} (get @(:tool-registry *server*) "clj-eval")]
24+
25+
(testing "successful evaluation"
26+
(let [result (implementation {:code "(+ 1 2)"})]
27+
(is (= {:content [{:type "text"
28+
:text "3\n"}]}
29+
result))))
30+
31+
(testing "divide by zero error"
32+
(let [result (implementation {:code "(/ 1 0)"})]
33+
(is (:isError result))
34+
(is (= "text" (-> result :content first :type)))
35+
(is (.contains
36+
(-> result :content first :text)
37+
"Divide by zero"))))
38+
39+
(testing "invalid syntax"
40+
(let [result (implementation {:code "(/ 1 0"})]
41+
(is (:isError result))
42+
(is (= "text" (-> result :content first :type)))
43+
(is (.contains
44+
(-> result :content first :text)
45+
"EOF while reading")))))))

components/mcp-server/test/mcp_clj/mcp_server_test.clj

Lines changed: 109 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,56 @@
1111
LinkedBlockingQueue
1212
TimeUnit]))
1313

14-
(def valid-client-info
15-
{:clientInfo {:name "test-client" :version "1.0"}
16-
:protocolVersion @#'mcp/server-protocol-version
17-
:capabilities {:tools {:listChanged false}}})
14+
(def test-tool
15+
"Test tool for server testing"
16+
{:name "test-tool"
17+
:description "A test tool for server testing"
18+
:inputSchema {:type "object"
19+
:properties {"value" {:type "string"}}
20+
:required ["value"]}
21+
:implementation (fn [{:keys [value]}]
22+
{:content [{:type "text"
23+
:text (str "test-response:" value)}]})})
24+
25+
(def error-test-tool
26+
"Test tool that always returns an error"
27+
{:name "error-test-tool"
28+
:description "A test tool that always returns an error"
29+
:inputSchema {:type "object"
30+
:properties {"value" {:type "string"}}
31+
:required ["value"]}
32+
:implementation (fn [_]
33+
{:content [{:type "text"
34+
:text "test-error"}]
35+
:isError true})})
36+
37+
(def test-prompt
38+
{:name "test-prompt"
39+
:description "A test prompt for server testing"
40+
:messages [{:role "system"
41+
:content {:type "text"
42+
:text "Hello"}}
43+
{:role "user"
44+
:content {:type "text"
45+
:text "Please say {{reply}}"}}]
46+
:arguments [{:name "reply"
47+
:description "something"
48+
:required true}]})
49+
1850

1951
#_{:clj-kondo/ignore [:uninitialized-var]}
2052
(def ^:private ^:dynamic *server*)
2153

2254
(defn with-server
2355
"Test fixture for server lifecycle"
2456
[f]
25-
(let [server (mcp/create-server {:port 0 :threads 2 :queue-size 10})]
57+
(let [server (mcp/create-server
58+
{:port 0
59+
:threads 2
60+
:queue-size 10
61+
:tools {"test-tool" test-tool
62+
"error-test-tool" error-test-tool}
63+
:prompts {"test-prompt" test-prompt}})]
2664
(try
2765
(binding [*server* server]
2866
(f))
@@ -211,7 +249,7 @@
211249
(future-cancel f))))))
212250

213251
(deftest tools-test
214-
(testing "server lifecycle with SSE"
252+
(testing "A server with tools"
215253
(let [port (port)
216254
url (format "http://localhost:%d" port)
217255
queue (LinkedBlockingQueue.)
@@ -234,29 +272,35 @@
234272
[state' result] (run-plan state)]
235273
(is (= :passed result))
236274
(testing "tool interactions"
237-
(let [state (assoc
238-
state'
239-
:plan
240-
[{:action :send
241-
:msg (json-request
242-
"tools/list"
243-
{}
244-
0)}
245-
{:action :receive
246-
:data
247-
{:event "message"
248-
:data
249-
(json-result
250-
{:tools
251-
[{:name "clj-eval",
252-
:description
253-
"Evaluates a Clojure expression and returns the result",
254-
:inputSchema
255-
{:type "object",
256-
:properties {:code {:type "string"}},
257-
:required ["code"]}}]}
258-
{}
259-
0)}}])
275+
(let [state
276+
(assoc
277+
state'
278+
:plan
279+
[{:action :send
280+
:msg (json-request
281+
"tools/list"
282+
{}
283+
0)}
284+
{:action :receive
285+
:data
286+
{:event "message"
287+
:data
288+
(json-result
289+
{:tools
290+
[{:name "test-tool",
291+
:description "A test tool for server testing",
292+
:inputSchema
293+
{:type "object",
294+
:properties {:value {:type "string"}},
295+
:required ["value"]}}
296+
{:name "error-test-tool",
297+
:description "A test tool that always returns an error",
298+
:inputSchema
299+
{:type "object",
300+
:properties {:value {:type "string"}},
301+
:required ["value"]}}]}
302+
{}
303+
0)}}])
260304
[state' result] (run-plan state)
261305
_ (testing "tools/list"
262306
(is (= :passed result) (pr-str state))
@@ -267,31 +311,9 @@
267311
[{:action :send
268312
:msg (json-request
269313
"tools/call"
270-
{:name "clj-eval"
271-
:arguments {:code "(+ 1 2)"}}
272-
0)}
273-
{:action :receive
274-
:data {:event "message"
275-
:data
276-
(json-result
277-
{:content
278-
[{:type "text"
279-
:text "3"}]}
280-
nil
281-
0)}}])
282-
[state' result] (run-plan state)
283-
_ (testing "successful tools/call"
284-
(is (= :passed result))
285-
(is (not (:failed state'))))
286-
state (assoc
287-
state'
288-
:plan
289-
[{:action :send
290-
:msg (json-request
291-
"tools/call"
292-
{:name "clj-eval"
314+
{:name "test-tool"
293315
:arguments
294-
{:code "(/ 1 0)"}}
316+
{:value "me"}}
295317
0)}
296318
{:action :receive
297319
:data
@@ -300,12 +322,12 @@
300322
(json-result
301323
{:content
302324
[{:type "text"
303-
:text "Error: Divide by zero"}]
304-
:isError true}
325+
:text "test-response:me"}]}
305326
nil
306327
0)}}])
307-
[state' result] (run-plan state)
308-
_ (testing "tools/call with eval error"
328+
[state' result] (testing "makes a successful tools/call"
329+
(run-plan state))
330+
_ (testing "makes a successful tools/call"
309331
(is (= :passed result))
310332
(is (not (:failed state'))))
311333
state (assoc
@@ -314,22 +336,24 @@
314336
[{:action :send
315337
:msg (json-request
316338
"tools/call"
317-
{:name "clj-eval"
339+
{:name "error-test-tool"
318340
:arguments
319-
{:code "(/ 1 0"}})}
341+
{:value "me"}}
342+
0)}
320343
{:action :receive
321344
:data
322345
{:event "message"
323346
:data
324347
(json-result
325348
{:content
326349
[{:type "text"
327-
:text "Error: EOF while reading"}]
350+
:text "test-error"}]
328351
:isError true}
329352
nil
330353
0)}}])
331-
[state' result] (run-plan state)
332-
_ (testing "tools/call with invalid clojure"
354+
[state' result] (testing "tools/call with an error"
355+
(run-plan state))
356+
_ (testing "tools/call with an error"
333357
(is (= :passed result))
334358
(is (not (:failed state'))))
335359
state (assoc
@@ -567,21 +591,29 @@
567591
[state' result] (run-plan state)]
568592
(is (= :passed result))
569593
(testing "prompt interactions"
570-
(let [state (assoc
571-
state'
572-
:plan
573-
[{:action :send
574-
:msg (json-request
575-
"prompts/list"
576-
{}
577-
0)}
578-
{:action :receive
579-
:data
580-
{:event "message"
581-
:data (json-result
582-
{:prompts []}
583-
nil
584-
0)}}])
594+
(let [state
595+
(assoc
596+
state'
597+
:plan
598+
[{:action :send
599+
:msg (json-request
600+
"prompts/list"
601+
{}
602+
0)}
603+
{:action :receive
604+
:data
605+
{:event "message"
606+
:data
607+
(json-result
608+
{:prompts
609+
[{:name "test-prompt",
610+
:description "A test prompt for server testing",
611+
:arguments
612+
[{:name "reply"
613+
:description "something"
614+
:required true}]}]}
615+
nil
616+
0)}}])
585617
[state' result] (run-plan state)
586618
_ (testing "prompts/list"
587619
(is (= :passed result)))]))))

0 commit comments

Comments
 (0)