Transparent LLM API monitoring proxy
Project description
Malcolm
[!WARNING] Malcolm is in early development. APIs and configuration may change between releases.
A transparent monitoring proxy for LLM API calls. Malcolm sits between your LLM client tools (like Claude Code or OpenCode) and the actual model backend, logging every request and response for inspection.
Why?
Tools like claude and opencode construct complex prompts with system instructions, tool definitions, and conversation history. Malcolm lets you see exactly what gets sent to the model. It's useful for debugging, understanding tool behavior, and optimizing prompts.
Quick start
# Either install from `pip`
pip install malcolm-proxy
# Or from `git`
git clone https://github.com/malcolm-proxy/malcolm
cd malcolm
uv pip install -e .
# Start the proxy
malcolm --malcolm-target-url=http://localhost:11434/v1
Then point your LLM tool to Malcolm:
ANTHROPIC_AUTH_TOKEN=ollama \
ANTHROPIC_BASE_URL=http://127.0.0.1:8900 \
ANTHROPIC_API_KEY="" \
claude --model qwen3-coder:30b
Or do both in a single command — Malcolm starts the proxy silently and opens Claude Code already wired to it. With no other arguments, the target defaults to Anthropic:
malcolm --launch-claude
For other backends, combine --launch-claude with the usual flags:
malcolm --launch-claude \
--malcolm-target-url=http://localhost:11434/v1 \
--anthropic-api-key="" --anthropic-auth-token=ollama \
--model=qwen3-coder:30b
When Claude Code exits, Malcolm shuts down. See docs/scenarios.md for one-liners covering Anthropic, OpenAI, and Ollama.
Browse logged requests with the terminal UI:
malcolm tui # uses default malcolm.db
malcolm tui --db-path ./other.db # use a specific database
Terminal UI
Malcolm includes an agnostic TUI for browsing logs directly from the terminal, without a browser.
Two-level drill-down: Request list → Detail view (annotations + full JSON with syntax highlighting).
The request list shows ID, timestamp, status code, and duration. When the llm_annotator annotator is enabled, additional columns (model, stream) appear dynamically from annotations.
| Key | Action |
|---|---|
↑ ↓ |
Navigate |
Enter |
Open |
Esc |
Back |
n / p |
Next / previous page |
f |
Toggle follow mode (auto-refresh every second) |
c |
Copy to clipboard (content view) |
w |
Toggle word wrap (content view) |
r |
Reload |
t |
Toggle dark/light theme |
q |
Quit |
The TUI reads directly from the SQLite database, so it works while the proxy is running. Use follow mode (f) to see new requests appear automatically, or press r to refresh manually.
Configuration
Environment variables
Core proxy settings via environment variables (or .env file):
| Variable | Default | Description |
|---|---|---|
MALCOLM_TARGET_URL |
(required) | Backend API base URL |
MALCOLM_TARGET_API_KEY |
(empty) | API key for the backend. If empty, forwards the client's Authorization header |
MALCOLM_HOST |
127.0.0.1 |
Listen address |
MALCOLM_PORT |
8900 |
Listen port |
MALCOLM_STORAGE_ENABLED |
true |
Enable/disable SQLite persistence |
MALCOLM_DB_PATH |
malcolm.db |
SQLite database file path |
MALCOLM_LOG_LEVEL |
info |
Log level |
MALCOLM_CONFIG_FILE |
malcolm.yaml |
Path to the transform pipeline config file |
The --launch-claude, --model, --anthropic-api-key, and --anthropic-auth-token flags are CLI-only — they configure the launched client, not the proxy itself, and have no MALCOLM_* env counterparts.
Transform pipeline
Transforms are configured in malcolm.yaml. Each transform is a pluggable module that can modify requests and responses as they pass through the proxy. The list order defines the pipeline order.
transforms:
- ghostkey
- translation:
direction: anthropic_to_openai
Available plugins (the transforms: list accepts both):
| Plugin | Kind | Config | Description |
|---|---|---|---|
llm_annotator |
annotator | (none) | Extracts LLM metadata (model, messages, tools, usage) as annotations for the TUI |
ghostkey |
transform | (none) | Obfuscates secrets (API keys, tokens) before they reach the backend |
translation |
transform | direction |
Protocol translation: anthropic_to_openai or openai_to_anthropic |
Transforms mutate the traffic; annotators only observe it and emit structured metadata (no mutation, errors never break forwarding). A single plugin may implement both roles.
Additional plugins can be installed as pip packages — Malcolm discovers them at startup via Python entry points. See malcolm-proxy/malcolm-transform-example for a working reference implementation you can install directly (uv pip install git+https://github.com/malcolm-proxy/malcolm-transform-example) or fork as a template for your own.
See docs/configuration.md for details and docs/scenarios.md for complete setup examples with Claude Code, OpenCode, and various backends (Anthropic, OpenAI, Ollama).
How it works
flowchart LR
Client["Your LLM tool<br><span style="color: lightgray;">claude / opencode / curl</span>"]
Malcolm["Malcolm<br><span style="color: lightgray;">localhost:8900</span>"]
Backend["Real LLM API"]
DB[("SQLite<br><span style="color: lightgray;">malcolm.db</span>")]
Client -- request --> Malcolm
Malcolm -- forward --> Backend
Backend -- response --> Malcolm
Malcolm -- response --> Client
Malcolm -. logs .-> DB
Malcolm acts as a catch-all proxy: it accepts requests in any format (OpenAI, Anthropic, or any other HTTP API), captures the full request, forwards it to the configured backend, captures the full response (including streaming), and stores everything in a local SQLite database for later inspection.
When client and backend speak different protocols, Malcolm can translate between them on the fly. Add translation to the transform pipeline in malcolm.yaml with the desired direction, and Malcolm will automatically convert requests, responses, and streaming events; including path rewriting (from /v1/messages to /v1/chat/completions and viceversa).
See docs/architecture.md for the full architecture overview.
Development
# Install with dev dependencies
uv sync
# Run tests
uv run pytest
# Run the proxy
uv run malcolm
License
Malcolm is distributed under a MIT license. See LICENSE for more details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file malcolm_proxy-0.1.0.tar.gz.
File metadata
- Download URL: malcolm_proxy-0.1.0.tar.gz
- Upload date:
- Size: 2.9 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c961846078aea5e170bb4c3051c02b8c5c7bdbfa98edc8b62d7c3442aede5a2f
|
|
| MD5 |
90d337cd4078df1962f530a3a0983528
|
|
| BLAKE2b-256 |
2b1a27f6d4694fd730f1c6c6464ffa5e7a5dc7f68037252fda033df3404b3612
|
Provenance
The following attestation bundles were made for malcolm_proxy-0.1.0.tar.gz:
Publisher:
publish.yml on malcolm-proxy/malcolm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
malcolm_proxy-0.1.0.tar.gz -
Subject digest:
c961846078aea5e170bb4c3051c02b8c5c7bdbfa98edc8b62d7c3442aede5a2f - Sigstore transparency entry: 1474318760
- Sigstore integration time:
-
Permalink:
malcolm-proxy/malcolm@17d586c26556fb71d03ec81c784dcda90b0fd03b -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/malcolm-proxy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@17d586c26556fb71d03ec81c784dcda90b0fd03b -
Trigger Event:
release
-
Statement type:
File details
Details for the file malcolm_proxy-0.1.0-py3-none-any.whl.
File metadata
- Download URL: malcolm_proxy-0.1.0-py3-none-any.whl
- Upload date:
- Size: 43.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a813464a0bd4d909dd087be8c5a17477f9c91610e07cd7776c5e89d51b32840f
|
|
| MD5 |
f7a8d90955d9e85dabbbe384bb29f688
|
|
| BLAKE2b-256 |
9ad6c53a93092deb3f06032c0f9549bfc5ee12f969e1a021257ade31b6bf7891
|
Provenance
The following attestation bundles were made for malcolm_proxy-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on malcolm-proxy/malcolm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
malcolm_proxy-0.1.0-py3-none-any.whl -
Subject digest:
a813464a0bd4d909dd087be8c5a17477f9c91610e07cd7776c5e89d51b32840f - Sigstore transparency entry: 1474318790
- Sigstore integration time:
-
Permalink:
malcolm-proxy/malcolm@17d586c26556fb71d03ec81c784dcda90b0fd03b -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/malcolm-proxy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@17d586c26556fb71d03ec81c784dcda90b0fd03b -
Trigger Event:
release
-
Statement type: