Skip to content

Conversation

@frontegg-david
Copy link
Contributor

@frontegg-david frontegg-david commented Jan 3, 2026

Summary by CodeRabbit

  • New Features

    • Introduced @frontmcp/di, a new dependency injection library with type-safe container, token factories, scoped providers, and hierarchical resolution support.
    • Added support for async-with decorator for custom initialization patterns.
  • Refactor

    • Migrated DI infrastructure to external library; simplified internal SDK dependencies and reorganized type imports.
    • Streamlined agent tool naming to remove prefixing logic.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 3, 2026

📝 Walkthrough

Walkthrough

A new standalone dependency injection library (@frontmcp/di) is introduced with complete type definitions, utilities, decorators, and container implementations for type-safe token-based dependency resolution. The SDK is then refactored to depend on this package, with DI-related types and utilities moved from local definitions to the new external module and re-exported for backward compatibility.

Changes

Cohort / File(s) Summary
New DI Library – Core & Config
libs/di/README.md, libs/di/jest.config.ts, libs/di/package.json, libs/di/project.json, libs/di/tsconfig.json, libs/di/tsconfig.lib.json, libs/di/tsconfig.spec.json
Documentation, build configuration, and TypeScript setup for the new DI library. Jest config sets coverage thresholds (70% statements, 55% branches, 90% functions, 70% lines).
DI Library – Tokens & Constants
libs/di/src/tokens/di.constants.ts, libs/di/src/tokens/token.factory.ts, libs/di/src/tokens/index.ts
Core token factory for creating type-safe DI tokens with configurable prefixes; exports DESIGN_PARAMTYPES, META_ASYNC_WITH, META_ASYNC_WITH_TOKENS symbols for metadata management.
DI Library – Interfaces
libs/di/src/interfaces/base.interface.ts, libs/di/src/interfaces/provider.interface.ts, libs/di/src/interfaces/registry.interface.ts, libs/di/src/interfaces/index.ts
Type system for DI: token types (Type, Reference, Token, ClassToken), provider shapes (VALUE, FACTORY, CLASS, CLASS_TOKEN), and container interfaces (DiContainerInterface with get/resolve/buildViews).
DI Library – Metadata
libs/di/src/metadata/provider.metadata.ts, libs/di/src/metadata/provider.schema.ts, libs/di/src/metadata/index.ts
Provider scope enumeration (GLOBAL, CONTEXT; deprecated SESSION, REQUEST), metadata interface, and Zod-based schema validation for providers.
DI Library – Records
libs/di/src/records/provider.record.ts, libs/di/src/records/index.ts
Normalized internal representations for provider kinds (CLASS_TOKEN, CLASS, VALUE, FACTORY, INJECTED) and their associated record types.
DI Library – Utilities
libs/di/src/utils/metadata.utils.ts, libs/di/src/utils/token.utils.ts, libs/di/src/utils/provider.utils.ts, libs/di/src/utils/index.ts
Utilities for metadata reflection (getMetadata, setMetadata, hasAsyncWith), token analysis (tokenName, isClass, isPromise, depsOfClass, depsOfFunc), and provider normalization with dependency discovery.
DI Library – Decorators
libs/di/src/decorators/async-with.decorator.ts, libs/di/src/decorators/index.ts
AsyncWith decorator for marking classes with static async initialization (with() method); stores metadata for lazy dependency token resolution.
DI Library – Registry & Container
libs/di/src/registry/registry.base.ts, libs/di/src/registry/simple.registry.ts, libs/di/src/registry/indexed.registry.ts, libs/di/src/registry/indexed.types.ts, libs/di/src/registry/container.ts, libs/di/src/registry/index.ts
Hierarchical registry abstractions: RegistryAbstract base class, SimpleRegistry, IndexedRegistry with lineage tracking and adoption support, and DiContainer with global/context-scoped providers, topological sorting, and view-based resolution.
DI Library – Public API Barrel
libs/di/src/index.ts
Single entry point exporting all public DI APIs: tokens, types, records, metadata, utilities, registries, and decorators.
DI Library – Test Suite
libs/di/src/__tests__/*.spec.ts
Comprehensive test coverage for decorators, container lifecycle, constants, indexing, metadata, provider kinds, utilities, and registries. Includes container scoping, async initialization, hierarchy resolution, and error handling scenarios.
SDK Integration – Dependencies
libs/sdk/package.json, libs/sdk/project.json, tsconfig.base.json, libs/sdk/tsconfig.lib.json
Add @frontmcp/di as a runtime dependency; configure external build targets and path mappings for module resolution.
SDK – Interfaces Refactored
libs/sdk/src/common/interfaces/base.interface.ts, libs/sdk/src/common/interfaces/index.ts, libs/sdk/src/common/interfaces/*.interface.ts (adapter, app, auth-provider, flow, hook, logger, plugin, prompt, provider, resource, scope, tool, etc.)
Remove local DI type definitions from base.interface.ts; re-export from @frontmcp/di for backward compatibility; update all interface imports to source types from new DI library. Minor signature updates (e.g., logger getters return function types directly instead of wrapped).
SDK – Records Refactored
libs/sdk/src/common/records/provider.record.ts, libs/sdk/src/common/records/index.ts, libs/sdk/src/common/records/*.record.ts
Remove local provider record enum/interfaces; re-export ProviderKind and record types from @frontmcp/di. Update all record imports and source imports to @frontmcp/di for Type, Token, etc.
SDK – Metadata Refactored
libs/sdk/src/common/metadata/provider.metadata.ts, libs/sdk/src/common/metadata/index.ts
Remove local ProviderScope and ProviderMetadata; import from @frontmcp/di and re-export; update schema validation to use external types.
SDK – Tokens & Utilities
libs/sdk/src/common/tokens/base.tokens.ts, libs/sdk/src/utils/token.utils.ts, libs/sdk/src/utils/metadata.utils.ts, libs/sdk/src/utils/index.ts
Replace tokenFactory manual symbol generation with createTokenFactory from @frontmcp/di; consolidate utility re-exports from new DI library (tokenName, isClass, depsOfClass, getMetadata, hasAsyncWith, etc.).
SDK – Registry & Entry Classes
libs/sdk/src/adapter/adapter.*.ts, libs/sdk/src/agent/agent.*.ts, libs/sdk/src/app/app.*.ts, libs/sdk/src/auth/auth.*.ts, libs/sdk/src/flows/flow.*.ts, libs/sdk/src/hook/hooks.*.ts, libs/sdk/src/logger/logger.*.ts, libs/sdk/src/plugin/plugin.*.ts, libs/sdk/src/prompt/prompt.*.ts, libs/sdk/src/provider/provider.*.ts, libs/sdk/src/resource/resource.*.ts, libs/sdk/src/scope/scope.*.ts, libs/sdk/src/tool/tool.*.ts, libs/sdk/src/regsitry/registry.*.ts, libs/sdk/src/common/entries/*.entry.ts, libs/sdk/src/common/dynamic/dynamic.plugin.ts
Update imports across all registry, utility, and entry classes to source Token, Type, tokenName, depsOfClass, getMetadata, etc. from @frontmcp/di instead of local modules. Minor formatting adjustments and method signature trailing semicolon additions. No semantic logic changes beyond import relocation.
SDK – Agent & Tool Simplification
libs/sdk/src/agent/agent.utils.ts, libs/sdk/src/agent/flows/call-agent.flow.ts, libs/sdk/src/tool/flows/tools-list.flow.ts, libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts, libs/sdk/src/agent/__tests__/agent.registry.test.ts
Remove AGENT_TOOL_PREFIX and helper functions (isAgentToolName, agentIdFromToolName); simplify agentToolName to return unsuffixed name. Consolidate agent and tool flows by removing conditional routing in call-tool handler; update test expectation from 'use-agent:tool-agent' to 'tool-agent'.
SDK – Decorator Updates
libs/sdk/src/common/decorators/prompt.decorator.ts
Update frontMcpPrompt to return FunctionalPromptResult (callable with metadata symbol property) instead of wrapped function; attach metadata via symbol key.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 A rabbit hops through dependency dreams,
Tokens and types flow in ordered streams,
Registries adopt with lineage care,
AsyncWith awaits with metadata to share,
From scattered types to one grand package—
The DI refactor's a beautiful baggage!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.06% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Introduce dependency injection core interfaces and utilities' clearly summarizes the main change - introducing a new DI library with interfaces and utilities.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 60d6f0e and 2865f25.

📒 Files selected for processing (5)
  • libs/sdk/src/agent/agent.instance.ts
  • libs/sdk/src/agent/agent.registry.ts
  • libs/sdk/src/agent/agent.scope.ts
  • libs/sdk/src/common/metadata/agent.metadata.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
💤 Files with no reviewable changes (1)
  • libs/sdk/src/tool/flows/tools-list.flow.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • libs/sdk/src/agent/agent.instance.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Use strict TypeScript mode with no any types without strong justification - use unknown instead for generic type defaults
Avoid non-null assertions (!) - use proper error handling and throw specific errors instead
Use type parameters with constraints instead of unconstrained generics
Do not mutate rawInput in flows - use state.set() for managing flow state instead

Files:

  • libs/sdk/src/agent/agent.scope.ts
  • libs/sdk/src/agent/agent.registry.ts
  • libs/sdk/src/common/metadata/agent.metadata.ts
libs/sdk/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

libs/sdk/src/**/*.ts: MCP response types should use strict MCP protocol types (GetPromptResult, ReadResourceResult) instead of unknown
Use specific error classes with MCP error codes instead of generic errors
Use getCapabilities() method for dynamic capability exposure instead of hardcoding capabilities
Use changeScope property instead of scope in change events to avoid confusion with Scope class
Validate URIs per RFC 3986 at metadata level using proper validation
Validate hook flows and fail fast on invalid hook configurations with specific error messages

Files:

  • libs/sdk/src/agent/agent.scope.ts
  • libs/sdk/src/agent/agent.registry.ts
  • libs/sdk/src/common/metadata/agent.metadata.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/agent/agent.scope.ts
  • libs/sdk/src/agent/agent.registry.ts
  • libs/sdk/src/common/metadata/agent.metadata.ts
🧠 Learnings (4)
📚 Learning: 2025-12-19T02:04:46.475Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: Applies to libs/sdk/src/**/*.ts : Use `changeScope` property instead of `scope` in change events to avoid confusion with Scope class

Applied to files:

  • libs/sdk/src/agent/agent.scope.ts
  • libs/sdk/src/common/metadata/agent.metadata.ts
📚 Learning: 2025-12-19T02:04:46.475Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: Applies to libs/sdk/src/**/index.ts : Export public API through barrel files - export users' needed items, no legacy exports with different names

Applied to files:

  • libs/sdk/src/agent/agent.scope.ts
📚 Learning: 2025-12-19T02:04:46.475Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: Applies to libs/sdk/src/common/records/**/*.ts : Centralize record types in common/records directory and import from there instead of module-specific files

Applied to files:

  • libs/sdk/src/agent/agent.scope.ts
  • libs/sdk/src/agent/agent.registry.ts
  • libs/sdk/src/common/metadata/agent.metadata.ts
📚 Learning: 2025-12-19T02:04:46.475Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: Applies to libs/sdk/src/**/*.ts : Use `getCapabilities()` method for dynamic capability exposure instead of hardcoding capabilities

Applied to files:

  • libs/sdk/src/agent/agent.scope.ts
🔇 Additional comments (3)
libs/sdk/src/agent/agent.registry.ts (1)

3-4: LGTM! Imports successfully migrated to centralized DI package.

The import refactoring correctly sources Token and tokenName from @frontmcp/di, consistent with the broader migration to extract DI utilities into a dedicated package. The existing usage of these imports throughout the file remains valid.

libs/sdk/src/agent/agent.scope.ts (1)

4-4: Import correctly sources DI primitives from @frontmcp/di.

The import statement properly retrieves Token, Type, and ProviderScope from the new dependency injection package. All three types are exported from the main package index and align with this PR's objective to extract DI utilities to a separate module.

libs/sdk/src/common/metadata/agent.metadata.ts (1)

2-2: Import migration from @frontmcp/di is correctly implemented.

The change properly imports Type, FuncType, and Token from @frontmcp/di v0.6.3, which correctly exports all three types. The public API remains unchanged (AgentType<T = unknown> maintains its signature), and this migration completes the DI refactoring without breaking changes.


Comment @coderabbitai help to get the list of available commands and usage tips.

@frontegg-david
Copy link
Contributor Author

frontegg-david commented Jan 3, 2026

⚠️ Snyk checks are incomplete.

Status Scanner Critical High Medium Low Total (0)
⚠️ Open Source Security 0 0 0 0 See details
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
libs/sdk/src/common/metadata/provider.metadata.ts (2)

9-16: Eliminate schema duplication with DI library.

The frontMcpProviderMetadataSchema is nearly identical to providerMetadataSchema defined in libs/di/src/metadata/provider.schema.ts. Both define the same fields with the same constraints.

Consider either:

  1. Importing and using providerMetadataSchema from @frontmcp/di if the schemas are meant to be identical
  2. Extending the base DI schema if SDK needs additional validation
  3. Documenting why separate schemas are needed if there's a specific reason
🔎 Option 1: Import from DI library
-import { ProviderScope, ProviderMetadata } from '@frontmcp/di';
+import { ProviderScope, ProviderMetadata, providerMetadataSchema } from '@frontmcp/di';
 import { RawZodShape } from '../types';
 
 /**
  * FrontMCP-specific schema for provider metadata validation.
  * Uses the base ProviderMetadata interface from DI.
  */
-export const frontMcpProviderMetadataSchema = z
-  .object({
-    id: z.string().optional(),
-    name: z.string().min(1),
-    description: z.string().optional(),
-    scope: z.nativeEnum(ProviderScope).optional().default(ProviderScope.GLOBAL),
-  } satisfies RawZodShape<ProviderMetadata>)
-  .passthrough();
+export const frontMcpProviderMetadataSchema = providerMetadataSchema;

9-16: Use .strict() instead of .passthrough() per coding guidelines.

The schema uses .passthrough() which allows additional properties not defined in the schema. According to the coding guidelines: "Use .strict() on all Zod schemas for validation."

As per coding guidelines, learnings indicate all Zod schemas should use .strict() for validation.

🔎 Proposed fix
 export const frontMcpProviderMetadataSchema = z
   .object({
     id: z.string().optional(),
     name: z.string().min(1),
     description: z.string().optional(),
     scope: z.nativeEnum(ProviderScope).optional().default(ProviderScope.GLOBAL),
   } satisfies RawZodShape<ProviderMetadata>)
-  .passthrough();
+  .strict();
🧹 Nitpick comments (25)
libs/sdk/src/logger/logger.utils.ts (1)

19-27: Replace any with unknown for strict type safety.

The function parameter and type assertion use any, which violates the coding guideline requiring strict TypeScript mode. Use unknown instead and apply proper type narrowing.

As per coding guidelines: "Use strict TypeScript mode with no any types without strong justification - use unknown instead for generic type defaults"

🔎 Proposed refactor using `unknown`
-export function normalizeLogger(item: any): LoggerRecord {
+export function normalizeLogger(item: unknown): LoggerRecord {
   if (isClass(item)) {
     const metadata = collectLoggerMetadata(item);
     return { kind: LoggerKind.CLASS_TOKEN, provide: item as Type<LogTransportInterface>, metadata };
   }
 
-  const name = (item as any)?.name ?? String(item);
+  const name = typeof item === 'object' && item !== null && 'name' in item 
+    ? String((item as { name: unknown }).name)
+    : String(item);
   throw new Error(`Invalid app '${name}'. Expected a class or remote app config object.`);
 }
libs/di/tsconfig.spec.json (1)

1-10: Consider using modern moduleResolution strategy.

The test configuration is correct and follows standard Jest patterns. However, moduleResolution: "node10" is an older resolution strategy.

💡 Optional: Modernize moduleResolution

Consider using a more modern resolution strategy if it aligns with other library test configurations in the workspace:

 {
   "extends": "./tsconfig.json",
   "compilerOptions": {
     "outDir": "../../dist/out-tsc",
     "module": "commonjs",
-    "moduleResolution": "node10",
+    "moduleResolution": "node",
     "types": ["jest", "node"]
   },
   "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"]
 }

Note: Only apply this if other library test configs use "node" for consistency. If the workspace standardizes on "node10" for Jest compatibility, keep as-is.

libs/sdk/src/adapter/adapter.utils.ts (1)

17-23: Consider using unknown instead of any for stricter type safety.

The as any cast bypasses type checking. Per coding guidelines, prefer unknown with proper type narrowing or define a specific interface for the expected shape.

🔎 Proposed type-safe approach
-  if (item && typeof item === 'object') {
-    const { provide, useClass, useValue, useFactory, inject } = item as any;
+  if (item && typeof item === 'object') {
+    const obj = item as Record<string, unknown>;
+    const { provide, useClass, useValue, useFactory, inject } = obj;

Then add type guards before using each property.

libs/di/jest.config.ts (1)

36-43: Consider raising the branch coverage threshold before stabilization.

The 55% branch threshold is notably lower than statements (70%) and functions (90%). While the comment explains this is for initial release, container logic with "many edge case branches" is precisely where untested branches could hide bugs.

Consider setting a timeline to raise this threshold (e.g., to 70%) before the library is marked stable, or adding a tracking issue.

libs/di/src/utils/metadata.utils.ts (1)

20-22: Consider using unknown as the default generic type.

The any default for T propagates untyped values. Using unknown as the default would encourage callers to specify their expected type.

🔎 Proposed change
-export function getMetadata<T = any>(key: any, target: any, propertyKey?: string | symbol): T | undefined {
+export function getMetadata<T = unknown>(key: any, target: any, propertyKey?: string | symbol): T | undefined {

The key and target parameters using any are acceptable since they mirror the reflect-metadata API signature.

libs/di/src/interfaces/registry.interface.ts (2)

44-47: Move inline import to top-level imports for consistency.

The inline import('../metadata/provider.metadata.js').ProviderScope is inconsistent with the top-level import style used elsewhere in this file.

🔎 Proposed fix
 import type { Token } from './base.interface.js';
 import type { ProviderRecord } from '../records/provider.record.js';
+import type { ProviderScope } from '../metadata/provider.metadata.js';
 
 // ...
 
-  getProviderScope(rec: ProviderRecord): import('../metadata/provider.metadata.js').ProviderScope;
+  getProviderScope(rec: ProviderRecord): ProviderScope;

64-76: Consider adding type parameters to Token for stronger typing.

The DiViews interface uses untyped Token which loses type information. Using Token<unknown> explicitly documents the intent.

🔎 Proposed improvement
 export interface DiViews {
-  global: ReadonlyMap<Token, unknown>;
-  context: Map<Token, unknown>;
+  global: ReadonlyMap<Token<unknown>, unknown>;
+  context: Map<Token<unknown>, unknown>;
 }
libs/di/src/registry/indexed.types.ts (1)

38-53: Consider adding type parameter to Token for consistency.

Similar to other interfaces, IndexedEntry<T> uses untyped Token on line 40. While the instance: T provides typing for the value, the token itself loses its type association.

🔎 Proposed change
 export interface IndexedEntry<T> {
-  /** Token for this entry */
-  token: Token;
+  /** Token for this entry */
+  token: Token<T>;

This ties the token's type parameter to the instance type.

libs/sdk/src/common/records/logger.record.ts (1)

15-15: Good simplification of the type alias.

The LoggerRecord type has been correctly simplified from union syntax (| LoggerClassToken) to a direct alias. Since there was only one type in the union, this improves readability without changing behavior.

libs/sdk/src/common/interfaces/provider.interface.ts (1)

4-4: Consider static analysis suggestion for empty interface.

Biome flags the empty ProviderInterface as equivalent to {} and suggests using a type alias instead. However, keeping it as an interface may be intentional to allow future extension or to serve as a marker type.

If the interface is purely a marker with no planned properties, consider:

export type ProviderInterface = Record<string, never>;

Otherwise, the current empty interface is acceptable for future extensibility.

Based on coding guidelines: As per static analysis tools, empty interfaces are flagged by Biome.

libs/di/src/__tests__/di.constants.spec.ts (1)

19-21: Consider making symbol description assertions more flexible.

The test asserts exact string match on META_ASYNC_WITH.toString() (line 20). Symbol descriptions are implementation details; if the symbol creation changes slightly (e.g., different spacing or naming), this test will break. Consider testing only that it's a symbol, or use a regex pattern for more flexibility.

🔎 Alternative approach using pattern matching
-    it('should have descriptive name', () => {
-      expect(META_ASYNC_WITH.toString()).toBe('Symbol(di:async-with)');
-    });
+    it('should have descriptive name', () => {
+      expect(META_ASYNC_WITH.toString()).toMatch(/Symbol\(.*async.*with.*\)/i);
+    });

Similarly for META_ASYNC_WITH_TOKENS at line 30.

libs/di/src/__tests__/metadata.utils.spec.ts (1)

10-47: Consider adding error case tests for robustness.

The test coverage for getMetadata is good for happy paths, but lacks error case testing (e.g., null/undefined targets, invalid metadata keys). While these utilities are thin wrappers over reflect-metadata, verifying error handling ensures the API behaves predictably under invalid conditions.

Based on learnings, test all code paths including error cases.

🔎 Suggested additional test cases
it('should handle null target gracefully', () => {
  const key = Symbol('test-key');
  expect(() => getMetadata(key, null as any)).not.toThrow();
});

it('should handle undefined target gracefully', () => {
  const key = Symbol('test-key');
  expect(() => getMetadata(key, undefined as any)).not.toThrow();
});
libs/sdk/src/common/interfaces/resource.interface.ts (1)

18-23: Consider stricter typing or add justification for any usage.

The FunctionResourceType uses any for both parameters and return type, which is permissive. Per coding guidelines, prefer unknown over any without strong justification.

Consider either:

  1. Use unknown for stricter type safety: (...args: unknown[]) => unknown
  2. Add a JSDoc comment explaining why any is necessary for this dynamic builder pattern
🔎 Proposed refactor with stricter types
 /**
  * Function-style resource type.
  * This represents resources created via resource() or resourceTemplate() builders.
  * The function returns a handler that will be invoked for the resource.
+ * Note: Uses 'unknown' instead of 'any' for type safety.
  */
-export type FunctionResourceType = (...args: any[]) => any;
+export type FunctionResourceType = (...args: unknown[]) => unknown;
libs/sdk/src/common/records/adapter.record.ts (1)

13-13: Minor: Inconsistent property delimiter.

Line 13 uses a semicolon as the property delimiter, while most TypeScript interfaces conventionally use commas or no delimiters. Consider using commas for consistency with typical interface style.

🔎 Suggested fix for consistency
 export interface AdapterClassTokenRecord {
   kind: AdapterKind.CLASS_TOKEN;
-  provide: Type;
+  provide: Type,
   metadata: AdapterMetadata;
 }
libs/sdk/src/common/interfaces/scope.interface.ts (1)

3-3: Consider adding members to ScopeInterface or converting to a type alias.

An empty interface is equivalent to {} and may not provide meaningful type safety. If this is intended as a marker interface for structural typing, consider adding a discriminant property or using a branded type instead.

🔎 Alternative approaches

Option 1: Add a discriminant property

-export interface ScopeInterface {}
+export interface ScopeInterface {
+  readonly __scopeBrand: unique symbol;
+}

Option 2: Use a type alias with branding

-export interface ScopeInterface {}
+export type ScopeInterface = { readonly __scopeBrand: unique symbol };

Option 3: If truly meant to accept any object

-export interface ScopeInterface {}
+export type ScopeInterface = Record<string, unknown>;

Based on static analysis, this addresses Biome lint rule lint/suspicious/noEmptyInterface.

libs/di/src/__tests__/indexed.registry.spec.ts (1)

17-22: Consider adding error case tests.

The test interface uses any for the data field, which is acceptable in test code. However, per coding guidelines, consider adding tests for error cases such as invalid record inputs or constructor validation failures to achieve comprehensive coverage.

libs/di/src/interfaces/provider.interface.ts (1)

66-72: Consider using unknown[] instead of any[] for the deps parameter.

Per coding guidelines, unknown should be preferred over any for generic type defaults. The with method's deps parameter uses any[], which bypasses type safety.

Suggested fix
 export interface AsyncProvider<T = unknown> {
   /**
    * Static async factory method.
    * Called instead of constructor when @AsyncWith decorator is used.
    */
-  with?(...deps: any[]): T | Promise<T>;
+  with?(...deps: unknown[]): T | Promise<T>;
 }
libs/di/src/records/provider.record.ts (1)

58-75: Consider using unknown for the value and factory return types.

Per coding guidelines, unknown is preferred over any. While these are internal records and type erasure occurs during normalization, using unknown would enforce explicit type assertions at consumption sites, improving type safety.

Suggested changes
 export interface ProviderFactoryRecord {
   kind: ProviderKind.FACTORY;
   provide: Token;
   inject: () => readonly Token[];
-  useFactory: (...args: any[]) => any | Promise<any>;
+  useFactory: (...args: unknown[]) => unknown | Promise<unknown>;
   metadata: ProviderMetadata;
 }

 export interface ProviderInjectedRecord {
   kind: ProviderKind.INJECTED;
   provide: Token;
-  value: any;
+  value: unknown;
   metadata: ProviderMetadata;
 }
libs/di/src/registry/registry.base.ts (1)

68-89: Consider using a type guard or interface for the addRegistry check.

The duck-typing check at line 70 uses an any cast. While functional, defining an interface for registries that support addRegistry would improve type safety and documentation.

Suggested improvement
interface RegistryHost {
  addRegistry(name: RegistryKind, registry: unknown): void;
}

function isRegistryHost(obj: unknown): obj is RegistryHost {
  return obj !== null && typeof obj === 'object' && typeof (obj as any).addRegistry === 'function';
}

// In constructor:
if (isRegistryHost(providers)) {
  providers.addRegistry(name, this);
}
libs/di/src/utils/provider.utils.ts (1)

150-197: Extract the shared provider dependency logic into a parameterized helper function.

Both providerDiscoveryDeps and providerInvocationTokens have identical structural logic—the same switch-case handling for each ProviderKind—but differ in the phase parameter passed to depsOfClassFn ('discovery' vs 'invocation'). Additionally, providerDiscoveryDeps has an unused localTokens parameter. Extract a shared helper that accepts phase as a parameter to reduce duplication:

function providerTokens(
  rec: ProviderRecord,
  phase: 'discovery' | 'invocation',
  depsOfClassFn: (klass: Type, phase: 'discovery' | 'invocation') => Type[],
): Token[] {
  // shared switch logic
}

Then have both functions delegate to it.

libs/di/src/utils/token.utils.ts (1)

64-86: The forWhat/phase parameters are not used to differentiate behavior.

  • readWithParamTypes: The forWhat parameter appears only in a single error message on line 73; all other logic is identical.
  • depsOfClass: The phase parameter is passed through to readWithParamTypes but not used in the constructor dependency logic (lines 102–110).
  • depsOfFunc: The phase parameter is entirely unused (lines 125–130).

Since these are public APIs, removing the parameters would be a breaking change. However, if phase differentiation was intended, the logic should be implemented; otherwise, consider documenting why the parameters are needed or removing them in a future major version.

libs/di/src/registry/indexed.registry.ts (2)

127-146: Consider adding guard for duplicate child adoption.

If adoptFromChild is called twice with the same child, it will create duplicate subscriptions without cleaning up the previous one. Consider adding a guard or cleanup:

🔎 Proposed fix
 adoptFromChild(child: IndexedRegistry<TInstance, TRecord, TIndexed, any, any>, childOwner: EntryOwnerRef): void {
+  // Clean up existing subscription if re-adopting same child
+  const existingUnsub = this.childSubscriptions.get(child);
+  if (existingUnsub) existingUnsub();
+
   // Get child's entries and relineage them
   const childRows = child.listAllIndexed();

265-272: Avoid non-null assertion for filter callback.

Per coding guidelines, prefer avoiding ! assertions. While the guard at line 266 makes this safe, TypeScript can infer this with a minor refactor:

🔎 Proposed fix
   return this.emitter.on((e) => {
-    if (opts.filter) {
-      const filtered = e.snapshot.filter((row) => opts.filter!(row.instance));
+    const { filter } = opts;
+    if (filter) {
+      const filtered = e.snapshot.filter((row) => filter(row.instance));
       callback({ ...e, snapshot: filtered });
     } else {
       callback(e);
     }
   });
libs/di/src/registry/container.ts (2)

153-170: Unnecessary as any cast in buildMap.

Since auto = false is passed to super (line 99), and buildGraph() is called at line 108 after this.normalizeProvider is assigned (line 101), the fallback cast at line 159 should never be needed. Consider removing the defensive cast or adding a comment explaining when it would apply.

🔎 Proposed fix
 protected override buildMap(list: ProviderType[]): RegistryBuildMapResult<ProviderRecord> {
   const tokens = new Set<Token>();
   const defs = new Map<Token, ProviderRecord>();
   const graph = new Map<Token, Set<Token>>();

-  // Use the normalizer stored on this or fall back to basic
-  const normalize = (this as any).normalizeProvider ?? createBasicNormalizer();
+  const normalize = this.normalizeProvider;

   for (const raw of list) {

356-401: Consider extracting common instantiation logic.

The hasAsyncWith/init pattern is duplicated across instantiateOne, buildIntoStore, and buildIntoStoreWithViews. A helper method could reduce duplication:

🔎 Sketch of potential helper
private async instantiateClass(
  klass: Ctor<any>,
  deps: any[],
  label: string
): Promise<any> {
  if (hasAsyncWith(klass)) {
    const out = (klass as any).with(...deps);
    return isPromise(out)
      ? await this.withTimeout(out, this.asyncTimeoutMs, `${label}.with(...)`)
      : out;
  }
  const instance = new klass(...deps);
  const init = (instance as any)?.init;
  if (typeof init === 'function') {
    const ret = init.call(instance);
    if (isPromise(ret)) await this.withTimeout(ret, this.asyncTimeoutMs, `${label}.init()`);
  }
  return instance;
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (3)
libs/di/src/registry/container.ts (2)

526-565: LGTM! Past issue resolved.

The resolve() method now properly handles async init() by throwing a clear error (lines 551-557), addressing the concern from previous reviews. The error message correctly guides users to use get() after initialize() instead.


770-822: LGTM! Past issue resolved.

The createBasicNormalizer now validates that the provide field exists for useValue (lines 784-786), useFactory (lines 796-798), and useClass (lines 809-811) providers, addressing the concern from previous reviews.

libs/di/src/__tests__/container.spec.ts (1)

1192-1302: LGTM! Past issue resolved.

Line 1193 now properly uses it.todo() to mark the unimplemented test, addressing the concern from previous reviews. The remaining tests in this section provide good coverage of edge cases and error handling.

🧹 Nitpick comments (3)
libs/di/src/registry/container.ts (3)

153-170: Remove unnecessary any cast.

Line 159 uses (this as any).normalizeProvider with a fallback to createBasicNormalizer(), but the constructor always sets this.normalizeProvider. The cast and fallback are unnecessary.

🔎 Proposed refactor
 protected override buildMap(list: ProviderType[]): RegistryBuildMapResult<ProviderRecord> {
   const tokens = new Set<Token>();
   const defs = new Map<Token, ProviderRecord>();
   const graph = new Map<Token, Set<Token>>();
 
-  // Use the normalizer stored on this or fall back to basic
-  const normalize = (this as any).normalizeProvider ?? createBasicNormalizer();
+  const normalize = this.normalizeProvider;
 
   for (const raw of list) {

Based on coding guidelines.


335-402: Consider type guards to avoid any casts.

Lines 359, 382, and throughout this method use (rec as any) to access provider-specific properties. While functional, consider introducing type guards or narrowing functions to avoid any casts.

Example pattern:

function isClassProvider(rec: ProviderRecord): rec is ClassProviderRecord {
  return rec.kind === ProviderKind.CLASS;
}

Then use:

if (isClassProvider(rec)) {
  const klass = rec.useClass; // properly typed
}

Based on coding guidelines.


268-280: Use specific timer type instead of any.

Line 271 declares timer as any. Use a more specific type like ReturnType<typeof setTimeout> or NodeJS.Timeout depending on your environment.

🔎 Proposed refactor
 private async withTimeout<T>(p: Promise<T>, ms: number, label: string): Promise<T> {
   if (ms <= 0 || !Number.isFinite(ms)) return p;
 
-  let timer: any;
+  let timer: ReturnType<typeof setTimeout>;
   try {

Based on coding guidelines.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a02e64 and 98bb666.

📒 Files selected for processing (15)
  • libs/di/src/__tests__/async-with.decorator.spec.ts
  • libs/di/src/__tests__/container.spec.ts
  • libs/di/src/__tests__/provider.metadata.spec.ts
  • libs/di/src/interfaces/base.interface.ts
  • libs/di/src/registry/container.ts
  • libs/sdk/src/common/decorators/prompt.decorator.ts
  • libs/sdk/src/common/dynamic/dynamic.plugin.ts
  • libs/sdk/src/common/interfaces/base.interface.ts
  • libs/sdk/src/common/interfaces/prompt.interface.ts
  • libs/sdk/src/common/metadata/flow.metadata.ts
  • libs/sdk/src/common/metadata/provider.metadata.ts
  • libs/sdk/src/common/records/provider.record.ts
  • libs/sdk/src/flows/flow.utils.ts
  • libs/sdk/src/utils/metadata.utils.ts
  • libs/sdk/src/utils/token.utils.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • libs/di/src/tests/provider.metadata.spec.ts
  • libs/sdk/src/flows/flow.utils.ts
  • libs/sdk/src/common/metadata/flow.metadata.ts
  • libs/sdk/src/common/dynamic/dynamic.plugin.ts
  • libs/di/src/interfaces/base.interface.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Use strict TypeScript mode with no any types without strong justification - use unknown instead for generic type defaults
Avoid non-null assertions (!) - use proper error handling and throw specific errors instead
Use type parameters with constraints instead of unconstrained generics
Do not mutate rawInput in flows - use state.set() for managing flow state instead

Files:

  • libs/sdk/src/common/metadata/provider.metadata.ts
  • libs/di/src/__tests__/container.spec.ts
  • libs/sdk/src/utils/token.utils.ts
  • libs/sdk/src/common/interfaces/prompt.interface.ts
  • libs/di/src/__tests__/async-with.decorator.spec.ts
  • libs/sdk/src/common/records/provider.record.ts
  • libs/sdk/src/common/decorators/prompt.decorator.ts
  • libs/sdk/src/utils/metadata.utils.ts
  • libs/di/src/registry/container.ts
  • libs/sdk/src/common/interfaces/base.interface.ts
libs/sdk/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

libs/sdk/src/**/*.ts: MCP response types should use strict MCP protocol types (GetPromptResult, ReadResourceResult) instead of unknown
Use specific error classes with MCP error codes instead of generic errors
Use getCapabilities() method for dynamic capability exposure instead of hardcoding capabilities
Use changeScope property instead of scope in change events to avoid confusion with Scope class
Validate URIs per RFC 3986 at metadata level using proper validation
Validate hook flows and fail fast on invalid hook configurations with specific error messages

Files:

  • libs/sdk/src/common/metadata/provider.metadata.ts
  • libs/sdk/src/utils/token.utils.ts
  • libs/sdk/src/common/interfaces/prompt.interface.ts
  • libs/sdk/src/common/records/provider.record.ts
  • libs/sdk/src/common/decorators/prompt.decorator.ts
  • libs/sdk/src/utils/metadata.utils.ts
  • libs/sdk/src/common/interfaces/base.interface.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/common/metadata/provider.metadata.ts
  • libs/di/src/__tests__/container.spec.ts
  • libs/sdk/src/utils/token.utils.ts
  • libs/sdk/src/common/interfaces/prompt.interface.ts
  • libs/di/src/__tests__/async-with.decorator.spec.ts
  • libs/sdk/src/common/records/provider.record.ts
  • libs/sdk/src/common/decorators/prompt.decorator.ts
  • libs/sdk/src/utils/metadata.utils.ts
  • libs/di/src/registry/container.ts
  • libs/sdk/src/common/interfaces/base.interface.ts
libs/sdk/src/common/records/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Centralize record types in common/records directory and import from there instead of module-specific files

Files:

  • libs/sdk/src/common/records/provider.record.ts
🧠 Learnings (14)
📚 Learning: 2025-12-19T02:04:46.475Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: Applies to libs/sdk/src/**/*.ts : Use `changeScope` property instead of `scope` in change events to avoid confusion with Scope class

Applied to files:

  • libs/sdk/src/common/metadata/provider.metadata.ts
  • libs/sdk/src/utils/token.utils.ts
📚 Learning: 2025-12-19T02:04:46.475Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: FrontMCP is a TypeScript-first schema validation framework - all types should align with MCP protocol definitions

Applied to files:

  • libs/sdk/src/common/metadata/provider.metadata.ts
  • libs/sdk/src/common/interfaces/prompt.interface.ts
  • libs/sdk/src/common/decorators/prompt.decorator.ts
📚 Learning: 2025-12-19T02:04:46.475Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: Applies to **/*.test.{ts,tsx} : Test all code paths including error cases and constructor validation

Applied to files:

  • libs/di/src/__tests__/container.spec.ts
📚 Learning: 2025-12-24T00:41:55.397Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:55.397Z
Learning: Applies to libs/uipack/src/{adapters,theme,build}/**/*.test.{ts,tsx,js,jsx} : Test component behavior across all platform configurations (OpenAI, Claude, etc.)

Applied to files:

  • libs/di/src/__tests__/container.spec.ts
📚 Learning: 2025-12-19T02:04:46.475Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: Applies to libs/sdk/src/**/index.ts : Export public API through barrel files - export users' needed items, no legacy exports with different names

Applied to files:

  • libs/sdk/src/utils/token.utils.ts
  • libs/sdk/src/utils/metadata.utils.ts
  • libs/sdk/src/common/interfaces/base.interface.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.{ts,tsx} : Never import React-free utilities from frontmcp/ui; use frontmcp/uipack for bundling, build tools, platform adapters, and theme utilities

Applied to files:

  • libs/sdk/src/utils/token.utils.ts
  • libs/sdk/src/common/decorators/prompt.decorator.ts
  • libs/sdk/src/utils/metadata.utils.ts
  • libs/sdk/src/common/interfaces/base.interface.ts
📚 Learning: 2025-12-24T00:41:55.397Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:55.397Z
Learning: Applies to libs/uipack/src/*/index.ts : Implement entry point re-exports in index.ts files for each module (adapters, build, bundler, etc.)

Applied to files:

  • libs/sdk/src/utils/token.utils.ts
  • libs/sdk/src/common/interfaces/base.interface.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/bundler/**/*.{ts,tsx} : The bundler module must re-export utilities from frontmcp/uipack/bundler and provide SSR component bundling functionality

Applied to files:

  • libs/sdk/src/utils/token.utils.ts
📚 Learning: 2025-12-24T00:41:55.397Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:55.397Z
Learning: Applies to libs/uipack/src/**/*.{ts,tsx,js,jsx} : No React dependency in frontmcp/uipack - use frontmcp/ui for React components and hooks

Applied to files:

  • libs/sdk/src/utils/token.utils.ts
📚 Learning: 2025-12-19T02:04:46.475Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: Applies to libs/sdk/src/**/*.ts : Use `getCapabilities()` method for dynamic capability exposure instead of hardcoding capabilities

Applied to files:

  • libs/sdk/src/utils/token.utils.ts
  • libs/sdk/src/utils/metadata.utils.ts
📚 Learning: 2025-12-19T02:04:46.475Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: Applies to libs/sdk/src/common/records/**/*.ts : Centralize record types in common/records directory and import from there instead of module-specific files

Applied to files:

  • libs/sdk/src/utils/token.utils.ts
  • libs/sdk/src/common/records/provider.record.ts
📚 Learning: 2025-12-19T02:04:46.475Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: Applies to libs/sdk/src/**/*.ts : MCP response types should use strict MCP protocol types (GetPromptResult, ReadResourceResult) instead of `unknown`

Applied to files:

  • libs/sdk/src/common/interfaces/prompt.interface.ts
  • libs/sdk/src/common/decorators/prompt.decorator.ts
📚 Learning: 2025-12-19T02:04:46.475Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: Applies to libs/sdk/src/**/*.ts : Use specific error classes with MCP error codes instead of generic errors

Applied to files:

  • libs/sdk/src/common/decorators/prompt.decorator.ts
📚 Learning: 2025-12-19T02:04:46.475Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: Applies to **/*.ts : Avoid non-null assertions (`!`) - use proper error handling and throw specific errors instead

Applied to files:

  • libs/di/src/registry/container.ts
🧬 Code graph analysis (3)
libs/di/src/__tests__/container.spec.ts (1)
libs/di/src/registry/container.ts (2)
  • DiContainer (73-765)
  • DiContainerOptions (42-54)
libs/sdk/src/common/interfaces/prompt.interface.ts (1)
libs/di/src/interfaces/base.interface.ts (2)
  • Type (13-15)
  • FuncType (21-21)
libs/sdk/src/common/decorators/prompt.decorator.ts (1)
libs/sdk/src/common/metadata/prompt.metadata.ts (1)
  • frontMcpPromptMetadataSchema (79-85)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Build Libraries
🔇 Additional comments (17)
libs/sdk/src/common/decorators/prompt.decorator.ts (4)

2-3: LGTM!

Clean import consolidation aligning with the DI refactoring.


27-32: LGTM!

Good use of unknown for the symbol index type, which follows the coding guidelines. The intersection type correctly models a callable with attached symbol-keyed metadata.


34-47: LGTM!

The type assertion on line 41 is justified since the symbol-indexed properties are assigned immediately after via Object.assign. This is a standard pattern for attaching metadata to functions while preserving type information.


50-50: LGTM!

Exports maintain backward compatibility with the aliased re-exports (Prompt, prompt), allowing existing consumers to continue using either naming convention.

libs/sdk/src/utils/metadata.utils.ts (1)

1-2: Re-export approach is correct and properly implemented.

The utilities getMetadata, setMetadata, and hasAsyncWith are correctly re-exported from @frontmcp/di with compatible signatures. The implementation maintains backward compatibility while following the barrel file export pattern specified in the coding guidelines. Active usage in libs/sdk/src/provider/provider.registry.ts (via the barrel file) confirms the implementation works as intended.

libs/sdk/src/common/interfaces/base.interface.ts (1)

1-16: Re-export addresses backward compatibility concern correctly.

The file properly re-exports the 14 DI types from @frontmcp/di for backward compatibility. Verification confirms all previously exported types are included—the speculation about missing "Resolve-related types" and "Mutable" was unfounded, as these were never exported as types from this module.

libs/di/src/registry/container.ts (1)

93-111: LGTM!

The constructor logic is correct and properly initializes the container with fallback normalizer, parent handling, and async timeout configuration.

libs/di/src/__tests__/async-with.decorator.spec.ts (1)

1-112: LGTM!

Comprehensive test coverage for the AsyncWith decorator, including metadata storage, lazy evaluation for TDZ avoidance, inheritance, and readonly tuple support. The tests are well-structured and cover both typical and edge cases.

libs/di/src/__tests__/container.spec.ts (4)

19-60: LGTM!

The helper function createContainer and basic construction tests provide good foundation. Tests properly verify empty containers, provider initialization, and parent-child relationships.


62-224: LGTM!

Thorough coverage of all provider kinds (CLASS_TOKEN, VALUE, FACTORY, CLASS) with appropriate dependency injection scenarios. Tests properly use reflection metadata to simulate decorated classes.


331-405: LGTM!

Comprehensive testing of scoped providers (GLOBAL/CONTEXT) and AsyncWith decorator support. Tests verify both synchronous and asynchronous initialization patterns, including proper dependency injection.


510-658: LGTM!

Excellent coverage of CONTEXT-scoped providers, including session isolation, dependency injection from GLOBAL scope, various provider types (factory, value, class), and pre-built provider handling. Error cases are properly tested.

libs/sdk/src/common/interfaces/prompt.interface.ts (2)

4-4: LGTM: DI types properly centralized.

The import refactoring to source FuncType, Token, and Type from @frontmcp/di aligns with the PR objective to centralize dependency injection primitives. This improves maintainability by eliminating duplication.


24-24: The addition of FunctionalPromptType to the PromptType union is backward-compatible and does not require type guard updates. Type discrimination in the codebase is based on runtime behavior checks (symbol metadata and isClass()), not union-based pattern matching. The existing code in normalizePrompt() and schema validation already handles this pattern correctly through symbol metadata checks (item[FrontMcpPromptTokens.type]), which are applied uniformly to all function-style prompts regardless of their specific type annotation.

Likely an incorrect or invalid review comment.

libs/sdk/src/common/records/provider.record.ts (1)

1-10: Re-export strategy is correct and complete.

The types being re-exported from @frontmcp/di match the source definitions exactly and are properly made available to the SDK codebase. Since this is the initial implementation, there are no previous local definitions to compare. All imports throughout the SDK correctly resolve through the index.ts exports, maintaining backward compatibility with the centralized record types guideline.

libs/sdk/src/common/metadata/provider.metadata.ts (2)

16-16: This is a justified breaking change aligning with coding guidelines.

Changing from .passthrough() to .strict() is intentional per the learning guideline to "use .strict() on all Zod schemas for validation." All existing provider metadata declarations in the codebase conform to the strict schema—they only define the fields specified in the schema (id, name, description, scope). No code currently passes unknown properties to provider metadata, so this change will not break existing code.


2-2: External types are identical, but verify the schema validation change from .passthrough() to .strict().

The external ProviderScope enum and ProviderMetadata interface from @frontmcp/di match the previous local definitions exactly. However, the schema validation was changed from .passthrough() to .strict(), which rejects unknown properties instead of allowing them. The usage at annotated-class.schema.ts:43 works around this by explicitly calling .passthrough() to override the strict behavior, but this strict validation in provider.decorator.ts:10 may cause issues if callers pass extra properties expecting them to be ignored.

⛔ Skipped due to learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:55.397Z
Learning: Applies to libs/uipack/src/**/*.{ts,tsx} : Use `.strict()` on all Zod schemas for validation
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: FrontMCP is a TypeScript-first schema validation framework - all types should align with MCP protocol definitions
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: Applies to libs/sdk/src/**/*.ts : Use `changeScope` property instead of `scope` in change events to avoid confusion with Scope class

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98bb666 and d2b8a9c.

📒 Files selected for processing (6)
  • libs/sdk/src/agent/__tests__/agent.registry.test.ts
  • libs/sdk/src/agent/agent.utils.ts
  • libs/sdk/src/agent/flows/call-agent.flow.ts
  • libs/sdk/src/common/metadata/tool.metadata.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
💤 Files with no reviewable changes (1)
  • libs/sdk/src/tool/flows/tools-list.flow.ts
✅ Files skipped from review due to trivial changes (1)
  • libs/sdk/src/agent/flows/call-agent.flow.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • libs/sdk/src/agent/agent.utils.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Use strict TypeScript mode with no any types without strong justification - use unknown instead for generic type defaults
Avoid non-null assertions (!) - use proper error handling and throw specific errors instead
Use type parameters with constraints instead of unconstrained generics
Do not mutate rawInput in flows - use state.set() for managing flow state instead

Files:

  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
  • libs/sdk/src/common/metadata/tool.metadata.ts
  • libs/sdk/src/agent/__tests__/agent.registry.test.ts
libs/sdk/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

libs/sdk/src/**/*.ts: MCP response types should use strict MCP protocol types (GetPromptResult, ReadResourceResult) instead of unknown
Use specific error classes with MCP error codes instead of generic errors
Use getCapabilities() method for dynamic capability exposure instead of hardcoding capabilities
Use changeScope property instead of scope in change events to avoid confusion with Scope class
Validate URIs per RFC 3986 at metadata level using proper validation
Validate hook flows and fail fast on invalid hook configurations with specific error messages

Files:

  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
  • libs/sdk/src/common/metadata/tool.metadata.ts
  • libs/sdk/src/agent/__tests__/agent.registry.test.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
  • libs/sdk/src/common/metadata/tool.metadata.ts
  • libs/sdk/src/agent/__tests__/agent.registry.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Test all code paths including error cases and constructor validation
Include error class instanceof checks in tests to validate error handling

Files:

  • libs/sdk/src/agent/__tests__/agent.registry.test.ts
🧠 Learnings (2)
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/react/hooks/**/*.{ts,tsx} : MCP bridge hooks (useMcpBridge, useCallTool, useToolInput, useToolOutput, useTheme) must be properly typed and handle loading/error states

Applied to files:

  • libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
  • libs/sdk/src/common/metadata/tool.metadata.ts
📚 Learning: 2025-12-19T02:04:46.475Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: FrontMCP is a TypeScript-first schema validation framework - all types should align with MCP protocol definitions

Applied to files:

  • libs/sdk/src/common/metadata/tool.metadata.ts
🔇 Additional comments (3)
libs/sdk/src/agent/__tests__/agent.registry.test.ts (1)

365-365: Update outdated documentation—the tool naming already uses standard names without a prefix.

The test expectation is correct. The codebase already generates tool names without the 'use-agent:' prefix (see agentToolName() in agent.utils.ts, which explicitly states: "Agents use standard tool names without any prefix"). However, outdated comments in the source files contradict this:

  • agent.registry.ts (line 321): Claims agents are exposed with name use-agent:<agent_id>
  • call-agent.flow.ts (line 120): Claims agents are invoked with use-agent:<agent_id> name

Update these comments to reflect the actual implementation, which uses the agent ID directly (e.g., 'tool-agent' not 'use-agent:tool-agent').

Likely an incorrect or invalid review comment.

libs/sdk/src/common/metadata/tool.metadata.ts (1)

253-254: This review comment's core concerns cannot be substantiated. A codebase-wide search found:

  • No AGENT_TOOL_PREFIX constant or import anywhere
  • No reserved prefix validation references or validation logic
  • No evidence of removed validation

The schema at lines 253-254 (id: z.string().optional() and name: z.string().min(1)) is simple and straightforward with no special handling or refinements. There is no indication that validation was loosened or removed—only basic type validation exists. The claim of a breaking change due to removed reserved-prefix validation cannot be verified.

Likely an incorrect or invalid review comment.

libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts (1)

26-52: LGTM! Robust error handling with proper schema validation.

The error handling implementation is comprehensive and correct:

  • Properly distinguishes FlowControl (control flow mechanism) from actual errors
  • Validates FlowControl.respond output against CallToolResultSchema to ensure MCP compliance
  • Uses InternalMcpError for proper MCP error formatting
  • Includes detailed logging for debugging

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
libs/sdk/src/common/interfaces/app.interface.ts (1)

4-6: Empty interface intentionally retained as marker - acknowledged.

The eslint-disable comment and JSDoc documentation adequately justify the marker interface pattern. The static analysis hint can be safely ignored in this case.

🧹 Nitpick comments (1)
libs/di/src/utils/provider.utils.ts (1)

79-102: Avoid any casts—use constrained type parameters or helpers.

Multiple any casts are used to access .name and .metadata properties (lines 83, 96, 100). Per coding guidelines, avoid any without strong justification.

🔎 Refactor to improve type safety

Define a helper type or use type guards:

type WithName = { name?: string };
type WithMetadata = { metadata?: ProviderMetadata };

// Then in the function:
const metadata: ProviderMetadata = {
  name: getMetadata(tokens.name, item) ?? (item as WithName).name ?? 'UnknownProvider',
  scope: getMetadata(tokens.scope, item),
  description: tokens.description ? getMetadata(tokens.description, item) : undefined,
  id: tokens.id ? getMetadata(tokens.id, item) : undefined,
};

// For plain classes:
const staticMeta = (item as WithMetadata).metadata;
return {
  kind: ProviderKind.CLASS_TOKEN,
  provide: item as Type,
  metadata: staticMeta ?? { name: (item as WithName).name ?? 'UnknownProvider' },
} satisfies ProviderClassTokenRecord;

This maintains the same runtime behavior while improving compile-time type safety.

As per coding guidelines, use unknown or constrained types instead of any.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2b8a9c and 51ad573.

📒 Files selected for processing (3)
  • libs/di/src/utils/provider.utils.ts
  • libs/sdk/src/adapter/adapter.utils.ts
  • libs/sdk/src/common/interfaces/app.interface.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • libs/sdk/src/adapter/adapter.utils.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Use strict TypeScript mode with no any types without strong justification - use unknown instead for generic type defaults
Avoid non-null assertions (!) - use proper error handling and throw specific errors instead
Use type parameters with constraints instead of unconstrained generics
Do not mutate rawInput in flows - use state.set() for managing flow state instead

Files:

  • libs/di/src/utils/provider.utils.ts
  • libs/sdk/src/common/interfaces/app.interface.ts
libs/**

⚙️ CodeRabbit configuration file

libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).

Files:

  • libs/di/src/utils/provider.utils.ts
  • libs/sdk/src/common/interfaces/app.interface.ts
libs/sdk/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

libs/sdk/src/**/*.ts: MCP response types should use strict MCP protocol types (GetPromptResult, ReadResourceResult) instead of unknown
Use specific error classes with MCP error codes instead of generic errors
Use getCapabilities() method for dynamic capability exposure instead of hardcoding capabilities
Use changeScope property instead of scope in change events to avoid confusion with Scope class
Validate URIs per RFC 3986 at metadata level using proper validation
Validate hook flows and fail fast on invalid hook configurations with specific error messages

Files:

  • libs/sdk/src/common/interfaces/app.interface.ts
🧠 Learnings (5)
📚 Learning: 2025-12-19T02:04:46.475Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: Applies to **/*.ts : Avoid non-null assertions (`!`) - use proper error handling and throw specific errors instead

Applied to files:

  • libs/di/src/utils/provider.utils.ts
📚 Learning: 2025-12-19T02:04:46.475Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T02:04:46.475Z
Learning: FrontMCP is a TypeScript-first schema validation framework - all types should align with MCP protocol definitions

Applied to files:

  • libs/sdk/src/common/interfaces/app.interface.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/**/*.{ts,tsx} : Never import React-free utilities from frontmcp/ui; use frontmcp/uipack for bundling, build tools, platform adapters, and theme utilities

Applied to files:

  • libs/sdk/src/common/interfaces/app.interface.ts
📚 Learning: 2025-12-24T00:41:55.397Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:55.397Z
Learning: Applies to libs/uipack/src/**/*.{ts,tsx} : Avoid using `any` type without justification in TypeScript

Applied to files:

  • libs/sdk/src/common/interfaces/app.interface.ts
📚 Learning: 2025-12-24T00:41:41.819Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/ui/CLAUDE.md:0-0
Timestamp: 2025-12-24T00:41:41.819Z
Learning: Applies to libs/ui/src/universal/**/*.{ts,tsx} : Universal app shell (UniversalApp, FrontMCPProvider) must provide platform-agnostic React context and initialization

Applied to files:

  • libs/sdk/src/common/interfaces/app.interface.ts
🧬 Code graph analysis (2)
libs/di/src/utils/provider.utils.ts (1)
libs/sdk/src/provider/provider.utils.ts (1)
  • normalizeProvider (12-64)
libs/sdk/src/common/interfaces/app.interface.ts (1)
libs/sdk/src/common/metadata/app.metadata.ts (1)
  • AppMetadata (197-197)
🪛 Biome (2.1.2)
libs/sdk/src/common/interfaces/app.interface.ts

[error] 6-6: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Lint & Format Checks
  • GitHub Check: Build Libraries
🔇 Additional comments (4)
libs/sdk/src/common/interfaces/app.interface.ts (1)

1-2: LGTM on the import migration.

The import of Type and ValueType from @frontmcp/di correctly aligns with the new DI library surface being introduced in this PR.

libs/di/src/utils/provider.utils.ts (3)

26-47: LGTM! Well-defined interfaces for provider normalization.

The ProviderTokens and ProviderNormalizerOptions interfaces are clearly documented and properly typed. The optional fields for description and id provide flexibility while maintaining type safety.


138-144: Error handling properly addresses circular reference concern.

The try-catch block correctly handles potential circular references in JSON.stringify, falling back to String(item). This addresses the previous review feedback.


186-204: LGTM! Invocation token resolution is correct.

The function properly handles all provider kinds and returns the appropriate dependency tokens for each case. The switch statement provides exhaustive coverage.

…iders and enhance factory provider dependency filtering
# Conflicts:
#	libs/sdk/src/tool/flows/tools-list.flow.ts
#	libs/sdk/src/transport/mcp-handlers/call-tool-request.handler.ts
@frontegg-david frontegg-david merged commit 71de741 into main Jan 3, 2026
21 of 22 checks passed
@frontegg-david frontegg-david deleted the extract-common branch January 3, 2026 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants