Skip to main content
@fentaris/core has three supported API tiers:
  • High-level application builders for most apps.
  • Advanced low-level APIs for explicit proxy and transport wiring.
  • Extension contracts for third-party integrations.
Use @fentaris/core as the primary import path. Use @fentaris/core/extensions when implementing custom extension contracts and you need names such as Policy without colliding with top-level declaration helpers. Avoid deep imports from @fentaris/core/dist/* or source-layout paths. Those paths are package internals and can move during pre-alpha.

Experimental plugin contracts

Plugin support is experimental and mostly placeholder-only in the current alpha surface. The package exposes the reserved contracts from @fentaris/core/experimental/plugins, but Fentaris does not yet provide a working plugin runtime. What exists today:
  • Manifest types.
  • Registry contract.
  • Loader contract.
  • Lifecycle hook contract.
  • Capability metadata placeholder.
What does not exist yet:
  • Real plugin loading flow.
  • Package discovery.
  • Activation runtime.
  • Permission negotiation.
  • Plugin auth integration.
  • CLI plugin management.
import type {
  PluginCapabilities,
  PluginLifecycleHooks,
  PluginLoader,
  PluginManifest,
  PluginRegistry,
} from "@fentaris/core/experimental/plugins";

class CustomPluginLoader implements PluginLoader {
  async load(name: string): Promise<PluginManifest> {
    return { name, version: "0.0.0" };
  }
}

const capabilities: PluginCapabilities = {
  requiresAuth: true,
  requiredPermissions: ["tools:call"],
};

const lifecycle: PluginLifecycleHooks = {
  async onActivate() {},
};
Do not rely on these contracts for production plugin behavior yet. They are a reserved boundary for future work and may change during alpha.

High-level app builders

Prefer fentaris() with declaration helpers for new applications:
import { fentaris, group, mcp, policy, stdio, user } from "@fentaris/core";

const proxy = fentaris({
  servers: [
    mcp("github", {
      transport: stdio({ command: "github-mcp-server" }),
    }),
    mcp("internal", {
      transport: stdio({ command: "internal-mcp-server" }),
    }),
  ],
  users: [user("alice")],
  groups: [
    group({
      id: "admins",
      users: [user("alice")],
      policy: policy("admins").mcp("*").allow("*"),
    }),
  ],
});
This syntax is the recommended default for application authors because it keeps server, user, group, policy, and credential declarations together.

Local capability declarations

Use app.local(name) for app-owned tools, resources, prompts, and completions. This is the preferred path when the behavior lives inside the Fentaris app and should still use policy, middleware, operation events, logging, and audit metadata.
import { fentaris, policy } from "@fentaris/core";

const app = fentaris({
  policy: policy("workspace")
    .mcp("workspace")
    .allow("status")
    .mcp("workspace")
    .allowCapability({ operation: "prompt:get", target: "review_pr", targetKind: "prompt" }),
});

app.local("workspace")
  .tool("status", { inputSchema: { type: "object" } }, async (ctx) => ({
    content: [{ type: "text", text: ctx.auth.authenticated ? "ok" : "anonymous" }],
  }))
  .prompt("review_pr", { arguments: [{ name: "diff" }] }, async (_ctx, params) => ({
    messages: [{ role: "user", content: { type: "text", text: String(params.arguments?.diff ?? "") } }],
  }));
Local namespace names must be unique across upstream MCP server names. Policies address local namespaces with .mcp(name).

Advanced low-level APIs

Use createProxy(), McpProxy, McpServer, and explicit transports when you need direct construction or custom downstream exposure:
import {
  McpProxy,
  McpServer,
  HttpProxyExposureTransport,
  StreamableHttpMcpTransport,
  createProxy,
} from "@fentaris/core";

const proxy = createProxy({
  servers: [
    new McpServer({
      name: "crm",
      transport: new StreamableHttpMcpTransport({
        url: new URL("https://crm.example.com/mcp"),
      }),
    }),
  ],
});

const explicitProxy = new McpProxy({
  servers: [
    new McpServer({
      name: "crm",
      transport: new StreamableHttpMcpTransport({
        url: new URL("https://crm.example.com/mcp"),
      }),
    }),
  ],
});

await explicitProxy.listen(new HttpProxyExposureTransport({ port: 4000 }));
await proxy.start({ port: 3000 });

Extension contracts

Extension authors can implement contracts from @fentaris/core/extensions.

Upstream transport

Custom FentarisTransport implementations remain the advanced escape hatch for protocol adapters, external runtimes, or behavior that should be packaged as an MCP server-like transport. For app-owned capabilities, prefer app.local(name).
import type { FentarisTransport } from "@fentaris/core/extensions";

class CustomTransport implements FentarisTransport {
  async listTools() {
    return { tools: [{ name: "search", inputSchema: { type: "object" as const } }] };
  }

  async callTool() {
    return { content: [{ type: "text" as const, text: "ok" }] };
  }

  async close() {}
}

Downstream exposure transport

import type { ProxyExposureHandle, ProxyExposureTransport, ProxyRuntime } from "@fentaris/core/extensions";

class CustomExposure implements ProxyExposureTransport<ProxyExposureHandle> {
  async listen(runtime: ProxyRuntime) {
    runtime.logger.info("custom exposure started");
    return { async close() {} };
  }
}

Policy

import type { Policy, PolicyDecision, ToolCallRequest } from "@fentaris/core/extensions";

class CustomPolicy implements Policy {
  readonly name = "custom";

  getPermissions() {
    return [{ tool: "*", effect: "allow" as const }];
  }

  evaluate(request: ToolCallRequest): PolicyDecision {
    return { allowed: request.toolName !== "dangerous" };
  }
}

Registry

import type { Registry } from "@fentaris/core/extensions";

class CustomRegistry implements Registry {
  async getUser(userId: string) {
    return { id: userId, plan: "team" };
  }

  async getSecrets() {
    return { token: "redacted" };
  }

  async getTokens() {
    return null;
  }
}

Rate limiter

import type { RateLimiter } from "@fentaris/core/extensions";

class CustomRateLimiter implements RateLimiter {
  async consume() {
    return true;
  }

  async checkLimit() {
    return true;
  }

  async recordCall() {}

  async getRemainingCalls() {
    return 99;
  }
}
consume(key) is the enforcement path used by middleware. Custom limiters and custom RateLimitStore implementations should perform the check and increment atomically for each limiter key, for example with a Redis Lua script, transaction, or native atomic counter operation. checkLimit() and recordCall() remain useful for diagnostics and compatibility, but they are not used for middleware enforcement.

Logger driver

import type { LoggerDriver, LogEntry } from "@fentaris/core/extensions";

class CustomLoggerDriver implements LoggerDriver {
  write(entry: LogEntry): void {
    console.log(entry.level, entry.message);
  }
}

Middleware and events

import type { Middleware, ProxyEventHandler } from "@fentaris/core/extensions";

const middleware: Middleware = async (ctx, next) => {
  ctx.log.info("operation", { operation: ctx.operation });
  return next();
};

const eventHandler: ProxyEventHandler = ({ ctx, durationMs }) => {
  ctx.log.info("completed", { operation: ctx.operation, durationMs });
};

Import policy

TierImport pathStability
High-level builders@fentaris/corePublic
Advanced runtime APIs@fentaris/corePublic advanced
Extension contracts@fentaris/core/extensions or top-level type aliasesPublic extension
Plugin contracts@fentaris/core/experimental/pluginsExperimental placeholder
Source files and dist/* internalsDeep package pathsInternal compatibility only