# RokLenarcic Mcp Server

> MCP Server library

- **Type:** MCP server
- **Install:** `agentstack add mcp-roklenarcic-mcp-server`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [RokLenarcic](https://agentstack.voostack.com/s/roklenarcic)
- **Installs:** 0
- **Category:** [Integrations](https://agentstack.voostack.com/c/integrations)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [RokLenarcic](https://github.com/RokLenarcic)
- **Source:** https://github.com/RokLenarcic/mcp-server

## Install

```sh
agentstack add mcp-roklenarcic-mcp-server
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# MCP Server

A lightweight Clojure library for building MCP (Model Context Protocol) servers. This library prioritizes flexibility and minimal dependencies, allowing you to integrate with your existing technology stack.

The library is currently in alpha stage with features being added incrementally.

[](https://clojars.org/org.clojars.roklenarcic/mcp-server)

```clojure
;; deps.edn
org.clojars.roklenarcic/mcp-server {:mvn/version "0.3.57"}
```

## Table of Contents

- [Why Use This Library?](#why-use-this-library)
- [Current Alpha Limitations](#current-alpha-limitations)
- [Protocol Version Support](#protocol-version-support)
- [Quick Start](#quick-start)
- [Alternative: Manual Configuration](#alternative-manual-configuration)
- [Streamable HTTP Transport](#streamable-http-transport)
- [JSON Serializers](#json-serializers)
- [Key Namespaces](#key-namespaces)
- [Session Management](#session-management)
- [Execution Models](#execution-models)
- [Handler Functions](#handler-functions)
- [Error Handling](#error-handling)
- [Tools](#tools)
- [Prompts](#prompts)
- [Resources](#resources)
- [Resource Templates](#resource-templates)
- [Pagination](#pagination)
- [Tool Parameter Validation](#tool-parameter-validation)
- [Completions](#completions)
- [Titles and Metadata](#titles-and-metadata)
- [Client Communication](#client-communication)
- [Elicitation](#elicitation)
- [Request Metadata](#request-metadata)
- [Logging](#logging)
- [Middleware](#middleware)
- [Errors](#errors)
- [Authentication](#authentication)
- [License](#license)

## Why Use This Library?

Existing MCP server implementations often force specific technology choices on you - they bundle JSON parsers, web servers, loggers, and synchronous/asynchronous patterns. This library takes a different approach by letting you choose your own components.

Why I Built This Instead of Using Existing Solutions

I initially tried wrapping the official Java MCP server from https://github.com/modelcontextprotocol/java-sdk, but encountered several issues:

- **Heavy dependencies**: Jackson, Reactor, and other large libraries
- **Java version requirements**: Uses Java records (Java 14 Preview/Java 16 standard)
- **Integration problems**: Couldn't get Jackson working properly with records
- **Forced logging**: Uses SLF4J with no alternative options
- **Complex async patterns**: Uses Reactive streams, which are difficult to debug
- **Always async**: Even "sync" servers use async internally
- **Framework assumptions**: Designed for Spring integration, not useful in Clojure

The complexity didn't match the value. If you want to build a simple STDIO MCP server with basic tools, why should you need to bundle a web server and deal with reactive flows?

## Protocol Version Support

The server advertises protocol version **2025-11-25** and accepts clients using either:

| Version | Status |
|---------|--------|
| `2025-11-25` | Fully supported (advertised) |
| `2025-06-18` | Accepted for backwards compatibility |

Clients requesting any other version will receive an `Invalid Request` error.

Features supported from the **2025-11-25** specification:

- Extended implementation metadata (title, description, icons, websiteUrl on serverInfo)
- Icons on tools, prompts, resources, and resource templates
- URL-mode elicitation (`elicitation/create` with `mode: "url"`)
- `notifications/elicitation/complete` notification
- Audio content type

The following features from the **2025-11-25** specification are **not yet implemented**:

- **Tasks** — The task state machine for long-running operations (`tasks/get`, `tasks/result`, `tasks/list`, `tasks/cancel`, task-augmented requests) is not supported. This includes the `execution.taskSupport` field on tool definitions.
- **Authorization** — The OAuth-based authorization framework for HTTP transports is not built-in. You can implement your own auth middleware around the Ring handler.
- **URLElicitationRequiredError** — The `-32042` error code that signals a URL-mode elicitation is required before a request can be processed is not emitted automatically. Handlers can return it manually via `core/->JSONRPCError`.
- **JSON Schema dialect enforcement** — The spec requires validating schemas according to their declared `$schema` dialect and rejecting unsupported dialects. The library passes schemas through as-is without dialect-level validation.

## Quick Start

First, choose a JSON serializer and add it to your dependencies. This example uses Charred:

```clojure
;; Add to deps.edn
com.cnuernber/charred {:mvn/version "1.037"}
```

Here's a complete weather service example:

```clojure
(ns example.weather
  (:require [org.clojars.roklenarcic.mcp-server.json.charred :as json]
            [org.clojars.roklenarcic.mcp-server.server :as server]))

(def server-info
  (server/server-info "Weather Service"
                      "1.0.0"
                      "This service provides various weather data"))

(defn get-weather [exchange {:keys [location]}]
  (format "Weather at location %s is %s, temperature is %s degrees"
          location
          (rand-nth ["sunny" "cloudy" "snowing" "rainy" "hailstorm"])
          (- 20 (rand-int 60))))

(def tool (server/tool
            "get_current_weather"
            "Reports current weather based on the location"
            (server/obj-schema nil
                               {:location (server/str-schema "Location of interest" nil)}
                               ["location"])
            get-weather))

(defn start []
  ;; Create template session
  (let [session (-> (server/make-session
                      ;; plug in some general server config
                      server-info
                      ;; pick a JSON serialization implementation
                      (json/serde {})
                      ;; additional context in the sessions
                      {})
                    ;; add a tool to that
                    (server/add-tool tool))]
    ;; Start STDIO server
    (server/start-server-on-streams session System/in System/out {})))
```

This creates a mock weather service that communicates over STDIO. The key components are:

- **JSON serialization**: We chose Charred for JSON handling
- **Session creation**: Created a session and added our tool
- **Transport**: Started a stream-based server on stdin/stdout

## Alternative: Manual Configuration

Since sessions are just maps, you can build them manually instead of using the helper functions:

```clojure
(ns example.weather2
  (:require [org.clojars.roklenarcic.mcp-server :as-alias mcp]
            [org.clojars.roklenarcic.mcp-server.json.charred :as json]
            [org.clojars.roklenarcic.mcp-server.server :as server]))

(defn get-weather [exchange {:keys [location]}]
  (format "Weather at location %s is %s, temperature is %s degrees"
          location
          (rand-nth ["sunny" "cloudy" "snowing" "rainy" "hailstorm"])
          (- 20 (rand-int 60))))

(defn start []
  (let [session-map {::mcp/server-info {:name "Weather Service"
                                        :version "1.0.0"
                                        :instructions "This service provides various weather data"}
                     ::mcp/serde (json/serde {})
                     ::mcp/dispatch-table (server/make-dispatch)
                     ::mcp/handlers
                     {:tools
                      {"get_current_weather" 
                       {:name "get_current_weather"
                        :description "Reports current weather based on the location"
                        :input-schema {:properties {:location {:type "string" 
                                                              :description "Location of interest"}}
                                       :required ["location"]
                                       :type "object"}
                        :handler get-weather}}}}]
    (server/start-server-on-streams (atom session-map) System/in System/out {})))
```

The dispatch table is a lookup map that routes JSON-RPC calls to their handlers.

## Streamable HTTP Transport

For HTTP-based communication, use `ring-handler` from the HTTP transport namespace. This implements the MCP Streamable HTTP transport on a single endpoint that handles three methods: `POST` for client to server JSON-RPC, `GET` for a server to client SSE stream, and `DELETE` to terminate a session. The result is a standard Ring handler that you can mount in any Ring-compatible HTTP server:

```clojure
(ns example.http-server
  (:require [org.clojars.roklenarcic.mcp-server.json.charred :as json]
            [org.clojars.roklenarcic.mcp-server.server :as server]
            [org.clojars.roklenarcic.mcp-server.server.http :as http]))

(defn start []
  (let [session (-> (server/make-session
                      (server/server-info "My Service" "1.0.0" "Description")
                      (json/serde {})
                      {})
                    (server/add-tool my-tool))
        sessions (http/memory-sessions-store)
        handler (http/ring-handler session sessions
                                   {:allowed-origins ["https://example.com"]
                                    :client-req-timeout-ms 120000})]
    ;; Mount `handler` in your preferred Ring-compatible HTTP server
    ;; (ring-jetty, http-kit, etc.)
    handler))
```

The `ring-handler` supports both synchronous and asynchronous Ring operation. The `memory-sessions-store` provides an in-memory session store backed by `ConcurrentHashMap`. You can implement the `Sessions` protocol for custom session storage (e.g., Redis-backed).

Options:
- `:allowed-origins` — collection of allowed Origin headers (`nil` permits all origins)
- `:client-req-timeout-ms` — timeout for client requests in milliseconds (default: 120000)
- `:sse-queue-capacity` — bounded buffer for server-to-client messages produced while no SSE stream is attached (default: 1024)
- `:sse-replay-capacity` — bounded replay buffer for `Last-Event-ID` resumability on GET reconnect (default: same as `:sse-queue-capacity`)

### Synchronous vs asynchronous Ring

The GET SSE channel behaves differently depending on which Ring arity your adapter invokes:

- **Synchronous (1-arity)**: the GET delivers any pending messages and detaches. Subsequent server-initiated requests/notifications are queued and delivered on the next `GET /mcp`.
- **Asynchronous (3-arity)**: the SSE stream stays open and live server-initiated messages are written to it immediately, until the client disconnects or `DELETE /mcp` is called.

Use an async Ring adapter for live server-to-client streaming over a long-lived SSE connection. Use the sync adapter only if your deployment polls `GET /mcp` or does not initiate server-side requests. Reconnecting clients may supply the standard `Last-Event-ID` header on `GET /mcp` to resume from the per-session replay buffer.

### Protocol headers

After the initialize handshake the server returns a `Mcp-Session-Id` header. Clients **must** include that header on every subsequent `POST`, `GET`, and `DELETE` request, and **should** include `MCP-Protocol-Version: 2025-06-18` on non-initialize requests. `POST` requests must declare `Content-Type: application/json` and `Accept: application/json, text/event-stream`; `GET` requests must declare `Accept: text/event-stream`. Requests carrying an unknown session id receive `404 Not Found`; mismatched headers return `400 Bad Request` or `406 Not Acceptable`.

## JSON Serializers

You can write your own integration, by extending the `org.clojars.roklenarcic.mcp-server.json-rpc/JSONSerialization` protocol, but there are many available already:

- org.clojars.roklenarcic.mcp-server.json.babashka/serde
- org.clojars.roklenarcic.mcp-server.json.charred/serde
- org.clojars.roklenarcic.mcp-server.json.cheshire/serde
- org.clojars.roklenarcic.mcp-server.json.clj-data/serde
- org.clojars.roklenarcic.mcp-server.json.jsonista/serde

Each requires its own dependency on the classpath. Some guidance on choosing:

- **Charred** — fastest option, good default for JVM Clojure
- **Cheshire** — most widely used in the Clojure ecosystem
- **Jsonista** — fast, Metosin ecosystem
- **Babashka JSON** — compatible with Babashka
- **clj-data** (`clojure.data.json`) — no extra dependencies beyond Clojure contrib

## Key Namespaces

```clojure
[org.clojars.roklenarcic.mcp-server.server :as server]
[org.clojars.roklenarcic.mcp-server.core :as core]
[org.clojars.roklenarcic.mcp-server :as-alias mcp]
```

## Session Management

The session is the central abstraction - it's a map stored in an atom that represents a client connection. Sessions allow you to:

- Store connection-specific data (database pools, authentication info)
- Modify internal behavior (change RPC handlers)
- Manage available tools, prompts, and resources

See the [Session Guide](doc/session.md) for detailed information (**essential reading**).

## Execution Models

This library supports both synchronous and asynchronous execution patterns. **Your handlers can return plain values or CompletableFuture instances.**

By default, handlers run synchronously. Functions that send requests to clients return `CompletableFuture` objects (or `nil` if the operation isn't supported).

See the [Sync/Async Guide](doc/async.md) for different approaches.

## Handler Functions

Most handlers receive an `exchange` parameter first. This is a `RequestExchange` object that contains the session and provides access to client communication functions.

Use `core/get-session` to extract the session from an exchange. Functions like `log-msg`, `list-roots`, and `sampling` send requests to the client and return CompletableFuture objects.

Outside of handlers, create an exchange from a session using `server/exchange`.

## Error Handling

Your handlers can return RPC error objects that will be sent to clients. Use the functions in the `core` namespace:

```clojure
;; For invalid input parameters
(core/invalid-params "Size should be one of S, M, L, XL")

;; For server-side problems
(core/internal-error "Database connection failed")

;; For missing resources
(core/resource-not-found "Cannot find URL")

;; For malformed requests (rarely used)
(core/invalid-request "Request format not understood")

;; For custom application errors
(core/->JSONRPCError -32123 "Application specific error" "Additional details")
```

## Tools

Tools can be added or removed from both template sessions and live sessions. When you modify a live session's tool list, the client automatically receives a notification.

```clojure
(defn get-weather [exchange arguments] ...)

(def tool (server/tool
            "get_current_weather"
            "Reports current weather based on the location"
            (server/obj-schema nil
                               {:location (server/str-schema "Location of interest" nil)}
                               ["location"])
            get-weather))

;; Add or remove tools (returns the session atom)
(server/add-tool session tool)
(server/remove-tool session "get_current_weather")
;; Client is automatically notified of changes
```

`server/tool` accepts the following optional keyword arguments (MCP 2025-06-18):

- `:title` — human-readable display name; clients SHOULD prefer it over `name`.
- `:output-schema` — JSON Schema describing the structure of `:structuredContent`
  the tool returns. See [Structured Tool Output](#structured-tool-output) below.
- `:_meta` — map of arbitrary metadata to attach to the tool. Keys under
  `:_meta` are preserved verbatim on the wire (no kebab→camelCase
  transformation). See [Titles and Metadata](#titles-and-metadata).

```clojure
(server/tool
  "get_current_weather"
  "Reports current weather based on the location"
  schema
  get-weather
  :title "Current Weather"
  :_meta {"com.example/cost-tier" "premium"})
```

### Tool Schemas

Parameter schemas use standard JSON Schema format. The `server` namespace provides helper functions for common patterns:

```cloj

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [RokLenarcic](https://github.com/RokLenarcic)
- **Source:** [RokLenarcic/mcp-server](https://github.com/RokLenarcic/mcp-server)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-roklenarcic-mcp-server
- Seller: https://agentstack.voostack.com/s/roklenarcic
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
