Sessions are the core abstraction in the MCP server - they represent client connections and contain all the state needed to handle requests.
Understanding the difference between template and live sessions is crucial for proper server configuration.
A template session is created by calling server/make-session. This session:
Live sessions are created when clients connect:
When you modify a template session, the changes only affect future live sessions created from that template. Existing live sessions remain unchanged.
When you modify a live session, the changes only affect that specific client connection.
Planning Your Modifications:
This design allows you to provide different tools, prompts, and resources for different clients if needed.
The best time to add your application dependencies is during template session creation:
(server/make-session server-info
(json/serde {})
;; Add your dependencies here
{:db-pool db-pool
:email-sender email-sender
:duct-system system
:config config})
Your handlers can access these dependencies through the exchange object:
(defn my-tool-handler [exchange arguments]
(let [session (core/get-session exchange)
db-pool (:db-pool @session)]
;; Use db-pool for database operations
...))
Stream transport uses only one session per server instance. Because
streams/run mutates the atom you pass in, you already hold a reference to
the live session — there is no separate "get session" step.
(server/start-server-on-streams session-template System/in System/out {})
If you only need per-request access to the session, use the exchange object
inside your handlers — no need to hold a reference externally.
Because streams/run operates on the atom you supply, retaining that atom
gives you full ongoing control over the running session — add or remove tools,
update resources, inspect state — at any time while run is blocking:
(require '[org.clojars.roklenarcic.mcp-server.server.streams :as streams])
(def my-session (atom @session-template))
;; Start the server in a separate thread; my-session is the live session.
(future (streams/run my-session System/in System/out {}))
;; Later, from any thread:
(server/add-tool my-session new-tool)
(server/remove-tool my-session "old-tool")
If you want to preserve session-template unchanged for future connections,
pass a fresh copy: (atom @session-template).
For the HTTP transport, every connected client has its own session atom.
The Sessions protocol (returned as :sessions from http/ring-handler)
provides access to all of them:
(require '[org.clojars.roklenarcic.mcp-server.server.http :as http])
(let [{:keys [handler sessions]} (http/ring-handler session-template opts)]
;; Broadcast a notification to every connected client:
(doseq [ss (http/all-sessions sessions)]
(server/add-tool (:session ss) new-tool)))
Monitor when the client's filesystem roots change:
(server/set-roots-changed-callback session
(fn [exchange]
(println "Client roots changed")
;; Update available resources based on new roots
))
Each session operates independently:
;; This affects only session-1
(server/add-tool session-1 weather-tool)
;; This affects only session-2
(server/add-tool session-2 calendar-tool)
;; session-1 has weather-tool but not calendar-tool
;; session-2 has calendar-tool but not weather-tool
This isolation enables:
For the streams transport, create a fresh copy of the template per connection, add user-specific tools before starting, then hold the atom for ongoing access:
(defn start-user-session [template-session user-id input-stream output-stream]
(let [session (atom @template-session)
user-tools (load-user-tools user-id)]
;; Customise before the connection starts.
(doseq [tool user-tools]
(server/add-tool session tool))
(swap! session assoc :user-id user-id)
;; Blocks until EOF/interrupt; session atom remains live throughout.
(future (streams/run session input-stream output-stream {}))
session))
For the HTTP transport, customise sessions from within the initialize handler
using the exchange object, or iterate http/all-sessions externally.
This pattern allows you to create personalised MCP servers for different users while sharing common infrastructure.
Can you improve this documentation? These fine people already did:
Rok Lenarcic & Rok LenarčičEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |