Skip to content
dawsonamfPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

toolbelt

toolbelt

Toolbelt lets you turn any chatbot with MCP support into a fully fledged coding agent.

It decouples your agentic infrastructure from the chatbot itself. Your tools, permissions, and infrastructure live in the MCP server, not baked into a vendor's product. Swap between web-based chatbots, terminal-based agents, and even chatbot apps on your iPhone without extensive work maintaining your setup.

It's a single MCP server bundling shell, filesystem, web search (Exa), library docs (Context7), and skills discovery tools. Supports two transport modes:

  • Local (stdio) — spawned directly by LM Studio or any stdio MCP client. No network exposure.
  • Remote (HTTP) — serves over Streamable HTTP behind a Cloudflare Quick Tunnel with bearer-token auth and full OAuth 2.0 support.

Tools

Tool Description
bash Run a shell command and return combined stdout/stderr
read_file Read a file from disk
write_file Write/overwrite a file (creates parent directories)
glob Glob filesystem search (recursive with **)
grep Search file contents with ripgrep (falls back to grep -rn)
list_dir List directory entries
list_skills Discover SKILL.md files with YAML frontmatter from a skills directory
web_search Search the web via Exa (titles, URLs, snippets)
web_crawl Fetch the full text content of a URL via Exa
context7_resolve Resolve a library name to a Context7-compatible library ID
context7_docs Fetch up-to-date library docs from Context7

Toggle any tool on/off in tools.toml — set enabled = false to disable. Restart the server to pick up changes. See Custom tools for how to add your own.

Installation

Pick whichever method suits you:

Quick install (no clone)

pip install git+https://github.com/dawsonamf/toolbelt.git

One-liner script

curl -fsSL https://raw.githubusercontent.com/dawsonamf/toolbelt/main/install.sh | bash

Clone + editable install (for development)

git clone https://github.com/dawsonamf/toolbelt.git
cd toolbelt
pip install -e .

PyPI (coming soon)

pip install toolbelt

API keys

Set API keys (add to your shell profile to make them permanent):

export EXA_API_KEY=...        # required for web_search / web_crawl
export CONTEXT7_API_KEY=...   # optional — avoids rate limits

Then run:

toolbelt --local   # local/stdio mode
toolbelt           # remote mode (requires cloudflared)

Local mode (stdio)

Run with --local to use stdio transport — no ports, no network:

python server.py --local

Wire to LM Studio

Edit ~/.lmstudio/mcp.json:

{
  "mcpServers": {
    "toolbelt": {
      "command": "toolbelt",
      "args": ["--local"],
      "env": {
        "EXA_API_KEY": "your_exa_key",
        "CONTEXT7_API_KEY": "your_context7_key"
      }
    }
  }
}

LM Studio spawns the process over stdio — fully localhost, no sockets.

Remote mode (Cloudflare tunnel)

Run without --local to start a Streamable HTTP server tunneled through a Cloudflare Quick Tunnel (no account required):

python server.py

On first run a bearer token is auto-generated and saved to ~/.toolbelt/token (mode 600). The tunnel URL and MCP client config snippet are printed to the console:

───────────────────────────────────────────────────────
  Toolbelt running (remote)

  Add to the "mcpServers" section of your MCP client config:

    "toolbelt": {
      "type": "streamableHttp",
      "url": "https://<random>.trycloudflare.com/mcp",
      "headers": {
        "Authorization": "Bearer <your-token>"
      }
    }

  OAuth (Claude Desktop / OAuth-only clients):

    MCP URL:       https://<random>.trycloudflare.com/mcp
    Client ID:     <auto-generated>
    Client Secret: <auto-generated>
───────────────────────────────────────────────────────

Requires cloudflared (brew install cloudflared on macOS).

Credential rotation

Rotate all auth credentials (bearer token, OAuth client ID, and client secret) in one shot:

toolbelt --rotate-auth

This deletes and regenerates ~/.toolbelt/token, client_id, and client_secret, prints the new values, and exits. Any MCP clients using the old credentials will need to be updated with the new ones.

OAuth 2.0

Remote mode exposes full OAuth 2.0 endpoints for clients that require them (e.g. Claude Desktop):

Endpoint Purpose
/.well-known/oauth-protected-resource RFC 9728 Protected Resource Metadata
/.well-known/oauth-authorization-server RFC 8414 Authorization Server Metadata
/oauth/authorize Authorization endpoint (code grant)
/oauth/token Token endpoint

Supported grant types: authorization_code (with PKCE / S256) and client_credentials. OAuth client ID and secret are auto-generated on first run and stored in ~/.toolbelt/.

Working directory

By default, tools operate relative to the process's current directory. You can set a fixed working directory so all path-based tools (bash, read_file, write_file, glob, grep, list_dir) resolve relative paths against it.

In order of priority (highest first):

  1. CLI flag — toolbelt --cwd /path/to/project
  2. Environment variable — export WORK_DIR=/path/to/project
  3. config.json — { "work_dir": "/path/to/project" }

A common pattern is to launch with the current shell directory:

toolbelt --local --cwd .

Absolute paths always pass through unchanged regardless of the WORK_DIR setting.

Self-hosted server (permanent URL)

The Cloudflare Quick Tunnel generates a random URL on every startup. If you want a stable, permanent URL, you can host the server on any VPS provider. The server already works over HTTP — just run it on a VPS with a fixed public address and configure your bearer token once. No code changes needed.

Configuration

tools.toml

Lives next to server.py. Toggle individual tools:

[tools.bash]
enabled = true

[tools.list_skills]
enabled = false

~/.toolbelt/config.json (optional)

Override settings like the skills directory or OAuth credentials:

{
  "skills_dir": "/path/to/skills",
  "client_id": "custom-id",
  "client_secret": "custom-secret"
}

Environment variables

Variable Description
EXA_API_KEY Exa API key (required for web_search / web_crawl)
CONTEXT7_API_KEY Context7 API key (optional, avoids rate limits)
TOOLBELT_CONFIG Override path to tools.toml
CONFIG_FILE Override path to config.json
SKILLS_DIR Override skills directory (default ~/.claude/skills)
WORK_DIR Default working directory for path-based tools
CLIENT_ID_FILE Override path to OAuth client ID file
CLIENT_SECRET_FILE Override path to OAuth client secret file

Skills discovery

The list_skills tool scans a directory for SKILL.md files and returns their paths and YAML frontmatter. The default directory is ~/.claude/skills, configurable via the SKILLS_DIR env var or skills_dir in config.json.

Custom tools

All tools are defined in toolbelt_server/__init__.py. Each tool is a plain Python function decorated with @mcp.tool(), optionally gated by the enabled() helper so it can be toggled in tools.toml.

Adding a tool

  1. Open toolbelt_server/__init__.py and add your function anywhere after the mcp = FastMCP("toolbelt") line (the # -- custom tools -- comment marks a good spot):
if enabled("my_tool"):
    @mcp.tool()
    def my_tool(arg: str) -> str:
        """Description shown to the LLM."""
        return f"got {arg}"
  1. Add a toggle entry in tools.toml:
[tools.my_tool]
enabled = true
  1. Restart the server.

How it works

  • mcp is a FastMCP server instance. @mcp.tool() registers the function as an MCP tool — the function name becomes the tool name, the docstring becomes its description, and the type-annotated parameters become the tool's input schema.
  • enabled("name") reads tools.toml and returns False when a tool is explicitly disabled, so the decorator is never called and the tool is never registered.
  • Tools that operate on file paths should use resolve_path(path) to respect the WORK_DIR setting.

Example: a timestamp tool

import datetime

if enabled("timestamp"):
    @mcp.tool()
    def timestamp() -> str:
        """Return the current UTC timestamp."""
        return datetime.datetime.now(datetime.timezone.utc).isoformat()
[tools.timestamp]
enabled = true

Development

pip install -e ".[dev]"   # install with dev deps (pytest, ruff)

make test       # run tests
make lint       # lint with ruff
make fmt        # auto-format with ruff
make run        # start in remote mode
make run-local  # start in local/stdio mode
make setup      # create an isolated .venv and install deps
make clean      # remove .venv, caches, bytecode

Tests

pytest tests/

Tests cover tool registration, config toggling, auth middleware, OAuth flows, and server startup.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages