CleverChatty now supports Streamable HTTP for MCP servers!

CleverChatty now supports Streamable HTTP for MCP servers!

**CleverChatty**, a lightweight AI Chat tool supporting multiple LLM providers, now includes support for **Streamable HTTP** with MCP servers. This update enables more flexible and efficient communication with AI models, making integration with custom tools and services even smoother. --- ## 🌐 What is CleverChatty? [CleverChatty](https://github.com/Gelembjuk/cleverchatty) is a minimalist AI chat interface that works with various large language model (LLM) providers — including OpenAI, Anthropic, Google, and local models like Ollama. It’s designed for users and developers who want a simple, extensible tool that supports **MCP-based tool usage**. Until now, CleverChatty only supported **STDIO** and **SSE (Server-Sent Events)** as transport protocols for connecting with MCP servers. With the latest update, it now supports **Streamable HTTP**, expanding compatibility and flexibility. --- ## 🚀 How to Use Streamable HTTP in CleverChatty Streamable HTTP is now the **default transport method** when the `transport` field is omitted but a `url` is present in the config file. To use **SSE**, explicitly set `"transport": "sse"`. ### ✅ Example Configuration ```json { "log_file_path": "", "model": "ollama:qwen2.5:3b", "mcpServers": { "http_streaming_weather_server": { "url": "http://weather-service:8000/mcp", "headers": [] }, "sse_weather_server": { "url": "http://weather-service:8001/sse", "transport": "sse", "headers": [] }, "stdio_weather_server": { "command": "uv", "args": ["weather-service.py"] } } } ``` --- ## 🧪 Example MCP Server: Supports STDIO, SSE, and Streamable HTTP The example below demonstrates an MCP server that supports **all three transports**. It also shows how to write a **universal server** capable of handling multiple protocols, making it perfect for testing CleverChatty. ✅ This server also includes a basic authentication layer and a command-execution tool for Linux/Mac. ```golang package main import ( "context" "fmt" "net/http" "os" "os/exec" "path/filepath" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) var requireAuth = false func main() { if len(os.Args) < 2 { binaryName := filepath.Base(os.Args[0]) fmt.Println("Usage: ./" + binaryName + " [OPTIONS]") fmt.Println("Usage: ./" + binaryName + " sse HOST:PORT") fmt.Println("Usage: ./" + binaryName + " http_streaming HOST:PORT") fmt.Println("Usage: ./" + binaryName + " stdio") os.Exit(1) } transport := os.Args[1] switch transport { case "stdio": runAsStdio() case "sse": if len(os.Args) < 3 { fmt.Println("sse transport requires a host and port argument (e.g., 0.0.0.0:8080)") os.Exit(1) } host_and_port := os.Args[2] runAsSSE(host_and_port) case "http_streaming": if len(os.Args) < 3 { fmt.Println("http streaming transport requires a host and port argument (e.g., 0.0.0.0:8080)") os.Exit(1) } host_and_port := os.Args[2] runAsHTTPStreaming(host_and_port) default: fmt.Printf("Unknown transport: %s\n", transport) os.Exit(3) } } func runAsStdio() { if err := server.ServeStdio(createServer()); err != nil { fmt.Printf("😡 Server error: %v\n", err) } } func runAsSSE(host_and_port string) { // for SSE we require auth requireAuth = true // Start the stdio server sObj := server.NewSSEServer(createServer(), server.WithSSEContextFunc(server.SSEContextFunc(httpContextFunction))) fmt.Println("🚀 SSE Server started") if err := sObj.Start(host_and_port); err != nil { fmt.Printf("😡 Server error: %v\n", err) } fmt.Println("👋 Server stopped") } func runAsHTTPStreaming(host_and_port string) { // for SSE we require auth requireAuth = true // Start the stdio server sObj := server.NewStreamableHTTPServer(createServer(), server.WithHTTPContextFunc(server.HTTPContextFunc(httpContextFunction))) fmt.Println("🚀 Streamable HTTP Server started") if err := sObj.Start(host_and_port); err != nil { fmt.Printf("😡 Server error: %v\n", err) } fmt.Println("👋 Server stopped") } // All the code below is not related to MCP server transport func createServer() *server.MCPServer { // Create MCP server s := server.NewMCPServer( "Server to manage a Linux instance", "1.0.0", ) execTool := mcp.NewTool("exec_cmd", mcp.WithDescription("Execute a Linux command with optional working directory"), mcp.WithString("command", mcp.Required(), mcp.Description("The full shell command to execute"), ), mcp.WithString("working_dir", mcp.Description("Optional working directory where the command should run"), ), ) s.AddTool(execTool, RequireAuth(execCmdHandler)) return s } // This function is used to extract the token from the HTTP request context. // It is used in the SSE and HTTP streaming transports to authenticate requests. func httpContextFunction(ctx context.Context, r *http.Request) context.Context { // Extract the Authorization header from the request and get the token from it header := r.Header.Get("Authorization") if header == "" { return ctx } // get token after Bearer token := header[len("Bearer "):] if token == "" { return ctx } fmt.Printf("😎 Token: %s\n", token) // add this token to the context. We will check it later ctx = context.WithValue(ctx, "token", token) return ctx } // RequireAuth is a middleware that checks if the request is authenticated. func RequireAuth(handler server.ToolHandlerFunc) server.ToolHandlerFunc { return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { if !requireAuth { // If we don't require auth, just call the handler return handler(ctx, request) } token, ok := ctx.Value("token").(string) if !ok || token != "expected-token" { // or validate it return mcp.NewToolResultError("unauthorized"), nil } return handler(ctx, request) } } func execCmdHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { args, ok := request.Params.Arguments.(map[string]interface{}) if !ok { return mcp.NewToolResultError("invalid arguments"), nil } cmdStr, ok := args["command"].(string) if !ok || cmdStr == "" { return mcp.NewToolResultError("command is required"), nil } // Optional working_dir var workingDir string if wd, ok := args["working_dir"].(string); ok { workingDir = wd } // Use "sh -c" to allow full shell command with arguments and operators cmd := exec.Command("sh", "-c", cmdStr) if workingDir != "" { cmd.Dir = workingDir } output, err := cmd.CombinedOutput() if err != nil { // Include both the error and output for context return mcp.NewToolResultError(fmt.Sprintf("execution failed: %v\n%s", err, output)), nil } return mcp.NewToolResultText(string(output)), nil } ``` You can run this server in different modes using: ```bash go run main.go http_streaming 0.0.0.0:8080 # or go run main.go sse 0.0.0.0:8080 # or compile and run for stdio go build -o myserver main.go ./myserver stdio ``` --- ## 🛠️ Setting up CleverChatty with Streamable HTTP To test CleverChatty with your new Streamable HTTP MCP server: ### 1. Create a `config.json` ```json { "log_file_path": "", "model": "ollama:qwen2.5:3b", "mcpServers": { "linux_server": { "url": "http://localhost:8080/mcp", "headers": [] } } } ``` ### 2. Start the MCP server ```bash go run main.go http_streaming 0.0.0.0:8080 ``` ### 3. Run CleverChatty ```bash go run github.com/gelembjuk/cleverchatty-cli@latest --config config.json ``` Or install and run: ```bash go install github.com/gelembjuk/cleverchatty-cli@latest cleverchatty --config config.json ``` --- ## ⚠️ Platform Note This MCP server uses `sh -c` for executing shell commands, so it works **only on Linux or macOS**. To run it on Windows, you’ll need to modify the `execCmdHandler` function to use a Windows-compatible command execution method. --- With Streamable HTTP support now built in, **CleverChatty** becomes even more versatile for developers building intelligent, tool-using AI agents. Whether you prefer SSE, STDIO, or now Streamable HTTP — you’ve got the freedom to choose what fits your deployment best.

Previous Post:

MCP, LLM
What’s Missing in MCP
9 June 2025

Over the past couple of months, I’ve been experimenting with the Model Context Protocol (MCP) — building AI agents and tools around it. While the experience has been promising, I’ve noticed a few areas where MCP could be expanded or improved.

Continue Reading
What’s Missing in MCP

Next Post:

MCP, LLM
Integrating Mem0 (mem-zero) with CleverChatty
18 June 2025

Integrating Mem0 memory model with CleverChatty-CLI for AI assistants.

Continue Reading
Integrating Mem0 (mem-zero) with CleverChatty