diff --git a/mcp/.dev.vars.example b/mcp/.dev.vars.example new file mode 100644 index 000000000..b61d97bfc --- /dev/null +++ b/mcp/.dev.vars.example @@ -0,0 +1,4 @@ +# Uncomment and point at a self-hosted Honcho to bypass the managed API. +# wrangler dev reads this file automatically when it exists as `.dev.vars`. +# For deployed Workers, use: wrangler secret put HONCHO_API_URL +# HONCHO_API_URL=http://127.0.0.1:28000 diff --git a/mcp/.gitignore b/mcp/.gitignore index 51fb86912..492977a58 100644 --- a/mcp/.gitignore +++ b/mcp/.gitignore @@ -12,6 +12,7 @@ dist/ .env .env.local .env.production +.dev.vars # IDE files .vscode/ diff --git a/mcp/README.md b/mcp/README.md index e7e3cd14a..ba90710b4 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -69,6 +69,28 @@ Built on: - **[@modelcontextprotocol/sdk](https://www.npmjs.com/package/@modelcontextprotocol/sdk)** — `McpServer` for tool registration - **[@honcho-ai/sdk](https://www.npmjs.com/package/@honcho-ai/sdk)** v2 — Honcho TypeScript SDK +## Self-Hosted Honcho + +If you run Honcho yourself (for privacy, latency, or offline use), deploy the +MCP Worker alongside your instance and set `HONCHO_API_URL` in its +environment. + +**Local dev (`bun run dev`):** create `mcp/.dev.vars`: + +``` +HONCHO_API_URL=http://127.0.0.1:28000 +``` + +**Deployed Worker:** + +```bash +wrangler secret put HONCHO_API_URL +# paste your URL when prompted +``` + +When `HONCHO_API_URL` is unset the Worker routes to `https://api.honcho.dev`, +so this change is backward-compatible. + ## Development ### Setup diff --git a/mcp/src/config.ts b/mcp/src/config.ts index bd440343b..58e4307e5 100644 --- a/mcp/src/config.ts +++ b/mcp/src/config.ts @@ -8,11 +8,21 @@ export interface HonchoConfig { workspaceId: string; } +export interface Env { + HONCHO_API_URL?: string; +} + /** - * Parse configuration from request headers. + * Parse configuration from request headers and Worker env bindings. * Throws on missing required fields so callers get clear errors. + * + * The Honcho API URL is read from the `HONCHO_API_URL` env var when set, + * allowing operators to run this Worker alongside a self-hosted Honcho + * instance (see the "Self-Hosted Honcho" section in README.md). It is + * intentionally not exposed as a request header: routing public requests + * to an internal URL would be a latency and security regression. */ -export function parseConfig(request: Request): HonchoConfig { +export function parseConfig(request: Request, env: Env = {}): HonchoConfig { const authHeader = request.headers.get("Authorization"); const trimmedAuthHeader = authHeader?.trim(); if (!trimmedAuthHeader?.startsWith("Bearer ")) { @@ -37,7 +47,7 @@ export function parseConfig(request: Request): HonchoConfig { apiKey, userName, assistantName: request.headers.get("X-Honcho-Assistant-Name")?.trim() || "Assistant", - baseUrl: "https://api.honcho.dev", + baseUrl: env.HONCHO_API_URL?.trim() || "https://api.honcho.dev", workspaceId: request.headers.get("X-Honcho-Workspace-ID")?.trim() || "default", }; } diff --git a/mcp/src/index.ts b/mcp/src/index.ts index 15733beb0..4e8951989 100644 --- a/mcp/src/index.ts +++ b/mcp/src/index.ts @@ -1,5 +1,5 @@ import { createMcpHandler } from "agents/mcp"; -import { parseConfig, createClient } from "./config.js"; +import { parseConfig, createClient, type Env } from "./config.js"; import { createServer } from "./server.js"; const CORS_ORIGIN = "*"; @@ -16,7 +16,7 @@ const CORS_HEADERS = { export default { async fetch( request: Request, - env: unknown, + env: Env, executionCtx: ExecutionContext, ): Promise { if (request.method === "OPTIONS") { @@ -25,7 +25,7 @@ export default { let config; try { - config = parseConfig(request); + config = parseConfig(request, env); } catch (e) { const message = e instanceof Error ? e.message : "Invalid request";