Recent Posts
Archives

Posts Tagged ‘Clojure’

PostHeaderIcon [reClojure2025] Writing Model Context Protocol (MCP) Servers in Clojure

Lecturer

Vedang Manerikar is the founder of Unravel.tech and a veteran software architect with over 15 years of experience in the Clojure ecosystem. Previously serving as the Head of Backend Engineering at Helpshift, Vedang has managed large-scale distributed systems and led complex technical migrations. At Unravel.tech, his work focuses on the intersection of Clojure and Artificial Intelligence, specifically building “Agentic Systems” and implementing Generative AI (GenAI) and Large Language Model (LLM) solutions. He is the author of mcp-cljc-sdk, a cross-platform Clojure SDK for the Model Context Protocol.

Abstract

The rapid advancement of Artificial Intelligence has created a need for standardized communication between AI agents and external systems. The Model Context Protocol (MCP), introduced by Anthropic, has emerged as a solution to the integration problem, providing a common interface for agents to interact with diverse data sources and tools. This article explores the architecture of MCP and argues that Clojure is uniquely positioned as an ideal language for implementing MCP servers. We analyze the protocol’s similarity to the Language Server Protocol (LSP), examine real-world applications in browser automation and communication platforms, and discuss how Clojure’s REPL-driven development and data-centric philosophy streamline the creation of powerful, composable AI workflows.

The Model Context Protocol: A New Standard for AI UX

At its core, MCP is an open standard designed to enable AI applications—such as Claude Desktop or Cursor—to access the external world in a structured manner. While one might ask why standard HTTP interfaces are insufficient, the answer lies in the integration problem. Without a standard, every AI agent would need a custom integration for every service (PostgreSQL, Google Drive, GitHub, etc.). MCP solves this by acting as a “USB port” for AI; developers write a server for their service once, and it becomes immediately accessible to any MCP-compliant agent.
Vedang describes MCP not just as a data access layer, but as a “baseline AI UX.” It defines how an agent discovers tools, reads resources, and follows prompts. This standardization allows for the creation of sophisticated workflows where an agent can, for example, use a Playwright MCP server to browse Hacker News, a WhatsApp MCP server to read messages, and a local filesystem server to summarize information and save it to a document. By providing a consistent interface, MCP shifts the focus from integration plumbing to the design of the agent’s behavior and user experience.

Clojure as the Premier Language for MCP

Clojure’s technical characteristics align remarkably well with the requirements of building MCP servers. The protocol is heavily reliant on JSON-RPC and the exchange of structured data, which plays directly into Clojure’s “data-as-code” philosophy. Vedang highlights several key reasons why Clojure developers are particularly well-prepared for the LLM world:
1. REPL-Driven Development: MCP servers often act as intermediaries between non-deterministic LLMs and deterministic systems. The ability to interactively test and refine server responses in a live REPL mirrors the iterative nature of working with AI.
2. Data Transformation: Clojure’s rich library for manipulating maps and vectors makes it trivial to transform complex API responses into the simplified “Context” required by LLMs.
3. Cross-Platform Capability: With the mcp-cljc-sdk, developers can write server logic once and deploy it on both the JVM (using clojure.main or GraalVM native images) and Node.js (via ClojureScript), providing flexibility in how the server is hosted and consumed.

Code Sample: Defining a Simple MCP Tool

(defmethod handle-request "tools/call" [request]
  (let [{:keys [name arguments]} (:params request)]
    (case name
      "get-weather" (let [city (:city arguments)]
                      {:content [{:type "text"
                                  :text (str "The weather in " city " is sunny.")}]})
      {:error "Tool not found"})))

Practical Applications and Agentic Workflows

The power of MCP is best demonstrated through real-world “Agentic” use cases. Vedang shares examples of servers he has developed to automate complex tasks. One such server integrates with WhatsApp, allowing an AI agent to scan chat groups for business leads. Instead of a human manually reading hundreds of messages, the agent uses the MCP server to fetch the latest messages, identifies intent, and provides a summarized report of actionable items.
Another significant application is in browser automation. Using an MCP server for Playwright, an AI can navigate the web as a user would—logging into sites, extracting data from dynamically rendered pages, and performing actions. This allows for prompts like “Find me a hotel within walking distance of the reClojure conference,” where the agent autonomously searches maps, checks availability, and compares prices. These examples illustrate how MCP enables the transition from simple chatbots to true “agents” capable of multi-step reasoning and interaction with the physical or digital world.

The Future of Content-Centric AI

Looking ahead, the evolution of MCP suggests a shift toward a “content-is-king” paradigm. Current AI interactions are often limited by the UX of the chat box. However, with MCP, the focus can move toward the actual content being produced or modified—whether that is a codebase, a spreadsheet, or a document. Vedang envisions a future where multiple coding agents can work in parallel on the same repository, coordinated through a “better Git” or similar bidi-rectinal communication protocols enabled by MCP.
By standardizing the way agents interact with our tools, MCP paves the way for a new generation of software that is designed from the ground up to be AI-enhanced. For the Clojure community, this represents a significant opportunity to lead the development of the “AI UX” by building robust, composable servers that unlock the full potential of Large Language Models.

Links:

PostHeaderIcon [reClojure2025] UI, Pure and Simple

Lecturer

Christian Johansen is a highly experienced software developer at the Norwegian Food Safety Authority, where he specializes in architecting robust systems using Clojure and Datomic. With a professional career spanning two decades, Christian has dedicated the last ten years to full-time Clojure development. His expertise is deeply rooted in web technologies, encompassing the entire delivery pipeline from infrastructure configuration and data modeling to sophisticated frontend implementation. Beyond his primary role, he is a prolific contributor to the Clojure ecosystem, maintaining various open-source libraries and frequently sharing insights through conference presentations, educational courses, and a collaborative professional blog.

Abstract

Modern frontend development is often plagued by the complexities of shared mutable state and pervasive side effects, which complicate testing and maintenance. This article examines the philosophical and technical foundations of achieving a truly functional user interface. By revisiting the original promise of React—that UI is a function of state—and refining it through the lens of Clojure’s immutability, we introduce a paradigm known as top-down rendering. Central to this discussion is Replicant, a small, dependency-free Clojure rendering library designed to treat UI as pure, deterministic data. We analyze the methodology of building modular UIs that decouple rendering from state management, utilize data-driven event handlers, and leverage declarative animations to create simpler, more testable applications.

Historical Context and the Pursuit of Purity

The evolution of modern web development reached a significant milestone in 2013 with the introduction of React. The framework proposed a revolutionary conceptual model: the user interface should be viewed as a pure function of application state. In this ideal scenario, developers would write code as if the entire UI were rendered from scratch with every update, leaving the heavy lifting of DOM manipulation to the framework. However, while React transformed the industry’s mental model, it did not fully deliver on the promise of functional purity. In practice, React applications often allow mutable state to proliferate throughout the component tree, leading to the very “side-effect-ridden” complexity it sought to solve.
The ClojureScript community recognized this gap early on. Developers sought a more rigorous adherence to functional principles. One notable advancement was the library Quiescent, which introduced the constraint of “top-down rendering.” In this model, components are prohibited from maintaining their own internal state or triggering their own re-renders. Instead, the entire UI is a literal projection of a central, immutable data structure. This approach aligns perfectly with Clojure’s core strengths, providing a foundation for UIs that are stateless, deterministic, and built entirely on data.

Methodology: Rendering with Replicant

Replicant serves as a realization of this top-down philosophy. It is a minimalist virtual DOM library that operates on Hiccup, a domain-specific language in Clojure that represents HTML structures as standard data vectors and maps. The core methodology involves creating pure functions that transform domain data into Hiccup data. Because these functions are pure, they are inherently predictable and easy to test in isolation.
To illustrate this, consider the rendering of a task card in a Kanban application. The developer defines a function that takes a task map and returns a Hiccup representation. Replicant’s render function then takes this data and a target DOM element to perform the initial mount. When the application state changes, the process is repeated: the pure function generates new Hiccup data, and Replicant calculates the minimal set of DOM mutations required to transition the view. This “diffing” process ensures efficiency without requiring the developer to manage state transitions manually.

Code Sample: Pure Hiccup Transformation

(defn render-task [task tags-lookup]
  [:div.task-card
   [:h3 (:task/title task)]
   [:div.tags
    (map (fn [tag-id]
           (let [tag (get tags-lookup tag-id)]
             [:span {:class (str "tag-" (:tag/color tag))}
              (:tag/label tag)]))
         (:task/tags task))]])

Advanced UI Patterns: Events and Animations

Beyond static rendering, the “pure and simple” approach extends to interactivity. In traditional frameworks, event handlers are often opaque functions that execute side effects directly. Replicant encourages data-driven event handlers. Instead of passing a callback function to an onClick attribute, the developer can pass a data structure—a vector or a map—representing the intent of the event. A central coordinator then interprets this data to update the global state. This decoupling makes the UI’s behavior as inspectable and testable as its appearance.
The same principle applies to complex UI requirements like animations and timed effects. By treating time and transitions as part of the data flow, developers can create declarative animations. These are not imperative commands to “fade in an element” but rather state-based descriptions of how an element should appear at a given point in the application lifecycle. This approach dramatically simplifies the creation of interactive features like drag-and-drop or live data streaming, as the UI remains a consistent reflection of the underlying store regardless of where the data originates.

Consequential Benefits and Conclusion

Adopting a stateless, data-centric approach to UI development yields significant benefits for software quality. Because the UI is composed of pure functions, it is highly modular and testable. Tools like Portfolio (similar to Storybook for ClojureScript) allow developers to render “scenes” in isolation by passing mock domain data to their rendering functions. This facilitates rapid prototyping and visual regression testing without the need to navigate through a live, stateful application.
Ultimately, the shift toward pure and simple UIs represents a move away from the “nashing of teeth” associated with shared mutable state. By leveraging Clojure’s immutable data structures and Replicant’s minimalist rendering engine, developers can build systems that are not only more robust and maintainable but also more enjoyable to create. The decoupling of rendering from state management allows for a degree of architectural clarity that is often missing in contemporary frontend development.

Links:

PostHeaderIcon Clojure: A Modern Lisp for Durable, Concurrent Software

Clojure: A Modern Lisp for Durable, Concurrent Software

In the evolving landscape of programming languages, Clojure distinguishes itself not through novelty or aggressive feature growth, but through deliberate restraint. Rather than chasing trends, it revisits enduring principles of computer science—immutability, functional composition, and symbolic computation—and applies them rigorously to contemporary software systems. This approach results in a language that feels both deeply rooted in tradition and sharply attuned to modern challenges.

Clojure appeals to developers and organizations that prioritize long-term correctness, conceptual coherence, and system resilience over short-term convenience.

What Clojure Is and What It Aims to Solve

Clojure is a functional, dynamically typed programming language that runs primarily on the Java Virtual Machine. It is a modern Lisp, and as such it adopts a uniform syntax in which code is represented as structured data. This design choice enables powerful programmatic manipulation of code itself, while also enforcing consistency across the language.

Unlike many earlier Lisp dialects, Clojure was explicitly designed for production systems. It assumes the presence of large codebases, multiple teams, and long-lived services. As a result, its design is deeply influenced by concerns such as concurrency, data integrity, and integration with existing ecosystems.

Historical Context and Design Motivation

Rich Hickey introduced Clojure publicly in 2007 after years of observing recurring failures in large software systems. His critique focused on the way mainstream languages conflate identity, state, and value. In mutable systems, objects change over time, and those changes must be coordinated explicitly when concurrency is involved. The resulting complexity often exceeds human reasoning capacity.

Clojure responds by redefining the problem. Instead of allowing values to change, it treats values as immutable and represents change as a controlled transition between values. This shift in perspective underpins nearly every aspect of the language.

Immutability as a Foundational Principle

In Clojure, immutability is the default. Data structures such as vectors, maps, and sets never change in place. Instead, operations that appear to modify data return new versions that share most of their internal structure with the original.

(def user {:name "Alice" :role "admin"})
(def updated-user (assoc user :role "editor"))

;; user remains unchanged
;; updated-user reflects the new role

Because values never mutate, functions cannot introduce hidden side effects. This dramatically simplifies reasoning, testing, and debugging, especially in concurrent environments.

Functional Composition in Practice

Clojure encourages developers to express computation as the transformation of data through a series of functions. Rather than focusing on control flow and state transitions, programs describe what should happen to data.

(defn even-squares [numbers]
  (->> numbers
       (filter even?)
       (map #(* % %))))

In this example, data flows through a pipeline of transformations. Each function is small, focused, and easily testable, which encourages reuse and composability over time.

Concurrency Through Explicit State Management

Clojure’s concurrency model separates identity from value. State is managed through explicit reference types, while the values themselves remain immutable. This design makes concurrent programming safer and more predictable.

(def counter (atom 0))
(swap! counter inc)

For coordinated updates across multiple pieces of state, Clojure provides software transactional memory, allowing several changes to occur atomically.

(def account-a (ref 100))
(def account-b (ref 50))

(dosync
  (alter account-a - 10)
  (alter account-b + 10))

Macros and Language Extension

Because Clojure code is represented as data, macros can transform programs before evaluation. This allows developers to introduce new syntactic constructs that feel native to the language rather than external utilities.

(defmacro unless [condition & body]
  `(if (not ~condition)
     (do ~@body)))

Although macros should be used with care, they play an important role in building expressive and coherent abstractions.

Interoperability with Java

Despite its distinct philosophy, Clojure integrates seamlessly with Java. Java classes can be instantiated and methods invoked directly, allowing developers to reuse existing libraries and infrastructure.

(import java.time.LocalDate)
(LocalDate/now)

Comparison with Java

Although Clojure and Java share the JVM, they differ fundamentally in how they model software. Java emphasizes object-oriented design, mutable state, and explicit control flow. Clojure emphasizes immutable data, functional composition, and explicit state transitions.

While Java has incorporated functional features over time, its underlying model remains object-centric. Clojure offers a more radical rethinking of program structure, often resulting in smaller and more predictable systems.

Comparison with Scala

Scala and Clojure are often compared as functional alternatives on the JVM, yet their philosophies diverge significantly. Scala embraces expressive power through advanced typing and rich abstractions, whereas Clojure seeks to reduce complexity by minimizing the language itself.

Both approaches are valid, but they reflect different beliefs about how developers best manage complexity.

Closing Perspective

Clojure is not designed for universal adoption. It demands a shift in how developers think about state, time, and behavior. However, for teams willing to embrace its principles, it offers a disciplined and coherent approach to building software that remains understandable, correct, and adaptable over time.