neuron-explorer
Qwen3.5‑4B 전용 조향형 뉴런 탐색기, 모든 MLP 뉴런을 밀어 결과를 보관·탐색
REST·MCP·OpenAPI 등에서 하나의 Zod 계약으로 검증·타입드 클라이언트·오류 처리를 지원합니다
One Zod contract across REST, MCP, OpenAPI, typed clients, and validation. Agents consume it with machine-actionable errors, /llms.txt, and MCP tools; a coding agent can author the whole surface witho
개발 도구TypeScript · JavaScriptGitHub @ninemindai온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
AI native API/MCP framework for the agent era. Built with agents, for agents.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
AgentBack is an AI-native API/MCP framework for the agent era, built with
agents, for agents. It lets REST endpoints, MCP tools, generated docs, typed
clients, tests, and runtime validation all share one Zod contract.
Your API has two audiences now: human developers and AI agents. Same operations,
different surfaces — REST and a typed client for devs, MCP tools and
machine-readable docs for agents.README 전문 보기 ↗AgentBack is an AI-native API/MCP framework for the agent era, built with agents, for agents. It lets REST endpoints, MCP tools, generated docs, typed clients, tests, and runtime validation all share one Zod contract.
Your API has two audiences now: human developers and AI agents. Same operations, different surfaces — REST and a typed client for devs, MCP tools and machine-readable docs for agents.
Most stacks make you describe each operation four or five times: a Zod schema, an OpenAPI block, an MCP tool definition, a client type. One contract, copied everywhere. Add a field, edit all of them. Miss one, they drift — quietly, until an agent sends the body your docs promised and your validator throws.
AgentBack describes it once.
const OrderId = z.object({id: z.string()});
const Order = z.object({id: z.string(), status: z.enum(['open', 'shipped'])});
@get('/orders/{id}', {path: OrderId, response: Order}) // → REST + OpenAPI + typed client
async getOrder(input) { /* … */ }
@tool('get_order', {input: OrderId, output: Order}) // → MCP tool, same schemas
async getOrderTool(input) { /* … */ }
One Zod schema becomes the validator, the z.infer type, the OpenAPI 3.1
contract, the MCP schema, the typed client, and the docs. Change the schema;
every boundary follows.
Not "LoopBack/Express, but newer." It's the layer that keeps one operation
coherent for the developers who build on your API and the agents that call it —
on a DI core you can extend, on any host (Node, Fastify, Hono, Bun, Deno,
Workers) from one fetch handler.
The foundation is a modern ESM port of LoopBack 4's proven dependency-injection
core: a hierarchical Context of Bindings with @inject, providers,
interceptors, extension points, lifecycle observers, and tag-based discovery.
REST controllers and MCP tool classes are just bindings the respective servers
find by tag — so adding a route or a tool is adding a class, not editing a
router file, an OpenAPI document, and a tool manifest.
Context + Binding DI with @inject, providers, interceptors, extensions, lifecycle observersZodType directlyz.toJSONSchema/explorer and MCP Inspector at /mcp-inspector/llms.txt + /llms-full.txt served from the same route registryconfirm: (payload-bound confirmation tokens) and
idempotency: (idempotency-key replay) on routes and tools@price('$0.001') meters a route or tool and (with
installPriceGate) refuses unpaid calls with an x402/MPP challenge{type, id} address — serialized turns,
idempotent retries, lease-free queries, and an optional event log@modelcontextprotocol/server / @modelcontextprotocol/client)Status: alpha. End-to-end examples and tests pass; the API surface is stabilizing but may still change between alpha releases.
The core product claim is one schema, every boundary. Compared with common Node/TypeScript service stacks, AgentBack optimizes for teams whose APIs are consumed by both applications and AI agents.
| Stack | Runtime contract | Service contract | Agent/tool contract |
|---|---|---|---|
| Express + raw Zod | Hand-wired Zod | Hand-written OpenAPI | Hand-written tool manifest |
| Fastify | JSON Schema/TypeBox | OpenAPI via @fastify/swagger | Custom adapter |
| Hono | Zod (validator) | OpenAPI via @hono/zod-openapi | Custom adapter |
| tRPC | Zod | TypeScript-only | Custom adapter |
| NestJS | class-validator | Swagger decorators | Custom adapter |
| FastAPI | Pydantic | OpenAPI from the same models | Custom adapter |
| AgentBack | Zod | OpenAPI from same Zod | MCP from same Zod |
Use it when you need HTTP APIs, MCP tools, docs, typed clients, policy checks, and usage rails to stay coherent as the system grows.
Fastify and Hono are transport runtimes, not competitors. They solve HTTP
plumbing; AgentBack solves the layer above — one Zod schema projected to REST,
OpenAPI, and MCP through a DI container. And it's host-portable: a
runtime-neutral RestServer.fetchHandler() runs the full pipeline (routing,
validation, DI, auth, streaming, uploads, MCP-over-HTTP) on Express, a native
Node listener, Fastify, Hono, Bun, Deno, and Workers — see
examples/hello-hosts and the
HTTP hosts guide.
Pick your host by class: RestApplication (a.k.a. ExpressRestApplication) for
the Node/Express host, or EdgeRestApplication for fetch/edge — the latter
installs no express/cors (the neutral middleware machinery lives in
@agentback/middleware). Ship either with agentback deploy vercel or
agentback deploy cloudflare (the @agentback/cli, with a nodejs_compat-aware
bundle-doctor preflight).
Full docs live in docs/ — a guided path from the core
ideas to building real apps:
The rest of this README is the one-page tour and code feel.
The DI framework is the foundation; everything else is built on it. The full
catalog — DI foundation, REST/MCP/clients, and platform components — is in
docs/packages.md; each package also ships its own
README.md under packages/.
Scaffold a new service (REST, MCP, or both) — no global install needed:
npm create agentback my-service # hybrid (REST + MCP)
pnpm create agentback my-service --template rest # REST only
pnpm create agentback my-api --template hybrid --drizzle --auth # with add-ons
Run npm create agentback with no name for an interactive wizard. See
create-agentback for templates,
capability flags (--drizzle/--auth/--console), and host options.
Or work in this repo against the bundled examples:
pnpm install
pnpm build
pnpm -F hello-rest start # REST + Swagger UI
pnpm -F hello-mcp test # MCP over stdio (drives the server with a test client)
pnpm -F hello-hybrid start # REST + MCP from one process, both UIs
For a complete, real-world build beyond the in-repo hello-* examples, see
ninemindai/agentback-demo —
a Weather MCP server where one Zod schema set is served over stdio, authenticated
HTTP, and a dev console, backed by the free Open-Meteo API.
This repo ships an agent skill —
skills/agentback — that teaches a coding agent the
framework's decorator patterns, slot-0 input-bundle convention, DI container,
auth stack, and schema-sharing client, with task-scoped reference files the
agent loads on demand. If you're building an app with AgentBack and an
agent is doing the typing, install the skill first; it encodes the conventions
that aren't guessable from type signatures alone.
The easiest install is skills.sh, which supports Claude Code, Codex, Cursor, Copilot, Gemini, and 20+ other agents:
npx skills add ninemindai/agentback # pick agents interactively
npx skills add ninemindai/agentback -a claude-code -a codex -y
npx skills add ninemindai/agentback -g -y # user-global, all agents
Once installed, the agent discovers it automatically; it activates when a task
mentions @agentback/* packages, the REST/MCP decorators, or hybrid
Zod-shared apps.
Agents without skill support — the skill is plain markdown with a YAML
description. Point the agent at it from your instructions file (e.g.
AGENTS.md):
When working with @agentback/\* packages, first read
skills/agentback/SKILL.md and the relevant file under
skills/agentback/references/ (REST, MCP tools, DI, auth, client sharing,
or composition) before writing code.
The skill's references/ directory splits the
deep material by task — rest-and-openapi.md, mcp-tools.md,
dependency-injection.md, auth-and-rate-limiting.md,
schema-sharing-and-client.md, composition-and-operations.md — so agents
pull only the context the current task needs.
A full code walkthrough — the DI container, a REST service, and an MCP server, in
compiling TypeScript drawn from the packages. Expand it, or jump straight to the
runnable examples/ and the guides.
import {Context, BindingScope, inject, injectable} from '@agentback/context';
import {Application} from '@agentback/core';
@injectable({scope: BindingScope.SINGLETON})
class Clock {
now() {
return new Date().toISOString();
}
}
@injectable()
class Greeter {
constructor(@inject('clock') private clock: Clock) {}
greet(name: string) {
return `Hello ${name} at ${this.clock.now()}`;
}
}
const app = new Application(); // Application IS a Context
app.bind('clock').toClass(Clock);
app.service(Greeter); // tag-based service binding
const greeter = await app.get<Greeter>('services.Greeter');
console.log(greeter.greet('world'));
The same Context/Binding/@inject machinery is what the REST and
MCP servers below use under the hood — controllers and tool classes are
just tagged bindings the servers discover at start time.
import {z} from 'zod';
import {api, get, post} from '@agentback/openapi';
import {RestApplication} from '@agentback/rest';
import {inject} from '@agentback/context';
const HelloPath = z.object({name: z.string().min(1).max(64)});
const Greeting = z.object({greeting: z.string()});
const EchoIn = z.object({text: z.string().min(1).max(280)});
const EchoOut = z.object({echoed: z.string(), at: z.string()});
@api({basePath: '/greet'})
class GreetingController {
@get('/hello/{name}', {path: HelloPath, response: Greeting})
async hello(input: {path: z.infer<typeof HelloPath>}) {
return {greeting: `Hello, ${input.path.name}!`};
}
@post('/echo', {body: EchoIn, response: EchoOut})
async echo(
input: {body: z.infer<typeof EchoIn>},
@inject('clock') clock: {now(): string},
) {
return {echoed: input.body.text, at: clock.now()};
}
}
const app = new RestApplication();
app.bind('clock').to({now: () => new Date().toISOString()});
app.restController(GreetingController);
await app.start();
// GET /openapi.json -> OpenAPI 3.1.1
// POST /greet/echo with {"text":""} -> 422 with Zod issues
Schemas live once on each verb decorator. The handler receives a single
input object with body/path/query/headers keys derived via
z.infer — change a schema and the parameter type updates. @inject
weaves in at slot 1+; on routes with no input schemas, slot 0 is yours
too (e.g. @get('/whoami') async whoami(@inject(USER) user) { … }).
import {z} from 'zod';
import {mcpServer, tool, MCPComponent} from '@agentback/mcp';
const ForecastInput = z.object({
city: z.string(),
days: z.number().int().min(1).max(7),
});
const ForecastOutput = z.object({
city: z.string(),
forecast: z.string(),
});
@mcpServer()
class WeatherTools {
@tool('get_forecast', {
description: 'Returns the forecast for a city.',
input: ForecastInput,
output: ForecastOutput,
})
async getForecast(input: z.infer<typeof ForecastInput>) {
return {city: input.city, forecast: 'sunny'};
}
}
const app = new RestApplication();
app.component(MCPComponent); // registers MCPServer
app.service(WeatherTools); // tag flows from @mcpServer's @bind metadata
await app.start();
// stdio MCP transport active by default; mount mcp-inspector for a UI
The schemas live once, on the decorator. The parameter type is derived
via z.infer<typeof ForecastInput> — if you change the schema, TS
rewrites the parameter type. With output: declared, the return type
is also constrained at compile time and validated at runtime; the MCP
SDK is given the schema so structured-content clients can consume it
directly. Omit output: to keep the method's return type unconstrained.
The @mcpServer() decorator uses @bind({tags: {mcpServer: true}}) under the
hood. When you bind the class with app.service() (or app.controller()),
the framework reads that metadata via createServiceBinding and the
mcpServer tag is applied automatically — no manual .tag() call needed.
Beyond schemas, the framework ships the conventions agents need from an API:
@post('/deploy', {body: DeployIn, response: DeployOut, confirm: true})
async deploy(input: {body: z.infer<typeof DeployIn>}) { … }
// 1st call -> 409 {error: {code: 'confirmation_required', confirmationToken, hint}}
// identical retry with x-confirmation-token header -> executes.
// The token is single-use and bound to the exact payload — a confirmed
// call cannot differ from the proposed one. Same flow on MCP tools via
// @tool(..., {confirm: true}) and a `confirmationToken` input property.
@post('/charge', {body: ChargeIn, response: ChargeOut, idempotency: true})
async charge(input: {body: z.infer<typeof ChargeIn>}) { … }
// Replaying an idempotency-key header returns the original result without
// re-executing; concurrent duplicates share one execution; errors aren't cached.
{error: {statusCode, code, message, issues?, schema?, retryable, hint?}} —
a stable code (never parse message), per-field issues, the violated
section's JSON Schema inline, and a one-line remediation hint. MCP tool
errors carry the same envelope as REST, so one parser self-corrects on
both surfaces.app.start() serves /llms.txt
(compact endpoint index) and /llms-full.txt (full inlined schemas) from
the same registry as /openapi.json; generateAgentContext() renders an
agent-context manual (CLAUDE.md shape) for the live API. Components append
sections via the AX_SECTION_TAG binding tag — installMcpHttp adds the
MCP tool listing automatically.mcpServer.toolCostReport()
token-prices every tools/list entry and totals what each connection costs
a caller's context window; formatToolCostReport() flags over-budget tools.Three packages were rewritten rather than ported wholesale because the upstream code carries baggage the fork doesn't want:
openapi — the upstream @loopback/openapi-v3 is built around
[email protected] loose types and a JSON-Schema-via-
@loopback/repository-json-schema pipeline. We emit OpenAPI 3.1.1
directly from Zod via z.toJSONSchema({target: 'draft-2020-12'}), which
aligns with OpenAPI 3.1's dialect.rest — the upstream @loopback/rest is ~10k LoC of sequences,
actions, and middleware composition. We have a minimal RestServer that
handles routing, Zod request/body validation, error mapping, and serves
the OpenAPI document.mcp-inspector — the official @modelcontextprotocol/inspector is
a CLI tool, not an embeddable UI. We ship a small DOM-built inspector
that talks to the in-process MCPServer directly via a JSON API.The DI foundation (metadata, context, core) and the
http-server / express integrations are faithful ESM ports — same
source as upstream LoopBack 4, with .js extensions on relative
imports, lodash → lodash-es, and p-event v6 named exports. The
DI semantics (binding scopes, @inject, providers, interceptors,
extension points, observers) match @loopback/context and
@loopback/core exactly; if you know LB4 DI, you already know this.
These are deliberately not in the framework — they reintroduce the upstream complexity the rewrite walked away from.
findRoute → parseParams → invoke → send → reject DI pipeline). RestServer is a monolithic dispatcher; per-route customization lives on the decorator, cross-cutting in middleware/interceptors, and deeper changes in subclassing RestServer and overriding dispatch / sendResult / sendError.@loopback/repository and the Filter<T> / Where<T> helpers.x-ts-type inlining — replaced by Zod schemas attached to route metadata.@oas.deprecated / @oas.tags / @oas.visibility decorator namespace.@agentback/express ships the LB4 middleware machinery, and RestApplication exposes app.middleware(...) and app.expressMiddleware(...). Registered middleware runs through the framework's chain before every route handler.cors: true (or pass CorsOptions) on RestServerConfig; the cors package is mounted globally.@agentback/mcp-http's installMcpHttp(app) exposes the in-process MCP server at /mcp so remote clients (Claude, Cursor) can reach the same tools; stdio remains the default. Supports OAuth 2.1 resource-server protection (auth: option + /.well-known/oauth-protected-resource), scope-based tool ACL (@tool(..., {scope})), and DNS-rebinding protection.We use Vitest (2000+ tests across the workspace), not LB4's mocha/should ports. Tests run against built dist/ — pnpm build before pnpm test.
Contributions welcome — see CONTRIBUTING.md for setup, conventions, and the proposal process.
pnpm install # install all workspace deps
pnpm build # tsc -b across the workspace
pnpm test # vitest run — IMPORTANT: requires a prior `pnpm build`
pnpm -F <pkg> build # build a single package
Tests run against built dist/. vitest.config.ts globs
packages/*/dist/__tests__/**/*.{test,spec,unit,integration,acceptance}.js,
so editing a .ts file requires pnpm build (or pnpm build:watch)
before pnpm test sees the change.
TypeScript project references keep incremental builds fast. ESLint flat config + Prettier match the LB4 conventions (single quotes, no bracket spacing, 80 col).
Schemas live on the decorator (@get/@post/...'s options object,
@tool's input/output). The handler always receives one
positional input bundle whose type is derived via z.infer. @inject
parameters live at slot 1+ when schemas are declared; when no schemas
are declared, slot 0 is free for @inject too. The slot-0 reservation
is enforced at decoration time, and URL placeholders are cross-checked
against the path: schema at app.start().
MIT.