Hono
Deploy your MCP server over HTTP using Hono.
Install
pnpm add @lynq/lynq @lynq/hono zod hononpm install @lynq/lynq @lynq/hono zod honoyarn add @lynq/lynq @lynq/hono zod honobun add @lynq/lynq @lynq/hono zod honoUsage
import { Hono } from "hono";
import { createMCPServer } from "@lynq/lynq";
import { mountLynq } from "@lynq/hono";
import { z } from "zod";
const server = createMCPServer({ name: "my-server", version: "1.0.0" });
server.tool(
"add",
{
description: "Add two numbers",
input: z.object({ a: z.number(), b: z.number() }),
},
(args, c) => c.text(String(args.a + args.b)),
);
const app = new Hono();
mountLynq(app, server);
export default app;The MCP endpoint is mounted at /mcp by default.
Under the hood
mountLynq calls server.http() internally to get a Web Standard (req: Request) => Promise<Response> handler, then wires it into Hono's routing. DNS rebinding protection is included by default -- the Host header is validated against allowed hostnames to prevent attacks on localhost servers.
Options
mountLynq(app, server, {
// Route path (default: "/mcp")
path: "/mcp",
// Override allowed hostnames for Host header validation
allowedHosts: ["localhost", "127.0.0.1", "::1"],
});path
The route path where the MCP endpoint is mounted. Defaults to "/mcp".
allowedHosts
Array of hostnames accepted in the Host header. Defaults to ["localhost", "127.0.0.1", "::1"]. Set to your production domain when deploying publicly.
Coexisting with Hono Middleware
Standard Hono middleware works alongside mountLynq. Apply middleware before mounting:
import { cors } from "hono/cors";
import { logger } from "hono/logger";
const app = new Hono();
app.use("*", logger());
app.use("*", cors());
mountLynq(app, server);
export default app;Pages
Auto-register OAuth callback and payment page routes. Specify only the providers you use:
mountLynq(app, server, {
pages: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
crypto: true,
},
});
// Registers:
// GET /lynq/auth/github/callback
// GET /lynq/payment/crypto
// POST /lynq/payment/crypto/callback
// GET /lynq/auth/success
// GET /lynq/payment/successUnspecified providers are not registered. Success pages are shared across all providers.
Provider Config
| Provider | Config | Routes |
|---|---|---|
github | { clientId, clientSecret } | GET /lynq/auth/github/callback |
google | { clientId, clientSecret } | GET /lynq/auth/google/callback |
stripe | { secretKey } | GET /lynq/payment/stripe/callback |
crypto | true or { rpcUrl? } | GET /lynq/payment/crypto + POST /lynq/payment/crypto/callback |
Each provider value can also be a string to redirect to a custom URL instead of using the default page.
pagesPrefix
URL prefix for all pages routes. Defaults to "/lynq".
mountLynq(app, server, {
pages: { github: { clientId: "...", clientSecret: "..." } },
pagesPrefix: "/my-app",
// → GET /my-app/auth/github/callback
});Next Steps
- HTTP -- raw
server.http()API, runtime examples - Express -- Express adapter
- Transports -- stateful vs sessionless
- Claude Code -- connect Claude Code to your HTTP server