One Server, Many Agents
Turn your MCP server into a shared, multi-app service—without transport gymnastics TL;DR: The MCP spec and official SDKs give you transports and sessions, but most examples wire a single connection to a single server instance. That’s fine until you want several IDEs, bots, and dashboards to share one server—and to host multiple apps behind clean auth and scopes. FrontMCP makes that the default with sessions, transport identity, per-app scoping, DI, decorators, and authentication out of the box.This is not a “stdio vs SSE” post. The issue is the connection model and defaults most examples
encourage—regardless of transport. Streamable HTTP + sessions is the spec-aligned path for multi-client servers;
FrontMCP productizes it and layers scoping, auth, and DI on top.
How the “one client, one server” pattern sneaks into production
If you follow the happy path:- You open the official
@modelcontextprotocol/sdkdocs. - You copy the example that spins up a server.
- You attach a transport (stdio, HTTP, SSE, or Streamable HTTP).
- You connect an MCP client, see your tools, call them, and ship a demo.
- One IDE ↔ one MCP client ↔ one server.
- Logs are readable.
- JSON-RPC messages look clean.
- You can restart everything with a single command.
- A second IDE instance for another developer.
- A background worker using the same tools.
- A dashboard that introspects tools and runs them on behalf of users.
- Maybe even multiple agents orchestrating workflows together.
- N agents = N server processes Each agent spins its own server, often as a child process or a separate container.
- Zombie servers When a client crashes, the server it launched may keep running.
- Scattered logs and state Caches, warmups, and logs live in different processes.
- Awkward auth stories Access tokens and OAuth flows are tied to a single client/server pair.
You accidentally treated your MCP server as a client-owned subprocess instead of a shared, long-lived service.
Why this isn’t really about stdio vs HTTP vs SSE
It’s tempting to blame the transport:- “Maybe stdio is the problem.”
- “Maybe SSE is the problem.”
- “Maybe I should just switch to Streamable HTTP and everything will be fine.”
- If your code assumes one transport = one connection = one client,
- Then it doesn’t matter whether that transport is stdio, SSE, or Streamable HTTP,
- You’ll still end up multiplying servers for every serious client.
- a homegrown session store,
- hand-managed transport IDs,
- per-app routing,
- DIY auth integration,
- and a bunch of conditionals sprinkled across tools and handlers.
The real problem you’re hitting
When you move beyond a single client, you suddenly need to solve:- Session IDs & transport identity So requests don’t collide across clients and nodes, and you can safely resume or route streams.
- Per-app scoping So multiple products/tenants can share one host without stepping on each other.
- Auth surfaces Remote IdP or local issuer, wired consistently and scoped correctly to apps and tools.
- Lifecycle management Sessions that can be created, resumed, and ended in a way that survives proxies, restarts, and load balancers.
- DI & hooks So you can inject config, secrets, caches, tenants, and logging without duplicating boilerplate in every tool.
- “Here are my tools and apps, written like normal TypeScript services.”
- “Give me a multi-agent, multi-app MCP server that Just Works.”
What we actually want from an MCP server
If you strip away the noise, most teams want their MCP server to behave like a modern web backend:-
Multi-client by design
- Many IDEs, agents, and services share one server.
- Sessions keep each client isolated and resumable.
- Transport identity is stable across reconnections and nodes.
-
Multi-app composition
- Group tools into apps by product/domain/tenant.
- Sometimes expose a unified toolbox.
- Sometimes isolate apps with separate auth and base paths.
- Sometimes expose an app as a standalone MCP server.
-
Auth-aware tool surface
- The tools a client sees are based on who is logged in, not just what’s deployed.
- OAuth scopes and identities matter.
-
Typed code with DI
- Tools look like clean TypeScript handlers.
- Inputs/outputs are validated with Zod.
- Dependencies (DB, caches, external APIs) are injected via DI.
-
Adapters for existing systems
- Already have REST APIs? Use OpenAPI, not Ctrl+C/Ctrl+V into new @Tool classes.
What FrontMCP flips to “on by default”
FrontMCP is a TypeScript-first framework that treats MCP servers like proper services, not disposable processes.Multi-client server
Start a Streamable HTTP host that accepts many clients concurrently. Sessions and transport IDs are handled for
you, so multiple IDEs, copilots, and workers can share one server safely.
Multi-app composition
Host several apps under one server or isolate them via
splitByApp. Each app gets clean base paths
and can define its own auth.Auth that fits
Use remote OAuth to front an external IdP, or local OAuth with a built-in issuer. Scope auth at
the server or per app.
DI + decorators
Describe servers, apps, tools, resources, and prompts with decorators. Inject providers with
GLOBAL,
SESSION, or REQUEST scope. Add hooks for logging, rates, and policy without
boilerplate.Standard MCP vs FrontMCP (visually)
Sometimes it’s easier to just see it:

- On the left: the “one client, one server” pattern duplicated per agent.
- On the right: one FrontMCP server hosting many apps and many agents in a spec-aligned way.
Show me the code (multi-agent, multi-app, scoped & authenticated)
Let’s look at what a multi-client, multi-app server looks like in FrontMCP. We’ll define:- A server with Streamable HTTP.
- Two apps:
billinganddocs. - Per-app auth for
billing.
src/server.ts
src/billing.app.ts
src/tools/create-invoice.tool.ts
- Connect over Streamable HTTP.
- Create their own sessions.
- Call
billing.create_invoice(if authorized). - Share the same app code and server instance.
Composition modes: one server, many ways to slice apps
The most underrated part of FrontMCP is how you can expose the same apps in different shapes—without changing the app code.splitByApp: false — unified tool surface per user
When splitByApp is false (the default):
- All your applications are combined under one MCP server.
- A connected client can list all tools the logged-in OAuth user is allowed to see, regardless of which app defines them.
- Auth still applies, but the visible tools and resources are filtered by the user’s identity and scopes.
- If two apps define tools with the same name, FrontMCP automatically prefixes the tool name with the app id
—for example:
billing.create_invoicevsops.create_invoice.
- A single, large toolbox in the IDE.
- Agents that can orchestrate across many domains (billing + docs + ops).
- One “MCP endpoint” for the whole suite, with per-user filtering.
splitByApp: true — per-app isolation
When splitByApp is true:
- Each app gets its own base path and can define its own auth.
- You can run multiple products/tenants under one server but with clearly separated surfaces.
- Clients can connect directly to the specific app they care about, while you still deploy just one server process.
- Apps have distinct consumers and security boundaries.
- You want a “microservices-like” feel, but still prefer one deployment unit.
- You’re onboarding teams gradually and want a clear line between their responsibilities.
Standalone apps under /{appId}
Some apps need to serve double duty:
- They live inside a grouped server (for internal or cross-app usage).
- They also need to behave as standalone MCP servers for specific clients.
- The app is served under the prefix
/{appId}as if it were its own MCP server. - From the client’s perspective,
/{appId}is a standalone MCP endpoint with its own discovery and tools. - From your perspective, it’s still the same app class and the same deployment.
- Expose a clean
/{appId}endpoint for partners while keeping internal tools under a unified host. - Gradually carve out apps from a single MCP server into more “independent” endpoints without duplicating code.
Plugging in existing REST APIs with OpenAPIAdapter
A common reaction to any MCP framework is:“This is cool, but I already have a REST API. Do I really have to rewrite everything as @Tool classes?”With FrontMCP, the answer is no.
Connect your REST API with OpenAPIAdapter
The OpenAPI adapter lets you generate tools directly from an OpenAPI spec:- Provide an OpenAPI document (URL or file).
- Specify the base URL for requests.
- Optionally plug in middleware for auth, tenancy, or logging.
- Get a bundle of MCP tools that call your REST endpoints.
src/openapi.app.ts
https://docs.agentfront.dev/0.3/adapters/openapi-adapter).
This is the fastest path to:
- Making your existing API “MCP-native.”
- Letting agents and IDEs call it via tools.
- Avoiding a full parallel tool implementation.
Migration playbook (keep your tools; change your host)
If you already have MCP servers or REST APIs, here’s a practical way to move into FrontMCP without a rewrite:1
Lift your tools into an app
Wrap existing handlers with
@Tool or tool()(handler) and group them
under @App. Or plug in the OpenAPI adapter to generate tools from your REST API.2
Choose composition
Start with
splitByApp: false for a unified tool surface, then introduce
splitByApp: true or standalone apps under /appId where isolation is needed.3
Wire auth once
Decide on remote vs local OAuth. Make your tool surface depend on the logged-in user and scopes,
not just the server process.
4
Expose discovery cleanly
Keep
entryPath in sync with your .well-known PRM resourcePath. Test behind
your real proxy/load balancer to validate streaming and keep-alives.5
Turn on observability
Add DI providers and hooks for logging, metrics, and tracing so you can see which apps and tools your agents
actually use.
FAQs
Is this about “legacy SSE vs stdio”? No. The issue is the single-connection default common in examples across transports. Streamable HTTP + sessions already solves multi-client semantics; FrontMCP makes that the default and adds scoping, auth, and DI. Can one server safely host multiple apps for multiple agents? Yes. WithsplitByApp: false, you get a unified tool surface per user (with automatic name-conflict prefixing). With splitByApp: true, each app gets isolated paths and optional per-app auth, while sharing the same underlying server. Standalone apps under /appId give you separate MCP endpoints when you need them.
Can I connect my current REST API without writing tools?
Yes. Use the OpenAPIAdapter to generate tools automatically from your existing REST API. Point FrontMCP at your OpenAPI definition and let the adapter create MCP tools for you—no manual tool classes required.
See: OpenAPI Adapter docs (based on this guide).

