# ToolHive Enterprise MCP Platform Comparison

## Overview

[Stacklok's ToolHive](https://github.com/stacklok/toolhive) (2K stars, Go) is the reference enterprise MCP platform. Unlike individual MCP servers (including ours), ToolHive is a management layer that runs, isolates, and governs multiple MCP servers. It is the most feature-complete enterprise MCP implementation as of May 2026.

---

## How ToolHive works

1. **Container-based isolation**: Each MCP server runs in an isolated OCI container (Docker or Kubernetes). This prevents one server from accessing another server's state, secrets, or network. The container lifecycle is managed by ToolHive's orchestrator.

2. **Centralized auth**: An embedded OAuth authorization server (vMCP) handles authentication for all managed MCP servers. Supports claim-based authorization for registry entries, OIDC federation, and enterprise IdP integration (Okta, Entra ID).

3. **Per-user per-tool rate limiting**: Rate limits can be configured per user, per tool, and per server. This is backed by Redis for horizontal scaling. The rate limiting is enforced at the ToolHive proxy layer, not inside individual MCP servers.

4. **Audit logging**: Every tool call is logged with user identity, tool name, arguments, result, and timing. The audit trail is queryable and exportable.

5. **Horizontal scaling**: Redis-backed session routing enables multiple ToolHive instances behind a load balancer. Session affinity is maintained via server-minted handles (aligned with SEP-2567 sessionless MCP).

6. **Local CLI mode**: Despite the Kubernetes-native architecture, ToolHive also supports a local CLI mode where containers run directly on the developer's machine. This bridges the gap between local development and production deployment.

---

## How our system differs

| Aspect            | ToolHive                         | Coda MCP Server                        |
| ----------------- | -------------------------------- | -------------------------------------- |
| **Scope**         | Platform managing N MCP servers  | Single MCP server                      |
| **Architecture**  | Proxy → container → MCP server   | Proxy → HTTP → Express API             |
| **Isolation**     | OCI containers                   | Process-level (stdio)                  |
| **Auth**          | Embedded OAuth server (vMCP)     | Bearer token forwarded to Express      |
| **Rate limiting** | Per-user per-tool (Redis-backed) | None at MCP layer (Express downstream) |
| **Audit**         | Centralized audit trail          | Structured stderr logging + OTel       |
| **Scaling**       | Redis-backed horizontal          | Single process per client              |
| **Language**      | Go                               | TypeScript                             |
| **Target**        | Enterprise multi-team deployment | Single developer/team                  |

---

## What we are NOT adopting and why

1. **Container-based isolation**: Our MCP server proxies to an Express API that already runs in a controlled environment (Fargate). The MCP server itself does not execute untrusted code — it is a thin proxy. Container isolation adds overhead without security benefit for our architecture.

2. **Embedded OAuth server**: Our auth is handled by the Express middleware stack, which already integrates with the platform's auth service. Adding an OAuth server to the MCP process would duplicate existing infrastructure.

3. **Multi-server management**: We have one MCP server exposing one tool set. ToolHive's value is managing heterogeneous MCP servers from different vendors — not our use case.

---

## What we are adopting

### 1. Per-tool rate limiting at the MCP layer

ToolHive's per-user per-tool rate limiting is the reference pattern. Our MCP server currently has no rate limiting — the spec says servers MUST rate-limit tool invocations. We should add a `RateLimiter` interface to `ToolExecutionContext` (same DI pattern as `ConcurrencyLimiter`), backed by `TokenBucketThrottler` from `@coda/async`.

**Implementation plan:**

- New interface: `RateLimiter { tryAcquire(): boolean }`
- New null object: `NULL_RATE_LIMITER` (always permits)
- Call `tryAcquire()` before `semaphore.acquire()` in `handleToolCall`
- On rejection: return `[RATE_LIMITED] Too many requests. Hint: Wait a moment and retry.` with `retryAfterMs`
- New env var: `CODA_MCP_RATE_LIMIT` (default 60 calls/min)
- Effort: ~2-3 hours

### 2. Audit logging pattern

ToolHive logs every tool call with identity, tool name, arguments, result, and timing. Our `logToolCall` already captures tool name, duration, ok/error, requestId, and traceparent — but not the tool arguments or result data. For audit purposes, we should consider logging a hash of the arguments (not the full arguments, which may contain PII).

### 3. Rate limit error code

ToolHive returns structured rate limit errors with `retryAfterMs`. Our error taxonomy (`tool-error.ts`) should add a `RATE_LIMITED` error code when we implement MCP-layer rate limiting.

---

## Patterns to monitor

- **Claim-based authorization**: ToolHive's claim-based auth maps well to our future COD-109 permission model. Claims like `tools.adjustments.write` could gate destructive tools.
- **MCP Apps in Playground**: ToolHive's interactive playground supports the ext-apps extension for inline UI. Relevant for revenue chart visualization in conversations.

---

## References

- [stacklok/toolhive](https://github.com/stacklok/toolhive) (GitHub)
- ToolHive docs: Enterprise MCP platform

---

_Comparison conducted 2026-05-10._
