Describe the bug
Providers registered on the server (top-level @FrontMcp({ providers: [...] })) are not visible to this.get(...) calls inside tools / resources defined in child @Apps. Calls fail with ProviderNotAvailableError: Provider "TemplateRegistry" is not available: not found in local or parent registries even when the @Provider decorator is applied, the class is imported correctly (same module identity), the class is added to @FrontMcp({ providers: [...] }), and the @App that owns the consuming tool is added to @FrontMcp({ apps: [...] }). The framework boots cleanly, server logs show "Scope ready — N apps, N tools", the tool registers (appears in tools/list), and the error fires at tool-execution time when this.get(...) walks local → parent registries and finds nothing. Tools in apps that also declare providers: [TemplateRegistry] on the @App succeed — so the binding works at app scope but the parent chain isn't traversed from server scope.
To Reproduce
Steps to reproduce the behavior:
-
Define a provider:
// src/providers/foo.provider.ts
import { Provider } from '@frontmcp/sdk';
@Provider({ name: 'Foo' })
export class Foo { hello() { return 'hi'; } }
-
Define a tool that consumes it (note no providers on the @App):
// src/apps/demo/tools/say.tool.ts
import { Tool, ToolContext, z } from '@frontmcp/sdk';
import { Foo } from '../../../providers/foo.provider';
@Tool({
name: 'say',
description: 'demo',
inputSchema: {},
outputSchema: { msg: z.string() },
})
export default class SayTool extends ToolContext {
async execute() {
return { msg: this.get(Foo).hello() }; // ← throws ProviderNotAvailableError
}
}
// src/apps/demo/demo.app.ts
import { App } from '@frontmcp/sdk';
import SayTool from './tools/say.tool';
@App({ id: 'demo', name: 'Demo', tools: [SayTool] }) // no `providers` field here
export class DemoApp {}
-
Register at server scope only:
// src/main.ts
import 'reflect-metadata';
import { FrontMcp } from '@frontmcp/sdk';
import { DemoApp } from './apps/demo/demo.app';
import { Foo } from './providers/foo.provider';
@FrontMcp({
info: { name: 'demo', version: '0.1.0' },
apps: [DemoApp],
providers: [Foo], // ← server-level registration
})
export default class Server {}
-
Call say — throws ProviderNotAvailableError.
Expected behavior
Per the create-provider skill (.claude/skills/frontmcp-development/references/create-provider.md):
Register at @App level for app-scoped, @FrontMcp for server-scoped
Server-scoped providers are shared; duplicating causes multiple instances
i.e. server-level providers should be visible to every app. The provider-registry's get(token) already recurses into parent registries (if (this.providers) return this.providers.get(token); in @frontmcp/sdk index.js ~line 51710), so the lookup machinery is in place — but the wiring between the server-level registry and the apps' local registries appears not to thread the server providers in as a parent.
Screenshots
N/A — CLI / framework issue, no UI.
Environment (please complete the following information):
- OS: macOS (Darwin 25.3.0)
- Node: v24.x
- Packages: @frontmcp/sdk 1.2.1, @frontmcp/testing 1.2.1, frontmcp 1.2.1
Additional context
Workaround: declare the providers redundantly inside every @App that needs them (providers: [TemplateRegistry, ComponentRegistry, …] on each @App). This works but:
- Each
@App instantiates its own copy of the provider — not strictly "shared singleton" semantics.
- For pure read-only services (registries over a static set) the duplication is harmless.
- For mutable shared state (e.g.
HistoryService once it persists generations across apps), this silently breaks by giving each app its own history.
If the intent is that @FrontMcp({ providers: [...] }) only applies to the framework's own internal services (and user providers must go on @Apps), update the create-provider skill to say so explicitly — the current text reads as if both registration sites are interchangeable for cross-app sharing. If the intent is that they ARE shared, this is a wiring bug in the SDK's multi-app scope construction.
Describe the bug
Providers registered on the server (top-level
@FrontMcp({ providers: [...] })) are not visible tothis.get(...)calls inside tools / resources defined in child@Apps. Calls fail withProviderNotAvailableError: Provider "TemplateRegistry" is not available: not found in local or parent registrieseven when the@Providerdecorator is applied, the class is imported correctly (same module identity), the class is added to@FrontMcp({ providers: [...] }), and the@Appthat owns the consuming tool is added to@FrontMcp({ apps: [...] }). The framework boots cleanly, server logs show "Scope ready — N apps, N tools", the tool registers (appears intools/list), and the error fires at tool-execution time whenthis.get(...)walks local → parent registries and finds nothing. Tools in apps that also declareproviders: [TemplateRegistry]on the@Appsucceed — so the binding works at app scope but the parent chain isn't traversed from server scope.To Reproduce
Steps to reproduce the behavior:
Define a provider:
Define a tool that consumes it (note no
providerson the@App):Register at server scope only:
Call
say— throwsProviderNotAvailableError.Expected behavior
Per the
create-providerskill (.claude/skills/frontmcp-development/references/create-provider.md):i.e. server-level
providersshould be visible to every app. The provider-registry'sget(token)already recurses into parent registries (if (this.providers) return this.providers.get(token);in@frontmcp/sdkindex.js~line 51710), so the lookup machinery is in place — but the wiring between the server-level registry and the apps' local registries appears not to thread the server providers in as a parent.Screenshots
N/A — CLI / framework issue, no UI.
Environment (please complete the following information):
Additional context
Workaround: declare the providers redundantly inside every
@Appthat needs them (providers: [TemplateRegistry, ComponentRegistry, …]on each@App). This works but:@Appinstantiates its own copy of the provider — not strictly "shared singleton" semantics.HistoryServiceonce it persists generations across apps), this silently breaks by giving each app its own history.If the intent is that
@FrontMcp({ providers: [...] })only applies to the framework's own internal services (and user providers must go on@Apps), update thecreate-providerskill to say so explicitly — the current text reads as if both registration sites are interchangeable for cross-app sharing. If the intent is that they ARE shared, this is a wiring bug in the SDK's multi-app scope construction.