conductor
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@conductorlist available providers"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
conductor
A tool gateway for AI agents — one MCP endpoint, many upstreams, real auth, real audit.
Highlights • Quick start • Configuration • Architecture • Writing a provider • Development
conductor speaks Model Context Protocol to your agent and aggregates tools from any number of backends behind bearer-token auth, group-based access control, and a full call-by-call audit log. Agents stop caring how a tool is implemented; you stop pasting API keys into agent configs.
Backends implement a common ToolProvider interface. Today: MCP upstream servers over stdio. Planned: OpenAPI, GraphQL, HTTP tools, and sandboxed code & CLI execution via OpenShell — install your CLI in the sandbox image and call it through sandbox_exec instead of writing a one-off MCP wrapper.
Why this exists
Connecting many MCP servers directly to an agent is a context-tax trap. Each upstream's tool list and schemas get pre-loaded into the LLM's context window before the user has typed a word — the official GitHub MCP alone is ~50K tokens, and model accuracy starts dropping past ~100K ("Lost in the Middle"). Conductor sits in front of N upstreams as a single MCP endpoint and gives agents lazy, governed discovery: see only what the caller's role permits, list providers and tools on demand instead of all-at-once, audit and rate-limit every call, and (planned) pre-filter tools by intent so the model never sees the other 95%. The CLI-vs-MCP debate misses the point — use a local CLI when you want context-cheap and personal; use conductor when you need governance, audit, RBAC, and one endpoint across teams.
┌──────────────┐ MCP/HTTP ┌───────────────────────────┐ stdio/… ┌─────────────┐
│ MCP client │ ──────────▶ │ conductor │ ──────────▶ │ upstream │
│ (agent/IDE) │ │ auth · groups · audit │ │ MCP server │
└──────────────┘ │ tool namespacing (__) │ └─────────────┘
└───────────────────────────┘ also: OpenAPI · GraphQL · …Related MCP server: mcp-hub
Highlights
One endpoint, many tools. Upstreams are abstracted behind a
ToolProvidercontract; tools are exposed namespaced as<provider>__<tool>.Lazy tool discovery. Built-in
conductor__list_providersandconductor__list_toolsmeta-tools let agents discover capabilities on demand instead of pre-loading every tool's schema into context. Provider-level descriptions and instructions are surfaced from each upstream'sserverInfo/initializepayload.Real auth. SHA-256 hashed API keys, timing-safe comparison. Plaintext keys never appear in config.
Group-based access control. Users belong to groups; groups grant providers (or
"*"for all).Audit everything. Every tool call records user, provider, tool, redacted args, duration, status, and request id.
Observability built in. Structured JSON logs; OpenTelemetry spans per call when
OTEL_EXPORTER_OTLP_ENDPOINTis set.Graceful lifecycle. Clean LIFO shutdown: HTTP → sessions → providers → audit → telemetry.
Zero-ceremony start. Ships a working config with a dummy key — clone, install, run, connect.
Quick start
RequiresNode.js ≥ 20.11 and pnpm ≥ 9.
pnpm install
pnpm build
CONDUCTOR_CONFIG=examples/conductor.json pnpm devThe bundled examples/conductor.json boots with user alice (API key changeme, hash baked in) and one upstream provider (everything via npx @modelcontextprotocol/server-everything).
Connect any MCP client:
URL: http://127.0.0.1:18080/mcp
Header: Authorization: Bearer changeme13 tools appear, namespaced as everything__*. Sanity check:
curl http://127.0.0.1:18080/health
# {"ok":true,"sessions":0,"providers":["everything"]}Use your own API key
pnpm hash-key my-secret-key
# sha256:7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069Copy examples/conductor.json, paste the hash into users[].apiKeyHash, point CONDUCTOR_CONFIG at your copy.
Configuration
A single JSON file, validated by Zod at load time. Schema lives at packages/server/src/conductor-config.ts. Unknown keys are rejected.
{
"server": { "host": "127.0.0.1", "port": 18080, "maxSessions": 100 },
"users": [
{ "name": "alice", "apiKeyHash": "sha256:…", "groups": ["admins"] }
],
"groups": [{ "name": "admins", "providers": ["*"] }],
"providers": [
{
"type": "mcp",
"name": "everything",
"transport": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-everything"],
"env": {}
}
],
"audit": { "type": "console" },
"telemetry": { "serviceName": "conductor", "otlpEndpoint": "" }
}Top-level fields
Field | Purpose |
|
|
|
|
|
|
| Discriminated on |
| Currently |
|
|
MCP provider options
Field | Default | Notes |
| — | How to spawn the upstream. |
|
| Deadline for the first |
|
| Per-call timeout forwarded as an |
|
| Exponential backoff between |
|
| |
|
|
Environment variables
Var | Meaning | Default |
| Path to |
|
| Overrides | — |
|
|
|
| OTLP HTTP base URL. Traces disabled if unset. | — |
Never commit plaintext API keys. Runpnpm hash-key <plaintext> and store only the resulting sha256:… digest.
Architecture
conductor is a pnpm workspace with strict dependency layering — core has no sibling deps, providers and the gateway depend only on core, and server wires it all together.
Package | Role |
| |
| |
| |
The HTTP MCP server. Auth, groups, audit wrapping, namespacing, session manager. Accepts any | |
The CLI ( |
Request lifecycle
Authenticate. Bearer token → timing-safe SHA-256 compare against each user's
apiKeyHash. Bad token →401.Resolve access. User's groups → set of reachable providers (
"*"expands to all configured).Session. New session = MCP
initializerequest. The gateway assembles a per-session MCP server that only advertises tools the caller may see.Namespace. Tools are registered as
<provider>__<tool>. The separator__is reserved; provider names must not contain it.Call. The handler decodes
<provider>__<tool>, forwards to the audit-wrapped provider with{ user, requestId, signal }. Providers must honoursignalfor cancellation.Audit + trace. One
AuditStore.insertCallper invocation with redacted args, status, duration, request id. One OTel span per call.Shutdown. LIFO: HTTP server → sessions → providers → audit → telemetry.
Repository layout
packages/
core/ foundation: ToolProvider, stores, logger, OTel, lifecycle
provider-mcp/ stdio MCP upstream adapter
provider-openshell/ OpenShell gRPC provider (stub; protos vendored)
gateway/ HTTP MCP server, auth, groups, audit, namespacing
server/ CLI binary: config + wiring
examples/
conductor.json working example config (alice / changeme)
scripts/
hash-api-key.ts prints sha256:<hex> for an API key
docs/
plans/ roadmap and implementation plansWriting a provider
Implement ToolProvider from @mcp-conductor/core:
import type {
ToolProvider,
ToolSpec,
ToolCallContext,
ToolCallResult,
} from "@mcp-conductor/core";
export class MyProvider implements ToolProvider {
readonly name = "my-provider";
async connect(): Promise<void> { /* … */ }
async close(): Promise<void> { /* … */ }
async listTools(): Promise<ToolSpec[]> { /* … */ }
async callTool(
name: string,
args: unknown,
ctx: ToolCallContext,
): Promise<ToolCallResult> {
// honour ctx.signal for cancellation
}
}Register it with the ProviderRegistry — or, for config-driven loading, add a branch to packages/server/src/provider-factory.ts and extend ProviderEntrySchema.
Development
pnpm build # tsc -p tsconfig.build.json per package
pnpm typecheck # tsc --noEmit (includes tests)
pnpm test # vitest — 255 tests across all packages
pnpm test:watch
pnpm clean # rm -rf dist/ .tsbuildinfoRun a single test file or test name:
pnpm test -- packages/gateway/tests/auth.test.ts
pnpm test -- packages/gateway/tests/auth.test.ts -t "rejects invalid bearer"This is ESM-only ("type": "module", NodeNext resolution). Local imports must use the .js extension — TypeScript compiles to ESM .js and NodeNext will not resolve extensionless imports.
Protocol notes
Transport. Streamable HTTP (
@modelcontextprotocol/sdk). Oneinitializerequest opens a session; subsequent requests must carrymcp-session-id. Non-initialize traffic without a session gets400.Tool schema. The gateway passes upstream JSON Schema through as
z.unknown()per property. Upstream remains the source of truth for validation — the gateway never rewrites tool inputs.Correlation. Every request gets an
X-Request-Id(echoed on the response and threaded throughToolCallContextand the audit log).
Seedocs/plans/ for the roadmap — OpenAPI, GraphQL, HTTP tools, and OpenShell sandboxed execution are next.
This server cannot be deployed
Maintenance
Related MCP Connectors
- gatewayOAuthai.sealgate
MCP gateway with runtime security policy, tool-call-level control, and audit of agent actions.
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
Zero-setup MCP gateway securely connecting AI to your tools with authentication and workflows
Zero-secret MCP gateway for AI agents: risk-scored, audited calls with human-in-the-loop approval.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceA gateway that aggregates multiple MCP servers into a single endpoint, namespacing their tools and forwarding calls, so an agent connects to one MCP to access the entire stack.MIT
- FlicenseNot gradedqualityBmaintenanceSingle gateway that aggregates dozens of upstream MCP servers, enabling AI clients to connect once and access all tools.-
- AlicenseNot gradedqualityDmaintenanceA unified MCP gateway that aggregates multiple MCP servers and API plugins behind a single endpoint with authentication, rate limiting, audit logging, REST API bridge, and web dashboard.8MIT

comind-mcpofficial
AlicenseAqualityBmaintenanceA gateway that connects MCP servers and REST APIs, allowing you to curate tools into groups and expose them as virtual MCP servers for agents.51MIT