diff --git a/mcp/README.md b/mcp/README.md index 3bd24217..21f345db 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -45,6 +45,7 @@ Every workspace-scoped tool takes a `workspace_id` argument. If you set `X-Honch ``` src/ index.ts # Worker entry point — parse config, delegate to MCP handler + stdio.ts # Local stdio host (bun run stdio) server.ts # createServer() — registers all tools on an McpServer config.ts # HonchoConfig, parseConfig(), createClientFactory() types.ts # ToolContext, result helpers @@ -84,6 +85,25 @@ wrangler secret put HONCHO_API_URL When `HONCHO_API_URL` is unset the Worker routes to `https://api.honcho.dev`, so this change is backward-compatible. +## Local stdio + +For a local Honcho instance, or any MCP client that spawns a process, run the +stdio host instead of the Worker. Point the client at `src/stdio.ts` directly +— `bun run stdio` writes lifecycle output to stdout and breaks the protocol. + +```bash +cd mcp && bun install + +claude mcp add honcho -- \ + -e HONCHO_API_KEY=hch-your-key-here \ + -e HONCHO_API_URL=http://127.0.0.1:28000 \ + -e HONCHO_WORKSPACE_ID=my-workspace \ + bun "$(pwd)/src/stdio.ts" +``` + +`HONCHO_API_URL` defaults to `https://api.honcho.dev`. `HONCHO_WORKSPACE_ID` is +optional; without it, pass `workspace_id` on each tool call. + ## Development ### Setup diff --git a/mcp/bunfig.toml b/mcp/bunfig.toml new file mode 100644 index 00000000..5c44f0c9 --- /dev/null +++ b/mcp/bunfig.toml @@ -0,0 +1,2 @@ +[loader] +".md" = "text" diff --git a/mcp/package.json b/mcp/package.json index 900df5cf..7f24e94e 100644 --- a/mcp/package.json +++ b/mcp/package.json @@ -11,6 +11,7 @@ "scripts": { "preinstall": "node -e \"const ua=process.env.npm_config_user_agent||'';if(ua.includes('npm')&&!ua.includes('bun')){console.error('āŒ Please use bun instead of npm!\\nšŸ“¦ Run: bun install\\n🌐 Install bun: https://bun.sh/');process.exit(1)}\"", "dev": "wrangler dev", + "stdio": "bun src/stdio.ts", "deploy": "wrangler deploy", "deploy:staging": "wrangler deploy --env staging" }, diff --git a/mcp/src/config.ts b/mcp/src/config.ts index ed70bbc1..ea15c135 100644 --- a/mcp/src/config.ts +++ b/mcp/src/config.ts @@ -3,7 +3,7 @@ import { Honcho } from "@honcho-ai/sdk"; export interface HonchoConfig { apiKey: string; baseUrl: string; - /** From X-Honcho-Workspace-ID when set. */ + /** From X-Honcho-Workspace-ID (HTTP) or HONCHO_WORKSPACE_ID (stdio). */ workspaceId?: string; } @@ -12,6 +12,12 @@ export interface Env { ALERT_WEBHOOK_URL?: string; } +export interface EnvConfig { + HONCHO_API_KEY?: string; + HONCHO_API_URL?: string; + HONCHO_WORKSPACE_ID?: string; +} + /** * Parse configuration from request headers and Worker env bindings. * Throws only when the Authorization bearer token is missing/empty. @@ -48,8 +54,23 @@ export function parseConfig(request: Request, env: Env = {}): HonchoConfig { }; } +/** Parse configuration from process env for the stdio host. */ +export function parseEnvConfig(env: EnvConfig): HonchoConfig { + const apiKey = env.HONCHO_API_KEY?.trim(); + if (!apiKey) { + throw new Error( + "Missing HONCHO_API_KEY. Set HONCHO_API_KEY to your Honcho API key.", + ); + } + return { + apiKey, + baseUrl: env.HONCHO_API_URL?.trim() || "https://api.honcho.dev", + workspaceId: env.HONCHO_WORKSPACE_ID?.trim() || undefined, + }; +} + export const MISSING_WORKSPACE_ID_MESSAGE = - "Missing workspace_id. Pass workspace_id on the next tool call, or set the X-Honcho-Workspace-ID header on the connection so it is used automatically."; + "Missing workspace_id. Pass workspace_id on the next tool call, or set X-Honcho-Workspace-ID (HTTP) / HONCHO_WORKSPACE_ID (stdio)."; export function resolveWorkspaceId( config: HonchoConfig, diff --git a/mcp/src/stdio.ts b/mcp/src/stdio.ts new file mode 100644 index 00000000..3a351651 --- /dev/null +++ b/mcp/src/stdio.ts @@ -0,0 +1,30 @@ +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { + createClientFactory, + createUnscopedClient, + parseEnvConfig, +} from "./config.js"; +import { createServer } from "./server.js"; + +declare const process: { + env: Record; + exit(code?: number): never; +}; + +try { + const config = parseEnvConfig({ + HONCHO_API_KEY: process.env.HONCHO_API_KEY, + HONCHO_API_URL: process.env.HONCHO_API_URL, + HONCHO_WORKSPACE_ID: process.env.HONCHO_WORKSPACE_ID, + }); + const server = createServer({ + config, + clientFor: createClientFactory(config), + unscoped: createUnscopedClient(config), + }); + await server.connect(new StdioServerTransport()); +} catch (e) { + const message = e instanceof Error ? e.message : String(e); + console.error(message); + process.exit(1); +}