diff --git a/README.md b/README.md index 596cf5c..83e06ba 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ For detailed implementation guides and examples specific to your framework, see: - **[Express.js Integration](./express/README.md)** - Complete guide for building MCP servers with Express.js - **[Next.js Integration](./next/README.md)** - Complete guide for building both MCP servers and clients with Next.js +- **[Hono Integration](./hono/README.md)** - Complete guide for building MCP servers with Hono ### Table of Contents diff --git a/hono/README.md b/hono/README.md new file mode 100644 index 0000000..5c9c23c --- /dev/null +++ b/hono/README.md @@ -0,0 +1,271 @@ +# MCP Tools - Hono Integration + +Hono utilities for building MCP servers with authentication support. These tools make it easy to add MCP (Model Context Protocol) endpoints to your existing Hono applications. + +## Installation + +Make sure you have the required dependencies installed: + +```bash +npm install @clerk/mcp-tools hono mcp-lite +``` + +If you're using Clerk for authentication, also install the Clerk backend SDK: + +```bash +npm install @clerk/backend +``` + +## Quick Start + +### Example with Clerk Authentication + +Here's a complete example using Clerk for authentication: + +```ts +import { Hono } from "hono"; +import { logger } from "hono/logger"; +// Hono with auth does not play nicely with @modelcontextprotocol/sdk yet, so we use the mcp-lite package +import { McpServer, StreamableHttpTransport } from "mcp-lite"; +import { createClerkClient } from "@clerk/backend"; +import { + mcpAuthClerk, + oauthCorsMiddleware, + protectedResourceHandlerClerk, + authServerMetadataHandlerClerk, +} from "@clerk/mcp-tools/hono"; + +type AppType = { + Bindings: { + CLERK_SECRET_KEY: string; + CLERK_PUBLISHABLE_KEY: string; + } +}; + +const app = new Hono(); + +const server = new McpServer({ + name: "clerk-mcp-server", + version: "1.0.0", +}); + +server.tool( + "get_clerk_user_data", + { + description: "Gets data about the Clerk user that authorized this request", + handler: async (_, { authInfo }) => { + const clerk = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY! }); + + if (!authInfo?.extra?.userId) { + return { + content: [{ type: "text", text: "Error: user not authenticated" }], + }; + } + + const user = await clerk.users.getUser(authInfo?.extra?.userId as string); + return { + content: [{ type: "text", text: JSON.stringify(user) }], + }; + } + } +); + +app.use(logger()); + +app.on( + ["GET", "OPTIONS"], + "/.well-known/oauth-protected-resource", + oauthCorsMiddleware, // <-- cors middleware is helpful for testing in the inspector + protectedResourceHandlerClerk() +); +app.on( + ["GET", "OPTIONS"], + "/.well-known/oauth-protected-resource/mcp", + oauthCorsMiddleware, + protectedResourceHandlerClerk({ + scopes_supported: ["profile", "email"], + }) +); +app.on( + ["GET", "OPTIONS"], + "/.well-known/oauth-authorization-server", + oauthCorsMiddleware, + authServerMetadataHandlerClerk +); + +app.post("/mcp", mcpAuthClerk, async (c) => { + const authInfo = c.get("auth"); + const transport = new StreamableHttpTransport(); + const mcpHttpHandler = transport.bind(server); + const response = await mcpHttpHandler(c.req.raw, { authInfo }); + return response; +}); + +export default app; +``` + +## Authentication Middleware + +### `mcpAuthClerk` + +Pre-configured authentication middleware for Clerk that automatically handles OAuth token verification. + +**Example:** + +```ts +import { mcpAuthClerk } from "@clerk/mcp-tools/hono"; + +// No additional configuration needed - uses Clerk's built-in token verification +app.post("/mcp", mcpAuthClerk, /** your mcp server handler */); +``` + +This middleware automatically: + +- Verifies OAuth access tokens using Clerk +- Handles authentication state +- Adds Clerk auth data to request context via `c.get("auth")` + +## Protected Resource Metadata + +### `protectedResourceHandlerClerk` + +Hono handler that returns OAuth protected resource metadata for Clerk integration, as required by [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728). + +**Example:** + +```ts +import { protectedResourceHandlerClerk } from "@clerk/mcp-tools/hono"; + +app.get( + "/.well-known/oauth-protected-resource", + protectedResourceHandlerClerk({ scopes_supported: ["email"] }) +); +``` + +## Authorization Server Metadata + +### `authServerMetadataHandlerClerk` + +Hono handler that returns OAuth authorization server metadata for Clerk integration, as defined by [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414). This endpoint provides clients with information about Clerk's OAuth authorization server capabilities and endpoints. + +**Example:** + +```ts +import { authServerMetadataHandlerClerk } from "@clerk/mcp-tools/hono"; + +// Serve authorization server metadata at the standard well-known location +app.get( + "/.well-known/oauth-authorization-server", + authServerMetadataHandlerClerk +); +``` + +**Note:** This handler requires the `CLERK_PUBLISHABLE_KEY` environment variable to be set, as it uses Clerk's public configuration to generate the metadata. + +## OAuth Metadata CORS Middleware + +### `oauthCorsMiddleware` + +Pre-configured CORS middleware specifically designed for OAuth protected resource metadata endpoints. This middleware is useful when testing authentication from browser-based MCP clients or the MCP inspector, for example with `npx @modelcontextprotocol/inspector`. + +**Example:** + +```ts +import { oauthCorsMiddleware } from "@clerk/mcp-tools/hono"; + +app.on( + ["GET", "OPTIONS"], + "/.well-known/oauth-protected-resource", + oauthCorsMiddleware, // <-- Apply CORS before your handler + protectedResourceHandlerClerk() +); +``` + +This middleware uses `hono/cors` under the hood to automatically: + +- Handle preflight OPTIONS requests +- Set appropriate CORS headers for OAuth metadata endpoints + +## Accessing Authentication Data in Tools + +Passing authentication data to your MCP tools is done via the `authInfo` parameter in the tool handler. + +The `@modelcontextprotocol/sdk` package requires that you do this by monkeypatching an `Express.Request` object, however, so it does not play nicely with Hono. + +The existing Hono MCP middleware does not yet support passing auth to MCP servers, but there is an open PR to add this support: https://github.com/honojs/middleware/pull/1318/files + +Alternative libraries like [`mcp-lite`](https://github.com/fiberplane/mcp) (used in the example above) do support the `authInfo` parameter, provided you pass it to the MCP server HTTP handler. + +```typescript +import { McpServer } from "mcp-lite"; + +const server = new McpServer({ + name: "my-server", + version: "1.0.0", +}); + +server.tool( + "my-tool", + "My tool", + { type: "object", properties: {} }, + async (args, { authInfo }) => { + return { content: [{ type: "text", text: `Hello, ${authInfo.extra.userId}!` }] }; + } +); + +app.post("/mcp", mcpAuthClerk, async (c) => { + const authInfo = c.get("auth"); + const transport = new StreamableHttpTransport(); + const mcpHttpHandler = transport.bind(server); + // pass the authInfo to the MCP server HTTP handler, making it available to the tool handlers + const response = await mcpHttpHandler(c.req.raw, { authInfo }); + return response; +}); +``` + +## Environment Variables + +When using Clerk integration, make sure to set: + +```bash +CLERK_PUBLISHABLE_KEY=pk_test_... +CLERK_SECRET_KEY=sk_test_... +``` + +The publishable key is used for generating OAuth metadata, while the secret key is used for server-side API calls to fetch user data. + +## Error Handling + +The middleware automatically handles common authentication errors: + +- **Missing Authorization header**: Returns 401 with `WWW-Authenticate` header pointing to your protected resource metadata +- **Invalid token format**: Throws an error with details about the expected format +- **Token verification failure**: Returns 401 with error details + +## Integration with Existing Hono Apps + +These utilities are designed to integrate seamlessly with existing Hono applications. You can: + +- Add MCP endpoints to existing routes +- Use your existing authentication middleware alongside MCP auth +- Combine with other Hono middleware (CORS, rate limiting, etc.) + +```ts +import cors from "cors"; +import { rateLimiter } from "hono-rate-limiter"; + +// Apply middleware in the order you need +app.use(cors()); +app.use( + rateLimiter({ + windowMs: 15 * 60 * 1000, + limit: 100, + }) +); + +app.post( + "/mcp", + mcpAuthClerk, // MCP authentication + /** your mcp server handler */ +); +``` diff --git a/hono/auth.ts b/hono/auth.ts new file mode 100644 index 0000000..44cbf4c --- /dev/null +++ b/hono/auth.ts @@ -0,0 +1,68 @@ +import { createClerkClient } from "@clerk/backend"; +import { TokenType } from "@clerk/backend/internal"; +import type { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js"; +import { env } from "hono/adapter"; +import { createMiddleware } from "hono/factory"; +import { getPRMUrl } from "./utils.js"; +import { verifyClerkToken } from "../server"; + +/** + * Hono middleware that enforces authentication for MCP requests using Clerk. + * + * Sets an "auth" variable on the request context, which matches the {@link AuthInfo} type from the MCP SDK. + */ +export const mcpAuthClerk = createMiddleware< + { Variables: { auth: AuthInfo } } +>(async (c, next) => { + const authHeader = c.req.header("Authorization"); + const [type, token] = authHeader?.split(" ") || []; + const bearerToken = type?.toLowerCase() === "bearer" ? token : undefined; + + // Return 401 with proper www-authenticate header if no authorization is provided + if (!bearerToken) { + // Get the resource metadata url for the protected resource + // We return this in the `WWW-Authenticate` header so the MCP client knows where to find the protected resource metadata + const resourceMetadataUrl = getPRMUrl(c.req.raw); + c.header( + "WWW-Authenticate", + // NOTE - The mcp sdk also adds `error` and `error_description` to this header as well, depending on the error + // see: https://github.com/modelcontextprotocol/typescript-sdk/blob/b28c297184cb0cb64611a3357d6438dd1b0824c6/src/server/auth/middleware/bearerAuth.ts#L76C1-L95C8 + `Bearer resource_metadata="${resourceMetadataUrl}"`, + ); + return c.json({ error: "Unauthorized" }, 401); + } + + try { + const secretKey = (env(c)?.CLERK_SECRET_KEY || "") as string; + const publishableKey = (env(c)?.CLERK_PUBLISHABLE_KEY || "") as string; + + const clerkClient = createClerkClient({ + secretKey, + publishableKey, + }); + + const requestState = await clerkClient.authenticateRequest(c.req.raw, { + secretKey, + publishableKey, + acceptsToken: TokenType.OAuthToken, + }); + + // This is the result of the authenticateRequest call, with the `TokenType.OAuthToken` type + const auth = requestState.toAuth(); + + const authInfo = verifyClerkToken(auth, token); + + // Require valid auth for this endpoint + if (!authInfo) { + return c.json({ error: "Unauthorized" }, 401); + } + + // Attach auth to Request and Hono context for downstream handlers + c.set("auth", authInfo); + + await next(); + } catch (error) { + console.error("Unexpected mcp auth middleware error:", error); + return c.json({ error: "Internal Server Error" }, 500); + } +}); diff --git a/hono/index.ts b/hono/index.ts new file mode 100644 index 0000000..5590abc --- /dev/null +++ b/hono/index.ts @@ -0,0 +1,6 @@ +export { mcpAuthClerk } from "./auth.js"; +export { + oauthCorsMiddleware, + protectedResourceHandlerClerk, + authServerMetadataHandlerClerk, +} from "./oauth-server-routes.js"; \ No newline at end of file diff --git a/hono/oauth-server-routes.ts b/hono/oauth-server-routes.ts new file mode 100644 index 0000000..89101a2 --- /dev/null +++ b/hono/oauth-server-routes.ts @@ -0,0 +1,108 @@ +import { Hono } from "hono"; +import { cors } from "hono/cors"; +import { env } from "hono/adapter"; +import { createFactory } from "hono/factory"; +import { + corsHeaders, + fetchClerkAuthorizationServerMetadata, + generateClerkProtectedResourceMetadata, +} from "../server"; +import { getResourceUrl } from "./utils.js"; + +/** + * Create middleware to handle CORS for the OAuth endpoints, + * this is useful for testing auth from a browser-based MCP client, + * or the MCP inspector (`npx @modelcontextprotocol/inspector`) + * + * @note Converts the CORS headers object from the @clerk/mcp-tools library + * into a format that Hono's CORS middleware can understand + * + * @see https://github.com/clerk/mcp-tools/blob/main/server.ts + */ +export const oauthCorsMiddleware = cors({ + origin: corsHeaders["Access-Control-Allow-Origin"], + // HACK - split the comma-separated list of methods into an array + allowMethods: corsHeaders["Access-Control-Allow-Methods"].split(","), + allowHeaders: [corsHeaders["Access-Control-Allow-Headers"]], + maxAge: parseInt(corsHeaders["Access-Control-Max-Age"], 10), +}); + +/** + * A Hono handler that will return OAuth protected resource metadata if you're using Clerk. + * + * Typically mounted at the routes: + * - `/.well-known/oauth-protected-resource` + * - `/.well-known/oauth-protected-resource/mcp` + * + * @see https://datatracker.ietf.org/doc/html/rfc9728#section-4.1 + * @example + * ```ts + * app.on( + * ["GET", "OPTIONS"], + * "/.well-known/oauth-protected-resource", + * oauthCorsMiddleware, + * protectedResourceHandlerClerk() + * ); + * + * app.on( + * ["GET", "OPTIONS"], + * "/.well-known/oauth-protected-resource/mcp", + * oauthCorsMiddleware, + * protectedResourceHandlerClerk({ scopes_supported: ["profile", "email"] }) + * ); + * ``` + */ +export const protectedResourceHandlerClerk = ( + properties?: Record +) => { + const factory = createFactory(); + const handlers = factory.createHandlers((c) => { + const publishableKey = env(c)?.CLERK_PUBLISHABLE_KEY; + if (!publishableKey) { + console.error( + "CLERK_PUBLISHABLE_KEY is not set for OAuth Authorization Server endpoint", + ); + return c.json({ error: "Internal Server Error" }, 500); + } + + const resourceUrl = getResourceUrl(c.req.raw); + + const result = generateClerkProtectedResourceMetadata({ + publishableKey: publishableKey as string, + resourceUrl, + properties, + }); + + return c.json(result); + }) + return handlers[0]; +} + +const authServerFactory = createFactory(); +const authServerHandlers = authServerFactory.createHandlers(async (c) => { + const publishableKey = env(c)?.CLERK_PUBLISHABLE_KEY; + if (!publishableKey) { + console.error( + "CLERK_PUBLISHABLE_KEY is not set for OAuth Authorization Server endpoint", + ); + return c.json({ error: "Internal Server Error" }, 500); + } + + // If CLERK_PUBLISHABLE_KEY is misconfigured, this will result in a 500 + const result = await fetchClerkAuthorizationServerMetadata({ + publishableKey: publishableKey as string, + }); + + return c.json(result); +}); + +/** + * Implement the OAuth Authorization Server endpoint + * + * Typically mounted at the route `/.well-known/oauth-authorization-server` + * + * @note - In this case, Clerk is the authorization server, so you shouldn't *need* to implement this; + * however, in earlier versions of the MCP spec (prior to 2025-06-18), this route was expected/required, + * so you can implement it for backwards compatibility with clients. + */ +export const authServerMetadataHandlerClerk = authServerHandlers[0]; diff --git a/hono/utils.ts b/hono/utils.ts new file mode 100644 index 0000000..bcc2e47 --- /dev/null +++ b/hono/utils.ts @@ -0,0 +1,19 @@ +/** + * Given a protected resource metadata url generate the url of the original resource + */ +export function getResourceUrl(req: Request) { + const url = new URL(req.url); + url.pathname = url.pathname.replace( + /\.well-known\/oauth-protected-resource\/?/, + "", + ); + return url.toString(); +} + +/** + * Get given a request, generate a protected resource metadata url for the given resource url + */ +export function getPRMUrl(req: Request) { + const url = new URL(req.url); + return `${url.origin}/.well-known/oauth-protected-resource${url.pathname}`; +} diff --git a/package-lock.json b/package-lock.json index be497f9..5f91f8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,18 @@ { "name": "@clerk/mcp-tools", - "version": "0.2.1", + "version": "0.3.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@clerk/mcp-tools", - "version": "0.2.1", + "version": "0.3.1", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.17.0" }, "devDependencies": { + "@clerk/backend": "^2.14.0", "@clerk/express": "^1.7.12", "@clerk/nextjs": "^6.26.0", "@types/better-sqlite3": "^7.6.13", @@ -19,6 +20,7 @@ "@types/node": "^24.1.0", "@types/pg": "^8.15.4", "better-sqlite3": "^11.10.0", + "hono": "^4.9.7", "next": "^15.4.4", "pg": "^8.16.3", "redis": "^5.5.6", @@ -27,6 +29,7 @@ }, "peerDependencies": { "better-sqlite3": "^8.7.0", + "hono": "^4.0", "pg": "^8.11.0", "redis": "^4.0.0" }, @@ -34,6 +37,9 @@ "better-sqlite3": { "optional": true }, + "hono": { + "optional": true + }, "pg": { "optional": true }, @@ -43,16 +49,15 @@ } }, "node_modules/@clerk/backend": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-2.5.2.tgz", - "integrity": "sha512-m3AsF3lOGUmzZOmMg7ovL5l8lzNHExM3mgRGguaOz9vAenRLPT9U+h+GVGP7URv6IPWVI5jhywlU8+MOK5tUIg==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-2.14.0.tgz", + "integrity": "sha512-EaPXIaOb3IVyn+3NRX9GVZeKk1eL1ugWOiyPzy7hfJvxRYhTBatZrwd32+nCkQ6igvRpRG4O+o5vWS1tSErbrg==", "dev": true, "license": "MIT", "dependencies": { - "@clerk/shared": "^3.15.0", - "@clerk/types": "^4.70.0", + "@clerk/shared": "^3.25.0", + "@clerk/types": "^4.86.0", "cookie": "1.0.2", - "snakecase-keys": "9.0.2", "standardwebhooks": "^1.0.0", "tslib": "2.8.1" }, @@ -132,14 +137,14 @@ } }, "node_modules/@clerk/shared": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-3.15.0.tgz", - "integrity": "sha512-yPsRYAJYSyniJItmXjz6eHOHcR75+SZV9K/UPp1djz/vyu4CCyndz83kc9xTa7MG/1Sfz2yKIIhyyB/21bjDoQ==", + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-3.25.0.tgz", + "integrity": "sha512-2Vb6NQqBA+1g7kfGct/OlSFmzU54/s4BQp3qeHwDqW1FgaU4MuXbqfBClI6AatxOC8Ux8W16Rvf705ViwFSxlw==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "@clerk/types": "^4.70.0", + "@clerk/types": "^4.86.0", "dequal": "2.0.3", "glob-to-regexp": "0.4.1", "js-cookie": "3.0.5", @@ -163,9 +168,9 @@ } }, "node_modules/@clerk/types": { - "version": "4.70.0", - "resolved": "https://registry.npmjs.org/@clerk/types/-/types-4.70.0.tgz", - "integrity": "sha512-WYqxeNVqeshuHRj0t+nIS5be0WlIqjudLamhqCNkstpkxSiVDHF1lyGEjPVYmbXwOzdnZoPtFqWEaiBEGaboJA==", + "version": "4.86.0", + "resolved": "https://registry.npmjs.org/@clerk/types/-/types-4.86.0.tgz", + "integrity": "sha512-YFaOYIAZWbpXehAmtgUB0YNf1v5b/hlwePvdqxlD5vdwrNsap28RpupWZat0hp1+PTtb9uAwSa5AFCOxkYLUJQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2074,13 +2079,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/change-case": { - "version": "5.4.4", - "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", - "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", - "dev": true, - "license": "MIT" - }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -2811,6 +2809,16 @@ "node": ">= 0.4" } }, + "node_modules/hono": { + "version": "4.9.7", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.9.7.tgz", + "integrity": "sha512-t4Te6ERzIaC48W3x4hJmBwgNlLhmiEdEE5ViYb02ffw4ignHNHa5IBtPjmbKstmtKa8X6C35iWwK4HaqvrzG9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -3008,19 +3016,6 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "node_modules/map-obj": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-5.0.2.tgz", - "integrity": "sha512-K6K2NgKnTXimT3779/4KxSvobxOtMmx1LBZ3NwRxT/MDIR3Br/fQ4Q+WCX5QxjyUR8zg5+RV9Tbf2c5pAWTD2A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -4180,21 +4175,6 @@ "is-arrayish": "^0.3.1" } }, - "node_modules/snakecase-keys": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-9.0.2.tgz", - "integrity": "sha512-Tr4gONsDj1Pa6HJH9D3b411r6tuRyCGgb1l7YpzDFp/thjVSWs7rcbNjyTyRqJi5SUV23sFpzf9epIJRbLR6Yw==", - "dev": true, - "license": "MIT", - "dependencies": { - "change-case": "^5.4.4", - "map-obj": "^5.0.2", - "type-fest": "^4.15.0" - }, - "engines": { - "node": ">=22" - } - }, "node_modules/source-map": { "version": "0.8.0-beta.0", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", @@ -4626,19 +4606,6 @@ "node": "*" } }, - "node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", diff --git a/package.json b/package.json index 6a40c03..00976ec 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,10 @@ "import": "./dist/express/index.js", "types": "./dist/express/index.d.ts" }, + "./hono": { + "import": "./dist/hono/index.js", + "types": "./dist/hono/index.d.ts" + }, "./stores": "./dist/stores", "./stores/fs": { "import": "./dist/stores/fs.js", @@ -74,6 +78,7 @@ "@modelcontextprotocol/sdk": "^1.17.0" }, "devDependencies": { + "@clerk/backend": "^2.14.0", "@clerk/express": "^1.7.12", "@clerk/nextjs": "^6.26.0", "@types/better-sqlite3": "^7.6.13", @@ -81,6 +86,7 @@ "@types/node": "^24.1.0", "@types/pg": "^8.15.4", "better-sqlite3": "^11.10.0", + "hono": "^4.9.7", "next": "^15.4.4", "pg": "^8.16.3", "redis": "^5.5.6", @@ -89,10 +95,14 @@ }, "peerDependencies": { "better-sqlite3": "^8.7.0", + "hono": "^4.0", "pg": "^8.11.0", "redis": "^4.0.0" }, "peerDependenciesMeta": { + "hono": { + "optional": true + }, "redis": { "optional": true }, @@ -109,6 +119,7 @@ "server.ts", "next/index.ts", "express/index.ts", + "hono/index.ts", "stores/fs.ts", "stores/redis.ts", "stores/postgres.ts", @@ -120,6 +131,7 @@ "dts": true, "clean": true, "external": [ + "hono", "redis", "pg", "better-sqlite3"