MCP Fusion/Core concepts/Routing
Routing
Auto discover tools from the file tree the way Next.js routes pages: folders become dotted names, exports resolve by convention and huge API surfaces group into a few smart tools.
MCP Fusion discovers tools from your file tree the way Next.js discovers pages. One file, one tool, the path is the name.
File based tools
src/tools/
billing/
get-invoice.ts → billing.get_invoice
refund.ts → billing.refund
support/
tickets/
search.ts → support.tickets.searchRegister them once at startup:
import { initMCPFusion, autoDiscover } from '@mcpfusion/core';
const f = initMCPFusion();
await autoDiscover(f, './src/tools');Each file default exports its builder, and the export resolution cascade accepts a default export, a named tool export, or any exported builder that reports its name.
Grouped tools
An API with hundreds of endpoints does not map to hundreds of tools. Every tool name, schema and description costs context window, and the agent's accuracy drops as the list grows. GroupedToolBuilder consolidates related actions into one tool with a discriminator:
f.group('billing')
.describe('Invoice operations')
.action('get', (a) => a
.withString('id', 'Invoice ID')
.requiredFor('get', ['id']))
.action('refund', (a) => a
.withString('id', 'Invoice ID')
.withNumber('amount_cents', 'Amount in CENTS')
.requiredFor('refund', ['id', 'amount_cents']))
.returns(InvoicePresenter);The agent calls billing with a discriminator argument, and the "Required for" annotations tell it which parameters each action needs. A flat surface of 500 endpoints becomes a handful of smart tools, which is the single biggest token saving available on large connectors. See Tools for the per tool builder and Governance for how grouped surfaces stay auditable.
Middleware chains
Middleware applies globally per directory tree and per tool:
// A middleware derives context; its return object merges into ctx.
const withTenant = f.middleware(async (ctx) => ({
tenantId: resolveTenant(ctx.request),
}));
f.query('billing.get_invoice').use(withTenant);- Global
f.middleware()runs for every tool .use()on a builder adds tool specific middleware- Chains are pre compiled at build time into an O(1) pipeline, so adding middleware does not slow the request path down
The context returned by middleware merges into ctx, typed through the initMCPFusion<AppContext>() generic. Every handler, middleware and Presenter downstream sees the same typed context, which is how tenant isolation stays consistent across a whole connector.
Startup and context
f.registry.attachToServer(server, {
contextFactory: async (extra) => ({
db: await pool.acquire(),
tenantId: resolveTenant(extra),
}),
});contextFactory runs per request, so per request resources such as database connections and tenant identity are fresh and isolated. Everything it returns is the ctx your handlers receive.
Next steps
- Tools: the builder itself, params and state gating
- Quickstart: a minimal working connector
- Deploy: ship the whole tree to Vinkius Cloud
