Skip to main content
Glama

hono-apcore

Hono 어댑터 — apcore AI-Perceivable 모듈 생태계용. Hono 앱을 MCP 도구 및 OpenAI 호환 함수 정의로 변환합니다 — 도구를 명시적으로 선언하거나 이미 있는 라우트를 스캔하여.

기능

  • 두 가지 진입점 — defineTool() / defineToolset()로 도구를 선언하거나, 코드 변경 없이 기존 라우트를 스캔합니다.

  • 라우트 재생 — 스캔된 라우트는 app.request()를 통해 다시 호출되는 모듈이 되므로, 미들웨어, 검증기, 오류 처리기가 모두 그대로 실행됩니다.

  • 단일 포트 — mountMcp()는 MCP 엔드포인트, Tool Explorer, /health를 동일한 Hono 앱에서 제공합니다.

  • 어노테이션 추론 — GET → readonly + cacheable, PUT → idempotent, DELETE → destructive (RFC 9110 안전 메서드 의미론)

  • 다중 스키마 — TypeBox, Zod 3, Zod 4, 일반 JSON Schema를 우선순위 체인으로 자동 감지합니다.

  • 컨텍스트, ACL, ID — apcore() 미들웨어는 W3C 추적 전파를 포함한 요청별 apcore Context를 구축하므로 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

  • Tool Explorer — 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은 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, …

모듈 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의 ID와 W3C 추적 헤더는 재생된 요청에 전달됩니다.

API 참조

createApcore(options)

HonoApcore를 반환합니다 — Registry, Executor, 그리고 모든 표면이 여기에 연결됩니다.

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?)

인스턴스와 요청별 apcore Context를 Hono 컨텍스트에 넣는 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를 구축합니다.

ID 해석 순서: 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

서버 ID

explorer / explorerPrefix / allowExecute

Tool Explorer 웹 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]입니다. 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가 아닐 때 바인딩 포트

선택적 피어는 재-export되지 않음

NestJS 어댑터와 달리 hono-apcore는 apcore-mcp / apcore-cli / apcore-a2a 표면을 재-export하지 않습니다. 그렇게 하면 이들이 즉시 로드되며, 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에서 직접 재-export됩니다.

예제

Example

Shows

examples/demo

전체 앱: 수작업 도구 및 라우트 스캐닝, JWT, ACL, 시스템 모듈, Docker

examples/acl_demo

apcore ACL이 관리하는 라우트 — 관리자 전용 orders.delete

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

상세 문서

스크립트

Command

Description

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