Simple LLM Terminal Agent¶
This repository contains a single-file Python agent (agent.py) that runs in a terminal. It is compatible with OpenAI-style chat APIs, supports native tools (webfetch, readfile, and optional bash), and integrates with one MCP server over HTTP JSON‑RPC. Everything runs in the terminal with concise output and optional debug logs.
Warning
This is a playground project developed to understand how agents work, not a production ready agent. Be very careful with the bash tool :-)
Usage¶
- OpenAI-compatible Endpoint:
-
uv run agent.py --model mlx-community/Qwen3-4B-Instruct-2507-4bit --api-base http://127.0.0.1:8080/v1 --api-key XXX -
Azure OpenAI v1 (API key):
-
uv run agent.py --model-provider azure --model <deployment_or_model_name> --azure-endpoint https://<resource>.openai.azure.com --api-key $AZURE_API_KEY -
With MCP tools:
uv run agent.py --model <model> --api-base <url> --mcp-url http://127.0.0.1:3000/mcp --mcp-config mcp-config.json
Notes
- Exit with exit or Ctrl+D.
- History is saved to ~/.agent_history.
- Thinking traces: models that emit reasoning_content will show it in dim gray unless --no-reasoning is supplied.
- Prompt/history behavior:
- On macOS with libedit, the input prompt is intentionally uncolored to avoid display glitches when navigating history.
- With GNU readline (e.g., when running via uv where gnureadline is available), the prompt is colored safely using non-printing markers so arrows/history render correctly.
- The agent fetches MCP tools at startup via tools/list and exposes them to the model; calls route through tools/call. SSE (text/event-stream) responses are supported.
Flags¶
--model <name>: model identifier or Azure deployment name--api-base <url>: base URL for OpenAI-compatible APIs (non-Azure)--api-key <key>: API key for OpenAI or Azure (OPENAI_API_KEY,AZURE_OPENAI_API_KEY, orAZURE_API_KEY)--model-provider <openai|azure>: select provider (openaidefault)--azure-endpoint <url>: Azure OpenAI endpoint, e.g.,https://<resource>.openai.azure.com(agent uses/openai/v1)--mcp-url <url>: MCP JSON‑RPC endpoint (optional)--mcp-config <path>: JSON file with MCP headers/extra params (see below)--mcp-ca-bundle <path>: Custom CA bundle for MCP HTTPS (use when your cert isn’t in Python’s trust store)--mcp-insecure: Skip MCP TLS verification (UNSAFE; for testing self-signed certs)--system-prompt <text>: override system prompt (optional)--debug-tools: show tool calls and I/O (incl. MCP JSON‑RPC)--debug-llm: show raw LLM traffic (requests/responses)--debug: convenience flag enabling both--debug-toolsand--debug-llm--no-color: disable colored terminal output--reasoning-effort <low|medium|high>: include{"reasoning":{"effort":...}}in chat requests (defaultmedium)--no-reasoning: hide thinking traces (reasoning_content); shown in dim gray by default--bash-enabled: Enable 'bash' tool. Security Warning! Use only n sandboxed environment!--readfile-bytes <int>: limit forreadfiletool output (first N bytes; default 4096)--webfetch-insecure: disable TLS certificate verification forwebfetch(UNSAFE; accepts self-signed certs)
Native Tools (exposed to the model)¶
bash({ command })- Runs a shell command and returns
{ stdout, stderr, exit_code } - Defaults:
timeout=30s,cwd=current directory - Availability: disabled by default; enabled when
--bash-enabledis provided. - WARNING Use only in sandboxed environment!
webfetch({ url })- Fetches the URL and returns plain text extracted from HTML
- Defaults:
user_agent="Mozilla/5.0 (Agent)", timeout=30s, TLS verification ON (use--insecureto allow self-signed certs) readfile({ filename })- Reads the first
Nbytes (set via--readfile-bytes) of a file in the current working directory - Returns
{ filename, bytes_read, content }
MCP Tools (dynamic)¶
- At startup, the agent POSTs
tools/listto--mcp-urland converts each tool to OpenAI-style tool definitions using theirinputSchema. - When the model calls one of these tools, the agent POSTs
tools/callwith{ name, arguments }and returns the server’s result. - Accept headers:
application/json, text/event-streamto support servers that stream JSON‑RPC via SSE. - Name normalization: the agent tolerates underscore/hyphen and case variations when matching tool names (e.g.,
dp_setmaps todp-set).
MCP config file (headers & extra params)¶
- Point the agent to a JSON file with
--mcp-config mcp-config.json(or envMCP_CONFIG). - Format:
json { "headers": { "Authorization": "Bearer TOKEN", "X-Env": "demo" }, "extra_params": { "context": { "site": "plant-1" } } } headersare merged into every MCP HTTP request (override defaults if keys clash).extra_paramsare merged into every JSON-RPCparamsobject (per-call params override the same keys).
Tool call logging¶
- Every tool call prints a concise, single-line message (regardless of debug flags), e.g.:
[tool] bash ls -la /tmp[tool] webfetch https://example.com[tool] dp_set -> dp-set {"datapoints":[...]}- The arrow indicates a normalized MCP tool name used for dispatch.
- If an assistant message includes both text and tool calls, the text is printed once (
assistant > ...) before the tools are executed.
Prompting tips¶
- To encourage tool calls with correct parameters, ask explicitly (e.g., “Use dp-set with its schema and set SYS:TEST to 1”).
- If a model does not support tool calling, you may need a different model or endpoint.
MCP Test with the Mockup¶
- A simple MCP server with SCADA mockup can be found in
MCPMOCKUP/. -
Usage:
usage: scadamcpmockup.py [-h] [--host HOST] [--port PORT] [--log-level LOG_LEVEL] --token TOKEN [--mount-path MOUNT_PATH] [--transport {sse,streamable-http}] [--issuer-url ISSUER_URL] [--resource-url RESOURCE_URL] -
Example:
uv run MCPMOCKUP/scadamcpmockup.py --token TOKEN --port 3000
Requirements¶
- Python >=3.12
openaiPython packagefastmcpPython package
File Overview¶
agent.py: main agent (single file)README.md: this documentMCPMOCKUP/scadamcpmockup.py: the MCP mockup