> ## Documentation Index
> Fetch the complete documentation index at: https://fentaris.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# End-to-end examples

> Full examples with multi-server routing, policy, and logging.

> Combine routing, tenant identity, policy, and logging in one proxy

These examples use the high-level Fentaris API. Start here when you want production-shaped wiring without dropping into low-level constructors.

## Multi-Tenant Tool Routing

Use `UserContext` to route requests and inject tenant-specific environment variables.

```ts theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { fentaris, mcp, stdio } from "@fentaris/core";

const proxy = fentaris({
  servers: [
    mcp("analytics", {
      displayName: "Analytics",
      transport: stdio({
        command: "node",
        args: ["./dist/analytics-server.js"],
      }),
      env: (user) => ({
        TENANT_ID: String(user.tenantId ?? "unknown"),
      }),
    }),
  ],
  user: (req) => ({
    id: req.headers["x-user-id"],
    tenantId: req.headers["x-tenant-id"],
  }),
});

await proxy.start();
```

### Why it matters

* Each tenant sees only their own data.
* You can keep the MCP server unchanged.

### Operational Tips

* Use a consistent `tenantId` key across middleware, logs, and env injection.
* Validate headers at the edge; never trust user input directly.

## Policy Enforcement with Middleware

```ts theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
proxy.use(async (ctx, next) => {
  const isAdmin = ctx.user.role === "admin";
  if (ctx.tool?.proxyName === "filesystem__delete" && !isAdmin) {
    return ctx.deny("Only admins can delete files.");
  }

  return next();
});
```

### Why it matters

* Centralized access control.
* A single place to audit policy changes.

### Operational Tips

* Log denies with the reason and `user.id`.
* Keep policy text human-readable; it becomes part of your audit trail.

## Observability with Trace Tags

```ts theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
proxy.use(async (ctx, next) => {
  ctx.log.setTag("trace", ctx.user.traceId ?? "no-trace");
  ctx.log.info("tool-call", { tool: ctx.tool?.proxyName });
  return next();
});
```

### Why it matters

* Correlate tool calls across services.
* Keep logs structured and consistent.

### Operational Tips

* Make sure `traceId` is propagated from your API gateway.
* Avoid logging payloads that may contain PII.

## Server-Scoped Hook for Diagnostics

```ts theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
proxy.mcp("diagnostics").on("tool:success", async ({ ctx, durationMs }) => {
  ctx.log.info("diagnostics.tool.success", {
    tool: ctx.tool?.name,
    durationMs,
  });
});
```

### Why it matters

* Hooks let you target a subset of tool calls.
* Perfect for audits and analytics.

### Operational Tips

* Send diagnostic logs to a lower-retention index.
* Use server-scoped handles such as `proxy.mcp("diagnostics")` to avoid ambiguity.

## Putting It All Together

In a real deployment, you will combine these patterns:

* **User resolver** for tenant identity and trace info.
* **Middleware** for policy and environment-specific rules.
* **Hooks** for targeted audits and analytics.

When in doubt, start with the [Tutorial](/getting-started/tutorial) and expand from there.

## Common Pitfalls

* Forgetting to close the proxy leads to orphaned server processes.
* Using unstable server names breaks tool prefixes for clients.
* Logging sensitive payloads can create compliance issues.

## Low-Level API

Use `McpProxy`, `McpServer`, and explicit transport classes only when you need direct construction or compatibility with older examples.

## Related Documentation

* [Tutorial](/getting-started/tutorial)
* [Proxy setup](/guides/proxy-setup)
* [McpProxy reference](/reference/mcp-proxy)
