Skip to main content
Glama

hono-apcore

Hono 适配器,用于 apcore AI-Perceivable 模块生态系统。将 Hono 应用转换为 MCP 工具和 OpenAI 兼容的函数定义——既可以通过显式声明工具,也可以通过扫描已有的路由。

特性

  • 两种方式 — 使用 defineTool() / defineToolset() 声明工具,或零代码更改扫描现有路由

  • 路由重放 — 扫描的路由成为通过 app.request() 回调的模块,因此中间件、验证器和错误处理程序仍然全部运行

  • 单端口 — mountMcp() 从同一个 Hono 应用提供 MCP 端点、工具浏览器和 /health

  • 注解推断 — GET → readonly + cacheable, PUT → idempotent, DELETE → destructive(RFC 9110 安全方法语义)

  • 多模式 — TypeBox、Zod 3、Zod 4 和纯 JSON Schema,通过优先级链自动检测

  • 上下文、ACL 和身份 — apcore() 中间件构建每个请求的 apcore Context,并带有 W3C 跟踪传播,因此 ACL 规则也管理你的路由

  • 运行时无关核心 — apcore-mcp、apcore-cli 和 apcore-a2a 是可选的对等依赖,按需加载,因此导入 hono-apcore 永远不会将 node:http 拖入边缘构建

  • CLI — hono-apcore scan | serve | export 可针对普通 Hono 应用工作

  • YAML 绑定 — 以声明方式注册模块,无需修改源代码

Related MCP server: hono-mcp-server

安装

npm install hono-apcore hono

可选的对等依赖,仅为你使用的功能安装:

npm install apcore-mcp @modelcontextprotocol/sdk   # MCP server + Tool Explorer
npm install @hono/node-server                      # mountMcp() on the Node runtime
npm install apcore-cli                             # CLI surface
npm install apcore-a2a                             # A2A agent surface
npm install @sinclair/typebox                      # TypeBox schemas (recommended)
npm install zod                                    # Zod schemas

要求: Node.js >= 18,Hono >= 4(已在 Hono 4.13 上测试)。

快速开始

1. 声明一些工具

// todo.tools.ts
import { Type } from '@sinclair/typebox';
import { defineToolset } from 'hono-apcore';

export const todoTools = defineToolset({
  namespace: 'todo',
  description: 'Todo list management',
  tags: ['todo'],
  tools: {
    list: {
      description: 'List all todos, optionally filtered by status',
      inputSchema: Type.Object({ done: Type.Optional(Type.Boolean()) }),
      annotations: { readonly: true, idempotent: true },
      handler: (inputs) => ({ todos: store.list(inputs.done as boolean | undefined) }),
    },
    add: {
      description: 'Add a new todo item',
      inputSchema: Type.Object({ title: Type.String() }),
      annotations: { readonly: false },
      handler: (inputs) => ({ todo: store.add(String(inputs.title)) }),
    },
  },
});

2. 将其接入应用

// app.ts
import { Hono } from 'hono';
import { apcore, createApcore } from 'hono-apcore';
import { todoTools } from './todo.tools.js';

export const ap = createApcore({
  tools: todoTools,
  mcp: { name: 'my-app', explorer: true, allowExecute: true },
});

export const app = new Hono();
app.use('*', apcore(ap));
app.get('/todos', (c) => c.json(store.list()));

3. 启动

// main.ts
import { serve } from '@hono/node-server';
import { app, ap } from './app.js';

await ap.init(app);              // register tools + scan routes
await ap.mountMcp(app);          // mount /mcp, /explorer, /health

serve({ fetch: app.fetch, port: 3000 });

你的应用现在响应:

  • REST 位于 http://localhost:3000/todos

  • MCP 位于 http://localhost:3000/mcp

  • 工具浏览器 位于 http://localhost:3000/explorer/

暴露能力的两种方式

defineTool() — 显式工具

这是 NestJS 的 @ApTool 装饰器的 Hono 对应物。Hono 没有可装饰的类或 DI 容器,因此工具是一个携带自身元数据和处理程序的普通对象。

import { defineTool } from 'hono-apcore';

const sendEmail = defineTool({
  namespace: 'email',
  name: 'send',                 // -> module id "email.send"
  description: 'Send an email',
  inputSchema: Type.Object({ to: Type.String(), body: Type.String() }),
  outputSchema: Type.Object({ messageId: Type.String() }),
  annotations: { readonly: false, destructive: false, requiresApproval: true },
  tags: ['email'],
  params: { to: 'Recipient address' },   // merged into the schema descriptions
  handler: async (inputs, context) => mailer.send(inputs, context),
});

字段

说明

id

原样使用。否则为 "<namespace>.<name>",其中 name 为蛇形命名

inputSchema / outputSchema

TypeBox、Zod 或纯 JSON Schema

annotations

readonly、destructive、idempotent、requiresApproval、openWorld、streaming、cacheable 等

params

每个参数的说明文本合并到输入模式中。JavaScript 无法像 Python 读取 docstring 那样在运行时读取函数的前导注释,因此这是显式的

handler

(inputs, context) => result。非对象结果会被包装为 { result }

路由扫描 — 零侵入工具

将扫描器指向应用,每个路由都会成为一个通过 app.request() 在进程内重放它的模块:

const ap = createApcore({
  routes: {
    excludePaths: ['/health', '/mcp*', '/explorer*'],
    modulePrefix: 'api',
  },
});

await ap.init(app);   // -> api.todos.list, api.todos.get, api.todos.create, …

模块 ID 来自路径和 HTTP 动词:

路由

模块 ID

推断的注解

GET /todos

todos.list

readonly, cacheable

GET /todos/:id

todos.get

readonly, cacheable

POST /todos

todos.create

—

PUT /todos/:id

todos.update

idempotent

DELETE /todos/:id

todos.delete

destructive

生成的输入模式为每个路径参数携带一个必需的字符串属性,外加一个自由形式的 query 对象(GET/DELETE)或 body 对象(POST/PUT/PATCH)。可以按路由覆盖其中任何部分:

routes: {
  overrides: {
    'GET /todos': {
      id: 'todo.all',
      description: 'Every todo, newest first',
      inputSchema: Type.Object({ done: Type.Optional(Type.Boolean()) }),
      annotations: { readonly: true, idempotent: true },
    },
    'DELETE /admin/wipe': { skip: true },
  },
}

由于执行通过 app.request() 返回,AI 调用与 HTTP 调用运行相同的代码路径——包括认证中间件、验证器、错误处理程序等。来自 apcore Context 的身份和 W3C 跟踪头会被转发到重放的请求上。

API 参考

createApcore(options)

返回一个 HonoApcore — 注册表、执行器和所有表面都挂载在它上面。

createApcore({
  extensionsDir?: string | null,   // scanned by Registry.discover()
  acl?: ACL,                       // enforced by the Executor on every call
  middleware?: Middleware[],       // apcore middleware installed on the Executor
  bindings?: string,               // YAML bindings file loaded during init()
  tools?: ApToolDefinition[],      // registered during init()
  routes?: RouteScanOptions,       // route-scanner configuration
  settings?: Partial<ApcoreSettings>,  // overrides for the APCORE_* settings
  mcp?: ApcoreMcpOptions,          // presence enables the MCP surface
  cli?: ApcoreCliOptions,          // presence enables the CLI surface
  a2a?: ApcoreA2aOptions,          // presence enables the A2A surface
})

方法

说明

init(app?, routeOptions?)

发现、注册工具和绑定,扫描路由,启动独立表面。幂等

ready()

等待进行中的 init()

registerTool(tool) / registerTools(tools)

在运行时注册工具定义

registerMethod(opts) / registerObject(opts)

注册普通服务对象的方法

scanRoutes(app, opts?)

扫描并注册应用的路由

routeOptions

此实例将使用的合并路由扫描选项

loadBindings(path?, resolver?)

加载 YAML 绑定文件

mountMcp(app, opts?)

将 /mcp、浏览器和 /health 挂载到应用中

toOpenaiTools(opts?)

OpenAI 兼容的函数定义

close()

关闭 MCP 和 A2A 表面

apcore(instance | options, middlewareOptions?)

Hono 中间件,将实例和每个请求的 apcore Context 放到 Hono 上下文上。

app.use('*', apcore(ap));

app.get('/orders', async (c) =>
  c.json(await getApcore(c).executor.call('orders.list', {}, getApcoreContext(c))),
);

变量映射被增强,因此 c.get('apcore') 和 c.get('apcoreContext') 也有类型。在从不调用模块的路由上传递 { skipContext: true },或传递 { contextFactory } 以接入真实认证。

HonoContextFactory

从 Hono 上下文、Request 或裸 Headers 构建 apcore Context。

身份解析顺序:x-user-id → Authorization: Bearer …(身份 id "bearer")→ 裸 x-roles 头(演示快捷方式)→ 匿名。traceparent 头提供跟踪 id;x-correlation-id(或 x-request-id)进入 context.data。

new HonoContextFactory({
  resolveIdentity: (headers) => identityFromSession(headers),  // wins over the above
  data: (headers) => ({ tenant: headers.get('x-tenant') }),
});

MCP

ApcoreMcpService 以两种方式运行 MCP 服务器。

嵌入式 — 一个进程,一个端口:

await ap.mountMcp(app, { endpoint: '/mcp', explorer: true, allowExecute: true });

这需要 @hono/node-server 在 c.env 上暴露的原始 Node 请求和响应对象,因此仅限 Node;其他运行时上的挂载处理程序会以该说明回答 501。endpoint 必须是 HTTP 服务器看到的路径——如果应用位于 basePath 下,请包含前缀。

独立 — 单独的端口,或用于 CLI 启动的服务器的 stdio:

createApcore({ mcp: { transport: 'streamable-http', host: '0.0.0.0', port: 8000 } });
// init() starts it, because `transport` was set explicitly

关键 MCP 选项:

字段

类型

说明

transport

'stdio' | 'streamable-http' | 'sse'

独立传输。设置它会使 init() 启动服务器

host / port

string / number

HTTP 传输的绑定地址

name / version

string

服务器身份

explorer / explorerPrefix / allowExecute

工具浏览器 Web UI

authenticator / requireAuth / exemptPaths

JWT 或自定义认证

tags / prefix

仅暴露匹配的模块

validateInputs

boolean

在每次调用时强制执行输入模式

observability

指标 + 使用中间件及其端点

outputFormat / outputFormatter / redactOutput / trace

结果序列化

approvalHandler / approvalStore / approvalNotify

破坏性工具的审批门

mcpMiddleware / mcpAcl

为 MCP 执行器提供的额外 apcore 中间件 / ACL

模式适配器

模式通过优先级链自动检测和转换:

适配器

优先级

输入

TypeBoxAdapter

100

@sinclair/typebox 模式

ZodAdapter

50

Zod 3(_def.typeName)和 Zod 4(_zod.def.type)

JsonSchemaAdapter

30

纯 JSON Schema 对象

检测是结构性的——运行时不会导入 TypeBox 或 Zod——因此宿主应用安装哪个(或都不安装)都可以。使用 SchemaExtractor.registerAdapter() 注册你自己的适配器。

YAML 绑定

无需修改源代码即可注册模块:

bindings:
  - module_id: email.send
    target: EmailService.send
    description: Send an email
    input_schema:
      type: object
      properties:
        to: { type: string }
    tags: [email, mutate]
    annotations:
      readonly: false
import { resolverFromObjects } from 'hono-apcore';

await ap.loadBindings('./bindings.yaml', resolverFromObjects({ EmailService: mailer }));

反过来,writeBindingsFile() 将扫描的模块序列化输出——这正是 hono-apcore scan --format yaml 所做的。

CLI

hono-apcore scan   ./src/app.ts            # print the modules a scan would produce
hono-apcore scan   ./src/app.ts --format yaml --out bindings.yaml
hono-apcore serve  ./src/app.ts --transport http --port 8000 --explorer
hono-apcore export ./src/app.ts --out tools.json

入口是 path[:export];导出默认为 default,然后是 app。如果模块以任何名称导出 HonoApcore,其配置——路由过滤器、模块前缀、MCP 选项——会被尊重,因此 scan 报告应用自身注册的模块;CLI 标志会覆盖它。没有实例的入口仍然有效,因此 serve 可以针对从未听说过 apcore 的应用运行。TypeScript 入口需要加载器:

npx tsx node_modules/.bin/hono-apcore scan ./src/app.ts

配置(APCORE_*)

每个 apcore 集成实现的标准设置,从环境读取并可通过 settings 覆盖:

Variable

Type

Default

Purpose

APCORE_ENABLED

bool

true

主开关——false 使 init() 成为空操作

APCORE_DEBUG

bool

false

详细日志/内省

APCORE_SCANNERS

list

["auto"]

启用的扫描器标识符

APCORE_INCLUDE_PATHS

list

[]

要包含的路由模式(空 = 全部)

APCORE_EXCLUDE_PATHS

list

[]

要排除的路由模式

APCORE_MODULE_PREFIX

str

""

附加到生成的模块 ID 的前缀

APCORE_AUTH_ENABLED

bool

false

MCP/A2A 端点需要身份验证

APCORE_AUTH_STRATEGY

str

"bearer"

bearer / session / custom

APCORE_TRANSPORT

str

"stdio"

MCP 传输:stdio / http / sse

APCORE_HOST

str

"0.0.0.0"

当传输不是 stdio 时的绑定地址

APCORE_PORT

int

8808

当传输不是 stdio 时的绑定端口

可选对等依赖不会被重新导出

与 NestJS 适配器不同,hono-apcore 不重新导出 apcore-mcp / apcore-cli / apcore-a2a 接口。这样做会使它们被急切加载,而 apcore-mcp 会引入 node:http——这会破坏从不使用 MCP 接口的 Workers、Deno 或 Bun 应用构建。请从它们各自的包中导入这些符号:

import { JWTAuthenticator, getCurrentIdentity } from 'apcore-mcp';
import { createCli } from 'apcore-cli';
import { A2AClient } from 'apcore-a2a';

apcore-js 和 apcore-toolkit 是硬依赖,因此它们的公共符号(ACL、Config、registerSysModules、TraceContext、BaseScanner、formatModules 等)直接从 hono-apcore 重新导出。

示例

示例

展示

examples/demo

完整应用:手写工具和路由扫描、JWT、ACL、系统模块、Docker

examples/acl_demo

由 apcore ACL 管理的路由——orders.delete 仅限管理员

pnpm install && pnpm build
cd examples/demo && pnpm install && pnpm dev

详细文档

脚本

命令

描述

pnpm build

编译 TypeScript

pnpm dev

监视模式编译

pnpm test

运行测试套件(vitest)

pnpm test:coverage

带覆盖率的测试(90% 阈值)

pnpm typecheck

类型检查(不输出)

pnpm lint

检查源代码和测试

许可证

Apache-2.0

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Exposes Hono API endpoints as Model Context Protocol tools, allowing LLMs to interact with your API routes through a dedicated MCP endpoint. It provides helpers to describe routes and includes a codemode for dynamic API interaction via search and execute tools.
    34 npm
    86
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Easily expose your Hono API endpoints as MCP tools with minimal configuration, supporting type-safe input handling and tool registration.
    34 npm
    2
    MIT