# @Provider

> Declare a shared service, like a database client or a cache, that tools, resources and prompts get with this.get().

Source: https://frontmcp.dev/reference/sdk/provider

`@Provider` declares a service that tools, resources and prompts share: a database client, an API wrapper, a cache, your configuration. FrontMCP creates it, keeps it for as long as its scope says, and hands it to any code that asks with `this.get()`. This is dependency injection: your tools say what they need, not how to build it.

```ts
@Provider(options)
class MyService { /* ... */ }
```

---

## Reference

### `@Provider(options)`

Apply `@Provider` to a class, list it in the `providers` of an app or of the server, and get it inside `execute()` with `this.get(MyService)`.

```ts ticket-store.ts
import { Provider, ProviderScope } from "@frontmcp/sdk";

@Provider({ name: "TicketStore", scope: ProviderScope.GLOBAL })
export class TicketStore {
  private tickets = new Map([["T-1", { id: "T-1", title: "Cannot log in", status: "open" }]]);

  find(id: string) {
    return this.tickets.get(id);
  }
}
```

```ts help-desk.app.ts
import { App } from "@frontmcp/sdk";
import { GetTicket } from "./get-ticket.tool";
import { TicketStore } from "./ticket-store";

@App({ id: "help-desk", name: "Help Desk", providers: [TicketStore], tools: [GetTicket] })
export class HelpDeskApp {}
```

[See more examples below.](#usage)

#### Options

| Option | Type | Description |
| --- | --- | --- |
| `name` | `string` | **Required.** Names the provider in logs and error messages. |
| `scope` | `ProviderScope` | How long one instance lives. Defaults to `ProviderScope.GLOBAL`. See [scopes](#scopes). |
| `description` | `string` | What the provider is for. |
| `id` | `string` | A stable identifier, if `name` isn't one. |

#### Scopes

| Scope | Value | One instance per |
| --- | --- | --- |
| `ProviderScope.GLOBAL` | `"global"` | Server. Created when the server starts, and kept until it stops. **The default.** |
| `ProviderScope.CONTEXT` | `"context"` | Session, for clients that open one (MCP before 2026-07-28). Under 2026-07-28, which has no sessions, one per request, for callers with a token too. Created when the request starts. |
| `ProviderScope.SESSION`, `ProviderScope.REQUEST` | `"session"`, `"request"` | Deprecated. Both mean `CONTEXT`. |

The Playground speaks 2026-07-28, so a `CONTEXT` provider in an example is new on every call.

#### Registering providers

A provider is available where it's registered, and nowhere else:

| Where | Visible to |
| --- | --- |
| `@App({ providers })` | That app's tools, resources and prompts, and its other providers. |
| `@FrontMcp({ providers })` | Every app on the server. |
| `@Plugin({ providers })` | The plugin only. Also list a provider in the plugin's `exports` to make it visible to the apps that use the plugin. Each app that installs the plugin gets its own instance. |

Each entry in `providers` is one of:

| Form | Example | Notes |
| --- | --- | --- |
| A class | `TicketStore` | Must be decorated with `@Provider`. The class is also the token you pass to `this.get()`. |
| A factory | `{ provide: DeskConfig, name: "DeskConfig", inject: () => [], useFactory: () => ({ slaHours: 4 }) }` | `name` is required. `inject` is a **function** that returns the tokens to pass to `useFactory`. `useFactory` can be `async`. Add `scope` to change the default `GLOBAL`. |
| An instance | `{ provide: TicketStore, useValue: new TicketStore() }` | `useValue` must be an instance of a `@Provider` class. |

`provide` is the token: a class, an abstract class, a symbol or a string. An abstract class makes a good token for configuration, because `this.get()` then knows its type.

#### `this.get(token)` and `this.tryGet(token)`

Available in `ToolContext`, `ResourceContext` and `PromptContext`.

- `this.get(token)` returns the instance. If no provider for `token` is registered where the caller can see it, it throws, and a tool call fails with `Provider "…" is not available`.
- `this.tryGet(token)` returns `undefined` instead of throwing.

For a symbol or string token, pass the type yourself: `this.get<number>(SLA_HOURS)`.

#### Constructor injection

In a project compiled by `tsc` with `emitDecoratorMetadata`, a provider's constructor parameters are injected by type:

```ts ticket-repo.ts
import { Provider } from "@frontmcp/sdk";
import { TicketStore } from "./ticket-store";

@Provider({ name: "TicketRepo" })
export class TicketRepo {
  constructor(private store: TicketStore) {}

  titleOf(id: string) {
    return this.store.find(id)?.title;
  }
}
```

The Playground can't run this: it compiles with esbuild, which doesn't emit decorator metadata, so `store` would be `undefined`. The examples below use factories with `inject` instead, which work everywhere.

#### Caveats

- A provider must be **registered** in `providers`. Decorating the class isn't enough.
- In FrontMCP 1.8, `providers` rejects `{ provide, useValue }` with a plain value and `{ provide, useClass }`, even though the TypeScript types allow them. [Use a factory.](#providing-a-value-or-configuration)
- A `GLOBAL` provider **can't depend on** a `CONTEXT` provider. The server fails to start.
- `GLOBAL` means one instance per server **process**. Several processes or serverless instances each have their own, so keep shared data in a database or Redis.
- A provider on an `@App` can declare hooks, and they run for that app's tools. Hooks on a `@FrontMcp` provider or a `CONTEXT` provider never run. See [where hooks can be declared](https://frontmcp.dev/reference/sdk/hooks#where-hooks-can-be-declared).

---

## Usage

### Sharing state between tools

A `GLOBAL` provider is created once, so every tool that gets it sees the same data. Call `assign_ticket`, then `my_queue`, and the ticket is there.

```ts tools.ts active
import { Tool, ToolContext, z } from "@frontmcp/sdk";
import { Assignments } from "./assignments";

@Tool({
  name: "assign_ticket",
  description: "Assign a ticket to a support agent",
  inputSchema: { id: z.string(), agent: z.string() },
})
export class AssignTicket extends ToolContext {
  async execute({ id, agent }: { id: string; agent: string }) {
    this.get(Assignments).assign(id, agent);
    return { id, agent };
  }
}

@Tool({
  name: "my_queue",
  description: "List the tickets assigned to an agent",
  inputSchema: { agent: z.string() },
})
export class MyQueue extends ToolContext {
  async execute({ agent }: { agent: string }) {
    return { tickets: this.get(Assignments).queue(agent) };
  }
}
```

```ts assignments.ts
import { Provider, ProviderScope } from "@frontmcp/sdk";

@Provider({ name: "Assignments", scope: ProviderScope.GLOBAL })
export class Assignments {
  private byTicket = new Map<string, string>();

  assign(id: string, agent: string) {
    this.byTicket.set(id, agent);
  }

  queue(agent: string) {
    return [...this.byTicket].filter(([, a]) => a === agent).map(([id]) => id);
  }
}
```

```ts help-desk.app.ts
import { App } from "@frontmcp/sdk";
import { AssignTicket, MyQueue } from "./tools";
import { Assignments } from "./assignments";

@App({ id: "help-desk", name: "Help Desk", tools: [AssignTicket, MyQueue], providers: [Assignments] })
export class HelpDeskApp {}
```

```ts queue.test.ts
import { test, expect } from "@frontmcp/testing";

test("the next call sees the assignment", async ({ mcp }) => {
  await mcp.tools.call("assign_ticket", { id: "T-1", agent: "nour" });
  await mcp.tools.call("assign_ticket", { id: "T-2", agent: "nour" });
  const result = await mcp.tools.call("my_queue", { agent: "nour" });
  expect(result.json()).toEqual({ tickets: ["T-1", "T-2"] });
});
```

### Keeping state for one request

A `CONTEXT` provider is new for each request (each session, for clients that use sessions). Within a request, every `this.get()` returns the same instance, so it can collect what happens during one call without leaking into the next.

```ts close-ticket.tool.ts active
import { Tool, ToolContext, z } from "@frontmcp/sdk";
import { AuditTrail } from "./audit-trail";

@Tool({ name: "close_ticket", description: "Close a ticket", inputSchema: { id: z.string() } })
export class CloseTicket extends ToolContext {
  async execute({ id }: { id: string }) {
    this.get(AuditTrail).record(`checked that ${id} exists`);
    this.get(AuditTrail).record(`closed ${id}`);
    return { id, status: "closed", trail: this.get(AuditTrail).steps };
  }
}
```

```ts audit-trail.ts
import { Provider, ProviderScope } from "@frontmcp/sdk";

@Provider({ name: "AuditTrail", scope: ProviderScope.CONTEXT })
export class AuditTrail {
  steps: string[] = [];

  record(step: string) {
    this.steps.push(step);
  }
}
```

```ts help-desk.app.ts
import { App } from "@frontmcp/sdk";
import { CloseTicket } from "./close-ticket.tool";
import { AuditTrail } from "./audit-trail";

@App({ id: "help-desk", name: "Help Desk", tools: [CloseTicket], providers: [AuditTrail] })
export class HelpDeskApp {}
```

```ts audit.test.ts
import { test, expect } from "@frontmcp/testing";

test("each call starts with an empty trail", async ({ mcp }) => {
  await mcp.tools.call("close_ticket", { id: "T-1" });
  const second = await mcp.tools.call("close_ticket", { id: "T-2" });
  expect(second.json<{ trail: string[] }>().trail).toEqual(["checked that T-2 exists", "closed T-2"]);
});
```

With `ProviderScope.GLOBAL`, the second call would return all four steps.

### Providing a value or configuration

To register something that isn't a `@Provider` class, like a settings object or a client from a library, use a factory. The object needs a `name`, and `inject` returns the tokens whose instances `useFactory` receives, in order.

<Examples title="Factories">

#### Example: A value
An abstract class works as a typed token: `this.get(DeskConfig)` knows it has a `slaHours`.

```ts help-desk.app.ts active
import { App, Tool, ToolContext, z } from "@frontmcp/sdk";

export abstract class DeskConfig {
  abstract slaHours: { high: number; normal: number };
}

@Tool({
  name: "reply_due",
  description: "How many hours we have to reply to a ticket of this priority",
  inputSchema: { priority: z.enum(["high", "normal"]) },
})
export class ReplyDue extends ToolContext {
  async execute({ priority }: { priority: "high" | "normal" }) {
    return { hours: this.get(DeskConfig).slaHours[priority] };
  }
}

@App({
  id: "help-desk",
  name: "Help Desk",
  tools: [ReplyDue],
  providers: [
    { provide: DeskConfig, name: "DeskConfig", inject: () => [], useFactory: () => ({ slaHours: { high: 4, normal: 24 } }) },
  ],
})
export class HelpDeskApp {}
```

#### Example: Built from another provider
`inject` lists the providers the factory needs. With `as const`, TypeScript infers the factory's parameter types from them.

```ts help-desk.app.ts active
import { App, Provider, Tool, ToolContext, z } from "@frontmcp/sdk";

@Provider({ name: "Settings" })
export class Settings {
  get(key: string) {
    return { SLA_HIGH_HOURS: "4", SLA_NORMAL_HOURS: "24" }[key];
  }
}

export abstract class DeskConfig {
  abstract slaHours: { high: number; normal: number };
}

@Tool({
  name: "reply_due",
  description: "How many hours we have to reply to a ticket of this priority",
  inputSchema: { priority: z.enum(["high", "normal"]) },
})
export class ReplyDue extends ToolContext {
  async execute({ priority }: { priority: "high" | "normal" }) {
    return { hours: this.get(DeskConfig).slaHours[priority] };
  }
}

@App({
  id: "help-desk",
  name: "Help Desk",
  tools: [ReplyDue],
  providers: [
    Settings,
    {
      provide: DeskConfig,
      name: "DeskConfig",
      inject: () => [Settings] as const,
      useFactory: (settings) => ({
        slaHours: { high: Number(settings.get("SLA_HIGH_HOURS")), normal: Number(settings.get("SLA_NORMAL_HOURS")) },
      }),
    },
  ],
})
export class HelpDeskApp {}
```

#### Example: Async
An `async` factory is awaited before the server takes requests, so tools never see a half-built provider.

```ts help-desk.app.ts active
import { App, Tool, ToolContext } from "@frontmcp/sdk";

export abstract class Roster {
  abstract agents: string[];
}

async function loadRoster() {
  await new Promise((resolve) => setTimeout(resolve, 50)); // stands in for a database query
  return { agents: ["nour", "sam", "li"] };
}

@Tool({ name: "list_agents", description: "List the support agents on the roster", inputSchema: {} })
export class ListAgents extends ToolContext {
  async execute() {
    return { agents: this.get(Roster).agents };
  }
}

@App({
  id: "help-desk",
  name: "Help Desk",
  tools: [ListAgents],
  providers: [{ provide: Roster, name: "Roster", inject: () => [], useFactory: loadRoster }],
})
export class HelpDeskApp {}
```

### Reading the request in a provider

Inject `FRONTMCP_CONTEXT` into a `CONTEXT` factory to build something for the current request: its id, its trace, who is calling. Here each ticket is stamped with the request that created it.

```ts help-desk.app.ts active
import { App, FRONTMCP_CONTEXT, ProviderScope, Tool, ToolContext, z } from "@frontmcp/sdk";

export abstract class RequestInfo {
  abstract requestId: string;
  abstract traceId: string;
}

@Tool({ name: "create_ticket", description: "Open a new support ticket", inputSchema: { title: z.string() } })
export class CreateTicket extends ToolContext {
  async execute({ title }: { title: string }) {
    const { requestId } = this.get(RequestInfo);
    return { id: "T-4", title, createdBy: requestId };
  }
}

@App({
  id: "help-desk",
  name: "Help Desk",
  tools: [CreateTicket],
  providers: [
    {
      provide: RequestInfo,
      name: "RequestInfo",
      scope: ProviderScope.CONTEXT,
      inject: () => [FRONTMCP_CONTEXT] as const,
      useFactory: (ctx) => ({ requestId: ctx.requestId, traceId: ctx.traceContext.traceId }),
    },
  ],
})
export class HelpDeskApp {}
```

```ts request.test.ts
import { test, expect } from "@frontmcp/testing";

test("each call has its own request id", async ({ mcp }) => {
  const a = (await mcp.tools.call("create_ticket", { title: "First" })).json<{ createdBy: string }>();
  const b = (await mcp.tools.call("create_ticket", { title: "Second" })).json<{ createdBy: string }>();
  expect(a.createdBy).toEqual(expect.any(String));
  expect(a.createdBy).not.toBe(b.createdBy);
});
```

`FRONTMCP_CONTEXT` only makes sense per request, so inject it into `CONTEXT` providers only.

A `CONTEXT` provider can also hold something built for the caller, like a client for your own API that acts as them. Under 2026-07-28, every request gets its own instance, whoever is calling, with a token or without, and even when two requests send the same `mcp-session-id` header. So one caller's client is never handed to another. This server uses `transparent` auth, and the test calls it as two users, one after the other:

```ts help-desk.app.ts active
import { App, FRONTMCP_CONTEXT, ProviderScope, Tool, ToolContext } from "@frontmcp/sdk";

/** A client for the ticket API that acts as the caller. */
export abstract class TicketApi {
  abstract actingAs: string | undefined;
}

@Tool({ name: "my_tickets", description: "List the tickets assigned to the caller", inputSchema: {} })
export class MyTickets extends ToolContext {
  async execute() {
    return { actingAs: this.get(TicketApi).actingAs };
  }
}

@App({
  id: "help-desk",
  name: "Help Desk",
  tools: [MyTickets],
  providers: [
    {
      provide: TicketApi,
      name: "TicketApi",
      scope: ProviderScope.CONTEXT,
      inject: () => [FRONTMCP_CONTEXT] as const,
      // In transparent mode, clientId is the token's sub.
      useFactory: (ctx) => ({ actingAs: ctx.authInfo.clientId }),
    },
  ],
})
export class HelpDeskApp {}
```

```ts main.ts
import { FrontMcp } from "@frontmcp/sdk";
import { HelpDeskApp } from "./help-desk.app";

export const config = {
  info: { name: "help-desk", version: "1.0.0" },
  apps: [HelpDeskApp],
  auth: {
    mode: "transparent" as const,
    provider: "https://auth.example.com",
    expectedAudience: "https://desk.example.com",
    allowAnonymous: true,
    providerConfig: { jwks: { keys: [{ kty: "EC", crv: "P-256", kid: "desk-1", alg: "ES256", use: "sig", x: "1CjT9Do3B6PgOMVsIY0Amz_l1QbuUfoNLcq3E1OFip0", y: "4Je6s1T1RHT7obF4lowUe-63m0pat0TZfmnRxLfDCE8" }] } },
  },
};

@FrontMcp(config)
export default class Server {}
```

```ts tokens.ts hidden
// Access tokens for two users, signed ahead of time with the key in main.ts.
export const NOUR = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImRlc2stMSJ9.eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJzdWIiOiJub3VyIiwiYXVkIjoiaHR0cHM6Ly9kZXNrLmV4YW1wbGUuY29tIiwiaWF0IjoxNzkwMDAwMDAwLCJleHAiOjQxMDI0NDQ4MDAsInNjb3BlIjoidGlja2V0czpyZWFkIHRpY2tldHM6d3JpdGUiLCJyb2xlcyI6WyJhZ2VudCJdLCJuYW1lIjoiTm91ciBIYWRkYWQiLCJlbWFpbCI6Im5vdXJAZXhhbXBsZS5jb20iLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsibGVhZCJdfSwidGVuYW50IjoiYWNtZSJ9.NGr_C0VEuc3p0Fc0GKq92Gytmn3Ig8XaDEO2GfA-X8g1rzN2flqNbuSB3Z_b1Mb0hy4mkv8KW7_ngW5sWE_YPw";
export const SAM = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImRlc2stMSJ9.eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJzdWIiOiJzYW0iLCJhdWQiOiJodHRwczovL2Rlc2suZXhhbXBsZS5jb20iLCJpYXQiOjE3OTAwMDAwMDAsImV4cCI6NDEwMjQ0NDgwMCwic2NvcGUiOiJ0aWNrZXRzOnJlYWQiLCJuYW1lIjoiU2FtIE9ydGl6In0.VHIKGx65AgtNp4Y4PtdLCJoQgXg5pOeBYp03oLgpr1l2elL9Gui6EcQuuBUeb-sYYFJo2-labA_nJwhSV9BLpw";
```

```ts callers.test.ts
import { test, expect } from "@frontmcp/testing";
import { FrontMcpInstance } from "@frontmcp/sdk";
import { config } from "./main";
import { NOUR, SAM } from "./tokens";

test("each caller gets a client of their own", async () => {
  // One server; each call is a 2026-07-28 request over HTTP with the caller's token.
  const handler = await FrontMcpInstance.createFetchHandler(config);
  const callAs = async (token: string) => {
    const response = await handler(
      new Request("https://desk.example.com/", {
        method: "POST",
        headers: {
          "content-type": "application/json",
          "mcp-protocol-version": "2026-07-28",
          "mcp-method": "tools/call",
          "mcp-name": "my_tickets",
          "mcp-session-id": "the-same-id",
          authorization: `Bearer ${token}`,
        },
        body: JSON.stringify({
          jsonrpc: "2.0",
          id: 1,
          method: "tools/call",
          params: { name: "my_tickets", arguments: {}, _meta: { "io.modelcontextprotocol/protocolVersion": "2026-07-28" } },
        }),
      }),
    );
    return (await response.json()).result.structuredContent;
  };
  expect(await callAs(NOUR)).toEqual({ actingAs: "nour" });
  expect(await callAs(SAM)).toEqual({ actingAs: "sam" });
});
```

Clients on protocol versions before 2026-07-28 keep a `CONTEXT` provider for their whole session, and that includes this one: FrontMCP 1.8 builds it on the session's first request, so later requests in the session see that request's `requestId`. When a value has to be right for every request, read it from `this.context` in the tool instead.

### Using a provider only if it's there

`this.tryGet()` returns `undefined` for a provider that isn't registered, so a tool can work with or without it. Here notifications are optional. Add `Notifier` to the app's `providers`, and `notified` becomes `true`.

```ts close-ticket.tool.ts active
import { Tool, ToolContext, z } from "@frontmcp/sdk";
import { Notifier } from "./notifier";

@Tool({ name: "close_ticket", description: "Close a ticket, and tell the customer if we can", inputSchema: { id: z.string() } })
export class CloseTicket extends ToolContext {
  async execute({ id }: { id: string }) {
    const notifier = this.tryGet(Notifier);
    notifier?.send(`Ticket ${id} was closed.`);
    return { id, status: "closed", notified: notifier !== undefined };
  }
}
```

```ts help-desk.app.ts
import { App } from "@frontmcp/sdk";
import { CloseTicket } from "./close-ticket.tool";

@App({ id: "help-desk", name: "Help Desk", tools: [CloseTicket], providers: [] })
export class HelpDeskApp {}
```

```ts notifier.ts
import { Provider } from "@frontmcp/sdk";

@Provider({ name: "Notifier" })
export class Notifier {
  send(message: string) {
    console.log(message);
  }
}
```

---

## Troubleshooting

### `Provider "TicketStore" is not available: not found in local or parent registries`

A tool asked for a provider that isn't registered anywhere it can see, so the call failed:

```ts help-desk.app.ts active
import { App, Provider, Tool, ToolContext, z } from "@frontmcp/sdk";

@Provider({ name: "TicketStore" })
export class TicketStore {
  find(id: string) {
    return { id, title: "Cannot log in" };
  }
}

@Tool({ name: "get_ticket", description: "Get one ticket by id", inputSchema: { id: z.string() } })
export class GetTicket extends ToolContext {
  async execute({ id }: { id: string }) {
    return this.get(TicketStore).find(id);
  }
}

// 🚩 TicketStore is decorated, but not in `providers`
@App({ id: "help-desk", name: "Help Desk", tools: [GetTicket] })
export class HelpDeskApp {}
```

Add it to the `providers` of the tool's app, or of `@FrontMcp` to share it with every app. A provider registered in **another** app isn't visible: apps don't see each other's providers.

### `providers items must be annotated with @Provider() | @FrontMcpProvider().`

The server fails to start with `@App invalid metadata for "providers"` and the index of the bad entry. FrontMCP 1.8 accepts three forms, and rejects everything else:

```ts
providers: [
  TicketStore, // ✅ a @Provider class
  { provide: DeskConfig, name: "DeskConfig", inject: () => [], useFactory: () => config }, // ✅ a named factory
  { provide: TicketStore, useValue: new TicketStore() }, // ✅ an instance of a @Provider class

  PlainClass, // 🚩 no @Provider decorator
  { provide: DeskConfig, useValue: config }, // 🚩 a plain value: use a factory
  { provide: DeskConfig, useClass: EnvConfig }, // 🚩 useClass: use a factory that returns new EnvConfig()
  { provide: DeskConfig, useFactory: () => config }, // 🚩 a factory without `name`
];
```

### `Invalid dependency: DEFAULT-scoped provider Audit cannot depend on scoped provider RequestLog.`

A `GLOBAL` provider (here `Audit`) injects a `CONTEXT` provider (`RequestLog`). The global one would outlive the request it was built for, so the server refuses to start. Make the dependent provider `CONTEXT` too, or pass the request-scoped value in as a method argument.

### `Provider TicketRepo depends on TicketStore, which is not registered (local or parent).`

A constructor parameter or an `inject` token names a provider that isn't registered. Add it to `providers`. If the message says `depends on Function` or `depends on Object`, the parameter's type was imported with `import type`, which leaves no class at run time for `emitDecoratorMetadata` to record. Use a normal `import`.

### A constructor parameter is `undefined`

The provider was built without its dependencies. Check that:

1. `tsconfig.json` sets `experimentalDecorators` and `emitDecoratorMetadata` to `true`, and your build uses `tsc` (or a compiler that emits decorator metadata).
2. `import "reflect-metadata"` is the first line of your entry file.

In the Playground this always happens, because esbuild doesn't emit decorator metadata. Use a factory with `inject` there.

### `Failed to construct provider "DeskConfig": Cannot read properties of undefined`

The factory was called without the providers it needs. Most often, `inject` is an array. It must be a function that returns one; FrontMCP ignores an array and calls the factory with no arguments:

```ts
// 🚩 An array: `settings` is undefined
{ provide: DeskConfig, name: "DeskConfig", inject: [Settings], useFactory: (settings) => ({ slaHours: settings.slaHours }) }

// ✅ A function that returns the array
{ provide: DeskConfig, name: "DeskConfig", inject: () => [Settings] as const, useFactory: (settings) => ({ slaHours: settings.slaHours }) }
```

TypeScript reports the array form as an error, unless it's been cast away. A `GLOBAL` factory runs when the server starts, so the server fails to start. A `CONTEXT` factory runs at the start of every request, so every call to that app's tools fails, even calls to tools that don't use it.

### State I stored in a provider is gone on the next call

Check the provider's scope. A `CONTEXT` provider is rebuilt for every request under MCP 2026-07-28, and for every session on older clients. Use `GLOBAL` for state that should last. If it already is `GLOBAL`, your server probably runs as several processes or serverless instances, each with its own copy; keep the state in a database or Redis.
