Skip to main content
Glama

hono-apcore

Адаптер Hono для экосистемы модулей apcore AI-Perceivable. Превращает приложение Hono в инструменты MCP и совместимые с OpenAI определения функций — либо объявляя инструменты явно, либо сканируя уже имеющиеся маршруты.

Возможности

  • Два способа — объявляйте инструменты с помощью defineTool() / defineToolset(), или сканируйте существующие маршруты без изменений кода

  • Повторный вызов маршрута — сканированный маршрут становится модулем, который вызывает обратно через app.request(), так что middleware, валидаторы и обработчики ошибок по-прежнему работают

  • Один порт — mountMcp() обслуживает конечную точку MCP, Tool Explorer и /health из одного и того же приложения Hono

  • Вывод аннотаций — GET → readonly + cacheable, PUT → idempotent, DELETE → destructive (семантика безопасных методов RFC 9110)

  • Мультисхема — TypeBox, Zod 3, Zod 4 и обычный JSON Schema, автоматически определяемые через цепочку приоритетов

  • Контекст, ACL и идентичность — middleware apcore() создаёт контекст apcore для каждого запроса с распространением трассировки W3C, так что правила ACL также управляют вашими маршрутами

  • Ядро, не зависящее от среды выполнения — apcore-mcp, apcore-cli и apcore-a2a — это опциональные пиры, загружаемые лениво, поэтому импорт hono-apcore никогда не тянет node:http в edge-сборку

  • 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

  • Tool Explorer на http://localhost:3000/explorer/

Два способа предоставить возможность

defineTool() — явные инструменты

Аналог декоратора @ApTool из NestJS для 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 преобразуется в snake_case

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, …

Идентификаторы модулей формируются из пути и HTTP-глагола:

Маршрут

Идентификатор модуля

Выведенные аннотации

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(), вызов ИИ проходит по тому же пути кода, что и HTTP-вызов — middleware аутентификации, валидаторы, обработчики ошибок и всё остальное. Идентичность и заголовки трассировки W3C из контекста apcore Context передаются в повторяемый запрос.

Справочник 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, Explorer и /health в приложение.

toOpenaiTools(opts?)

Совместимые с OpenAI определения функций.

close()

Завершает работу поверхностей MCP и A2A.

apcore(instance | options, middlewareOptions?)

Middleware 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

Создаёт контекст apcore Context из контекста Hono, объекта Request или простых Headers.

Разрешение идентичности по порядку: x-user-id → Authorization: Bearer … (идентификатор "bearer") → простой заголовок x-roles (демонстрационный ярлык) → аноним. Заголовок traceparent предоставляет идентификатор трассировки; 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 });

Для этого нужны сырые объекты запроса и ответа Node, которые @hono/node-server предоставляет в c.env, поэтому это работает только в Node; смонтированный обработчик на другой среде выполнения отвечает 501 с этим объяснением. endpoint должен быть путём, как его видит HTTP-сервер — включите префикс, если приложение находится под basePath.

Автономный — отдельный порт или stdio для сервера, запущенного из CLI:

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

Веб-интерфейс Tool Explorer.

authenticator / requireAuth / exemptPaths

JWT или пользовательская аутентификация.

tags / prefix

Предоставлять только соответствующие модули.

validateInputs

boolean

Принудительно проверять схемы ввода при каждом вызове.

observability

Метрики + middleware использования и их конечные точки.

outputFormat / outputFormatter / redactOutput / trace

Сериализация результатов.

approvalHandler / approvalStore / approvalNotify

Шлюз одобрения для разрушительных инструментов.

mcpMiddleware / mcpAcl

Дополнительный middleware apcore / ACL для исполнителя MCP.

Адаптеры схем

Схемы автоматически обнаруживаются и преобразуются через цепочку приоритетов:

Адаптер

Приоритет

Вход

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:

Переменная

Тип

По умолчанию

Назначение

APCORE_ENABLED

bool

true

Главный переключатель — false делает init() no-op

APCORE_DEBUG

bool

false

Подробное логирование / интроспекция

APCORE_SCANNERS

list

["auto"]

Идентификаторы включённых сканеров

APCORE_INCLUDE_PATHS

list

[]

Шаблоны маршрутов для включения (пусто = все)

APCORE_EXCLUDE_PATHS

list

[]

Шаблоны маршрутов для исключения

APCORE_MODULE_PREFIX

str

""

Префикс, добавляемый к сгенерированным идентификаторам модулей

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 — что ломает сборку приложения для Workers, Deno или Bun, которое никогда не использует поверхность MCP. Импортируйте эти символы из их собственных пакетов:

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

Маршруты, управляемые ACL apcore — 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