Skip to content

Charmbracelet Crush

Charmbracelet Crush is the glamourous coding agent. More about it can be found in Charm Land. Here we outline a minimial configuration to use Crush with TU Wien's Acqueduct Infrastructure.

Configuring Crush

Crush looks for a configuaration file either in the folder you call it from, crush.json or in your home folder ~/.config/crush/crush.json.

{
  "$schema": "https://charm.land/crush.json",
  "providers": {
    "ollama" : {
        "name": "aqueduct",
        "base_url" : "https://aqueduct.ai.datalab.tuwien.ac.at/",
        "api_key"  : "sk-PASTE-YOUR-GLAMOUROUS-AQUEDUCT-KEY-HERE",
        "type"     : "openai",
        "models"   : 
        [
          {
              "name"   : "qwen-3.5-35b",
              "id"     : "qwen-3.5-35b",
              "context_window" : 130000,
              "default_max_tokens" : 50000,
          },
          {
              "name"   : "qwen-3.5-397b",
              "id"     : "qwen-3.5-397b",
              "context_window" : 130000,
              "default_max_tokens" : 50000,
          }
        ]
    }
  }
}

Ctrl+l then lets you change models at runtime inside crush. context_window is here set to about half the actual context window of the models. It is common for Crush to overshoot the context window. Lowering it assures summerization passes are triggered earlier, hopefully preventing context window exhaustion.

Adding Aqueduct Hosted MCP Servers to Crush:

The Aqueduct Website contains several MCP servers that you might want to use inside Crush, such as TU Website search or the Campus IT Service Knowledge Base. However Crush is not fully compatible with the way Aqueduct hosted MCP Servers are handled. As a workaround you might use the following wrapper script, adapting BASE_URL to the url of the MCP and AUTH_HEADER with your authentication token:

#!/usr/bin/env python3
"""
MCP stdio wrapper for aqueduct MCP HTTP endpoints.
Translates stdio MCP protocol to HTTP Streamable protocol.
"""
import json
import sys
import requests

BASE_URL = "https://aqueduct.ai.datalab.tuwien.ac.at/mcp-servers/tu-search/mcp"
AUTH_HEADER = "Bearer sk-PASTE-YOUR-GLAMOUROUS-AQUEDUCT-KEY-HERE"

session = requests.Session()
session.headers.update({
    "Authorization": AUTH_HEADER,
    "Content-Type": "application/json",
})
session_id = None

def send_request(payload):
    global session_id
    headers = dict(session.headers)
    if session_id:
        headers["Mcp-Session-Id"] = session_id

    resp = session.post(BASE_URL, headers=headers, json=payload, timeout=30)

    # Capture session ID from response
    new_session_id = resp.headers.get("Mcp-Session-Id")
    if new_session_id:
        session_id = new_session_id

    return resp.json()

def main():
    for line in sys.stdin:
        try:
            msg = json.loads(line.strip())
            method = msg.get("method", "")

            # Handle notifications/initialized
            if method == "notifications/initialized":
                send_request(msg)
                # Don't respond to notifications

            # Handle ping
            elif method == "ping":
                print(json.dumps({"jsonrpc": "2.0", "id": msg.get("id"), "result": {}}), flush=True)

            else:
                # Handle all other possible methods with direct passthrough
                result = send_request(msg)
                print(json.dumps(result), flush=True)

        except Exception as e:
            error = {
                "jsonrpc": "2.0",
                "id": msg.get("id") if 'msg' in locals() else None,
                "error": {"code": -32603, "message": str(e)}
            }
            print(json.dumps(error), flush=True)

if __name__ == "__main__":
    main()

The MCP server can than be added to your crush.json config file:

{
  "$schema": "https://charm.land/crush.json",
  "providers": {

  ...

  },
  "mcp": {
    "tu-search": {
      "type": "stdio",
      "command": "python3",
      "args": ["/path/to/the/wrapper/script/above.py"]
    }
  }
}

Tip

Crush has a skill to allow you to understand and edit its config file from within crush. If you want to modify the crush.json file, just ask crush what you wish to do. I.e. ask for limiting commands to be executed, adding hooks and all kind of other things.

Tip

In case you want to configure self written MCPs and automate custom workflows with crush, we provide a video for that: https://www.youtube.com/watch?v=L7OWwEOmlZw