> ## 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.

# Setup

> Configure a Fentaris proxy with the high-level TypeScript API.

Use `fentaris()` as the app boundary, declare upstream MCP servers with high-level helpers, and keep local endpoint defaults in `fentaris.json`.

## Prerequisites

* Node.js 20+ (LTS recommended)
* pnpm or npm

## Quick Start

<CodeGroup>
  ```bash pnpm theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
  pnpm add @fentaris/core
  ```

  ```bash npm theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
  npm i @fentaris/core
  ```
</CodeGroup>

Create `fentaris.json` at the project root:

```json theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "name": "fentaris-proxy",
  "packageManager": "pnpm",
  "entrypoint": "src/index.ts",
  "port": 4000,
  "path": "/mcp",
  "authDir": ".fentaris"
}
```

Then create `src/index.ts`:

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

const app = fentaris();

app.server("filesystem", {
  displayName: "Filesystem",
  transport: stdio({
    command: "npx",
    args: ["-y", "@modelcontextprotocol/server-filesystem", "./demo-files"],
  }),
});

await app.start();
```

`app.start()` reads `port` and `path` from the nearest `fentaris.json` when they are not passed explicitly. With the config above, the proxy listens at `http://localhost:4000/mcp`.

## Add a Second Server

Register each upstream MCP server with its own `app.server(...)` call:

```ts theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
app.server("custom", {
  displayName: "Custom Tools",
  transport: stdio({
    command: "node",
    args: ["./dist/server.js"],
  }),
  env: (user) => ({
    USER_ID: user.id ?? "anonymous",
  }),
});
```

The first argument is the Fentaris server name. Fentaris uses it for routing, policy, middleware context, logs, hooks, and proxied tool names.

<Note>
  `app.server(...)` is an alias for `app.mcp(...)`. Use `app.server(...)` when the page is explaining upstream server registration, and use policy APIs for access control.
</Note>

## Add Middleware

```ts theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
app.use(async (ctx, next) => {
  ctx.log.info("tool-call", {
    server: ctx.server?.name,
    tool: ctx.tool?.name,
  });

  return next();
});
```

Middleware runs before matching calls are forwarded to the upstream MCP server. Use it for request-time checks, logging, argument validation, and approval workflows.

## Strict TypeScript Example

In strict TypeScript projects, annotate exported middleware and policies at the boundary and let callback parameters infer from Fentaris:

```ts theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
import {
  bearer,
  credential,
  credentialJson,
  fentaris,
  group,
  mcp,
  policy,
  streamableHttp,
  user,
  type Middleware,
} from "@fentaris/core";

const requestTags: Middleware = async (ctx, next) => {
  ctx.log.setTag("subject", ctx.subject?.id ?? "anonymous");
  ctx.log.setTag("operation", ctx.operation);

  if (ctx.server?.name === "github" && ctx.tool?.name === "delete_repo") {
    return ctx.deny("Repository deletion is not allowed from this proxy.");
  }

  return next();
};

const maintainers = policy("maintainers")
  .mcp("github")
  .allow("search_issues")
  .mcp("github")
  .allow("create_issue");

const app = fentaris({
  autoLog: true,
  servers: [
    mcp("github", {
      transport: streamableHttp({ url: "https://github.example/mcp" }),
      auth: bearer(credential("github.token")),
    }),
  ],
  groups: [
    group({
      id: "maintainers",
      users: [
        user("alice", {
          apiKeys: [credentialJson("users.alice.apiKeys.0")],
        }),
      ],
      credentials: {
        "github.token": credentialJson("groups.maintainers.github.token"),
      },
      policy: maintainers,
    }),
  ],
});

app.use(requestTags);

await app.start();
```

Use `type Middleware` for shared middleware functions. Inline `app.use(async (ctx, next) => ...)` callbacks also infer contextual `ctx` and `next` types.

The declarative `fentaris({ ... })` form keeps group credentials next to the users and policy that own them. Use incremental calls such as `app.server(...)`, `app.policy(...)`, and `app.group(...)` when the app does not need to declare group- or user-scoped credential sources.

<Note>
  `app.group("maintainers").mcp("github")` returns a scoped middleware and event handle for an already declared server. It does not register an upstream transport or group credentials.
</Note>

## Connect an MCP Client

Point your MCP client to the endpoint from `fentaris.json`:

```bash theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
http://localhost:4000/mcp
```

Fentaris exposes one endpoint and forwards allowed requests to the registered upstream MCP servers.

<Tip>
  Change `port` and `path` in `fentaris.json` instead of hardcoding endpoint settings in `src/index.ts`.
</Tip>

## Verify Available Tools

If you are working in Node, you can list tools directly:

```ts theme={null} theme={"theme":{"light":"github-light","dark":"github-dark"}}
const tools = await app.listTools(undefined, { id: "demo-user" });
console.log(tools.tools.map((tool) => tool.name));
```

## Low-Level API

The class constructors are still available for advanced cases that need explicit object construction:

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

For normal applications, prefer `fentaris()`, `app.server(...)`, and `stdio(...)`.

## Related Documentation

* [Quickstart](/getting-started/quickstart)
* [Tutorial](/getting-started/tutorial)
* [Client Configs](/guides/client-configs)
* [Config file](/reference/config-file)
* [McpProxy reference](/reference/mcp-proxy)
