Toolmesh

io.github.DunkelCloud由 GitHub 帳號 DunkelCloud 發布v0.5.0更新於 Oct 24, 2026

Self-hosted MCP gateway. Turn any REST API into MCP tools via YAML (DADL). Authz, audit, policies.

已驗證Streamable HTTP可網頁執行雲端與基礎設施開發者工具安全與監控

概覽

AI 產生的概覽

自架式的 MCP 閘道,可透過 YAML 把任意 REST API 變成 MCP 工具,並提供授權、憑證注入、策略攔截與稽核。

可以做什麼
  • 用宣告式 YAML 檔把 REST API 暴露成 MCP 工具,同時代理既有的 MCP 伺服器
  • 透過 Code Mode 的中繼工具探索並執行工具,在一次往返中批次呼叫多個 API
  • 依使用者、角色、方案或公司以 OpenFGA 檢查每次工具呼叫,並只向呼叫方顯示其可用的工具
  • 在執行時注入後端憑證,並以 JS 策略攔截輸入、過濾輸出
  • 把每次工具呼叫記錄到結構化日誌或可查詢的 SQLite 稽核紀錄,並提供 Prometheus 指標
  • 透過內建檔案代理上傳與下載檔案
適用情境
適合為 AI 代理執行多個 MCP 伺服器或 REST 後端,且需要集中式存取控制、密鑰管理、稽核日誌與較小工具定義負擔的情境。也適合希望不寫包裝伺服器就把內部 REST API 開放給助理的團隊。
執行需求
需要自行部署,通常使用 Docker Compose,監聽 8123 埠,MCP 端點為 /mcp。多數用戶端要求 HTTPS,因此需要 Caddy、Cloudflare Tunnel、nginx 或 Traefik 等終止 TLS 的反向代理。驗證需要 TOOLMESH_AUTH_PASSWORD 或 TOOLMESH_API_KEY,或在 config/users.yaml 與 config/apikeys.yaml 中設定使用者與金鑰;OAuth 狀態存放於 Redis。後端憑證以環境變數提供。
安裝前請注意
未設定密碼或 API 金鑰時所有請求都會被拒絕;從無授權版本升級的安裝會一直對所有已驗證呼叫方開放全部工具,直到選定預設或撰寫授權規則。檔案代理預設開啟且無配額上限。除錯日誌可能把查詢字串中的 API 金鑰寫入日誌,指標端點無需驗證。工具呼叫可能寫入或修改所連後端的資料,應仔細檢視授權與拒絕規則。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Toolmesh,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Web executable,透過 Streamable HTTP。 遠端服務在工作區中設定後即可從網頁執行環境執行。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

ToolMesh — Let AI agents touch real systems. Safely.

The missing control layer between AI agents and enterprise systems. ToolMesh turns uncontrolled AI tool calls into a governed, auditable process — and connects any REST API or MCP server in minutes, not months.

[Go] [License] [CI] [Go Report Card]

30 lines of YAML. No server to build.

In practice, MCP servers only expose a fraction of the REST API they wrap — and you'll hit the gaps fast. ToolMesh lets you replace the wrapper layer with .dadl files — a declarative YAML format that describes any REST API as MCP tools. No wrapper server to build, deploy, or maintain.

Current:    Claude → ToolMesh → MCP Server → REST APIWith DADL:  Claude → ToolMesh → REST API (via .dadl file)

You don't write the YAML by hand. You ask an LLM. Claude, GPT, Gemini — any model that knows the DADL spec generates a working .dadl file in seconds. Describe what you need, drop the file into config/dadl/, done.

"Create a DADL for the GitHub API — list repos, open issues, and create pull requests."

10 seconds. Works with any LLM that knows the format.

And unlike MCP gateways that just pass tool calls through, ToolMesh adds what production deployments actually need:

  • Credential Security — secrets injected at execution time, never in prompts or LLM client configs
  • Authorization — on by default: who may call which tool, by user, role, plan or company (OpenFGA, built in)
  • Input & Output Gating — JS policies block confidential data and filter responses
  • Audit Trail — every tool call recorded with structured logging or queryable SQLite

The Six Pillars

PillarWhat it doesBacked by
Any Backend30 lines of DADL replace a whole MCP server. Also proxies existing MCP servers.Go MCP SDK + DADL (.dadl files)
Code Mode15 MCP servers at once? Without ToolMesh, impossible. Code Mode cuts 50,000+ tokens to ~1,000.AST-parsed tool calls
Credential StoreSecrets injected at execution time — never in prompts, never in LLM client configsPer-request injection via Executor pipeline
OpenFGAFine-grained authorization, on by default with nothing to install (user → plan → tool). Example: free users get read-only, pro gets everything.OpenFGA, embedded or external
GateBlock confidential data before execution, redact PII in responsesgoja
AuditEvery tool call recorded and queryable — answer "what did that agent do?" with SQLslog / SQLite

Try the demo

Want to try ToolMesh before installing? Connect to our public demo instance — no Docker, no config, no API keys:

demo.toolmesh.io — Hacker News APIs via ToolMesh. Works with Claude Desktop, Claude Code, and ChatGPT. Login: dadl / toolmesh.

Quickstart

bash
# Clonegit clone https://github.com/DunkelCloud/ToolMesh.gitcd ToolMesh
# Configurecp .env.example .env# IMPORTANT: Set a password — without it, all requests are rejected:#   TOOLMESH_AUTH_PASSWORD=my-secret-password# Or set an API key for programmatic access:#   TOOLMESH_API_KEY=my-api-key
# Optional: local overrides (build locally, external OpenFGA, HTTPS proxy, ...)# cp docker-compose.override.yml.example docker-compose.override.yml# # then edit docker-compose.override.yml — picked up automatically by Docker Compose
# Start (authorization is on: the password/API key above gets every tool;# without a password or API key every request is rejected)docker compose up -d
# Verify it's running (default port: 8123)curl http://localhost:8123/health
# MCP endpoint: http://localhost:8123/mcp# Note: Most MCP clients require HTTPS — see TLS section below

TLS (important)

ToolMesh itself serves plain HTTP. Most MCP clients — including Claude Desktop — require HTTPS and will reject http:// URLs. You need a TLS-terminating reverse proxy in front of ToolMesh:

OptionWhen to use
CaddySelf-hosted with a public domain — automatic Let's Encrypt certs
Cloudflare TunnelNo open ports needed, zero-config TLS
nginx / TraefikAlready in your stack

For local development only, you can bypass TLS by editing claude_desktop_config.json by hand (the GUI enforces https://).

Connect to Claude Desktop

Add to your Claude Desktop MCP config:

json
{  "mcpServers": {    "toolmesh": {      "url": "https://toolmesh.example.com/mcp"    }  }}

For local development without TLS proxy:

json
{  "mcpServers": {    "toolmesh": {      "url": "http://localhost:8123/mcp"    }  }}

Connect to Claude.ai (Custom Connector)

ToolMesh supports OAuth 2.1 with PKCE S256 for remote access. Configure users in config/users.yaml and use the public HTTPS URL as the MCP endpoint.

Authentication

ToolMesh supports two authentication methods that can be used independently or together. All OAuth state (tokens, auth codes, clients) is persisted in Redis and survives server restarts.

OAuth 2.1 (Interactive Login)

Define users in config/users.yaml with bcrypt-hashed passwords:

yaml
users:  - username: admin    password_hash: "$2a$10$..."    company: dunkelcloud    plan: pro    roles: [admin]

Generate password hashes with any bcrypt-capable utility:

bash
htpasswd -nbBC 10 "" "my-password" | cut -d: -f2

For single-user setups, TOOLMESH_AUTH_PASSWORD still works as a fallback. Configure the identity with TOOLMESH_AUTH_USER, TOOLMESH_AUTH_PLAN, and TOOLMESH_AUTH_ROLES (defaults: owner, pro, admin).

API Keys (Programmatic Access)

Define API keys in config/apikeys.yaml. Each entry names its key by the SHA-256 of the key:

yaml
keys:  - key_sha256: "<SHA-256 of the key, 64 hex characters>"    user_id: claude-code-user    company_id: dunkelcloud    plan: pro    roles: [tool-executor]

Generate a key and its key_sha256:

bash
KEY="tm_$(openssl rand -hex 32)"printf '%s' "$KEY" | sha256sum | cut -d' ' -f1

Each key maps to a distinct user identity with its own plan and roles, which flow through to authorization.

Entries with a bcrypt key_hash, as files written for earlier versions have them, keep working without any change. Until its key has been used once after startup, such an entry costs a bcrypt comparison for every bearer credential that nothing else recognizes; key_sha256 is looked up directly and is meant for randomly generated keys. See docs/configuration.md for the details and for how the two forms differ.

For single-key setups, TOOLMESH_API_KEY still works as a fallback. The same TOOLMESH_AUTH_USER, TOOLMESH_AUTH_PLAN, and TOOLMESH_AUTH_ROLES variables control the identity.

Unauthenticated Requests

A request to /mcp without a valid credential is answered 401 with a WWW-Authenticate: Bearer challenge that carries resource_metadata, as the MCP authorization specification asks. MCP clients start the OAuth flow, or refresh an expired token, on that status. Such requests are logged at INFO and counted in toolmesh_logins_total{method="anonymous"}. See docs/configuration.md.

DCR Rate Limiting

Dynamic Client Registration is rate-limited to 5 registrations per hour per IP to prevent abuse (with Redis; a Redis outage lets registrations through and is logged). A registered client that never obtains a token is forgotten after a day, and one that does after 90 days without a new token (TOOLMESH_DCR_CLIENT_TTL, TOOLMESH_DCR_CLIENT_IDLE_TTL). See docs/configuration.md.

File Broker

The file broker (upload_file, POST /files/upload, download URLs under /blobs/) can be switched off with TOOLMESH_FILE_BROKER_ENABLED=false and bounded with a quota in bytes, in total and per user (TOOLMESH_FILE_BROKER_MAX_TOTAL_BYTES, TOOLMESH_FILE_BROKER_MAX_BYTES_PER_USER). It is on and unbounded by default. See docs/configuration.md.

Login Throttling

Failed password logins are limited per account and per client address: by default 5 failures for one account from one address, 20 for one account from all addresses together, and 50 from one address across all accounts, each within 15 minutes. Beyond that the login answers 429 until the window has passed. Every failed login is logged at WARN and counted in toolmesh_logins_total{result="failure"}. Already issued tokens and API keys are not affected by a lockout. See docs/configuration.md for the variables and the reverse-proxy requirement of the per-address limits.

The number of bcrypt comparisons that run at the same time is bounded for the whole process (TOOLMESH_BCRYPT_MAX_CONCURRENT, default: half of the available CPUs), so that password logins cannot take the CPU away from everything else. See docs/configuration.md.

Authorization

Every tool call is checked, and tool listings show each caller only the tools it may call. Nothing has to be set up: ToolMesh runs OpenFGA inside its own process and keeps its data in data/authz.db.

TOOLMESH_AUTHZ_MODE selects the engine:

ModeBehavior
embedded (default)Checks run inside ToolMesh. Grants come from a preset or from config/authz.yaml.
externalChecks go to an OpenFGA server you operate (OPENFGA_API_URL, OPENFGA_STORE_ID). ToolMesh never writes to it.
disabledNo checks. Reported at every start and recorded in the audit trail.

Grants refer to the users, roles, plans and companies from users.yaml, apikeys.yaml or the single password/API key. Without a grants file a preset applies:

PresetWho may call what
single-userthe owner (TOOLMESH_AUTH_USER) and role admin: every tool. Default without users.yaml/apikeys.yaml.
teamas single-user, plus plan pro: every tool, plan free: every read tool. Default with users.yaml or apikeys.yaml.
openevery authenticated caller: every tool

For finer control, copy config/authz.yaml.example to config/authz.yaml; grants can name all tools, backends, single tools or DADL access classes (read, write, ...), denies block any of these and win over every grant, and changes take effect within seconds:

yaml
grants:  - plan: pro    backends: [github, deepl]  - plan: free    access: [read]  - role: support    tools: [zammad_list_tickets]denies:  - plan: pro    tools: [stripe_create_refund]

An installation upgraded from a version without authorization keeps every tool open to every authenticated caller (preset open) and says so at every start, until you pick a preset or write grants. OPENFGA_MODE is deprecated: restrict maps to external, bypass to embedded. See docs/configuration.md.

Security posture at startup

ToolMesh is secure-by-default: it ships with no individual default that relaxes a control silently. At boot it logs a single security-posture summary that lists every control still in a relaxed state (missing auth credential, authorization disabled or open to everyone, open CORS, debug tools, login limits switched off) together with how to harden it. In the default production posture these are logged at WARN; set TOOLMESH_DEV=true on a local-development machine to report the same facts once at INFO so the warnings don't become background noise. TOOLMESH_DEV changes only the log level of this summary — it never relaxes a setting on its own.

Configuration

See docs/configuration.md for all environment variables.

Timeout tuning

VariableDefaultDescription
TOOLMESH_MCP_TIMEOUT120HTTP client timeout (seconds) for calls to downstream MCP servers
TOOLMESH_EXEC_TIMEOUT120Tool execution timeout (seconds) — context deadline for backend calls

Increase these for backends that need more time (e.g. browser-based web fetchers):

bash
TOOLMESH_MCP_TIMEOUT=180TOOLMESH_EXEC_TIMEOUT=180

Logging

ToolMesh uses structured logging via slog. The default level is info. Set LOG_LEVEL=debug to trace the full MCP request/response flow when diagnosing a problem — but note that debug logs include complete request URLs and payloads, so for query-string API keys the credential ends up in the log. Keep debug off in production. Per-backend debug files, log formats, and all logging variables are documented in docs/configuration.md.

Metrics (Prometheus)

ToolMesh exposes Prometheus metrics on a separate listener (default host port 9090) — login counts by method and result, tool-call rates by backend and outcome, and a latency histogram with REST-tuned buckets. The endpoint is unauthenticated, so the Compose file binds the host port to 127.0.0.1 by default (reachable locally and from Prometheus on the same Docker network, never from a public interface). Set TOOLMESH_METRICS_HOST=0.0.0.0 only if you scrape from another host and have firewalled the port yourself.

Configure via TOOLMESH_METRICS_PORT, TOOLMESH_METRICS_ENABLED, and TOOLMESH_METRICS_LABEL_TOOL in .env. See docs/metrics.md for the full schema, example PromQL queries, and a sample scrape configuration.

Debug tools (opt-in)

Set TOOLMESH_DEBUG_TOOLS=true in .env to expose two diagnostic MCP tools — debug_echo (returns size and SHA-256 of the supplied payload, no backend) and debug_generate (produces a printable string of N bytes). Use them to probe transport-level size limits between the calling LLM and ToolMesh, or to verify round-trip byte integrity. Off by default; intended for development and incident triage, not production.

Architecture

See docs/architecture.md for the full architecture documentation.

                          ┌─────────────────────────────────┐                          │          ToolMesh               │                          │                                 │                          │  Redis · OpenFGA · Audit        │                          │  Credential Store · JS Gate     │                          │                                 │AI Agent ──MCP──────────▶ │   AuthZ ▸ Creds ▸ Gate ▸ Exec  │                          │                                 │                          └──┬──────┬───────┬───────┬───────┘                             │      │       │       │                          MCP Client  .dadl   .dadl   .dadl                             │      │       │       │                             ▼      ▼       ▼       ▼                          MCP     Stripe  GitHub  Vikunja                          Server   API     API     API

Adding an External MCP Server

Create or edit config/backends.yaml:

yaml
backends:  - name: memorizer    transport: http    url: "https://memorizer.example.com/mcp"    api_key_env: "MEMORIZER_API_KEY"

Set the credential as an environment variable:

bash
CREDENTIAL_MEMORIZER_API_KEY=sk-mem-xxxxx

Tools from each backend are exposed with a prefix (e.g. memorizer_retrieve_knowledge). Credentials are injected by the Executor at runtime via the CredentialStore — the LLM never sees API keys.

REST Proxy Mode (DADL)

When an MCP server doesn't expose an endpoint you need, describe it in a .dadl file and ToolMesh calls the REST API directly — no wrapper server needed. Both modes run in parallel.

Add a REST backend to config/backends.yaml:

yaml
backends:  - name: vikunja    transport: rest    dadl: /app/dadl/vikunja.dadl    url: "https://vikunja.example.com/api/v1"

For internal services with private IPs or self-signed certificates:

yaml
backends:  - name: internal-api    transport: rest    dadl: internal.dadl    url: "https://192.168.1.50:8443/api"    allow_private_url: true    # allow private/loopback addresses (default: true)    tls_skip_verify: true      # accept self-signed certificates (default: false)

Want Claude to list GitHub issues? Here's all it takes:

yaml
tools:  list_issues:    method: GET    path: /repos/{owner}/{repo}/issues    description: "List issues for a repository"    params:      owner: { type: string, in: path, required: true }      repo:  { type: string, in: path, required: true }      state: { type: string, in: query }

ToolMesh handles auth, pagination, retries, and error mapping. DADL supports bearer tokens, OAuth2, session auth, API keys, automatic pagination, retry with backoff, response transformation, composite tools, and more.

For the full spec, examples, and the community registry, see dadl.ai. The fastest way to create a .dadl file is asking any LLM that knows the format.

Code Mode

Connect 15 MCP servers to a single AI agent? Without ToolMesh, that simply does not work — the context window fills up, the client chokes. Code Mode makes it possible.

Instead of exposing hundreds of individual tool definitions (50,000+ tokens), ToolMesh exposes two meta-tools: discover_tools and execute_code. The LLM gets compact TypeScript interfaces (~1,000 tokens) and writes JavaScript against them:

javascript
const repos = await toolmesh.github_list_repos({ sort: "updated" });const issues = await toolmesh.github_list_issues({  owner: repos[0].owner.login,  repo: repos[0].name,  state: "open"});

Multiple API calls in a single round-trip. ToolMesh parses the code, extracts tool calls, and routes them through the full execution pipeline.

Extension Model

ToolMesh uses a registry-based extension model inspired by Go's database/sql driver pattern. Three component types are extensible via init() registration:

ComponentBuilt-inConfig
Credential StoreembeddedCREDENTIAL_STORE=<name>
Tool Backendmcp, rest (DADL), echoconfig/backends.yaml
Gate EvaluatorgojaGATE_EVALUATORS=<list>

Enterprise extensions (InfisicalStore, VaultStore, Compliance-LLM, etc.) are planned and will be included via Go build tags: go build -tags enterprise ./cmd/toolmesh.

See docs/architecture.md for details.

Who's behind this

ToolMesh is built and maintained by Axel Dunkel at Dunkel Cloud GmbH — a German company building infrastructure that connects AI agents to real systems. ToolMesh and DADL grew out of the production cloud and edge infrastructure we've run for businesses for years, so this isn't a side project behind an anonymous handle: it runs in our own stack, and the same people who answer [email protected] write the code.

The companion DADL registry currently ships 27 community API descriptions covering ~3,300 generated tools — from a 4-tool Hacker News reader to a 608-tool NetBox DCIM/IPAM surface — each with a visible API-coverage percentage so you can see exactly how complete it is.

Contributing

See CONTRIBUTING.md.

License

Apache 2.0 — Copyright 2025–2026 Dunkel Cloud GmbH

來源:README.md,提交 6b540dc

工具

0
SourceWeft 尚未讀取此伺服器的工具。

版本歷史

3
  1. v0.5.0最新Oct 23, 2026
  2. v0.4.1Sep 25, 2026
  3. v0.3.0Sep 16, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通必需25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式必需10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分