Skip to content

Conversation

@frontegg-david
Copy link
Contributor

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

Summary by CodeRabbit

  • New Features

    • Remote MCP: connect to remote servers, discover and use remote tools/resources/prompts with capability caching, resilience (retry, circuit breaker, health checks), and runtime proxying.
    • Demo & E2E: new remote-demo app and comprehensive end-to-end tests covering remote orchestration, tools, prompts, resources, and error scenarios.
  • Plugins

    • Cache plugin: tool-pattern caching, default TTL, and header-based cache bypass.
  • Authentication

    • Anonymous fallback: transparent/public anonymous auth/session handling.
  • Documentation

    • Expanded CachePlugin and Remote Apps docs with examples.
  • Chores

    • Logging enum naming normalized; enclave-vm dependency bumped.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 5, 2026

📝 Walkthrough

Walkthrough

Adds comprehensive remote MCP support: new McpClientService, remote app metadata/schemas, factories and instance adapters, remote-capability caching, resilience primitives (retry/circuit-breaker/health), registry/flow changes for remote adoption, new Remote* errors, E2E remote demo, cache plugin enhancements, and a LogLevel enum rename.

Changes

Cohort / File(s) Change Summary
Remote MCP core & client
libs/sdk/src/remote-mcp/*, libs/sdk/src/remote-mcp/mcp-client.service.ts, libs/sdk/src/remote-mcp/mcp-client.types.ts, libs/sdk/src/remote-mcp/index.ts
New McpClientService, typed client types, connection lifecycle, capability discovery, proxying (callTool/readResource/getPrompt), auth resolution, eventing, and public exports.
Resilience primitives
libs/sdk/src/remote-mcp/resilience/*
New retry, circuit-breaker, and health-check modules (with managers and public APIs).
Capability cache & factories
libs/sdk/src/remote-mcp/cache/*, libs/sdk/src/remote-mcp/factories/*
New CapabilityCache and factory layers: context classes, record builders, and instance creators for remote tools/resources/prompts.
App metadata, schemas & normalization
libs/sdk/src/common/metadata/app.metadata.ts, libs/sdk/src/common/schemas/annotated-class.schema.ts, libs/sdk/src/common/interfaces/app.interface.ts, libs/sdk/src/common/tokens/app.tokens.ts, libs/sdk/src/app/app.utils.ts
Added RemoteAppMetadata, validation schemas, annotated-class acceptance for remote configs, tokens, and normalizeApp handling for remote entries.
App instances & entries
libs/sdk/src/app/instances/*, libs/sdk/src/common/entries/app.entry.ts
New AppRemoteInstance, isRemote getter on AppEntry, and minimal registries for remote apps.
Registries & entry widening
libs/sdk/src/tool/*, libs/sdk/src/resource/*, libs/sdk/src/prompt/*
Registries adopt remote/local paths, widened public APIs to Entry types (ToolEntry/ResourceEntry/PromptEntry), added register*Instance methods, and updated event snapshot types.
Flows: remote capability preloads & dedupe
libs/sdk/src/*/flows/*
Added ensureRemoteCapabilities pre-stage to many flows, remote-aware lookups/fallbacks, and deduplication in list flows; merged metadata preserved in call finalization.
Remote errors & MCP codes
libs/sdk/src/errors/*
New Remote* error classes, added MCP_ERROR_CODES UNAUTHORIZED and FORBIDDEN, and re-exported remote errors.
Transport & HTTP flow updates
libs/sdk/src/transport/*, libs/sdk/src/scope/flows/http.request.flow.ts, libs/sdk/src/transport/mcp-handlers/initialize-request.handler.ts
LocalTransportAdapter advertises pre-advertised remote capabilities; http.request flow delegates to streamable handler with try/catch; initialize handler uses scope metadata for serverInfo.
Auth flows: anonymous fallback
libs/sdk/src/auth/flows/*
Added handleAnonymousFallback and shared anonymous session creation for public/transparent-anon modes; plan updates included.
Hook registry refinement
libs/sdk/src/hooks/hook.registry.ts
Stricter owner-kind filtering and guard for unknown owner kinds.
Log level & logger updates
libs/sdk/src/common/types/options/logging.options.ts, libs/sdk/src/logger/instances/*, apps/e2e/demo-e2e-*/src/main.ts
Removed deprecated LogLevel.VERBOSE, added LogLevel.Verbose and Off, updated logger mappings and demo usages across apps.
Cache plugin enhancements & tests
plugins/plugin-cache/src/*, plugins/plugin-cache/src/__tests__/*
Added toolPatterns, bypassHeader, isCacheable API, per-tool TTL precedence, header bypass logic, _meta cache hit augmentation, and expanded tests.
E2E remote demo & infra
apps/e2e/demo-e2e-remote/src/**, apps/e2e/demo-e2e-remote/e2e/**, apps/e2e/demo-e2e-remote/jest.e2e.config.ts, apps/e2e/demo-e2e-remote/webpack*.config.js, apps/e2e/demo-e2e-remote/tsconfig*.json, apps/e2e/demo-e2e-remote/project.json
New demo app: LocalTestApp (tools/resources/prompts), LocalTestMcpServer, RemoteGatewayServer, comprehensive E2E tests, and build/test configs.
Public exports & barrels
libs/sdk/src/index.ts, libs/sdk/src/remote-mcp/*/index.ts, libs/sdk/src/remote-mcp/factories/index.ts
New re-exports exposing remote-mcp APIs, factories, resilience and cache modules via barrels.
Transport adapter (local) updates
libs/sdk/src/transport/adapters/transport.local.adapter.ts
Detects remote app configs in scope metadata, merges remote capabilities into advertised server options, and exposes hasRemoteApps.
Deps bump
package.json, libs/uipack/package.json, plugins/plugin-codecall/package.json
Bumped enclave-vm dependency from ^2.2.0 → ^2.3.0 across packages.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client
    participant Gateway as FrontMCP Gateway
    participant AppReg as AppRegistry
    participant McpClient as McpClientService
    participant Remote as Remote MCP App
    participant ToolReg as ToolRegistry
    participant Flow as CallToolFlow

    Client->>Gateway: register remote app metadata
    Gateway->>AppReg: normalizeApp -> create remote app entry
    AppReg->>McpClient: connect(remote app config)
    McpClient->>Remote: establish transport & negotiate
    Remote-->>McpClient: connection + capabilities
    McpClient->>ToolReg: build remote tool entries (factories)
    Client->>Flow: call tool request
    Flow->>McpClient: ensureRemoteCapabilities
    Flow->>ToolReg: find tool (local then remote)
    Flow->>McpClient: callTool(appId, toolName, args)
    McpClient->>Remote: proxy execute (with retry/circuit/health)
    Remote-->>McpClient: result or error
    McpClient-->>Flow: normalized result (or Remote*Error)
    Flow-->>Client: final response (may include _meta cache info)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐰 I hopped through code and crossed the net so wide,

I knitted remote apps where capabilities hide,
Cache and breakers hum, health-checks keep the beat,
Tools and prompts now meet where gateways greet,
A tiny rabbit cheers — new connections are live!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically describes the main feature being implemented: remote MCP server orchestration with proxy entries for tools, resources, and prompts.
Docstring Coverage ✅ Passed Docstring coverage is 80.65% which is sufficient. The required threshold is 65.00%.
✨ Finishing touches
  • 📝 Generate docstrings

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

@frontegg-david
Copy link
Contributor Author

frontegg-david commented Jan 5, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source 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: 19

Fix all issues with AI Agents 🤖
In @apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts:
- Around line 77-80: Replace the use of the open any in the tools.map callback
with unknown: change the parameter from (t: any) to (t: unknown) and then safely
access name with either a proper type from the test framework (preferred) or a
narrow/type-guard before reading .name (or use a cast only after checking t is
an object with a name property) so the console.log call in the tools.map line
handles unknown values safely and satisfies the strict TypeScript guideline.

In
@apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/slow-operation.tool.ts:
- Around line 4-6: The inputSchema currently allows delayMs up to 300000 (5
minutes) which exceeds the Jest E2E timeout; change the max for delayMs in the
inputSchema to a value below the test timeout (e.g., 100000) to leave a buffer,
or alternatively add a comment above inputSchema documenting that delays must
not exceed the E2E timeout (120000ms) and enforce that constraint in the schema
by setting z.number().min(0).max(100000).describe('Delay in milliseconds (max
~100s to fit E2E timeout)').

In @docs/draft/docs/plugins/cache-plugin.mdx:
- Around line 218-220: The documented default for the bypass header (ParamField
path="bypassHeader") is missing the required prefix and should match the
implementation; update the default value from 'frontmcp-disable-cache' to
'x-frontmcp-disable-cache' so it aligns with the behavior described in
cache.types.ts and the note requiring the x-frontmcp-* prefix, ensuring docs and
code use the same header name.
- Around line 347-350: The example curl in the cache plugin docs uses the wrong
header name; replace the header string "frontmcp-disable-cache" with the correct
default "x-frontmcp-disable-cache" in the code block showing the cache-bypass
example so the curl command sends the proper header.

In @libs/plugins/src/cache/cache.plugin.ts:
- Around line 217-238: The code assumes cached is an object and spreads it into
cachedWithMeta which will throw for primitives returned by
getValue<T=unknown>(); update the logic in the cache hit path (symbols: cached,
cachedRecord, cachedWithMeta, flowCtx.state.rawOutput, toolContext.respond) to
first check the runtime type of cached: if cached is a non-null plain object
(typeof cached === 'object' && cached !== null && !Array.isArray(cached))
merge/assign _meta as you currently do; otherwise wrap the primitive/array into
a stable envelope (e.g. { value: cached, _meta: { cache: 'hit' } }) so meta is
always present and safe to access, then set flowCtx.state.rawOutput and call
toolContext.respond with that envelope; ensure this preserves existing object
behavior and avoids spreading non-objects (note getValue<T=unknown>() can return
any type).

In @libs/sdk/src/app/app.utils.ts:
- Around line 37-47: The code uses a redundant cast and an unsafe any: remove
the unnecessary "as RemoteAppMetadata" cast because isRemoteAppConfig already
narrows item, and replace "useValue: null as any" with a type-safe null (e.g.,
"null as unknown" or the concrete Remote instance type) so the return preserves
type safety; update the block that constructs provide, metadata,
AppKind.REMOTE_VALUE, and useValue accordingly (referencing isRemoteAppConfig,
RemoteAppMetadata, provide, and useValue).

In @libs/sdk/src/app/instances/app.remote.instance.ts:
- Around line 390-409: The getOrCreateMcpClientService function currently
accepts scope: any; replace this with a proper type (e.g., a new or existing
Scope interface that includes an optional mcpClientService: McpClientService and
logger) or use scope: unknown and add a type guard/assertion before accessing
properties. Update references inside getOrCreateMcpClientService to perform a
safe type check/cast (or assert) so scope.logger and scope.mcpClientService are
accessed only after confirming the shape, and ensure you set the
mcpClientService property on the correctly typed scope before returning the
service.
- Around line 299-307: The onCapabilityChange subscription created on
this.mcpClient is never stored or unsubscribed, causing a memory leak; store the
returned unsubscribe function (e.g. const unsubscribeCapability =
this.mcpClient.onCapabilityChange(...)) as a property on the instance (e.g.
this._unsubscribeCapability) and call it when the instance is torn down (add
invocation in the existing dispose/disconnect method or implement a dispose()
that calls this._unsubscribeCapability()). Ensure you also null out the stored
reference after calling it and guard against multiple disposals.
- Around line 455-460: Change the mapRemoteAuth signature to accept the proper
metadata type instead of any: replace the parameter type of
mapRemoteAuth(remoteAuth?: any) with remoteAuth?: RemoteAuthConfig (import
RemoteAuthConfig from the metadata types), keep the return type
McpRemoteAuthConfig | undefined and cast/convert remoteAuth to
McpRemoteAuthConfig as needed inside mapRemoteAuth; also add the
RemoteAuthConfig import to the top of the file and update any usages that call
mapRemoteAuth to satisfy the stronger type.

In @libs/sdk/src/errors/remote.errors.ts:
- Around line 122-158: Add MCP-specific auth error codes to the MCP_ERROR_CODES
enum/object by defining entries for -32001 (Missing credentials/token) and
-32003 (Invalid/insufficient credentials), then update RemoteAuthError and
RemoteAuthorizationError to use the appropriate MCP code (set
RemoteAuthError.mcpErrorCode = -32003 and set
RemoteAuthorizationError.mcpErrorCode = -32003 or to -32001 where you treat
missing credentials specifically); locate the symbols MCP_ERROR_CODES,
RemoteAuthError, and RemoteAuthorizationError in the diff and change their
mcpErrorCode values and the enum/object to include the new numeric constants.

In @libs/sdk/src/hooks/hook.registry.ts:
- Around line 161-173: The hook filtering in the function that returns
allHooks.filter(...) currently treats any non-scope/plugin owner as if it were
an app by falling back to ID comparison; update this predicate to explicitly
handle null owner, 'scope'/'plugin', and 'app' (check hook.metadata.owner.kind
=== 'app' and compare ids), and throw a clear Error for any other unexpected
owner kinds (e.g., 'adapter' or 'agent') so hooks with invalid owner kinds fail
fast with a descriptive message.

In @libs/sdk/src/index.ts:
- Line 14: The SDK currently exports the remote module but lacks user-facing
docs for the client API; add a new draft doc under docs/draft (e.g.,
docs/draft/docs/sdk/remote-client.mdx) that documents how to instantiate and use
McpClientService and the proxy entry classes ProxyToolEntry, ProxyResourceEntry,
and ProxyPromptEntry: include examples for creating a McpClientService
(auth/config hooks), registering/constructing
ProxyToolEntry/ProxyResourceEntry/ProxyPromptEntry, calling their primary
methods, and any lifecycle/teardown considerations; reference the public exports
in libs/sdk/src/index.ts so examples import from the SDK package (not internal
paths), and link this new page from the existing authentication remote docs
(remote.mdx and remote-proxy.mdx).

In @libs/sdk/src/remote/mcp-client.service.ts:
- Around line 588-601: The try/catch around new
StreamableHTTPClientTransport(...) is ineffective because the constructor is
synchronous and connection failures happen in connect(); change the logic so you
instantiate the StreamableHTTPClientTransport as before but perform the fallback
in its connect phase: when transportType === 'http' create the
StreamableHTTPClientTransport (using httpOptions?.fallbackToSSE ?? true),
attempt await transport.connect(), and if connect() throws and fallbackToSSE is
true then log the debug message, instantiate SSEClientTransport and call its
connect(); rethrow the original error only if fallback is disabled. Reference
StreamableHTTPClientTransport, SSEClientTransport, connect(), transportType and
httpOptions?.fallbackToSSE to find where to apply this change.
- Around line 518-524: The switch case for 'forward' declares headerName in the
case body without a block; wrap the case body in braces to create block scope
(e.g., case 'forward': { ... break; }) so headerName (and any other locals) are
scoped to that case; ensure you include the existing early return/logic
(gatewayAuthInfo check and this.logger.warn) and return statement inside the new
block and preserve the break to avoid fall-through.
- Around line 364-404: The authContext parameter is accepted but never forwarded
to the underlying MCP client; update callTool, readResource, and getPrompt to
pass authContext headers through to connection.client calls so call-level
authentication is forwarded. Locate the calls to connection.client.callTool,
connection.client.readResource, and connection.client.getPrompt inside the
methods callTool, readResource, and getPrompt and replace the current
placeholder/undefined options argument with the authContext headers (e.g. pass
an options/metadata object that includes headers: authContext?.headers),
ensuring you handle the case where authContext is undefined (preserve current
behavior when no headers are present) and keep error handling identical.

In @libs/sdk/src/resource/resource.registry.ts:
- Around line 136-145: The code unsafely casts ResourceEntry to ResourceInstance
when calling makeRow for entries from getResources()/getResourceTemplates();
change makeRow's signature to accept ResourceEntry (private makeRow(token:
Token, instance: ResourceEntry, lineage: EntryLineage, source:
ResourceRegistry): IndexedResource) and update all callers (including the loop
using getResources/getResourceTemplates and any other callsites) to pass
ResourceEntry directly; ensure any internal logic in makeRow that previously
relied on ResourceInstance methods narrows or type-guards for
ResourceInstance/ProxyResourceEntry as needed rather than using a cast so the
function correctly handles both ResourceInstance and ProxyResourceEntry.

In @libs/sdk/src/tool/tool.registry.ts:
- Around line 106-130: The code incorrectly duck-types remote apps via
getMcpClient and unsafely casts ProxyToolEntry to ToolInstance while lacking
error handling; instead, replace the fragile typeof check with a robust
metadata/interface check on the app (e.g., an isRemote flag or an interface
method on the app descriptor) before treating it as remote, wrap the call to
app.tools.getTools() in a try/catch and log errors, validate each item in
remoteTools (ensure it is a ProxyToolEntry or ToolEntry) and avoid casting to
ToolInstance—modify usage of makeRow/IndexedTool.instance to accept the correct
entry type or convert ProxyToolEntry to a real ToolInstance via an explicit
adapter function, and keep adoptFromChild for local ToolRegistry cases (symbols
to inspect: getMcpClient, app.tools.getTools, remoteTools, ProxyToolEntry,
ToolInstance, makeRow, IndexedTool.instance, adoptFromChild).
🧹 Nitpick comments (18)
libs/sdk/src/scope/flows/http.request.flow.ts (1)

337-404: Consider consolidating duplicate handler logic.

The handleSse, handleStreamableHttp, and handleStatefulHttp methods now have identical implementations—all invoke handle:streamable-http and continue with next(). While this works correctly, you could extract the common logic to reduce duplication and improve maintainability.

💡 Example consolidation approach

You could create a shared helper method:

private async handleStreamableTransport(context: string) {
  try {
    const response = await this.scope.runFlow('handle:streamable-http', this.rawInput);
    if (response) {
      this.respond(response);
    }
    this.next();
  } catch (error) {
    if (!(error instanceof FlowControl)) {
      this.logError(error, context);
    }
    throw error;
  }
}

Then simplify each handler:

@Stage('handleSse', { filter: ... })
async handleSse() {
  return this.handleStreamableTransport('handleSse');
}

@Stage('handleStreamableHttp', { filter: ... })
async handleStreamableHttp() {
  return this.handleStreamableTransport('handleStreamableHttp');
}

@Stage('handleStatefulHttp', { filter: ... })
async handleStatefulHttp() {
  return this.handleStreamableTransport('handleStatefulHttp');
}
libs/sdk/src/transport/mcp-handlers/initialize-request.handler.ts (1)

112-114: Consider supporting a separate title field in server metadata.

Line 114 uses configuredInfo.name for the title field. While this is functional, the MCP protocol's title field is typically a human-readable display name that may differ from the technical name. Consider whether scope.metadata.info should support an optional title field for better customization.

🔎 Suggested enhancement
-      title: configuredInfo.name,
+      title: configuredInfo.title ?? configuredInfo.name,

Then update the type definition of scope.metadata.info to include an optional title field.

libs/plugins/src/cache/cache.plugin.ts (1)

145-165: Consider logging bypass header check failures.

The error handling silently returns false when context storage is unavailable. While this is safe behavior, it could make debugging difficult when the bypass header isn't working as expected.

🔎 Proposed enhancement
     } catch {
       // Context storage not available - bypass header cannot be checked
+      this.logger?.debug('Cache bypass header check failed: context storage unavailable');
       return false;
     }
libs/sdk/src/errors/remote.errors.ts (1)

250-260: Consider adding mcpErrorCode to RemoteUnsupportedTransportError.

This error extends PublicMcpError but unlike other public errors in this file, it doesn't define an mcpErrorCode property. For consistency and proper MCP protocol alignment, consider adding the appropriate error code.

🔎 Proposed fix
 export class RemoteUnsupportedTransportError extends PublicMcpError {
   readonly transportType: string;
+  readonly mcpErrorCode = MCP_ERROR_CODES.INVALID_REQUEST;

   constructor(transportType: string) {
libs/sdk/src/resource/resource.registry.ts (1)

130-132: Use a proper type guard instead of inline type assertion.

The current approach uses an inline type assertion to check for getMcpClient. This pattern should be extracted into a reusable type guard function for better type safety and consistency across registries.

🔎 Proposed type guard helper

Consider adding a type guard utility (e.g., in libs/sdk/src/app/app.utils.ts):

export function isRemoteApp(app: unknown): app is { getMcpClient: () => unknown } {
  return (
    typeof app === 'object' &&
    app !== null &&
    'getMcpClient' in app &&
    typeof (app as any).getMcpClient === 'function'
  );
}

Then use it here:

-        // Check if this is a remote app (has getMcpClient method)
-        // Remote apps use RemoteResourceRegistry which isn't registered as a child registry
-        const isRemoteApp = typeof (app as { getMcpClient?: unknown }).getMcpClient === 'function';
+        const isRemoteApp = isRemoteApp(app);
apps/e2e/demo-e2e-remote/project.json (1)

72-80: Consider removing passWithNoTests: true from E2E test target.

The test:e2e target has passWithNoTests: true (line 78), which allows the test suite to pass even when no tests are found. This is unusual for E2E tests and may mask issues where tests fail to load or discover properly.

If this is a temporary measure during development, consider removing it once E2E tests are in place. Otherwise, document why this configuration is needed.

apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/resources/status.resource.ts (1)

14-15: Consider documenting the module-scoped start time.

The serverStartTime tracks when the module was first loaded, not when the server actually started. For E2E tests this is likely acceptable, but consider adding a comment to clarify this behavior or moving the tracking to the server initialization if precise server uptime is needed.

Optional: Add clarifying comment
+// Track module load time (approximates server start time for E2E tests)
 const serverStartTime = Date.now();
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/prompts/greeting.prompt.ts (1)

12-14: Defensive fallback contradicts required: true schema declaration.

The name argument is marked required: true in the decorator (line 7), but line 13 provides a fallback 'World' if args['name'] is falsy. If the framework enforces required arguments before execute() is called, this fallback is unreachable dead code. If not, it silently masks a contract violation.

Consider either:

  1. Removing the fallback if the framework guarantees required args
  2. Changing required: false if the fallback is intentional
apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts (2)

34-38: Hardcoded delay is fragile for CI environments.

The 2000ms sleep (line 37) after health check is a race condition waiting to happen. If MCP handlers need more initialization time, this arbitrary wait may be insufficient on slower CI runners or excessive on fast machines.

Consider implementing a readiness probe that polls the MCP endpoint until handlers respond, or increase the delay with a comment explaining the rationale.


214-230: Caching test doesn't verify cache behavior.

The test acknowledges (line 228-229) that it doesn't actually verify caching occurred — it only confirms both calls succeed. Without comparing timestamps or checking cache headers/metrics, this test provides limited value beyond the existing tool execution tests.

Consider either:

  1. Adding actual cache verification (e.g., check response metadata or timing differences)
  2. Renaming to clarify it's a smoke test for repeated calls
libs/sdk/src/common/metadata/app.metadata.ts (2)

304-310: Consider adding validation constraints for numeric transport options.

The schema allows any numbers for timeout, retryAttempts, and retryDelayMs. Consider adding reasonable bounds to catch configuration errors early.

🔎 Proposed schema refinements
 const remoteTransportOptionsSchema = z.object({
-  timeout: z.number().optional(),
-  retryAttempts: z.number().optional(),
-  retryDelayMs: z.number().optional(),
+  timeout: z.number().int().positive().max(300000).optional(),
+  retryAttempts: z.number().int().nonnegative().max(10).optional(),
+  retryDelayMs: z.number().int().nonnegative().max(60000).optional(),
   fallbackToSSE: z.boolean().optional(),
   headers: z.record(z.string(), z.string()).optional(),
 });

337-338: Verify URL validation for 'url' transport type.

The url field uses z.string() without URL validation. For urlType: 'url', this should be a valid HTTP(S) URL. Consider adding conditional validation or at minimum a non-empty check.

🔎 Proposed validation improvement
-  url: z.string(),
+  url: z.string().min(1),

For stricter validation, you could add a .refine() that validates URL format when urlType === 'url'.

libs/sdk/src/remote/mcp-client.service.ts (1)

682-722: Capability change detection only compares counts, not content.

The emitCapabilityChangeIfNeeded method only compares array lengths. If a tool is renamed or replaced with another tool (same count), no change event is emitted. Consider comparing tool names or using a hash for more accurate change detection.

libs/sdk/src/app/instances/app.remote.instance.ts (1)

106-114: Unsafe cast from ProxyToolEntry[] to ToolInstance[].

The cast snapshot as unknown as readonly ToolInstance[] bypasses type checking. If ProxyToolEntry implements ToolInstance, declare this relationship properly; otherwise, this could cause runtime issues.

Consider having ProxyToolEntry explicitly implement or extend the ToolInstance interface to make this relationship type-safe.

libs/sdk/src/remote/entries/proxy-resource.entry.ts (1)

213-229: Empty URI fallback in parseOutput may produce invalid results.

When this.uri is undefined (for templates), the fallback this.uri || '' produces an empty URI in the wrapped response, which may confuse consumers.

🔎 Proposed improvement
     // Otherwise, wrap it in a text content
     return {
       contents: [
         {
-          uri: this.uri || '',
+          uri: this.uri || this.uriTemplate || 'unknown',
           mimeType: 'text/plain',
           text: typeof raw === 'string' ? raw : JSON.stringify(raw),
         },
       ],
     };
libs/sdk/src/remote/entries/proxy-tool.entry.ts (3)

270-327: JSON Schema to Zod conversion is limited but functional.

The parseRemoteInputSchema method handles basic JSON Schema types but doesn't support:

  • Nested objects (recursive schemas)
  • allOf, oneOf, anyOf combinators
  • $ref references
  • Format constraints (email, uri, etc.)

For a proxy tool, this is acceptable since the remote server performs actual validation. Document this limitation or consider using a library like json-schema-to-zod for comprehensive conversion.


218-227: Input parsing re-creates Zod schema on every call.

The parseInput method creates a new z.object() on each invocation. Consider caching the compiled schema in the constructor for better performance.

🔎 Proposed optimization
+  /** Cached input validator */
+  private readonly inputValidator?: z.ZodObject<Record<string, z.ZodTypeAny>>;

   constructor(...) {
     // ... existing code ...
     this.inputSchema = this.parseRemoteInputSchema(remoteTool.inputSchema) as unknown as InSchema;
+    if (Object.keys(this.inputSchema).length > 0) {
+      this.inputValidator = z.object(this.inputSchema as Record<string, z.ZodTypeAny>);
+    }
     // ...
   }

   override parseInput(input: CallToolRequest['params']): CallToolRequest['params']['arguments'] {
-    if (this.inputSchema && Object.keys(this.inputSchema).length > 0) {
-      const inputSchemaObj = z.object(this.inputSchema);
-      return inputSchemaObj.parse(input.arguments);
+    if (this.inputValidator) {
+      return this.inputValidator.parse(input.arguments);
     }
     return input.arguments || {};
   }

160-164: Unsafe cast for outputSchema.

The cast 'json' as OutSchema bypasses type checking. While this works at runtime, it could mask type mismatches. Consider if OutSchema should default to a string literal type.

📜 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 07c9b88 and daff75c.

📒 Files selected for processing (62)
  • apps/e2e/demo-e2e-agents/src/main.ts
  • apps/e2e/demo-e2e-cache/src/main.ts
  • apps/e2e/demo-e2e-codecall/src/main.ts
  • apps/e2e/demo-e2e-errors/src/main.ts
  • apps/e2e/demo-e2e-multiapp/src/main.ts
  • apps/e2e/demo-e2e-notifications/src/main.ts
  • apps/e2e/demo-e2e-openapi/src/main.ts
  • apps/e2e/demo-e2e-orchestrated/src/main.ts
  • apps/e2e/demo-e2e-providers/src/main.ts
  • apps/e2e/demo-e2e-public/src/main.ts
  • apps/e2e/demo-e2e-redis/src/main.ts
  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts
  • apps/e2e/demo-e2e-remote/jest.e2e.config.ts
  • apps/e2e/demo-e2e-remote/project.json
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/index.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/prompts/greeting.prompt.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/resources/status.resource.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/add.tool.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/echo.tool.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/ping.tool.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/slow-operation.tool.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/main.ts
  • apps/e2e/demo-e2e-remote/src/main.ts
  • apps/e2e/demo-e2e-remote/tsconfig.app.json
  • apps/e2e/demo-e2e-remote/tsconfig.json
  • apps/e2e/demo-e2e-remote/webpack.config.js
  • apps/e2e/demo-e2e-remote/webpack.local.config.js
  • apps/e2e/demo-e2e-serverless/src/main.ts
  • apps/e2e/demo-e2e-transparent/src/main.ts
  • apps/e2e/demo-e2e-transport-recreation/src/main.ts
  • apps/e2e/demo-e2e-ui/src/main.ts
  • docs/draft/docs/plugins/cache-plugin.mdx
  • docs/draft/docs/servers/apps.mdx
  • libs/plugins/src/cache/__tests__/cache.plugin.test.ts
  • libs/plugins/src/cache/cache.plugin.ts
  • libs/plugins/src/cache/cache.types.ts
  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/common/interfaces/app.interface.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/common/schemas/annotated-class.schema.ts
  • libs/sdk/src/common/tokens/app.tokens.ts
  • libs/sdk/src/common/types/options/logging.options.ts
  • libs/sdk/src/errors/index.ts
  • libs/sdk/src/errors/remote.errors.ts
  • libs/sdk/src/hooks/hook.registry.ts
  • libs/sdk/src/index.ts
  • libs/sdk/src/logger/instances/instance.console-logger.ts
  • libs/sdk/src/logger/instances/instance.logger.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/remote/entries/index.ts
  • libs/sdk/src/remote/entries/proxy-prompt.entry.ts
  • libs/sdk/src/remote/entries/proxy-resource.entry.ts
  • libs/sdk/src/remote/entries/proxy-tool.entry.ts
  • libs/sdk/src/remote/index.ts
  • libs/sdk/src/remote/mcp-client.service.ts
  • libs/sdk/src/remote/mcp-client.types.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/scope/flows/http.request.flow.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/sdk/src/tool/tool.registry.ts
  • libs/sdk/src/transport/mcp-handlers/initialize-request.handler.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.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/index.ts
  • apps/e2e/demo-e2e-remote/src/main.ts
  • apps/e2e/demo-e2e-remote/jest.e2e.config.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/ping.tool.ts
  • libs/sdk/src/hooks/hook.registry.ts
  • apps/e2e/demo-e2e-ui/src/main.ts
  • libs/sdk/src/errors/index.ts
  • libs/sdk/src/transport/mcp-handlers/initialize-request.handler.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/prompts/greeting.prompt.ts
  • apps/e2e/demo-e2e-agents/src/main.ts
  • libs/sdk/src/logger/instances/instance.logger.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/main.ts
  • apps/e2e/demo-e2e-cache/src/main.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/index.ts
  • libs/plugins/src/cache/__tests__/cache.plugin.test.ts
  • apps/e2e/demo-e2e-transport-recreation/src/main.ts
  • apps/e2e/demo-e2e-serverless/src/main.ts
  • apps/e2e/demo-e2e-transparent/src/main.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/echo.tool.ts
  • libs/sdk/src/common/types/options/logging.options.ts
  • apps/e2e/demo-e2e-codecall/src/main.ts
  • libs/plugins/src/cache/cache.plugin.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/resources/status.resource.ts
  • apps/e2e/demo-e2e-notifications/src/main.ts
  • libs/sdk/src/remote/entries/index.ts
  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/tool/tool.registry.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/slow-operation.tool.ts
  • apps/e2e/demo-e2e-redis/src/main.ts
  • apps/e2e/demo-e2e-public/src/main.ts
  • libs/sdk/src/scope/flows/http.request.flow.ts
  • apps/e2e/demo-e2e-orchestrated/src/main.ts
  • libs/sdk/src/common/tokens/app.tokens.ts
  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts
  • libs/plugins/src/cache/cache.types.ts
  • libs/sdk/src/remote/mcp-client.service.ts
  • apps/e2e/demo-e2e-providers/src/main.ts
  • apps/e2e/demo-e2e-openapi/src/main.ts
  • libs/sdk/src/remote/mcp-client.types.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/add.tool.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/common/schemas/annotated-class.schema.ts
  • libs/sdk/src/resource/resource.registry.ts
  • apps/e2e/demo-e2e-multiapp/src/main.ts
  • libs/sdk/src/common/interfaces/app.interface.ts
  • libs/sdk/src/remote/entries/proxy-prompt.entry.ts
  • libs/sdk/src/remote/entries/proxy-tool.entry.ts
  • libs/sdk/src/remote/entries/proxy-resource.entry.ts
  • libs/sdk/src/remote/index.ts
  • apps/e2e/demo-e2e-errors/src/main.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/logger/instances/instance.console-logger.ts
  • libs/sdk/src/errors/remote.errors.ts
  • libs/sdk/src/prompt/prompt.registry.ts
libs/sdk/src/**/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Export public API through barrel files - export users' needed items, no legacy exports with different names

Files:

  • libs/sdk/src/index.ts
  • libs/sdk/src/errors/index.ts
  • libs/sdk/src/remote/entries/index.ts
  • libs/sdk/src/remote/index.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/index.ts
  • libs/sdk/src/hooks/hook.registry.ts
  • libs/sdk/src/errors/index.ts
  • libs/sdk/src/transport/mcp-handlers/initialize-request.handler.ts
  • libs/sdk/src/logger/instances/instance.logger.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/sdk/src/common/types/options/logging.options.ts
  • libs/sdk/src/remote/entries/index.ts
  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/tool/tool.registry.ts
  • libs/sdk/src/scope/flows/http.request.flow.ts
  • libs/sdk/src/common/tokens/app.tokens.ts
  • libs/sdk/src/remote/mcp-client.service.ts
  • libs/sdk/src/remote/mcp-client.types.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/common/schemas/annotated-class.schema.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/common/interfaces/app.interface.ts
  • libs/sdk/src/remote/entries/proxy-prompt.entry.ts
  • libs/sdk/src/remote/entries/proxy-tool.entry.ts
  • libs/sdk/src/remote/entries/proxy-resource.entry.ts
  • libs/sdk/src/remote/index.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/logger/instances/instance.console-logger.ts
  • libs/sdk/src/errors/remote.errors.ts
  • libs/sdk/src/prompt/prompt.registry.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/index.ts
  • libs/sdk/src/hooks/hook.registry.ts
  • libs/sdk/src/errors/index.ts
  • libs/sdk/src/transport/mcp-handlers/initialize-request.handler.ts
  • libs/sdk/src/logger/instances/instance.logger.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/plugins/src/cache/__tests__/cache.plugin.test.ts
  • libs/sdk/src/common/types/options/logging.options.ts
  • libs/plugins/src/cache/cache.plugin.ts
  • libs/sdk/src/remote/entries/index.ts
  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/tool/tool.registry.ts
  • libs/sdk/src/scope/flows/http.request.flow.ts
  • libs/sdk/src/common/tokens/app.tokens.ts
  • libs/plugins/src/cache/cache.types.ts
  • libs/sdk/src/remote/mcp-client.service.ts
  • libs/sdk/src/remote/mcp-client.types.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/common/schemas/annotated-class.schema.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/common/interfaces/app.interface.ts
  • libs/sdk/src/remote/entries/proxy-prompt.entry.ts
  • libs/sdk/src/remote/entries/proxy-tool.entry.ts
  • libs/sdk/src/remote/entries/proxy-resource.entry.ts
  • libs/sdk/src/remote/index.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/logger/instances/instance.console-logger.ts
  • libs/sdk/src/errors/remote.errors.ts
  • libs/sdk/src/prompt/prompt.registry.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/plugins/src/cache/__tests__/cache.plugin.test.ts
  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts
docs/draft/docs/**

⚙️ CodeRabbit configuration file

docs/draft/docs/**: This folder holds the draft/source docs that humans are expected to edit. When authors want to add or change documentation, they should do it here. The Codex workflow uses these drafts, together with the code diff, to generate the latest docs under docs/docs/. As a reviewer: - Encourage contributors to add/update content here instead of docs/docs/. - It is fine to do structural/content feedback here (clarity, examples, etc).

Files:

  • docs/draft/docs/plugins/cache-plugin.mdx
  • docs/draft/docs/servers/apps.mdx
docs/**

⚙️ CodeRabbit configuration file

docs/**: Repository documentation for the SDK, using MDX and hosted by Mintlify. See more specific rules for: - docs/docs/** (latest rendered docs, automation-only) - docs/v/** (archived versions, read-only) - docs/draft/docs/** (human-editable drafts) - docs/blogs/** (blogs, human edited) - docs/docs.json (Mintlify navigation)

Files:

  • docs/draft/docs/plugins/cache-plugin.mdx
  • docs/draft/docs/servers/apps.mdx
🧠 Learnings (24)
📓 Common learnings
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
📚 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/index.ts
  • libs/sdk/src/errors/index.ts
  • libs/sdk/src/remote/entries/index.ts
  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/remote/index.ts
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)

Applied to files:

  • libs/sdk/src/index.ts
  • apps/e2e/demo-e2e-remote/tsconfig.app.json
  • libs/sdk/src/remote/entries/index.ts
  • apps/e2e/demo-e2e-orchestrated/src/main.ts
  • apps/e2e/demo-e2e-remote/webpack.config.js
  • libs/sdk/src/remote/index.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/index.ts
  • apps/e2e/demo-e2e-remote/webpack.local.config.js
  • apps/e2e/demo-e2e-remote/webpack.config.js
📚 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/index.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/index.ts
  • apps/e2e/demo-e2e-remote/tsconfig.app.json
  • apps/e2e/demo-e2e-remote/tsconfig.json
  • libs/sdk/src/remote/entries/index.ts
  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/remote/index.ts
  • libs/sdk/src/logger/instances/instance.console-logger.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/index.ts
  • libs/sdk/src/transport/mcp-handlers/initialize-request.handler.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/prompts/greeting.prompt.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/main.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/resources/status.resource.ts
  • libs/sdk/src/remote/mcp-client.service.ts
  • libs/sdk/src/remote/mcp-client.types.ts
  • libs/sdk/src/common/schemas/annotated-class.schema.ts
  • libs/sdk/src/remote/entries/proxy-prompt.entry.ts
  • libs/sdk/src/remote/index.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/errors/remote.errors.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/index.ts
  • apps/e2e/demo-e2e-remote/src/main.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/ping.tool.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/main.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/index.ts
  • apps/e2e/demo-e2e-transport-recreation/src/main.ts
  • apps/e2e/demo-e2e-codecall/src/main.ts
  • libs/plugins/src/cache/cache.plugin.ts
  • apps/e2e/demo-e2e-public/src/main.ts
  • apps/e2e/demo-e2e-orchestrated/src/main.ts
  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts
  • libs/sdk/src/remote/mcp-client.service.ts
  • apps/e2e/demo-e2e-providers/src/main.ts
  • apps/e2e/demo-e2e-openapi/src/main.ts
  • libs/sdk/src/remote/mcp-client.types.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/add.tool.ts
  • libs/sdk/src/common/schemas/annotated-class.schema.ts
  • libs/sdk/src/common/interfaces/app.interface.ts
  • libs/sdk/src/remote/index.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/errors/remote.errors.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/index.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/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/index.ts
  • libs/sdk/src/remote/mcp-client.types.ts
  • libs/sdk/src/common/schemas/annotated-class.schema.ts
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/{build,bundler}/**/*.{ts,tsx,js,jsx} : For server-side MDX rendering with bundled React, use frontmcp/ui/renderers instead of frontmcp/uipack/renderers

Applied to files:

  • libs/sdk/src/index.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/index.ts
  • libs/sdk/src/errors/index.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/main.ts
  • libs/sdk/src/remote/mcp-client.service.ts
  • libs/sdk/src/remote/mcp-client.types.ts
  • libs/sdk/src/common/schemas/annotated-class.schema.ts
  • apps/e2e/demo-e2e-errors/src/main.ts
  • libs/sdk/src/errors/remote.errors.ts
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/{theme,adapters,bundler}/**/*.{test,spec}.{ts,tsx,js,jsx} : Test behavior across all supported platform configurations (OpenAI, Claude, etc.)

Applied to files:

  • apps/e2e/demo-e2e-remote/jest.e2e.config.ts
  • apps/e2e/demo-e2e-remote/tsconfig.app.json
  • apps/e2e/demo-e2e-remote/tsconfig.json
  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.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 : Validate hook flows and fail fast on invalid hook configurations with specific error messages

Applied to files:

  • libs/sdk/src/hooks/hook.registry.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 `changeScope` property instead of `scope` in change events to avoid confusion with Scope class

Applied to files:

  • libs/sdk/src/hooks/hook.registry.ts
  • apps/e2e/demo-e2e-remote/tsconfig.app.json
  • apps/e2e/demo-e2e-remote/tsconfig.json
  • apps/e2e/demo-e2e-transparent/src/main.ts
  • apps/e2e/demo-e2e-errors/src/main.ts
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/{theme,build,bundler}/**/*.{ts,tsx,js,jsx} : Do not hard-code CDN URLs - use theme.cdn configuration instead

Applied to files:

  • apps/e2e/demo-e2e-remote/webpack.local.config.js
  • apps/e2e/demo-e2e-remote/webpack.config.js
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/{package.json,*.ts,*.tsx,*.js,*.jsx} : Do not add React dependencies to frontmcp/uipack - it must remain React-free. Use frontmcp/ui for React components.

Applied to files:

  • apps/e2e/demo-e2e-remote/tsconfig.app.json
📚 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 : Use strict TypeScript mode with no `any` types without strong justification - use `unknown` instead for generic type defaults

Applied to files:

  • apps/e2e/demo-e2e-remote/tsconfig.app.json
  • apps/e2e/demo-e2e-remote/tsconfig.json
📚 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/**/*.test.{ts,tsx} : Use React Testing Library for component tests and include SSR/hydration tests for all interactive components

Applied to files:

  • apps/e2e/demo-e2e-remote/tsconfig.app.json
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/{src}/**/*.{ts,tsx,js,jsx} : Do not expose internal error details in public APIs - use sanitized error messages

Applied to files:

  • libs/sdk/src/errors/index.ts
  • apps/e2e/demo-e2e-errors/src/main.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 : Validate URIs per RFC 3986 at metadata level using proper validation

Applied to files:

  • libs/sdk/src/app/app.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: Use Nx for build system with commands like `nx build sdk`, `nx test`, `nx run-many -t test`

Applied to files:

  • apps/e2e/demo-e2e-remote/project.json
📚 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 : Do not mutate rawInput in flows - use state.set() for managing flow state instead

Applied to files:

  • libs/sdk/src/scope/flows/http.request.flow.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 (18)
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/ping.tool.ts (2)
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/add.tool.ts (1)
  • Tool (17-30)
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/echo.tool.ts (1)
  • Tool (16-29)
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/prompts/greeting.prompt.ts (1)
libs/sdk/src/index.ts (1)
  • GetPromptResult (17-17)
apps/e2e/demo-e2e-remote/src/local-mcp-server/main.ts (2)
apps/e2e/demo-e2e-public/src/main.ts (1)
  • FrontMcp (6-25)
apps/e2e/demo-e2e-remote/src/main.ts (1)
  • FrontMcp (8-60)
libs/plugins/src/cache/__tests__/cache.plugin.test.ts (1)
libs/plugins/src/cache/cache.symbol.ts (1)
  • CacheStoreToken (4-4)
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/echo.tool.ts (4)
libs/cli/src/templates/3rd-party-integration/src/tools/example.list.ts (1)
  • inputSchema (12-17)
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/add.tool.ts (1)
  • Tool (17-30)
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/ping.tool.ts (1)
  • Tool (15-29)
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/slow-operation.tool.ts (1)
  • Tool (18-40)
libs/plugins/src/cache/cache.plugin.ts (4)
libs/sdk/src/common/interfaces/flow.interface.ts (1)
  • FlowCtxOf (13-13)
libs/sdk/src/index.ts (1)
  • FrontMcpContextStorage (26-26)
libs/plugins/src/cache/cache.symbol.ts (1)
  • CacheStoreToken (4-4)
libs/sdk/src/store/adapters/store.vercel-kv.adapter.ts (1)
  • ttl (202-206)
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/slow-operation.tool.ts (4)
libs/cli/src/templates/3rd-party-integration/src/tools/example.list.ts (1)
  • inputSchema (12-17)
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/add.tool.ts (1)
  • Tool (17-30)
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/echo.tool.ts (1)
  • Tool (16-29)
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/ping.tool.ts (1)
  • Tool (15-29)
libs/sdk/src/scope/flows/http.request.flow.ts (3)
libs/sdk/src/logger/instances/instance.logger.ts (1)
  • error (77-79)
libs/sdk/src/common/interfaces/execution-context.interface.ts (1)
  • error (188-190)
libs/sdk/src/common/interfaces/flow.interface.ts (1)
  • FlowControl (19-43)
libs/sdk/src/common/tokens/app.tokens.ts (1)
libs/sdk/src/common/tokens/base.tokens.ts (1)
  • tokenFactory (9-9)
apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts (3)
libs/sdk/src/logger/instances/instance.logger.ts (1)
  • error (77-79)
libs/sdk/src/app/instances/app.remote.instance.ts (3)
  • tools (335-337)
  • resources (339-341)
  • prompts (343-345)
scripts/bump-synchronized-versions.mjs (1)
  • result (130-130)
libs/sdk/src/remote/mcp-client.service.ts (3)
libs/sdk/src/remote/index.ts (14)
  • McpClientServiceOptions (33-33)
  • McpClientService (11-11)
  • McpClientConnection (16-16)
  • McpRemoteCapabilities (30-30)
  • McpConnectRequest (34-34)
  • McpCapabilityChangeCallback (39-39)
  • McpConnectionChangeCallback (40-40)
  • McpRemoteAuthContext (28-28)
  • McpUnsubscribeFn (41-41)
  • McpRemoteAuthConfig (27-27)
  • McpHttpTransportOptions (21-21)
  • McpConnectionStatus (17-17)
  • McpCapabilityChangeEvent (31-31)
  • McpStaticCredentials (26-26)
libs/sdk/src/remote/mcp-client.types.ts (13)
  • McpClientServiceOptions (207-218)
  • McpClientConnection (32-49)
  • McpRemoteCapabilities (176-187)
  • McpConnectRequest (223-238)
  • McpCapabilityChangeCallback (289-289)
  • McpConnectionChangeCallback (294-294)
  • McpRemoteAuthContext (162-167)
  • McpUnsubscribeFn (299-299)
  • McpRemoteAuthConfig (135-157)
  • McpHttpTransportOptions (76-87)
  • McpConnectionStatus (27-27)
  • McpCapabilityChangeEvent (192-198)
  • McpStaticCredentials (125-130)
libs/sdk/src/errors/remote.errors.ts (10)
  • RemoteConnectionError (15-29)
  • RemoteNotConnectedError (327-334)
  • RemoteCapabilityDiscoveryError (269-281)
  • RemoteToolNotFoundError (76-86)
  • RemoteToolExecutionError (167-183)
  • RemoteResourceNotFoundError (91-101)
  • RemoteResourceReadError (188-204)
  • RemotePromptNotFoundError (106-116)
  • RemotePromptGetError (209-225)
  • RemoteAuthError (125-139)
apps/e2e/demo-e2e-remote/webpack.config.js (1)
apps/e2e/demo-e2e-remote/webpack.local.config.js (2)
  • require (1-1)
  • require (2-2)
libs/sdk/src/remote/mcp-client.types.ts (1)
libs/sdk/src/index.ts (1)
  • GetPromptResult (17-17)
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/add.tool.ts (4)
libs/cli/src/templates/3rd-party-integration/src/tools/example.list.ts (1)
  • inputSchema (12-17)
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/echo.tool.ts (1)
  • Tool (16-29)
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/ping.tool.ts (1)
  • Tool (15-29)
apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/slow-operation.tool.ts (1)
  • Tool (18-40)
libs/sdk/src/app/instances/app.remote.instance.ts (6)
libs/sdk/src/common/interfaces/internal/registry.interface.ts (5)
  • ToolRegistryInterface (115-129)
  • ResourceRegistryInterface (131-146)
  • PromptRegistryInterface (148-160)
  • PluginRegistryInterface (107-109)
  • AdapterRegistryInterface (111-113)
libs/sdk/src/remote/entries/proxy-tool.entry.ts (2)
  • ProxyToolEntry (108-339)
  • createProxyToolEntry (385-394)
libs/sdk/src/remote/index.ts (10)
  • ProxyToolEntry (46-46)
  • ProxyResourceEntry (53-53)
  • ProxyPromptEntry (60-60)
  • McpClientService (11-11)
  • McpConnectRequest (34-34)
  • McpTransportType (20-20)
  • McpRemoteAuthConfig (27-27)
  • createProxyToolEntry (48-48)
  • createProxyResourceEntry (55-55)
  • createProxyPromptEntry (62-62)
libs/sdk/src/remote/entries/proxy-resource.entry.ts (2)
  • ProxyResourceEntry (85-292)
  • createProxyResourceEntry (346-355)
libs/sdk/src/remote/entries/proxy-prompt.entry.ts (2)
  • ProxyPromptEntry (82-237)
  • createProxyPromptEntry (278-287)
libs/sdk/src/remote/mcp-client.types.ts (3)
  • McpConnectRequest (223-238)
  • McpTransportType (71-71)
  • McpRemoteAuthConfig (135-157)
libs/sdk/src/common/interfaces/app.interface.ts (1)
libs/sdk/src/common/metadata/app.metadata.ts (1)
  • RemoteAppMetadata (228-302)
libs/sdk/src/remote/entries/proxy-prompt.entry.ts (5)
libs/sdk/src/remote/index.ts (5)
  • ProxyPromptRecord (63-63)
  • ProxyPromptContext (61-61)
  • McpClientService (11-11)
  • McpRemoteAuthContext (28-28)
  • ProxyPromptEntry (60-60)
libs/sdk/src/remote/mcp-client.service.ts (1)
  • McpClientService (73-760)
libs/sdk/src/remote/mcp-client.types.ts (1)
  • McpRemoteAuthContext (162-167)
libs/sdk/src/scope/scope.instance.ts (1)
  • Scope (37-380)
libs/sdk/src/common/entries/prompt.entry.ts (3)
  • PromptGetExtra (11-13)
  • ParsedPromptResult (15-15)
  • PromptSafeTransformResult (16-16)
libs/sdk/src/remote/entries/proxy-resource.entry.ts (4)
libs/sdk/src/remote/mcp-client.service.ts (1)
  • McpClientService (73-760)
libs/sdk/src/common/interfaces/resource.interface.ts (1)
  • ResourceCtorArgs (44-49)
libs/sdk/src/remote/mcp-client.types.ts (1)
  • McpRemoteAuthContext (162-167)
libs/sdk/src/common/entries/resource.entry.ts (3)
  • ResourceReadExtra (11-13)
  • ParsedResourceResult (15-15)
  • ResourceSafeTransformResult (16-16)
🪛 ast-grep (0.40.3)
libs/sdk/src/remote/entries/proxy-resource.entry.ts

[warning] 255-255: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^${regexStr}$)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

🪛 Biome (2.1.2)
libs/sdk/src/remote/mcp-client.service.ts

[error] 567-569: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ 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). (1)
  • GitHub Check: Build Libraries

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: 0

🧹 Nitpick comments (2)
libs/sdk/src/tool/flows/call-tool.flow.ts (2)

597-601: Add runtime type guard for rawMeta before spreading.

The code uses type assertions without runtime validation to extract _meta from rawOutput. If rawOutput._meta exists but is not actually a Record (e.g., a primitive or array), the spread operation on line 600 could behave unexpectedly.

Consider adding a runtime type check to ensure type safety:

🔎 Proposed refactor with runtime type guard
 // Preserve any _meta from rawOutput (e.g., cache plugin adds cache: 'hit')
-const rawMeta = (rawOutput as Record<string, unknown>)?.['_meta'] as Record<string, unknown> | undefined;
-if (rawMeta) {
+const rawMeta = (rawOutput as Record<string, unknown>)?._meta;
+if (rawMeta && typeof rawMeta === 'object' && !Array.isArray(rawMeta)) {
-  result._meta = { ...result._meta, ...rawMeta };
+  result._meta = { ...result._meta, ...(rawMeta as Record<string, unknown>) };
 }

597-614: Consider combining all _meta merges into a single operation.

The code performs sequential spreads for _meta (lines 600 and 612), creating intermediate objects. While this works correctly, combining them would improve readability and make the precedence order more explicit.

🔎 Proposed refactor combining all merges
 // Preserve any _meta from rawOutput (e.g., cache plugin adds cache: 'hit')
 const rawMeta = (rawOutput as Record<string, unknown>)?.['_meta'] as Record<string, unknown> | undefined;
-if (rawMeta) {
-  result._meta = { ...result._meta, ...rawMeta };
-}

 // Apply UI result if available (from applyUI stage)
 if (uiResult) {
   result.content = uiResult.content;
   // Set structuredContent from UI result (contains raw tool output)
   // Cast to Record<string, unknown> since MCP protocol expects object type
   if (uiResult.structuredContent !== undefined && uiResult.structuredContent !== null) {
     result.structuredContent = uiResult.structuredContent as Record<string, unknown>;
   }
-  if (uiMeta) {
-    result._meta = { ...result._meta, ...uiMeta };
-  }
 }

+// Merge all metadata sources with explicit precedence: parse result < rawMeta < uiMeta
+result._meta = {
+  ...result._meta,
+  ...(rawMeta || {}),
+  ...(uiMeta || {}),
+};

This makes the merge precedence explicit and reduces intermediate object allocations.

📜 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 daff75c and dc01a12.

📒 Files selected for processing (1)
  • libs/sdk/src/tool/flows/call-tool.flow.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/tool/flows/call-tool.flow.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/tool/flows/call-tool.flow.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/tool/flows/call-tool.flow.ts

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

Fix all issues with AI Agents 🤖
In @libs/sdk/src/remote/mcp-client.service.ts:
- Around line 54-61: DEFAULT_OPTIONS advertises defaultTimeout, maxRetries and
retryDelayMs but the implementation never uses them; either remove these fields
from McpClientServiceOptions and DEFAULT_OPTIONS or wire them into the request
path. To implement: read options.defaultTimeout, options.maxRetries and
options.retryDelayMs in the McpClientService constructor and apply them inside
the HTTP request method (e.g., the method that performs outbound calls in this
service) by using an AbortController/timeout that cancels the fetch/axios call
after defaultTimeout, and wrap the request in a retry loop that retries up to
maxRetries with a delay of retryDelayMs between attempts; keep
capabilityRefreshInterval handling as-is. Ensure the DEFAULT_OPTIONS and public
McpClientServiceOptions type reflect whichever choice you pick (remove unused
keys if you remove support).
🧹 Nitpick comments (3)
libs/sdk/src/remote/mcp-client.service.ts (3)

122-127: Consider parameterizing client name and version.

The MCP client is initialized with hardcoded name: 'frontmcp-gateway' and version: '1.0.0'. For better maintainability, consider sourcing these from package.json or accepting them as constructor options, especially if this service is used in different contexts or the version needs to track the SDK version.


260-269: Fetch resource templates in parallel with other capabilities.

Resource templates are fetched sequentially after the parallel batch of tools, resources, and prompts. Include listResourceTemplatesInternal in the Promise.all call to reduce discovery latency.

🔎 Proposed optimization
-      const [toolsResult, resourcesResult, promptsResult] = await Promise.all([
+      const [toolsResult, resourcesResult, resourceTemplatesResult, promptsResult] = await Promise.all([
         this.listToolsInternal(connection),
         this.listResourcesInternal(connection),
+        this.listResourceTemplatesInternal(connection),
         this.listPromptsInternal(connection),
       ]);
-
-      // Also fetch resource templates
-      const resourceTemplatesResult = await this.listResourceTemplatesInternal(connection);

725-739: Prevent overlapping capability refresh operations.

The setInterval callback invokes the async discoverCapabilities() without checking if a previous refresh is still in progress. If discovery takes longer than capabilityRefreshInterval, multiple refreshes can run concurrently, potentially causing race conditions when updating capabilities, unnecessary load on the remote server, and duplicate change events.

🔎 Recommended guard mechanism

Add a flag to track refresh state:

   // Refresh timers
   private readonly refreshTimers: Map<string, ReturnType<typeof setInterval>> = new Map();
+  private readonly refreshInProgress: Map<string, boolean> = new Map();

Then guard the refresh callback:

   private startCapabilityRefresh(appId: string): void {
     if (this.options.capabilityRefreshInterval <= 0) {
       return;
     }

     const timer = setInterval(async () => {
+      if (this.refreshInProgress.get(appId)) {
+        this.logger.debug(`Skipping capability refresh for ${appId} - previous refresh still in progress`);
+        return;
+      }
+      this.refreshInProgress.set(appId, true);
       try {
         await this.discoverCapabilities(appId);
       } catch (error) {
         this.logger.warn(`Capability refresh failed for ${appId}: ${(error as Error).message}`);
+      } finally {
+        this.refreshInProgress.set(appId, false);
       }
     }, this.options.capabilityRefreshInterval);

     this.refreshTimers.set(appId, timer);
   }

Also clear the flag in stopCapabilityRefresh:

   private stopCapabilityRefresh(appId: string): void {
     const timer = this.refreshTimers.get(appId);
     if (timer) {
       clearInterval(timer);
       this.refreshTimers.delete(appId);
+      this.refreshInProgress.delete(appId);
     }
   }
📜 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 dc01a12 and 909922a.

📒 Files selected for processing (1)
  • libs/sdk/src/remote/mcp-client.service.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/remote/mcp-client.service.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/remote/mcp-client.service.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/remote/mcp-client.service.ts
🧠 Learnings (3)
📚 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/remote/mcp-client.service.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/remote/mcp-client.service.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/remote/mcp-client.service.ts
🧬 Code graph analysis (1)
libs/sdk/src/remote/mcp-client.service.ts (2)
libs/sdk/src/remote/mcp-client.types.ts (11)
  • McpClientServiceOptions (207-218)
  • McpRemoteCapabilities (176-187)
  • McpConnectRequest (223-238)
  • McpConnectionInfo (54-62)
  • McpRemoteAuthContext (162-167)
  • McpUnsubscribeFn (299-299)
  • McpRemoteAuthConfig (135-157)
  • McpHttpTransportOptions (76-87)
  • McpConnectionStatus (27-27)
  • McpCapabilityChangeEvent (192-198)
  • McpStaticCredentials (125-130)
libs/sdk/src/errors/remote.errors.ts (10)
  • RemoteConnectionError (15-29)
  • RemoteNotConnectedError (327-334)
  • RemoteCapabilityDiscoveryError (269-281)
  • RemoteToolNotFoundError (76-86)
  • RemoteToolExecutionError (167-183)
  • RemoteResourceNotFoundError (91-101)
  • RemoteResourceReadError (188-204)
  • RemotePromptNotFoundError (106-116)
  • RemotePromptGetError (209-225)
  • RemoteAuthError (125-139)
⏰ 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). (1)
  • GitHub Check: Build Libraries

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: 2

Fix all issues with AI Agents 🤖
In @libs/sdk/src/app/app.utils.ts:
- Around line 24-30: The type guard isRemoteAppConfig currently only checks that
url is a string; update it to import and call isValidMcpUri and return false if
the URL does not pass RFC 3986 validation (so isRemoteAppConfig(item) must
verify typeof obj['url'] === 'string' && isValidMcpUri(obj['url'])). Also update
the frontMcpRemoteAppMetadataSchema to add the same RFC 3986 URL validation (use
a custom validator that delegates to isValidMcpUri) so both the runtime type
guard and the schema enforce the same URI rules; reference the symbols
isRemoteAppConfig, RemoteAppMetadata, frontMcpRemoteAppMetadataSchema, and
isValidMcpUri when making the changes.
♻️ Duplicate comments (6)
libs/sdk/src/app/app.utils.ts (1)

44-58: Past review partially addressed; consider type-safe alternatives for placeholder.

The redundant cast has been removed (line 46) and null as any has been replaced with null as unknown as AppEntry (line 55), which is an improvement. However, the double cast pattern still bypasses type safety—the type system cannot enforce that this placeholder will be replaced before use.

Consider whether AppRecord's useValue could be made optional for REMOTE_VALUE entries, or if initialization could use a factory pattern that returns properly typed instances. This would eliminate the need for type assertions entirely.

💡 Example type-safe alternative

If the AppRecord type definition allows, consider:

return {
  kind: AppKind.REMOTE_VALUE,
  provide,
  useValue: undefined, // If useValue is optional
  metadata,
};

Or use a factory function that returns the properly typed instance after initialization.

libs/sdk/src/tool/tool.registry.ts (1)

108-130: Unsafe type cast persists despite past review feedback.

Line 124 still casts remoteTool as ToolInstance, but app.tools.getTools() returns ToolEntry[] per the interface. The makeRow method signature expects ToolInstance, but you're passing ProxyToolEntry objects (which extend ToolEntry, not ToolInstance). This creates a type contract violation.

The validation on line 120 only checks for a name property, which doesn't guarantee the object has all ToolInstance-specific methods that callers of IndexedTool.instance may expect.

Consider updating makeRow to accept ToolEntry instead of ToolInstance, similar to how ResourceRegistry was updated:

🔎 Proposed fix
-  private makeRow(token: Token, instance: ToolInstance, lineage: EntryLineage, source: ToolRegistry): IndexedTool {
+  private makeRow(token: Token, instance: ToolEntry, lineage: EntryLineage, source: ToolRegistry): IndexedTool {

And update IndexedTool.instance type in tool.types.ts to ToolEntry.

libs/sdk/src/remote/mcp-client.service.ts (4)

364-404: authContext parameter accepted but never forwarded.

The callTool method accepts authContext?: McpRemoteAuthContext (line 368) but never uses it. The MCP client call on line 384-387 doesn't forward any authentication headers. This means call-level authentication configured via resolveAuthHeaders won't actually reach the remote server.

🔎 Proposed fix concept

The MCP SDK's callTool method may need to receive auth headers through request options. Investigate whether the Client.callTool method supports a metadata/options parameter for headers:

// If supported by the SDK:
const result = await connection.client.callTool(
  { name: toolName, arguments: args },
  undefined,
  { headers: authContext?.headers } // Forward auth headers
);

395-397: Unsafe type cast bypasses type safety.

The as unknown as CallToolResult cast discards type information. Verify the actual return type from the MCP SDK and handle appropriately rather than force-casting.


409-409: authContext also unused in readResource and getPrompt.

Both methods accept the authContext parameter but never forward it to the underlying client calls, same issue as callTool.

Also applies to: 438-443


589-602: SSE fallback logic in createTransport won't trigger as intended.

The new StreamableHTTPClientTransport() constructor is synchronous and won't throw connection errors. The try/catch here catches only constructor failures (e.g., invalid URL), not connection failures which occur during client.connect(). The SSE fallback should be restructured to handle errors at the connection level.

🔎 Proposed restructuring

Move the fallback logic to be triggered when client.connect(transport) fails in the connect() method, rather than in createTransport():

// In connect() method:
try {
  await client.connect(transport);
} catch (error) {
  if (transportType === 'http' && httpOptions?.fallbackToSSE !== false) {
    const sseTransport = new SSEClientTransport(new URL(url));
    await client.connect(sseTransport);
    // Update connection.transport to sseTransport
  } else {
    throw error;
  }
}
🧹 Nitpick comments (8)
libs/plugins/src/cache/cache.plugin.ts (3)

25-42: Consider edge case handling for empty or wildcard-only patterns.

The pattern matching logic is generally sound, but doesn't guard against edge cases like empty patterns ('') which would match empty tool names, or wildcard-only patterns ('***') which would match any tool name.

While these may be rare in practice, adding validation would make the behavior more predictable.

🔎 Suggested guard
 function matchesToolPattern(toolName: string, patterns: string[]): boolean {
   if (!patterns || patterns.length === 0) return false;
 
   return patterns.some((pattern) => {
+    // Skip empty or whitespace-only patterns
+    if (!pattern || pattern.trim().length === 0) return false;
+    
     if (pattern.includes('*')) {

145-165: Improve error handling: avoid silent catch-all.

The try-catch block swallows all exceptions without logging, which could mask DI configuration issues or runtime problems. While the safe default (return false) prevents cache bypass, silent failures make debugging difficult.

Consider logging errors or being more specific about expected exceptions.

🔎 Suggested improvement
   private shouldBypassCache(_flowCtx: FlowCtxOf<'tools:call-tool'>): boolean {
     const bypassHeader = this.options.bypassHeader ?? DEFAULT_BYPASS_HEADER;
 
     try {
       // Get custom headers from the context storage
       // Headers with x-frontmcp-* prefix are stored in metadata.customHeaders
       const contextStorage = this.get(FrontMcpContextStorage);
       const context = contextStorage?.getStore();
       const customHeaders = context?.metadata?.customHeaders;
 
       if (!customHeaders) return false;
 
       // Headers are stored lowercase in customHeaders
       const headerKey = bypassHeader.toLowerCase();
       const headerValue = customHeaders[headerKey];
       return headerValue === 'true' || headerValue === '1';
-    } catch {
+    } catch (error) {
       // Context storage not available - bypass header cannot be checked
+      // Log at debug level to aid troubleshooting without spamming logs
+      // this.logger?.debug?.('Failed to check bypass header:', error);
       return false;
     }
   }

Note: Adjust logging based on your logger availability.


277-283: Clarify public API documentation: method only checks patterns, not metadata.

The docstring states "based on metadata or tools list" but the method signature only accepts toolName, so it cannot check metadata. The implementation only validates against toolPatterns.

For a public API method in a publishable SDK, consider either:

  1. Updating the docstring to accurately describe that it only checks configured patterns
  2. Adding an optional metadata parameter to provide complete cacheability logic
  3. Renaming to matchesToolPattern or isToolPatternCacheable for clarity
🔎 Suggested documentation fix
   /**
-   * Check if a tool is cacheable based on metadata or tools list.
+   * Check if a tool matches any configured cache patterns.
+   * Note: This only checks toolPatterns configuration, not per-tool metadata.
    * This can be used by other plugins or flows to determine cacheability.
    */
   isCacheable(toolName: string): boolean {

Note: Based on coding guidelines for publishable SDK libraries.

apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts (2)

34-38: Hardcoded 2-second delay may be fragile.

The fixed 2-second wait after health check could cause flaky tests if the MCP handlers need more or less time on different environments. Consider implementing a polling mechanism that checks for MCP readiness explicitly.

🔎 Suggested approach
// Instead of fixed delay, poll for MCP readiness
const waitForMcpReady = async (maxAttempts = 10, delayMs = 500) => {
  for (let i = 0; i < maxAttempts; i++) {
    try {
      // Attempt a lightweight MCP operation to verify readiness
      await localMcpServer?.checkMcpReady();
      return;
    } catch {
      await new Promise((resolve) => setTimeout(resolve, delayMs));
    }
  }
  throw new Error('MCP server did not become ready in time');
};

144-152: Timing assertion may be flaky in CI environments.

The 50ms tolerance (450ms for 500ms delay) could cause intermittent failures on slower CI runners or under load. Consider increasing the tolerance or using a relative comparison.

🔎 Proposed fix
-      expect(elapsed).toBeGreaterThanOrEqual(450); // Allow some tolerance
+      expect(elapsed).toBeGreaterThanOrEqual(400); // Allow 20% tolerance for CI variability
libs/sdk/src/remote/mcp-client.service.ts (1)

676-722: Capability change detection only compares counts, not content.

The emitCapabilityChangeIfNeeded method only compares array lengths, so it won't detect changes where a tool is removed and a different one is added (same count). Consider comparing tool/resource/prompt names or using a hash of the capability set.

libs/sdk/src/app/instances/app.remote.instance.ts (2)

117-126: Consider documenting the cast or adding a type guard.

The cast from ProxyToolEntry[] to ToolInstance[] assumes that proxy entries implement the ToolInstance interface. While this may be correct, consider:

  1. Adding a comment explaining why this cast is safe, or
  2. Using a type constraint in the ProxyToolEntry definition to make this relationship explicit.
📝 Add explanatory comment
-      // Cast proxy entries as ToolInstance - they implement the same interface
+      // Cast is safe: ProxyToolEntry implements ToolInstance interface through duck typing
       snapshot: snapshot as unknown as readonly ToolInstance[],

109-113: Consider logging errors in callback notification.

The empty catch block silently swallows errors from subscriber callbacks. While this prevents one subscriber from breaking others, consider logging these errors for debugging purposes.

🔎 Proposed enhancement
       try {
         cb(event);
-      } catch {
-        // Ignore callback errors
+      } catch (error) {
+        // Log but don't propagate subscriber errors to prevent cascade failures
+        // Note: logger not available in registry; consider adding if needed
       }
📜 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 909922a and 210f720.

📒 Files selected for processing (16)
  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/slow-operation.tool.ts
  • docs/draft/docs/plugins/cache-plugin.mdx
  • libs/plugins/src/cache/cache.plugin.ts
  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/errors/mcp.error.ts
  • libs/sdk/src/errors/remote.errors.ts
  • libs/sdk/src/hooks/hook.registry.ts
  • libs/sdk/src/remote/mcp-client.service.ts
  • libs/sdk/src/remote/mcp-client.types.ts
  • libs/sdk/src/resource/resource.events.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/resource/resource.types.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/sdk/src/tool/tool.registry.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • docs/draft/docs/plugins/cache-plugin.mdx
  • apps/e2e/demo-e2e-remote/src/local-mcp-server/apps/local-test/tools/slow-operation.tool.ts
  • libs/sdk/src/hooks/hook.registry.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/resource/resource.types.ts
  • libs/sdk/src/resource/resource.events.ts
  • libs/sdk/src/errors/mcp.error.ts
  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts
  • libs/plugins/src/cache/cache.plugin.ts
  • libs/sdk/src/tool/tool.registry.ts
  • libs/sdk/src/remote/mcp-client.service.ts
  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/remote/mcp-client.types.ts
  • libs/sdk/src/errors/remote.errors.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/resource/resource.types.ts
  • libs/sdk/src/resource/resource.events.ts
  • libs/sdk/src/errors/mcp.error.ts
  • libs/sdk/src/tool/tool.registry.ts
  • libs/sdk/src/remote/mcp-client.service.ts
  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/remote/mcp-client.types.ts
  • libs/sdk/src/errors/remote.errors.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/resource/resource.types.ts
  • libs/sdk/src/resource/resource.events.ts
  • libs/sdk/src/errors/mcp.error.ts
  • libs/plugins/src/cache/cache.plugin.ts
  • libs/sdk/src/tool/tool.registry.ts
  • libs/sdk/src/remote/mcp-client.service.ts
  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/remote/mcp-client.types.ts
  • libs/sdk/src/errors/remote.errors.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:

  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts
🧠 Learnings (19)
📚 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/resource/resource.types.ts
  • libs/sdk/src/app/app.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 `changeScope` property instead of `scope` in change events to avoid confusion with Scope class

Applied to files:

  • libs/sdk/src/resource/resource.events.ts
  • libs/sdk/src/app/instances/app.remote.instance.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/errors/mcp.error.ts
  • libs/sdk/src/remote/mcp-client.service.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/remote/mcp-client.types.ts
  • libs/sdk/src/errors/remote.errors.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/errors/mcp.error.ts
  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts
  • libs/sdk/src/remote/mcp-client.service.ts
  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/remote/mcp-client.types.ts
  • libs/sdk/src/errors/remote.errors.ts
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/{theme,adapters,bundler}/**/*.{test,spec}.{ts,tsx,js,jsx} : Test behavior across all supported platform configurations (OpenAI, Claude, etc.)

Applied to files:

  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.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:

  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts
  • libs/plugins/src/cache/cache.plugin.ts
  • libs/sdk/src/remote/mcp-client.service.ts
  • libs/sdk/src/remote/mcp-client.types.ts
  • libs/sdk/src/errors/remote.errors.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 : Use strict TypeScript mode with no `any` types without strong justification - use `unknown` instead for generic type defaults

Applied to files:

  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts
  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/app/instances/app.remote.instance.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:

  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts
  • libs/sdk/src/app/app.utils.ts
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/{src}/**/*.{ts,tsx} : Do not use `any` type without justification in TypeScript code

Applied to files:

  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts
  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/app/instances/app.remote.instance.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:

  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.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} : Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)

Applied to files:

  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.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/react/hooks/**/*.{ts,tsx} : MCP bridge hooks (useMcpBridge, useCallTool, useToolInput, useToolOutput, useTheme) must be properly typed and handle loading/error states

Applied to files:

  • apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts
  • libs/sdk/src/tool/tool.registry.ts
  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/remote/mcp-client.types.ts
📚 Learning: 2025-11-05T15:00:47.800Z
Learnt from: frontegg-david
Repo: agentfront/frontmcp PR: 11
File: libs/core/src/auth/jwks/jwks.service.ts:62-0
Timestamp: 2025-11-05T15:00:47.800Z
Learning: In the FrontMCP codebase (libs/core/src/auth/jwks/jwks.service.ts), the verifyGatewayToken method intentionally skips signature verification when running in development-only no-auth mode, using decodeJwtPayloadSafe instead of jwtVerify. This is a deliberate design for local development convenience and should not be flagged as a security issue when the PR or code context indicates development/no-auth mode.

Applied to files:

  • libs/sdk/src/remote/mcp-client.service.ts
  • libs/sdk/src/app/instances/app.remote.instance.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 : Validate URIs per RFC 3986 at metadata level using proper validation

Applied to files:

  • libs/sdk/src/app/app.utils.ts
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)

Applied to files:

  • libs/sdk/src/app/app.utils.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/app/app.utils.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} : Avoid using any type without justification; all props, return types, and generics must be properly typed

Applied to files:

  • libs/sdk/src/app/app.utils.ts
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/{src}/**/*.{ts,tsx} : Always validate component options before use

Applied to files:

  • libs/sdk/src/app/app.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/app/instances/app.remote.instance.ts
🧬 Code graph analysis (6)
apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts (2)
libs/sdk/src/logger/instances/instance.logger.ts (1)
  • error (77-79)
libs/sdk/src/app/instances/app.remote.instance.ts (3)
  • tools (349-351)
  • resources (353-355)
  • prompts (357-359)
libs/sdk/src/tool/tool.registry.ts (1)
libs/sdk/src/app/instances/app.remote.instance.ts (1)
  • AppRemoteInstance (251-549)
libs/sdk/src/app/app.utils.ts (1)
libs/sdk/src/common/metadata/app.metadata.ts (1)
  • RemoteAppMetadata (228-302)
libs/sdk/src/app/instances/app.remote.instance.ts (7)
libs/sdk/src/remote/mcp-client.service.ts (1)
  • McpClientService (72-770)
libs/sdk/src/remote/index.ts (10)
  • McpClientService (11-11)
  • ProxyToolEntry (46-46)
  • ProxyResourceEntry (53-53)
  • ProxyPromptEntry (60-60)
  • McpConnectRequest (34-34)
  • McpTransportType (20-20)
  • McpRemoteAuthConfig (27-27)
  • createProxyToolEntry (48-48)
  • createProxyResourceEntry (55-55)
  • createProxyPromptEntry (62-62)
libs/sdk/src/common/interfaces/internal/registry.interface.ts (3)
  • ToolRegistryInterface (115-129)
  • ResourceRegistryInterface (131-146)
  • PromptRegistryInterface (148-160)
libs/sdk/src/remote/entries/proxy-tool.entry.ts (2)
  • ProxyToolEntry (108-339)
  • createProxyToolEntry (385-394)
libs/sdk/src/remote/entries/proxy-resource.entry.ts (2)
  • ProxyResourceEntry (85-292)
  • createProxyResourceEntry (346-355)
libs/sdk/src/remote/entries/proxy-prompt.entry.ts (2)
  • ProxyPromptEntry (82-237)
  • createProxyPromptEntry (278-287)
libs/sdk/src/remote/mcp-client.types.ts (3)
  • McpConnectRequest (221-236)
  • McpTransportType (71-71)
  • McpRemoteAuthConfig (135-157)
libs/sdk/src/resource/resource.registry.ts (2)
libs/sdk/src/scope/scope.instance.ts (1)
  • apps (325-327)
libs/sdk/src/app/instances/app.remote.instance.ts (1)
  • AppRemoteInstance (251-549)
libs/sdk/src/errors/remote.errors.ts (1)
libs/sdk/src/errors/mcp.error.ts (3)
  • InternalMcpError (133-146)
  • PublicMcpError (113-127)
  • MCP_ERROR_CODES (8-25)
⏰ 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: Build Libraries
  • GitHub Check: Lint & Format Checks
🔇 Additional comments (28)
libs/plugins/src/cache/cache.plugin.ts (5)

19-23: LGTM: Clear default bypass header.

The constant follows the documented x-frontmcp-* prefix pattern and is well-documented.


129-139: LGTM: Clear cacheability logic.

The method correctly prioritizes explicit metadata over configured patterns, providing a sensible fallback hierarchy.


170-175: LGTM: Clean TTL resolution.

The method provides a clear precedence: metadata TTL → configured default → hardcoded default (86400s).


177-249: LGTM: Properly addresses previous type safety concern.

The updated logic correctly:

  • Checks bypass headers before cache operations
  • Uses both fullName and name for flexible pattern matching
  • Guards metadata augmentation with isPlainObject check (lines 221-237), resolving the previous review's concern about spreading primitives

The fix appropriately returns primitives and arrays as-is when they cannot have _meta attached.


251-275: LGTM: Consistent bypass and pattern logic.

The write path correctly mirrors the read path with bypass checks and dual pattern matching for both fullName and name.

apps/e2e/demo-e2e-remote/e2e/remote.e2e.test.ts (4)

1-16: Well-structured E2E test suite for remote MCP orchestration.

The test file provides comprehensive coverage of remote MCP server functionality including connection, discovery, tool execution, resource access, prompts, caching, error handling, and concurrency.


75-85: Previous any type issue has been properly addressed.

The code now correctly uses unknown type with proper type narrowing via typeof and in checks, complying with the strict TypeScript guidelines.


238-261: Good error handling test coverage.

Tests properly verify error behavior for non-existent tools, missing required parameters, and invalid parameter types. The assertions correctly check for isError: true rather than expecting exceptions.


263-292: Comprehensive concurrency testing.

Tests cover both concurrent local tool calls and mixed local/remote operations, which is essential for validating the gateway's ability to handle parallel requests.

libs/sdk/src/resource/resource.types.ts (1)

24-44: Type change to ResourceEntry properly supports both local and remote resources.

The change from ResourceInstance to ResourceEntry for IndexedResource.instance correctly reflects that the registry can now hold both local ResourceInstance objects and remote ProxyResourceEntry objects. The inline comment clarifies this dual purpose.

libs/sdk/src/errors/mcp.error.ts (1)

8-25: New MCP error codes properly extend the error code surface.

The addition of UNAUTHORIZED (-32001) and FORBIDDEN (-32003) appropriately follows the JSON-RPC specification for server error codes (-32000 to -32099). These codes support the new remote authentication error handling paths.

libs/sdk/src/resource/resource.events.ts (1)

1-22: Event payload type correctly updated to ResourceEntry.

The change to ResourceEntry[] for the snapshot field aligns with the broader refactoring to support both local and remote resources. The changeScope property is correctly used per coding guidelines to avoid confusion with the Scope class.

libs/sdk/src/tool/tool.registry.ts (1)

131-140: Local app adoption path correctly preserved.

The fallback for local apps properly continues using the existing adoption flow through child ToolRegistry instances, maintaining backward compatibility.

libs/sdk/src/remote/mcp-client.service.ts (2)

514-525: Switch case properly uses block scope now.

The 'forward' case body is now wrapped in braces, addressing the previous Biome noSwitchDeclarations issue.


725-748: Good protection against overlapping capability refreshes.

The refreshInProgress Set prevents concurrent refresh operations for the same app, which could cause race conditions or redundant network calls.

libs/sdk/src/resource/resource.registry.ts (4)

132-166: Remote resource adoption properly implemented with type-safe approach.

The implementation correctly:

  1. Uses instanceof AppRemoteInstance for type-safe remote app detection
  2. Validates remote resource entries before processing
  3. Uses ResourceEntry type directly without unsafe casting
  4. Includes proper error handling and logging

This approach should serve as the model for the similar logic in ToolRegistry.


452-475: makeRow signature correctly updated to accept ResourceEntry.

The method now accepts ResourceEntry instead of ResourceInstance, eliminating the need for unsafe type casts when handling remote proxy entries. This is the correct fix that addresses the type safety concern from previous reviews.


500-521: providerIdOf updated to work with ResourceEntry base type.

The method now safely accesses metadata via the base entry interface and uses proper type narrowing with unknown and type guards. This is a good example of handling heterogeneous entry types safely.


225-243: Public API consistently uses ResourceEntry return types.

All public methods that expose resources now return ResourceEntry or arrays thereof, providing a consistent interface that works with both local ResourceInstance and remote ProxyResourceEntry objects.

Also applies to: 255-258, 263-274, 279-288, 296-298, 301-303, 345-345, 421-424, 428-442

libs/sdk/src/remote/mcp-client.types.ts (2)

6-17: LGTM: Proper use of MCP protocol types.

The imports correctly use strict MCP protocol types (CallToolResult, ReadResourceResult, GetPromptResult) from the MCP SDK, aligning with coding guidelines and learnings for MCP response types.

Based on learnings: MCP response types should use strict MCP protocol types.


135-157: LGTM: Well-structured authentication configuration.

The McpRemoteAuthConfig union type provides a clean discriminated union with proper typing for each auth mode. The mapper function in the 'mapped' mode correctly accepts AuthInfo | undefined and returns credentials, supporting both authenticated and unauthenticated gateway contexts.

libs/sdk/src/app/instances/app.remote.instance.ts (4)

39-42: LGTM: Proper typing replaces any type.

The new ScopeWithMcpClient interface provides proper typing for the scope parameter, addressing the previous review comment about avoiding any types.

Based on coding guidelines: "Use strict TypeScript mode with no any types without strong justification."


268-269: LGTM: Memory leak fixed with proper cleanup.

The capability change subscription is now properly stored in _unsubscribeCapability and cleaned up in the disconnect() method, preventing the memory leak identified in previous reviews.

Also applies to: 313-321, 380-383


410-426: LGTM: Proper typing for scope parameter.

The method now uses ScopeWithMcpClient instead of any, addressing the previous review comment about type safety.

Based on coding guidelines: Avoid any types without strong justification.


472-478: LGTM: Proper typing for remoteAuth parameter.

The method now uses RemoteAuthConfig (imported at line 14) instead of any, addressing the previous review comment.

Based on coding guidelines: Use strict TypeScript mode with no any types.

libs/sdk/src/errors/remote.errors.ts (3)

122-158: LGTM: Correct MCP authentication error codes.

The authentication error classes now use the proper MCP-specific error codes:

  • RemoteAuthError uses MCP_ERROR_CODES.UNAUTHORIZED (-32001) for missing credentials
  • RemoteAuthorizationError uses MCP_ERROR_CODES.FORBIDDEN (-32003) for invalid/insufficient credentials

This addresses the previous review comment and aligns with MCP JSON-RPC 2.0 authentication error conventions.

Based on learnings: Use specific error classes with MCP error codes instead of generic errors.


15-46: LGTM: Connection errors properly classified as internal.

Both RemoteConnectionError and RemoteDisconnectError correctly extend InternalMcpError, which is appropriate since connection failures are typically infrastructure/internal issues rather than client errors.


55-67: LGTM: Proper use of MCP error codes and HTTP status codes.

The error classes correctly map to appropriate MCP error codes:

  • RemoteTimeoutError: INTERNAL_ERROR (-32603) with 504 Gateway Timeout
  • RemoteToolNotFoundError: METHOD_NOT_FOUND (-32601) with 404
  • RemoteResourceNotFoundError: RESOURCE_NOT_FOUND (-32002) with 404
  • RemotePromptNotFoundError: METHOD_NOT_FOUND (-32601) with 404
  • Execution errors: INTERNAL_ERROR (-32603) with 500

These mappings align with MCP protocol conventions and provide appropriate HTTP-level semantics.

Based on learnings: Use specific error classes with MCP error codes.

Also applies to: 76-116, 167-225

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

Fix all issues with AI Agents 🤖
In @libs/sdk/src/app/instances/app.remote.instance.ts:
- Around line 402-409: reconnect() currently only re-establishes the client and
recreates proxies but does not re-subscribe to capability change notifications
after disconnect() unsubscribes; extract the capability subscription logic from
initialize() into a private helper (e.g., subscribeToCapabilityChanges or
restoreSubscriptions) and call that helper from both initialize() and
reconnect() (after discoverAndCreateProxies()) so capability change handlers are
re-registered when the instance reconnects.
♻️ Duplicate comments (1)
libs/sdk/src/prompt/prompt.registry.ts (1)

182-199: Fix unsafe type cast and Symbol handling in remote prompt adoption.

The issues flagged in the previous review remain:

  1. Unsafe cast (Line 192): remotePrompt as PromptInstance is incorrect. getPrompts() returns PromptEntry[], but makeRow expects PromptInstance. Unlike resource.registry.ts which updated makeRow to accept ResourceEntry, this file's makeRow signature (line 410-415) still requires PromptInstance.

  2. Symbol uniqueness (Line 192): Symbol(remotePrompt.name) creates a new unique symbol on each call. If discoverAndCreateProxies() is called during capability refresh, prompts will be re-added with different symbols, creating duplicates since localRows is never cleared for remote prompts.

🔎 Proposed fix

Update makeRow signature to accept PromptEntry (aligning with resource.registry.ts pattern):

  private makeRow(
    token: Token,
-   instance: PromptInstance,
+   instance: PromptEntry,
    lineage: EntryLineage,
    source: PromptRegistry,
  ): IndexedPrompt {

And remove the unsafe cast:

-         const row = this.makeRow(Symbol(remotePrompt.name), remotePrompt as PromptInstance, prepend, this);
+         const row = this.makeRow(Symbol(remotePrompt.name), remotePrompt, prepend, this);

Additionally, consider clearing remote prompt rows before re-adoption or using stable tokens via Symbol.for().

🧹 Nitpick comments (2)
libs/sdk/src/app/instances/app.remote.instance.ts (1)

117-126: Avoid unsafe double cast through unknown.

The cast snapshot as unknown as readonly ToolInstance[] bypasses type checking. ProxyToolEntry extends ToolEntry, not ToolInstance. While they may be structurally compatible at runtime, this pattern masks potential type mismatches.

Consider updating ToolChangeEvent.snapshot type to use ToolEntry[] (the common base), or document why this cast is safe if ProxyToolEntry implements all required ToolInstance behavior.

🔎 Alternative approach

If ToolChangeEvent must use ToolInstance, consider adding a type assertion helper that validates compatibility:

private createEvent(kind: ToolChangeKind, snapshot: ProxyToolEntry[]): ToolChangeEvent {
  this.version++;
  return {
    kind,
    changeScope: 'global' as ToolChangeScope,
    version: this.version,
    // ProxyToolEntry implements ToolEntry which is compatible with snapshot expectations
    snapshot: snapshot as readonly ToolEntry[],
  };
}

Or update the event type definition to accept ToolEntry[] for broader compatibility.

libs/sdk/src/resource/resource.registry.ts (1)

419-429: Avoid any type in disambiguate function.

The pool parameter is typed as Map<string, any> but should use ResourceEntry to match the actual usage. Per coding guidelines, any types should be avoided.

🔎 Proposed fix
-   function disambiguate(candidate: string, pool: Map<string, any>, cfg: Required<ResourceExportOptions>): string {
+   function disambiguate(candidate: string, pool: Map<string, ResourceEntry>, cfg: Required<ResourceExportOptions>): string {
📜 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 210f720 and e8f1780.

📒 Files selected for processing (5)
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/common/entries/app.entry.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/tool/tool.registry.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • libs/sdk/src/tool/tool.registry.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/common/entries/app.entry.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/resource/resource.registry.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/entries/app.entry.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/resource/resource.registry.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/entries/app.entry.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/resource/resource.registry.ts
🧠 Learnings (6)
📚 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/prompt/prompt.registry.ts
  • libs/sdk/src/app/instances/app.remote.instance.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/prompt/prompt.registry.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/app/instances/app.remote.instance.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 `changeScope` property instead of `scope` in change events to avoid confusion with Scope class

Applied to files:

  • libs/sdk/src/app/instances/app.remote.instance.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/app/instances/app.remote.instance.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/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/app/instances/app.remote.instance.ts
🧬 Code graph analysis (3)
libs/sdk/src/common/entries/app.entry.ts (3)
libs/sdk/src/common/metadata/app.metadata.ts (1)
  • AppMetadata (350-350)
libs/sdk/src/common/records/app.record.ts (1)
  • AppRecord (23-23)
libs/sdk/src/common/interfaces/app.interface.ts (1)
  • AppInterface (6-6)
libs/sdk/src/app/instances/app.remote.instance.ts (9)
libs/sdk/src/remote/mcp-client.service.ts (1)
  • McpClientService (72-770)
libs/sdk/src/remote/index.ts (8)
  • McpClientService (11-11)
  • ProxyToolEntry (46-46)
  • ProxyResourceEntry (53-53)
  • ProxyPromptEntry (60-60)
  • McpConnectRequest (34-34)
  • McpTransportType (20-20)
  • McpRemoteAuthConfig (27-27)
  • createProxyToolEntry (48-48)
libs/sdk/src/common/interfaces/internal/registry.interface.ts (3)
  • ToolRegistryInterface (115-129)
  • ResourceRegistryInterface (131-146)
  • PromptRegistryInterface (148-160)
libs/sdk/src/remote/entries/proxy-tool.entry.ts (2)
  • ProxyToolEntry (108-339)
  • createProxyToolEntry (385-394)
libs/sdk/src/remote/entries/proxy-resource.entry.ts (1)
  • ProxyResourceEntry (85-292)
libs/sdk/src/remote/entries/proxy-prompt.entry.ts (1)
  • ProxyPromptEntry (82-237)
libs/sdk/src/common/metadata/app.metadata.ts (2)
  • RemoteAppMetadata (228-302)
  • RemoteAuthConfig (193-223)
libs/sdk/src/provider/provider.registry.ts (1)
  • ProviderRegistry (32-983)
libs/sdk/src/remote/mcp-client.types.ts (3)
  • McpConnectRequest (221-236)
  • McpTransportType (71-71)
  • McpRemoteAuthConfig (135-157)
libs/sdk/src/resource/resource.registry.ts (2)
libs/sdk/src/scope/scope.instance.ts (1)
  • apps (325-327)
libs/sdk/src/resource/resource.types.ts (1)
  • ResourceExportOptions (50-55)
⏰ 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: Build Libraries
  • GitHub Check: Lint & Format Checks
🔇 Additional comments (11)
libs/sdk/src/common/entries/app.entry.ts (1)

17-25: LGTM! Clean virtual method pattern for remote app detection.

The isRemote getter with default false and clear documentation enables polymorphic adoption strategies in registries. This aligns well with the AppRemoteInstance override returning true.

libs/sdk/src/prompt/prompt.registry.ts (2)

118-131: LGTM! Clear branching for remote vs local app adoption.

The conditional logic correctly distinguishes between remote and local apps, delegating to appropriate adoption methods.


201-212: LGTM! Correct delegation to child registry adoption.

The method properly filters for app-owned registries and delegates to the existing adoptFromChild mechanism.

libs/sdk/src/app/instances/app.remote.instance.ts (5)

35-42: LGTM! Proper typing replaces previous any usage.

The ScopeWithMcpClient interface provides type safety for the scope parameter, addressing the previous review feedback about avoiding any types. Based on coding guidelines.


132-217: LGTM! Clean proxy registry implementations.

Both RemoteResourceRegistry and RemotePromptRegistry correctly implement their respective interfaces with proper storage and lookup mechanisms.


305-339: LGTM! Proper initialization with subscription cleanup.

The initialization correctly:

  • Builds connection request
  • Connects to remote server
  • Discovers and creates proxies
  • Stores the capability change subscription for later cleanup (addressing previous memory leak concern)

415-457: LGTM! Proper typing and request construction.

The getOrCreateMcpClientService method now uses proper typing via ScopeWithMcpClient, and buildConnectRequest correctly maps metadata to the connection request format.


488-556: LGTM! Comprehensive proxy discovery and creation.

The method properly:

  • Clears existing proxies before refresh
  • Discovers capabilities from remote server
  • Creates proxy entries for tools, resources, templates, and prompts
  • Awaits each proxy's ready state before adding to registries
libs/sdk/src/resource/resource.registry.ts (3)

128-141: LGTM! Consistent remote vs local app adoption pattern.

The initialization branching mirrors the pattern in prompt.registry.ts, maintaining consistency across registries.


194-217: Good implementation with proper typing.

The method correctly:

  • Validates remote resources before adding (lines 206-209)
  • Uses ResourceEntry type in makeRow (addressing previous review feedback)
  • Handles errors gracefully with logging

Note: Symbol(remoteResource.name) creates unique symbols per call, which could theoretically cause duplicates if resources are refreshed. However, since discoverAndCreateProxies in AppRemoteInstance clears the remote registries before re-populating, this is mitigated at the source.


464-487: LGTM! Correct generalization to ResourceEntry.

The makeRow signature now accepts ResourceEntry instead of ResourceInstance, which:

  • Addresses the previous review feedback about unsafe casts
  • Properly supports both ResourceInstance and ProxyResourceEntry inputs
  • Aligns with the broader API surface updates throughout the file

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: 0

♻️ Duplicate comments (2)
libs/sdk/src/app/app.utils.ts (2)

22-37: RFC 3986 URI validation properly implemented.

The type guard now validates URLs using isValidMcpUri at line 34, addressing the previous review comment about RFC 3986 compliance. The validation occurs both here in the type guard and in the schema (frontMcpRemoteAppMetadataSchema), providing defense in depth.


50-65: Type safety improvements addressed.

The redundant cast was removed (line 53 now uses type guard narrowing directly), and null as any was changed to null as unknown as AppEntry (line 62). The placeholder pattern is well-documented with comments explaining that actual AppRemoteInstance is created during AppRegistry.initialize.

🧹 Nitpick comments (2)
libs/sdk/src/common/metadata/app.metadata.ts (2)

313-317: Add non-empty validation for credential value.

The value field in remoteStaticCredentialsSchema accepts empty strings, which would cause silent auth failures at runtime. Consider adding .min(1) to ensure credentials are non-empty.

🔎 Suggested fix
 const remoteStaticCredentialsSchema = z.object({
   type: z.enum(['bearer', 'basic', 'apiKey']),
-  value: z.string(),
+  value: z.string().min(1, 'Credential value cannot be empty'),
   headerName: z.string().optional(),
 });

305-311: Consider adding non-negative constraints for numeric options.

Numeric transport options like timeout, retryAttempts, and retryDelayMs currently accept negative values which would be invalid at runtime.

🔎 Suggested fix
 const remoteTransportOptionsSchema = z.object({
-  timeout: z.number().optional(),
-  retryAttempts: z.number().optional(),
-  retryDelayMs: z.number().optional(),
+  timeout: z.number().nonnegative().optional(),
+  retryAttempts: z.number().int().nonnegative().optional(),
+  retryDelayMs: z.number().nonnegative().optional(),
   fallbackToSSE: z.boolean().optional(),
   headers: z.record(z.string(), z.string()).optional(),
 });
📜 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 e8f1780 and aa1d7a6.

📒 Files selected for processing (2)
  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/common/metadata/app.metadata.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/app/app.utils.ts
  • libs/sdk/src/common/metadata/app.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/app/app.utils.ts
  • libs/sdk/src/common/metadata/app.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/app/app.utils.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
🧠 Learnings (12)
📚 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 : Validate URIs per RFC 3986 at metadata level using proper validation

Applied to files:

  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/common/metadata/app.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/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/app/app.utils.ts
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)

Applied to files:

  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/common/metadata/app.metadata.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/app/app.utils.ts
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/{src}/**/*.{ts,tsx} : Do not use `any` type without justification in TypeScript code

Applied to files:

  • libs/sdk/src/app/app.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 **/*.ts : Use strict TypeScript mode with no `any` types without strong justification - use `unknown` instead for generic type defaults

Applied to files:

  • libs/sdk/src/app/app.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 : MCP response types should use strict MCP protocol types (GetPromptResult, ReadResourceResult) instead of `unknown`

Applied to files:

  • libs/sdk/src/app/app.utils.ts
  • libs/sdk/src/common/metadata/app.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 **/*.ts : Avoid non-null assertions (`!`) - use proper error handling and throw specific errors instead

Applied to files:

  • libs/sdk/src/app/app.utils.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} : Avoid using any type without justification; all props, return types, and generics must be properly typed

Applied to files:

  • libs/sdk/src/app/app.utils.ts
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/{src}/**/*.{ts,tsx} : Always validate component options before use

Applied to files:

  • libs/sdk/src/app/app.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/app/app.utils.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/{validation,**}/**/*.{ts,tsx} : Use `.strict()` on all Zod schemas for validation

Applied to files:

  • libs/sdk/src/app/app.utils.ts
🧬 Code graph analysis (1)
libs/sdk/src/app/app.utils.ts (1)
libs/sdk/src/common/metadata/app.metadata.ts (1)
  • RemoteAppMetadata (229-303)
⏰ 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: Build Libraries
  • GitHub Check: Lint & Format Checks
🔇 Additional comments (4)
libs/sdk/src/common/metadata/app.metadata.ts (4)

142-175: Well-structured transport options interface.

The RemoteTransportOptions interface is well-documented with sensible defaults noted in JSDoc. The separation of transport-specific options from auth configuration follows good design principles.


319-332: Good use of discriminated union for auth configuration.

The remoteAuthConfigSchema correctly uses z.discriminatedUnion on the mode field, which enables proper type narrowing and provides clear error messages when validation fails.


338-346: **Schema correctly validates URL format per RFC 3986.**The use of .refine(isValidMcpUri, { message: ... }) at line 339-341 correctly implements RFC 3986 URI validation at the schema level. "Refinements are a way to perform custom validation that Zod doesn't provide a native API for" and "Refinement functions should never throw. Instead they should return a falsy value to signal failure." The isValidMcpUri function follows this pattern appropriately.


226-303: Well-documented remote app metadata interface.

The expanded RemoteAppMetadata interface is comprehensive with clear JSDoc documentation for each transport type (url, worker, npm, esm) and their expected URL formats. The new fields (namespace, transportOptions, remoteAuth, refreshInterval) are well-designed to support the remote MCP orchestration feature.

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: 10

Fix all issues with AI Agents 🤖
In @libs/sdk/src/app/instances/app.remote.instance.ts:
- Around line 130-132: The code casts this.metadata to any to access cacheTTL
when initializing capabilityCache; instead extend the RemoteAppMetadata type to
include an optional cacheTTL?: number and update the metadata type import so you
can safely read (this.metadata).cacheTTL, or if you cannot change the type, add
a type guard that narrows this.metadata to a typed interface that includes
cacheTTL before using it in the CapabilityCache constructor (reference the
capabilityCache initialization and RemoteAppMetadata type).
- Around line 393-395: The code casts this.metadata to any to read cacheTTL
before calling this.capabilityCache.set; remove the unsafe any cast by adding
cacheTTL to the instance metadata type and then read it with proper typing
(e.g., const cacheTTL = this.metadata.cacheTTL ?? 60000) so
capabilityCache.set(this.id, capabilities, cacheTTL) uses a correctly typed
value; update the metadata interface where it’s defined (same location fixed for
the constructor issue) and remove the any cast here.

In @libs/sdk/src/prompt/prompt.registry.ts:
- Around line 182-199: The adoptPromptsFromRemoteApp method is unsafely casting
remotePrompt to PromptInstance and creating non-stable Symbols; change makeRow
to accept PromptEntry (not PromptInstance) and pass the prompt without casting,
and replace Symbol(remotePrompt.name) with a stable identifier (e.g.,
Symbol.for(remotePrompt.name)) or clear/cleanup stale entries in localRows
before re-adoption to avoid duplicate prompts; update references in makeRow,
localRows handling, and any callers to reflect the PromptEntry parameter type
change.

In @libs/sdk/src/remote-mcp/cache/capability-cache.ts:
- Around line 73-80: The set method in capability-cache currently overwrites the
capabilities' original fetchedAt with a new Date(); instead, preserve the
existing fetchedAt from the McpRemoteCapabilities passed in. Update the
set(appId: string, capabilities: McpRemoteCapabilities, ttl?: number)
implementation to compute effectiveTTL as before, but when calling
this.cache.set use capabilities.fetchedAt (or fall back to
capabilities.fetchedAt if nullable) for the stored fetchedAt field rather than
creating new Date(); keep expiresAt computed from Date.now() + effectiveTTL and
leave capabilities unchanged.

In @libs/sdk/src/remote-mcp/mcp-client.service.ts:
- Around line 469-538: The callTool operation lacks a per-request timeout:
create an AbortController at the start of the operation (use a configurable
timeout like this.options.requestTimeoutMs or add one), set a timer to call
controller.abort() when the timeout elapses, pass controller.signal to
connection.client.callTool instead of the current undefined second arg, clear
the timer on success, and ensure abort errors are mapped/handled (wrap as
RemoteToolExecutionError or treat as transient for withRetry and circuit/health
handling) so hung remote calls get cancelled deterministically.
- Around line 482-485: The methods callTool, readResource, and getPrompt accept
an authContext parameter but never forward it to the underlying MCP client;
update the calls (e.g., connection.client.callTool,
connection.client.readResource, connection.client.getPrompt) to pass authContext
into the client API (likely as a header or options argument) so the auth
information is propagated, or if the MCP client API does not support
authContext, remove the unused authContext parameter from those method
signatures and callers to avoid confusion; locate the methods in
mcp-client.service.ts (symbols: callTool, readResource, getPrompt,
connection.client.*) and either add the authContext argument to the client calls
or remove the parameter accordingly.
- Around line 734-748: The current try/catch around new
StreamableHTTPClientTransport inside the 'http' case is ineffective because the
constructor is synchronous and network errors happen on client.connect(); change
the flow so you always instantiate StreamableHTTPClientTransport(new URL(url))
(no constructor try/catch), then call transport.connect() inside a try/catch; on
connection failure and if httpOptions?.fallbackToSSE is true, log the fallback,
instantiate SSEClientTransport(new URL(url)), call its connect(), and return the
connected SSE transport; if fallback is disabled rethrow the original connection
error. Ensure references to StreamableHTTPClientTransport, SSEClientTransport,
and connect() are updated accordingly.

In @libs/sdk/src/remote-mcp/resilience/retry.ts:
- Around line 96-99: Remove the non-null assertion on lastError and make the
function type-safe: validate that maxAttempts is >= 1 at the start (throw a
RangeError if not) or ensure lastError is defined before throwing by replacing
"throw lastError!" with "throw lastError ?? new Error('No error captured during
retry')" (and keep the loop/assignment logic in the retry function and
references to lastError intact).

In @libs/sdk/src/tool/tool.registry.ts:
- Around line 166-187: adoptToolsFromRemoteApp currently casts ProxyToolEntry to
ToolInstance and generates transient symbols with Symbol(remoteTool.name);
instead widen the types so makeRow and IndexedTool.instance accept the base
ToolEntry (not only ToolInstance) and stop the unsafe cast (pass remoteTool as
ToolEntry), and replace Symbol(remoteTool.name) with a stable symbol lookup
(e.g., maintain a Map<string, symbol> on the registry/this to getOrCreate a
symbol for remoteTool.name) so repeated refreshes reuse the same symbol; update
signatures of makeRow and IndexedTool.instance to accept ToolEntry and propagate
that type change through creation of EntryLineage rows.
🧹 Nitpick comments (13)
libs/sdk/src/tool/tool.utils.ts (1)

80-87: LGTM - formatting simplification looks good.

The single-line case statement is cleaner and the logic is preserved.

One optional consideration: adding an exhaustive check via a default case could guard against future ToolKind values slipping through unhandled at runtime. This is a minor defensive coding suggestion.

🔎 Optional: Add exhaustive check
 export function toolDiscoveryDeps(rec: ToolRecord): Token[] {
   switch (rec.kind) {
     case ToolKind.FUNCTION:
       return depsOfFunc(rec.provide, 'discovery');
     case ToolKind.CLASS_TOKEN:
       return depsOfClass(rec.provide, 'discovery');
+    default: {
+      const _exhaustive: never = rec.kind;
+      throw new Error(`Unhandled ToolKind: ${_exhaustive}`);
+    }
   }
 }
libs/sdk/src/prompt/prompt.registry.ts (1)

492-525: Consider using a type guard instead of casting with manual validation.

The validation logic (lines 497-505) manually checks for required properties before the cast. This pattern works but could be improved with a type guard function for better type safety and reusability:

function isValidPromptInstance(entry: PromptEntry): entry is PromptInstance {
  const inst = entry as PromptInstance;
  return !!inst.record?.provide && !!inst.record?.kind && !!inst.record?.metadata && typeof inst.name === 'string';
}

Also, as per coding guidelines, consider using specific error classes instead of generic Error for the validation failures.

libs/sdk/src/tool/tool.registry.ts (1)

457-471: Validation logic is sound; consider type guard for consistency.

The validation at lines 463-471 properly checks required properties before use. The documentation accurately describes the scope binding behavior.

For consistency with TypeScript patterns and potential reuse, consider extracting the validation into a type guard:

function isValidToolInstance(entry: ToolEntry): entry is ToolInstance {
  const inst = entry as ToolInstance;
  return !!inst.record?.provide && !!inst.record?.kind && 
         !!inst.record?.metadata && typeof inst.name === 'string';
}

This would allow the cast to be type-safe after the guard check.

libs/sdk/src/remote-mcp/resilience/retry.ts (1)

104-143: Consider enhancing error detection with structured properties.

The string-based error detection is practical but fragile—error messages vary across environments and can change. Consider supplementing with structured property checks:

export function isTransientError(error: Error): boolean {
  // Check for Node.js error codes
  const code = (error as NodeJS.ErrnoException).code;
  if (code && ['ECONNREFUSED', 'ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND'].includes(code)) {
    return true;
  }
  
  // Check for HTTP status codes if available
  const status = (error as { status?: number }).status;
  if (status && (status === 429 || (status >= 500 && status <= 599))) {
    return true;
  }
  
  // Fall back to message matching
  const message = error.message?.toLowerCase() || '';
  // ... existing checks
}

This would be more reliable for Node.js environments while maintaining the message-based fallback.

libs/sdk/src/remote-mcp/cache/capability-cache.ts (1)

143-154: Consider adding a cleanup method for expired entries.

The cache uses lazy deletion (only cleaning up on get()), but getCachedAppIds() and getStats() iterate over entries without removing expired ones. For long-running servers with many transient remote apps, expired entries could accumulate.

Consider adding an explicit cleanup method:

/**
 * Remove all expired entries from the cache.
 * Call periodically or before getStats() for accurate counts.
 */
purgeExpired(): number {
  const now = Date.now();
  let purged = 0;
  for (const [appId, cached] of this.cache.entries()) {
    if (cached.expiresAt <= now) {
      this.cache.delete(appId);
      purged++;
    }
  }
  return purged;
}
libs/sdk/src/remote-mcp/factories/record-builders.ts (1)

56-69: Consider adding remote annotations to resources and prompts for consistency.

Tool records include helpful remote-tracking annotations (frontmcp:remote, frontmcp:remoteAppId, frontmcp:remoteTool), but buildRemoteResourceRecord, buildRemoteResourceTemplateRecord, and buildRemotePromptRecord don't add similar annotations. This inconsistency may complicate debugging and tracing of remote entity origins.

🔎 Example annotation addition for resources
   const metadata: ResourceMetadata = {
     name: resourceName,
     description: remoteResource.description || `Remote resource: ${remoteResource.name}`,
     uri: remoteResource.uri,
     mimeType: remoteResource.mimeType,
+    annotations: {
+      'frontmcp:remote': true,
+      'frontmcp:remoteAppId': remoteAppId,
+      'frontmcp:remoteResource': remoteResource.name,
+    },
   };
libs/sdk/src/remote-mcp/resilience/health-check.ts (2)

106-113: Timeout timer continues running after race resolves.

The setTimeout in the timeout promise isn't cleared when checkFn() wins the race. While this doesn't cause functional issues, it leaves a dangling timer until it fires. Consider using AbortController or clearing the timeout.

🔎 Proposed fix using cleanup
   try {
-    // Create a timeout promise
-    const timeoutPromise = new Promise<never>((_, reject) => {
-      setTimeout(() => reject(new Error('Health check timeout')), this.options.timeoutMs);
-    });
-
-    // Race check against timeout
-    await Promise.race([this.checkFn(), timeoutPromise]);
+    // Create a timeout promise with cleanup
+    let timeoutId: ReturnType<typeof setTimeout>;
+    const timeoutPromise = new Promise<never>((_, reject) => {
+      timeoutId = setTimeout(() => reject(new Error('Health check timeout')), this.options.timeoutMs);
+    });
+
+    try {
+      // Race check against timeout
+      await Promise.race([this.checkFn(), timeoutPromise]);
+    } finally {
+      clearTimeout(timeoutId!);
+    }

192-217: markHealthy() and markUnhealthy() don't update lastResult.

After calling these methods, getLastResult() returns stale data from the previous check() call. This may confuse consumers who expect lastResult to reflect the current status. Consider updating lastResult in these methods.

🔎 Proposed fix for markHealthy
   markHealthy(): void {
     const previousStatus = this.status;
     this.status = 'healthy';
     this.consecutiveSuccesses++;
     this.consecutiveFailures = 0;

+    this.lastResult = {
+      status: this.status,
+      lastChecked: new Date(),
+      consecutiveFailures: this.consecutiveFailures,
+      consecutiveSuccesses: this.consecutiveSuccesses,
+    };
+
     if (previousStatus !== 'healthy') {
       this.options.onStatusChange(this.appId, 'healthy', previousStatus);
     }
   }
libs/sdk/src/remote-mcp/resilience/circuit-breaker.ts (1)

233-240: getBreaker() ignores options if breaker already exists.

When getBreaker(appId, options) is called for an existing breaker, the provided options are silently ignored. This could lead to confusion if callers expect their options to be applied. Consider either updating the existing breaker's options or logging a warning.

🔎 Proposed warning addition
   getBreaker(appId: string, options?: CircuitBreakerOptions): CircuitBreaker {
     let breaker = this.breakers.get(appId);
-    if (!breaker) {
+    if (breaker) {
+      // Existing breaker - options parameter is ignored
+      return breaker;
+    } else {
       breaker = new CircuitBreaker(appId, { ...this.defaultOptions, ...options });
       this.breakers.set(appId, breaker);
+      return breaker;
     }
-    return breaker;
   }
libs/sdk/src/remote-mcp/mcp-client.service.ts (2)

150-155: CircuitBreakerManager onStateChange callback missing appId.

The callback receives (state, prev) but in a multi-app scenario, you need to know which app's circuit breaker changed state. The CircuitBreakerOptions.onStateChange signature takes (state, previousState) without appId, making this log message incomplete for debugging.

🔎 Consider wrapping with per-app callbacks

The issue is in the CircuitBreakerOptions interface which doesn't include appId. Either:

  1. Update CircuitBreakerOptions.onStateChange signature to include appId
  2. Or set per-breaker callbacks when creating them via getBreaker()

543-567: readResource() and getPrompt() lack resilience features.

Unlike callTool(), these methods don't have circuit breaker checks, retry logic, or health status updates. For consistency and reliability, consider applying the same resilience patterns to all proxy execution methods.

libs/sdk/src/app/instances/app.remote.instance.ts (1)

401-454: Consider parallel initialization for better performance.

The sequential await instance.ready calls for each tool, resource, and prompt could be slow with many capabilities. Consider batching with Promise.all if the instances don't have interdependencies.

🔎 Suggested optimization
-    // Register tools using standard ToolInstance with dynamic context class
-    for (const remoteTool of capabilities.tools) {
-      const toolInstance = createRemoteToolInstance(/* ... */);
-      await toolInstance.ready;
-      this._tools.registerToolInstance(toolInstance);
-    }
+    // Register tools in parallel
+    const toolInstances = capabilities.tools.map(remoteTool => 
+      createRemoteToolInstance(remoteTool, this.mcpClient, this.id, this.scopeProviders, this.appOwner, namespace)
+    );
+    await Promise.all(toolInstances.map(ti => ti.ready));
+    toolInstances.forEach(ti => this._tools.registerToolInstance(ti));

Apply similarly for resources, templates, and prompts.

libs/sdk/src/resource/resource.registry.ts (1)

569-602: Good validation but consider type refinement.

The validation is thorough, checking for required properties before registration. However, the cast resource as ResourceInstance (line 571) assumes the parameter is always a ResourceInstance. Since the parameter type is ResourceEntry, this could cause issues if a ProxyResourceEntry or other ResourceEntry subtype is passed.

Consider either:

  1. Updating the parameter type to be more specific if only ResourceInstance is expected
  2. Using type guards to safely access record properties
🔎 Suggested type guard approach
   registerResourceInstance(resource: ResourceEntry): void {
-    // Validate that we have a proper instance with required properties
-    const instance = resource as ResourceInstance;
-
-    // Check for required properties that make it a valid registerable instance
-    if (!instance.record || !instance.record.provide) {
+    // Validate using type guards
+    if (!('record' in resource) || !resource.record || !('provide' in resource.record) || !resource.record.provide) {
       throw new Error('Resource instance is missing required record.provide property');
     }
+    const instance = resource as ResourceInstance;
📜 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 aa1d7a6 and 1feb2c0.

📒 Files selected for processing (23)
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/app/instances/index.ts
  • libs/sdk/src/index.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/prompt/prompt.utils.ts
  • libs/sdk/src/remote-mcp/cache/capability-cache.ts
  • libs/sdk/src/remote-mcp/cache/index.ts
  • libs/sdk/src/remote-mcp/factories/context-factories.ts
  • libs/sdk/src/remote-mcp/factories/index.ts
  • libs/sdk/src/remote-mcp/factories/instance-factories.ts
  • libs/sdk/src/remote-mcp/factories/record-builders.ts
  • libs/sdk/src/remote-mcp/index.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
  • libs/sdk/src/remote-mcp/mcp-client.types.ts
  • libs/sdk/src/remote-mcp/resilience/circuit-breaker.ts
  • libs/sdk/src/remote-mcp/resilience/health-check.ts
  • libs/sdk/src/remote-mcp/resilience/index.ts
  • libs/sdk/src/remote-mcp/resilience/retry.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/resource/resource.utils.ts
  • libs/sdk/src/tool/tool.instance.ts
  • libs/sdk/src/tool/tool.registry.ts
  • libs/sdk/src/tool/tool.utils.ts
💤 Files with no reviewable changes (2)
  • libs/sdk/src/prompt/prompt.utils.ts
  • libs/sdk/src/resource/resource.utils.ts
✅ Files skipped from review due to trivial changes (1)
  • libs/sdk/src/app/instances/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • libs/sdk/src/index.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/tool/tool.utils.ts
  • libs/sdk/src/remote-mcp/cache/index.ts
  • libs/sdk/src/tool/tool.instance.ts
  • libs/sdk/src/remote-mcp/resilience/health-check.ts
  • libs/sdk/src/remote-mcp/resilience/retry.ts
  • libs/sdk/src/remote-mcp/factories/instance-factories.ts
  • libs/sdk/src/remote-mcp/resilience/circuit-breaker.ts
  • libs/sdk/src/remote-mcp/cache/capability-cache.ts
  • libs/sdk/src/tool/tool.registry.ts
  • libs/sdk/src/remote-mcp/factories/context-factories.ts
  • libs/sdk/src/remote-mcp/resilience/index.ts
  • libs/sdk/src/remote-mcp/index.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/remote-mcp/mcp-client.types.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
  • libs/sdk/src/remote-mcp/factories/index.ts
  • libs/sdk/src/remote-mcp/factories/record-builders.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/resource/resource.registry.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/tool/tool.utils.ts
  • libs/sdk/src/remote-mcp/cache/index.ts
  • libs/sdk/src/tool/tool.instance.ts
  • libs/sdk/src/remote-mcp/resilience/health-check.ts
  • libs/sdk/src/remote-mcp/resilience/retry.ts
  • libs/sdk/src/remote-mcp/factories/instance-factories.ts
  • libs/sdk/src/remote-mcp/resilience/circuit-breaker.ts
  • libs/sdk/src/remote-mcp/cache/capability-cache.ts
  • libs/sdk/src/tool/tool.registry.ts
  • libs/sdk/src/remote-mcp/factories/context-factories.ts
  • libs/sdk/src/remote-mcp/resilience/index.ts
  • libs/sdk/src/remote-mcp/index.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/remote-mcp/mcp-client.types.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
  • libs/sdk/src/remote-mcp/factories/index.ts
  • libs/sdk/src/remote-mcp/factories/record-builders.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/resource/resource.registry.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/tool/tool.utils.ts
  • libs/sdk/src/remote-mcp/cache/index.ts
  • libs/sdk/src/tool/tool.instance.ts
  • libs/sdk/src/remote-mcp/resilience/health-check.ts
  • libs/sdk/src/remote-mcp/resilience/retry.ts
  • libs/sdk/src/remote-mcp/factories/instance-factories.ts
  • libs/sdk/src/remote-mcp/resilience/circuit-breaker.ts
  • libs/sdk/src/remote-mcp/cache/capability-cache.ts
  • libs/sdk/src/tool/tool.registry.ts
  • libs/sdk/src/remote-mcp/factories/context-factories.ts
  • libs/sdk/src/remote-mcp/resilience/index.ts
  • libs/sdk/src/remote-mcp/index.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/remote-mcp/mcp-client.types.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
  • libs/sdk/src/remote-mcp/factories/index.ts
  • libs/sdk/src/remote-mcp/factories/record-builders.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/resource/resource.registry.ts
libs/sdk/src/**/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Export public API through barrel files - export users' needed items, no legacy exports with different names

Files:

  • libs/sdk/src/remote-mcp/cache/index.ts
  • libs/sdk/src/remote-mcp/resilience/index.ts
  • libs/sdk/src/remote-mcp/index.ts
  • libs/sdk/src/remote-mcp/factories/index.ts
🧠 Learnings (10)
📓 Common learnings
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
📚 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/tool/tool.utils.ts
  • libs/sdk/src/tool/tool.registry.ts
  • libs/sdk/src/app/instances/app.remote.instance.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/remote-mcp/cache/index.ts
  • libs/sdk/src/remote-mcp/resilience/index.ts
  • libs/sdk/src/remote-mcp/index.ts
  • libs/sdk/src/remote-mcp/factories/index.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/remote-mcp/cache/index.ts
  • libs/sdk/src/remote-mcp/cache/capability-cache.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)

Applied to files:

  • libs/sdk/src/remote-mcp/cache/index.ts
  • libs/sdk/src/remote-mcp/index.ts
  • libs/sdk/src/remote-mcp/factories/index.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/remote-mcp/cache/index.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/remote-mcp/factories/index.ts
  • libs/sdk/src/remote-mcp/factories/record-builders.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/remote-mcp/resilience/health-check.ts
  • libs/sdk/src/remote-mcp/resilience/retry.ts
  • libs/sdk/src/remote-mcp/factories/instance-factories.ts
  • libs/sdk/src/remote-mcp/resilience/circuit-breaker.ts
  • libs/sdk/src/remote-mcp/factories/context-factories.ts
  • libs/sdk/src/remote-mcp/index.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/remote-mcp/mcp-client.types.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
  • libs/sdk/src/remote-mcp/factories/record-builders.ts
  • libs/sdk/src/app/instances/app.remote.instance.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/remote-mcp/resilience/health-check.ts
  • libs/sdk/src/remote-mcp/resilience/retry.ts
  • libs/sdk/src/remote-mcp/resilience/circuit-breaker.ts
  • libs/sdk/src/remote-mcp/factories/context-factories.ts
  • libs/sdk/src/remote-mcp/resilience/index.ts
  • libs/sdk/src/remote-mcp/index.ts
  • libs/sdk/src/remote-mcp/mcp-client.types.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
  • libs/sdk/src/app/instances/app.remote.instance.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/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/tool/tool.registry.ts
  • libs/sdk/src/remote-mcp/mcp-client.types.ts
  • libs/sdk/src/app/instances/app.remote.instance.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/remote-mcp/index.ts
  • libs/sdk/src/remote-mcp/mcp-client.types.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
🧬 Code graph analysis (7)
libs/sdk/src/remote-mcp/resilience/health-check.ts (2)
libs/sdk/src/remote-mcp/index.ts (5)
  • HealthStatus (64-64)
  • HealthCheckResult (65-65)
  • HealthCheckOptions (66-66)
  • HealthChecker (62-62)
  • HealthCheckManager (63-63)
libs/sdk/src/remote-mcp/resilience/index.ts (5)
  • HealthStatus (19-19)
  • HealthCheckResult (20-20)
  • HealthCheckOptions (21-21)
  • HealthChecker (17-17)
  • HealthCheckManager (18-18)
libs/sdk/src/remote-mcp/resilience/retry.ts (3)
libs/sdk/src/remote-mcp/index.ts (5)
  • RetryOptions (54-54)
  • withRetry (50-50)
  • isTransientError (51-51)
  • isConnectionError (52-52)
  • isAuthError (53-53)
libs/sdk/src/remote-mcp/resilience/index.ts (5)
  • RetryOptions (6-6)
  • withRetry (6-6)
  • isTransientError (6-6)
  • isConnectionError (6-6)
  • isAuthError (6-6)
libs/sdk/src/agent/adapters/base.adapter.ts (1)
  • sleep (196-198)
libs/sdk/src/remote-mcp/factories/instance-factories.ts (2)
libs/sdk/src/tool/tool.instance.ts (1)
  • ToolInstance (37-163)
libs/sdk/src/remote-mcp/factories/record-builders.ts (4)
  • buildRemoteToolRecord (46-77)
  • buildRemoteResourceRecord (87-110)
  • buildRemoteResourceTemplateRecord (120-143)
  • buildRemotePromptRecord (153-180)
libs/sdk/src/remote-mcp/cache/capability-cache.ts (1)
libs/sdk/src/remote-mcp/mcp-client.types.ts (1)
  • McpRemoteCapabilities (176-187)
libs/sdk/src/tool/tool.registry.ts (2)
libs/sdk/src/scope/scope.instance.ts (2)
  • apps (325-327)
  • Scope (37-380)
libs/sdk/src/common/entries/base.entry.ts (1)
  • EntryLineage (6-6)
libs/sdk/src/remote-mcp/factories/record-builders.ts (6)
libs/sdk/src/remote-mcp/factories/index.ts (6)
  • buildRemoteToolRecord (19-19)
  • createRemoteToolContextClass (12-12)
  • buildRemoteResourceRecord (20-20)
  • createRemoteResourceContextClass (13-13)
  • buildRemoteResourceTemplateRecord (21-21)
  • createRemotePromptContextClass (14-14)
libs/sdk/src/remote-mcp/index.ts (7)
  • buildRemoteToolRecord (76-76)
  • McpClientService (11-11)
  • createRemoteToolContextClass (72-72)
  • buildRemoteResourceRecord (77-77)
  • createRemoteResourceContextClass (73-73)
  • buildRemoteResourceTemplateRecord (78-78)
  • createRemotePromptContextClass (74-74)
libs/sdk/src/common/records/tool.record.ts (1)
  • ToolClassTokenRecord (10-14)
libs/sdk/src/remote-mcp/factories/context-factories.ts (3)
  • createRemoteToolContextClass (27-38)
  • createRemoteResourceContextClass (47-57)
  • createRemotePromptContextClass (67-78)
libs/sdk/src/common/records/resource.record.ts (2)
  • ResourceClassTokenRecord (14-18)
  • ResourceTemplateClassTokenRecord (39-43)
libs/sdk/src/common/records/prompt.record.ts (1)
  • PromptClassTokenRecord (10-14)
libs/sdk/src/app/instances/app.remote.instance.ts (9)
libs/sdk/src/remote-mcp/index.ts (9)
  • McpClientService (11-11)
  • CapabilityCache (45-45)
  • McpConnectRequest (34-34)
  • McpTransportType (20-20)
  • McpRemoteAuthConfig (27-27)
  • createRemoteToolInstance (81-81)
  • createRemoteResourceInstance (82-82)
  • createRemoteResourceTemplateInstance (83-83)
  • createRemotePromptInstance (84-84)
libs/sdk/src/common/interfaces/internal/registry.interface.ts (5)
  • PluginRegistryInterface (107-109)
  • AdapterRegistryInterface (111-113)
  • ToolRegistryInterface (115-129)
  • ResourceRegistryInterface (131-146)
  • PromptRegistryInterface (148-160)
libs/sdk/src/common/metadata/app.metadata.ts (2)
  • RemoteAppMetadata (229-303)
  • RemoteAuthConfig (194-224)
libs/sdk/src/remote-mcp/cache/capability-cache.ts (1)
  • CapabilityCache (36-178)
libs/sdk/src/resource/resource.registry.ts (1)
  • ResourceRegistry (36-649)
libs/sdk/src/prompt/prompt.registry.ts (1)
  • PromptRegistry (30-541)
libs/sdk/src/common/records/app.record.ts (1)
  • AppRecord (23-23)
libs/sdk/src/remote-mcp/factories/index.ts (4)
  • createRemoteToolInstance (27-27)
  • createRemoteResourceInstance (28-28)
  • createRemoteResourceTemplateInstance (29-29)
  • createRemotePromptInstance (30-30)
libs/sdk/src/remote-mcp/factories/instance-factories.ts (4)
  • createRemoteToolInstance (40-50)
  • createRemoteResourceInstance (67-77)
  • createRemoteResourceTemplateInstance (94-104)
  • createRemotePromptInstance (121-131)
⏰ 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: Build Libraries
  • GitHub Check: Lint & Format Checks
🔇 Additional comments (32)
libs/sdk/src/tool/tool.instance.ts (1)

128-131: Good addition for exhaustive switch handling.

The default case ensures that any future or deprecated tool kinds are caught explicitly rather than falling through silently. The type cast for the error message is appropriate here since TypeScript narrows this.record.kind to never in the default branch.

Consider using a specific error class (e.g., InvalidToolKindError) for consistency with the codebase's error handling patterns, though a generic Error is acceptable for this internal exhaustive check. As per coding guidelines, specific error classes with MCP error codes are preferred.

libs/sdk/src/remote-mcp/cache/index.ts (1)

1-6: LGTM!

Clean barrel file that exports exactly the needed public API items (CapabilityCache and CapabilityCacheConfig) without legacy exports or aliases. Based on learnings, this follows the recommended pattern for barrel files.

libs/sdk/src/remote-mcp/resilience/retry.ts (1)

55-60: LGTM!

The sleep utility is correctly implemented. While a similar implementation exists in base.adapter.ts, the duplication is acceptable given the function's simplicity and the desire to keep the resilience module self-contained.

libs/sdk/src/remote-mcp/resilience/index.ts (1)

1-22: LGTM!

Well-organized barrel file that consolidates the resilience module's public API. Exports are properly categorized by source module, and the type keyword is correctly used for type-only exports. Based on learnings, this follows the recommended pattern for barrel files.

libs/sdk/src/remote-mcp/factories/context-factories.ts (3)

27-38: LGTM - Factory correctly creates dynamic tool context class.

The factory properly captures dependencies via closure and returns a typed context class that delegates to the MCP client. The use of strict MCP protocol type CallToolResult aligns with the coding guidelines.


47-57: LGTM - Resource context factory with proper typing.

The generic Params constraint and ReadResourceResult return type follow MCP protocol conventions. The unused _params parameter is acceptable since the URI already contains the resolved parameters for remote resources.


33-35: The suggestion is incorrect. PromptContext does not extend ExecutionContextBase and therefore does not have a getAuthInfo() method—it directly has the readonly authInfo: AuthInfo; property. The use of this.authInfo on line 74 is the correct approach for this class. The apparent inconsistency across the three factory functions reflects their different inheritance patterns: ToolContext and ResourceContext both extend ExecutionContextBase (which provides getAuthInfo()), while PromptContext is standalone and directly accesses its authInfo property.

Likely an incorrect or invalid review comment.

libs/sdk/src/remote-mcp/factories/instance-factories.ts (1)

40-50: LGTM - Clean factory pattern implementation.

All four instance factories follow a consistent pattern: delegate to record builders, then instantiate the appropriate Instance class. The separation of concerns between record building and instance creation is well-designed, allowing remote entities to participate in the hook lifecycle like local entities.

Also applies to: 67-77, 94-104, 121-131

libs/sdk/src/remote-mcp/factories/record-builders.ts (1)

166-171: LGTM - Prompt arguments mapping is defensive.

The optional chaining with fallback to empty array (remotePrompt.arguments?.map(...) || []) correctly handles prompts that don't define arguments.

libs/sdk/src/remote-mcp/resilience/health-check.ts (2)

44-163: Well-designed health checker with proper overlap prevention.

The isChecking flag prevents overlapping checks (lines 92-101), and the status transition logic correctly handles thresholds for both healthy and unhealthy states. The callback invocation is properly guarded with status change detection.


223-293: LGTM - HealthCheckManager provides clean lifecycle management.

The manager properly handles checker lifecycle with addChecker stopping existing checkers before creating new ones, and dispose cleanly stopping all checkers and clearing the map.

libs/sdk/src/remote-mcp/factories/index.ts (1)

1-31: LGTM - Clean barrel exports.

The barrel file properly organizes exports into logical groups (context factories, record builders, instance factories) with clear comments. This aligns with the coding guideline to "export users' needed items, no legacy exports with different names."

libs/sdk/src/remote-mcp/resilience/circuit-breaker.ts (3)

103-109: Consider if clearing all failures on success is the intended behavior.

In closed state, a single success clears the entire failures array (line 107). This aggressive reset may mask intermittent issues. An alternative is to let failures naturally expire via the sliding window (cleanOldFailures). However, this is a valid design choice for "self-healing" behavior.

Is this aggressive failure clearing intentional for the self-healing use case?


42-201: Well-implemented circuit breaker with proper state machine.

The state transitions are correct: closed → open (on threshold), open → half-open (on timeout), half-open → closed (on success threshold) or half-open → open (on failure). The sliding window cleanup and proper counter management ensure predictable behavior.


206-217: LGTM - CircuitOpenError provides useful context.

The error includes appId, nextAttemptTime, and a human-readable message with the wait time. This enables callers to implement appropriate retry-after behavior.

libs/sdk/src/remote-mcp/mcp-client.service.ts (3)

173-249: Well-structured connection lifecycle with proper cleanup.

The connect() method properly handles existing connections, stores config for reconnection, updates status, and initializes health checking. The cleanup on successful connection (resetting reconnect attempts) is a good self-healing pattern.


685-716: LGTM - Comprehensive dispose method.

The dispose() method properly cleans up all resources: refresh timers, reconnect timers, health check manager, circuit breakers, connections, and callbacks. The parallel disconnect with Promise.all is appropriate.


920-960: Good exponential backoff implementation for auto-reconnect.

The scheduleAutoReconnect() correctly implements exponential backoff (delay * 2^attempts), respects max attempts, and cleans up on success. The recursive scheduling on failure is appropriate for self-healing behavior.

libs/sdk/src/remote-mcp/mcp-client.types.ts (4)

1-18: Well-structured imports using strict MCP protocol types.

The imports correctly use specific MCP protocol types (CallToolResult, ReadResourceResult, GetPromptResult) from the SDK rather than unknown, aligning with the TypeScript-first approach. Based on learnings, this follows the guideline to use strict MCP protocol types.


24-116: Connection and transport types are well-designed.

The discriminated transport options (McpHttpTransportOptions, McpWorkerTransportOptions, McpEsmTransportOptions) provide clear, type-safe configuration for different transport mechanisms. The optional properties with documented defaults are helpful.


135-157: Auth configuration uses proper discriminated unions.

The McpRemoteAuthConfig type provides a clean discriminated union for different authentication strategies. The mapped mode's async mapper function provides flexibility for credential resolution.


311-348: Result types correctly wrap MCP protocol types with operational metadata.

The McpRemoteCallToolResult, McpRemoteReadResourceResult, and McpRemoteGetPromptResult types properly wrap the strict MCP protocol types (CallToolResult, ReadResourceResult, GetPromptResult) while adding operational metadata like durationMs and success. This follows the guideline to use strict MCP protocol types. Based on learnings.

libs/sdk/src/app/instances/app.remote.instance.ts (5)

40-65: Proper typing for scope with MCP client service.

The ScopeWithMcpClient interface correctly types the scope object for getOrCreateMcpClientService, avoiding the any type issue flagged in previous reviews.


148-178: Initialization properly handles capability change subscription.

The initialization correctly stores the unsubscribe function in _unsubscribeCapability (line 164), addressing the previous memory leak concern. Error handling is appropriate with logging.


188-207: Lazy loading with proper concurrency handling.

The ensureCapabilitiesLoaded method correctly handles concurrent access by tracking loadingPromise and ensuring it's cleared in the finally block. This prevents duplicate capability fetches.


277-290: Disconnect properly cleans up subscription.

The disconnect method correctly unsubscribes from capability changes before disconnecting, and clears the unsubscribe function reference to prevent double-unsubscribe issues.


292-300: Missing capability change re-subscription after reconnect.

After disconnect() unsubscribes from capability changes (lines 279-282), reconnect() does not re-establish the subscription. This means capability change notifications will be lost after a reconnect cycle.

🔎 Proposed fix

Extract subscription logic to a reusable helper and call it from both initialize() and reconnect():

+  /**
+   * Subscribe to capability changes for this remote app
+   */
+  private subscribeToCapabilityChanges(): void {
+    const logger = this.scopeProviders.getActiveScope().logger;
+    this._unsubscribeCapability = this.mcpClient.onCapabilityChange((event) => {
+      if (event.appId === this.id) {
+        logger.info(`Remote capabilities changed for ${this.id}: ${event.kind}`);
+        this.capabilityCache.invalidate(this.id);
+        this.capabilitiesLoaded = false;
+      }
+    });
+  }
+
   async reconnect(): Promise<void> {
     await this.mcpClient.reconnect(this.id);
     this.isConnected = true;
     this.capabilitiesLoaded = false;
     this.capabilityCache.invalidate(this.id);
+    this.subscribeToCapabilityChanges();
   }

Then update initialize() to use the helper as well.

Likely an incorrect or invalid review comment.

libs/sdk/src/remote-mcp/index.ts (1)

1-85: Well-structured barrel exports for remote MCP module.

The barrel file properly exports the public API surface with clear organization by category (service, types, cache, resilience, factories). Type exports use the type keyword appropriately. This follows the guideline to export users' needed items through barrel files. Based on learnings.

libs/sdk/src/resource/resource.registry.ts (4)

127-141: Clean separation of remote vs local app resource adoption.

The conditional logic properly distinguishes remote apps (which expose resources directly via proxies) from local apps (which use the hierarchical registry pattern). This aligns with the broader remote MCP architecture.


198-217: Robust remote resource adoption with validation.

The method properly validates remote resources (checking for name property), handles errors gracefully with logging, and continues processing on invalid entries rather than failing entirely. The use of Symbol(remoteResource.name) for tokens is appropriate for dynamically discovered resources.


219-230: Local app adoption reuses existing patterns.

The adoptResourcesFromLocalApp method properly reuses the existing adoptFromChild mechanism, maintaining consistency with the established registry hierarchy pattern.


464-487: Type signature correctly updated to accept ResourceEntry.

The makeRow method signature change from ResourceInstance to ResourceEntry (line 466) addresses the previous type safety concern. This allows both ResourceInstance and proxy entries to be properly indexed.

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: 0

🧹 Nitpick comments (1)
libs/sdk/src/tool/tool.events.ts (1)

32-34: Consider adding error handling to emit for resilience.

Unlike PromptEmitter.emit (which includes try-catch error handling), this implementation will stop notifying remaining listeners if any listener throws an error. Consider adding defensive error handling similar to the prompt emitter.

🔎 Suggested defensive error handling
 emit(e: ToolChangeEvent) {
-  for (const l of [...this.listeners]) l(e);
+  for (const l of [...this.listeners]) {
+    try {
+      l(e);
+    } catch (err) {
+      // Log error but continue notifying other listeners
+      // Use safe logging to avoid Node.js 24 util.inspect bug with Zod errors
+      console.error('ToolEmitter listener error:', err instanceof Error ? err.message : 'Unknown error');
+    }
+  }
 }
📜 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 1feb2c0 and 2fbd8db.

📒 Files selected for processing (12)
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/common/tokens/app.tokens.ts
  • libs/sdk/src/prompt/prompt.events.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/prompt/prompt.types.ts
  • libs/sdk/src/remote-mcp/cache/capability-cache.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
  • libs/sdk/src/remote-mcp/resilience/retry.ts
  • libs/sdk/src/tool/tool.events.ts
  • libs/sdk/src/tool/tool.registry.ts
  • libs/sdk/src/tool/tool.types.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • libs/sdk/src/remote-mcp/resilience/retry.ts
  • libs/sdk/src/remote-mcp/cache/capability-cache.ts
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Enable and use strict TypeScript settings - no any types without strong justification, use unknown for generic type defaults instead
Avoid non-null assertions (!) - use proper error handling and throw specific errors when values are missing instead
Always use @frontmcp/utils for cryptographic operations - use hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode instead of node:crypto
Always use @frontmcp/utils for file system operations - use readFile, writeFile, mkdir, rename, unlink, stat, etc. instead of fs/promises or node:fs
Use constrained generic type parameters with Record<string, string> or similar constraints instead of unconstrained any defaults

Files:

  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/prompt/prompt.events.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/prompt/prompt.types.ts
  • libs/sdk/src/common/tokens/app.tokens.ts
  • libs/sdk/src/tool/tool.events.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/tool/tool.types.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
  • libs/sdk/src/tool/tool.registry.ts
**/libs/{sdk,adapters,plugins}/**/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/libs/{sdk,adapters,plugins}/**/src/**/*.ts: MCP response types must use strictly typed MCP protocol definitions, not unknown - use Promise<GetPromptResult>, Promise<ReadResourceResult>, and similar MCP-defined types for execute() and read() methods
Validation flow pattern - execute/read methods return strictly typed MCP responses, parseOutput normalizes various input shapes, flows finalize using entry's parse methods

Files:

  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/prompt/prompt.events.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/prompt/prompt.types.ts
  • libs/sdk/src/common/tokens/app.tokens.ts
  • libs/sdk/src/tool/tool.events.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/tool/tool.types.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
  • libs/sdk/src/tool/tool.registry.ts
**/libs/sdk/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Create shared base classes for common functionality - use ExecutionContextBase as parent class for ToolContext, ResourceContext, and similar context classes

Files:

  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/prompt/prompt.events.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/prompt/prompt.types.ts
  • libs/sdk/src/common/tokens/app.tokens.ts
  • libs/sdk/src/tool/tool.events.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/tool/tool.types.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
  • libs/sdk/src/tool/tool.registry.ts
**/libs/{sdk,adapters}/**/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use getCapabilities() method in registries for dynamic capability exposure - do not hardcode capabilities in transport adapters

Files:

  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/prompt/prompt.events.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/prompt/prompt.types.ts
  • libs/sdk/src/common/tokens/app.tokens.ts
  • libs/sdk/src/tool/tool.events.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/tool/tool.types.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
  • libs/sdk/src/tool/tool.registry.ts
**/*.{ts,tsx,md}

📄 CodeRabbit inference engine (CLAUDE.md)

Document known limitations clearly in code comments and README files

Files:

  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/prompt/prompt.events.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/prompt/prompt.types.ts
  • libs/sdk/src/common/tokens/app.tokens.ts
  • libs/sdk/src/tool/tool.events.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/tool/tool.types.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
  • libs/sdk/src/tool/tool.registry.ts
**/libs/**/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add backwards compatibility exports in new libraries - avoid legacy prefixes like PT-001 in test names

Files:

  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/prompt/prompt.events.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/prompt/prompt.types.ts
  • libs/sdk/src/common/tokens/app.tokens.ts
  • libs/sdk/src/tool/tool.events.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/tool/tool.types.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
  • libs/sdk/src/tool/tool.registry.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/prompt/prompt.registry.ts
  • libs/sdk/src/prompt/prompt.events.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/prompt/prompt.types.ts
  • libs/sdk/src/common/tokens/app.tokens.ts
  • libs/sdk/src/tool/tool.events.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/tool/tool.types.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
  • libs/sdk/src/tool/tool.registry.ts
**/libs/{sdk,adapters,plugins}/**/src/**/*event*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use changeScope instead of scope property name in change event types to avoid confusion with Scope class

Files:

  • libs/sdk/src/prompt/prompt.events.ts
  • libs/sdk/src/tool/tool.events.ts
**/libs/{sdk,adapters,plugins}/**/src/**/*metadata*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Validate URIs per RFC 3986 at metadata level using Zod schema validation with isValidMcpUri refinement

Files:

  • libs/sdk/src/common/metadata/app.metadata.ts
🧠 Learnings (17)
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters}/**/src/**/*.ts : Use getCapabilities() method in registries for dynamic capability exposure - do not hardcode capabilities in transport adapters

Applied to files:

  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
  • libs/sdk/src/tool/tool.registry.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters,plugins}/**/src/**/*.ts : MCP response types must use strictly typed MCP protocol definitions, not `unknown` - use `Promise<GetPromptResult>`, `Promise<ReadResourceResult>`, and similar MCP-defined types for execute() and read() methods

Applied to files:

  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/prompt/prompt.events.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/prompt/prompt.types.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/sdk/src/common/records/**/*.ts : Centralize record types in common/records directory - import AnyResourceRecord and similar types from common/records, not from module-specific files

Applied to files:

  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/tool/tool.types.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters,plugins}/**/src/**/*event*.ts : Use `changeScope` instead of `scope` property name in change event types to avoid confusion with Scope class

Applied to files:

  • libs/sdk/src/prompt/prompt.events.ts
  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/tool/tool.events.ts
  • libs/sdk/src/tool/tool.registry.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters,plugins}/**/src/**/*error*.ts : Use specific MCP error classes with MCP error codes instead of generic errors - define error classes with `mcpErrorCode` property and `toJsonRpcError()` method

Applied to files:

  • libs/sdk/src/app/instances/app.remote.instance.ts
  • libs/sdk/src/common/metadata/app.metadata.ts
  • libs/sdk/src/remote-mcp/mcp-client.service.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/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/app/instances/app.remote.instance.ts
  • libs/sdk/src/tool/tool.registry.ts
📚 Learning: 2026-01-06T02:34:55.680Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.680Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `any` type without justification in TypeScript files

Applied to files:

  • libs/sdk/src/app/instances/app.remote.instance.ts
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/{src}/**/*.{ts,tsx} : Do not use `any` type without justification in TypeScript code

Applied to files:

  • libs/sdk/src/app/instances/app.remote.instance.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/*.{ts,tsx} : Avoid non-null assertions (!) - use proper error handling and throw specific errors when values are missing instead

Applied to files:

  • libs/sdk/src/app/instances/app.remote.instance.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters,plugins}/**/src/**/*metadata*.ts : Validate URIs per RFC 3986 at metadata level using Zod schema validation with `isValidMcpUri` refinement

Applied to files:

  • libs/sdk/src/common/metadata/app.metadata.ts
📚 Learning: 2026-01-06T02:34:55.680Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.680Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • libs/sdk/src/common/metadata/app.metadata.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/plugins/**/src/**/*.ts : Use module augmentation to extend ExecutionContextBase with new properties - declare module 'frontmcp/sdk' and define interface extensions for plugin context properties

Applied to files:

  • libs/sdk/src/common/metadata/app.metadata.ts
📚 Learning: 2026-01-06T02:34:55.680Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.680Z
Learning: Applies to libs/plugins/**/*plugin.ts : Use module augmentation for context properties via `declare module 'frontmcp/sdk'` combined with runtime plugin metadata `contextExtensions`, not module-level side effects

Applied to files:

  • libs/sdk/src/common/metadata/app.metadata.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters,plugins}/**/src/**/*.ts : Validation flow pattern - execute/read methods return strictly typed MCP responses, parseOutput normalizes various input shapes, flows finalize using entry's parse methods

Applied to files:

  • libs/sdk/src/common/metadata/app.metadata.ts
📚 Learning: 2026-01-06T02:34:55.680Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.680Z
Learning: Applies to libs/plugins/**/*.ts : Extend tool metadata using `declare global` pattern to allow tools to specify plugin-specific options in their decorators

Applied to files:

  • libs/sdk/src/tool/tool.types.ts
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/index.{ts,js} : Export all public APIs through appropriate entry points (frontmcp/uipack, frontmcp/uipack/adapters, frontmcp/uipack/theme, etc.)

Applied to files:

  • libs/sdk/src/tool/tool.types.ts
📚 Learning: 2025-11-05T15:00:47.800Z
Learnt from: frontegg-david
Repo: agentfront/frontmcp PR: 11
File: libs/core/src/auth/jwks/jwks.service.ts:62-0
Timestamp: 2025-11-05T15:00:47.800Z
Learning: In the FrontMCP codebase (libs/core/src/auth/jwks/jwks.service.ts), the verifyGatewayToken method intentionally skips signature verification when running in development-only no-auth mode, using decodeJwtPayloadSafe instead of jwtVerify. This is a deliberate design for local development convenience and should not be flagged as a security issue when the PR or code context indicates development/no-auth mode.

Applied to files:

  • libs/sdk/src/remote-mcp/mcp-client.service.ts
🧬 Code graph analysis (3)
libs/sdk/src/prompt/prompt.registry.ts (4)
libs/sdk/src/common/interfaces/prompt.interface.ts (1)
  • scope (84-86)
libs/sdk/src/scope/scope.instance.ts (2)
  • apps (325-327)
  • Scope (37-380)
libs/sdk/src/prompt/prompt.types.ts (1)
  • PromptExportOptions (34-39)
libs/sdk/src/prompt/prompt.instance.ts (1)
  • PromptInstance (23-135)
libs/sdk/src/remote-mcp/mcp-client.service.ts (3)
libs/sdk/src/remote-mcp/resilience/retry.ts (3)
  • RetryOptions (6-21)
  • withRetry (65-106)
  • isTransientError (111-150)
libs/sdk/src/remote-mcp/resilience/circuit-breaker.ts (3)
  • CircuitBreakerOptions (8-19)
  • CircuitBreakerManager (222-268)
  • CircuitBreaker (42-201)
libs/sdk/src/remote-mcp/resilience/health-check.ts (4)
  • HealthCheckOptions (17-30)
  • HealthCheckManager (223-293)
  • HealthStatus (6-6)
  • HealthChecker (44-218)
libs/sdk/src/tool/tool.registry.ts (5)
libs/sdk/src/scope/scope.instance.ts (2)
  • apps (325-327)
  • Scope (37-380)
libs/sdk/src/common/entries/base.entry.ts (1)
  • EntryLineage (6-6)
libs/sdk/src/tool/tool.types.ts (1)
  • ExportNameOptions (20-25)
libs/sdk/src/common/interfaces/index.ts (1)
  • Token (10-10)
libs/sdk/src/tool/tool.instance.ts (2)
  • getMetadata (99-101)
  • ToolInstance (37-163)
⏰ 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 (14)
libs/sdk/src/prompt/prompt.events.ts (2)

3-3: LGTM! Clean migration to PromptEntry.

The update from PromptInstance to PromptEntry in both the import and the snapshot field aligns with the PR's goal of unifying local and remote prompt representations. The changeScope property naming is correctly used as per coding guidelines.

Also applies to: 13-13


26-36: Good defensive error handling in emit.

The error handling with safe logging specifically addresses the Node.js 24 util.inspect bug with Zod errors, which is a thoughtful implementation detail.

libs/sdk/src/prompt/prompt.types.ts (1)

3-3: LGTM! Clear documentation of the PromptEntry migration.

The type update from PromptInstance to PromptEntry is well-documented with inline JSDoc explaining that the instance field can now represent either local or remote prompt entries. This aligns with the PR's remote MCP orchestration objectives.

Also applies to: 17-18

libs/sdk/src/tool/tool.events.ts (1)

1-1: LGTM! Consistent migration to ToolEntry.

The update from ToolInstance to ToolEntry aligns with the broader migration to unified local/remote tool representations. The changeScope property naming is correctly used as per coding guidelines.

Also applies to: 19-19

libs/sdk/src/tool/tool.types.ts (1)

1-1: LGTM! Consistent ToolEntry migration with clear documentation.

The type update from ToolInstance to ToolEntry mirrors the pattern used in prompt.types.ts, with clear inline documentation explaining that the instance field can represent either local or remote tool entries.

Also applies to: 8-9

libs/sdk/src/common/tokens/app.tokens.ts (1)

30-34: LGTM! New remote app metadata fields added consistently.

The five new metadata fields (namespace, transportOptions, remoteAuth, refreshInterval, cacheTTL) are added using the consistent tokenFactory.meta() pattern and align with the PR's remote MCP server orchestration objectives. These additive changes expand the configuration surface for remote apps without breaking existing functionality.

libs/sdk/src/tool/tool.registry.ts (1)

166-189: Excellent resolution of previous issues! LGTM.

The critical issues from past reviews have been properly addressed:

Stable token generation (Line 181): Now uses Symbol.for() with app.id and tool name, ensuring the same symbol is returned across multiple calls. This prevents duplicate entries if capabilities are refreshed.

Type safety (Line 182): remoteTool is passed directly to makeRow without unsafe casting. The signature of makeRow (Line 379) has been correctly widened to accept ToolEntry instead of requiring ToolInstance.

Error handling: Try-catch block logs errors via scope logger, allowing graceful degradation if remote tool fetching fails.

Validation: Checks for valid tool name before processing each remote tool.

The implementation follows the established pattern and maintains type safety throughout the adoption flow.

libs/sdk/src/prompt/prompt.registry.ts (1)

182-201: Previous critical issues successfully resolved! LGTM.

The code properly addresses the concerns from past reviews:

Stable token generation (Line 193): Uses Symbol.for() with a composite key of app.id and prompt name. This ensures deterministic tokens across registry operations, preventing duplicate entries during capability refreshes.

Type safety (Line 194): The remotePrompt is passed directly to makeRow without any unsafe casting. The method signature at Line 412 correctly accepts PromptEntry as the base type.

Error handling (Lines 183, 198-200): Try-catch block with scope-aware error logging provides graceful failure handling.

Validation (Lines 188-190): Validates prompt name before processing, with warning logs for invalid entries.

The implementation is consistent with the tool registry pattern and maintains proper type safety throughout.

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

346-348: URI validation follows coding guidelines correctly.

The implementation properly uses isValidMcpUri refinement for URL validation at the metadata schema level, as specified in the coding guidelines for metadata files.

Based on learnings: URI validation should use Zod schema refinement with isValidMcpUri at metadata level per RFC 3986.


145-340: Well-designed remote configuration types.

The new remote app configuration structure is comprehensive and well thought out:

Discriminated union pattern (Lines 326-339): RemoteAuthConfig uses mode-based discrimination ('static' | 'forward' | 'oauth'), enabling type-safe access to mode-specific fields.

Resilience controls (Lines 145-175): Transport options include timeout, retries, delay, and SSE fallback—covering common failure scenarios.

Flexible authentication (Lines 180-224): Supports static credentials, token forwarding, and OAuth, accommodating different security models.

Orchestration features (Lines 265-301): Namespace isolation, capability caching, and refresh intervals enable robust multi-server coordination.

Documentation: JSDoc comments clearly explain each option with defaults, making the API self-documenting.

The schemas correctly mirror the TypeScript interfaces, ensuring runtime validation matches compile-time types.

libs/sdk/src/remote-mcp/mcp-client.service.ts (2)

209-223: LGTM - SSE fallback properly implemented.

The fallback logic now correctly handles connection failures during client.connect() rather than during transport construction. The use of shouldFallbackToSSE() and createFallbackSSETransport() provides a clean separation of concerns.


483-489: Authentication design is documented and consistent.

The log message correctly explains that auth headers are configured at connection time via transportOptions.headers (see createTransport() at lines 779-782). This connection-level authentication is a valid design choice for remote MCP servers.

Note: If per-request authentication is needed in the future, the MCP SDK's client methods would need to support passing headers per-call.

libs/sdk/src/app/instances/app.remote.instance.ts (2)

116-116: LGTM - Capability subscription cleanup properly implemented.

The memory leak identified in past reviews has been addressed:

  • Unsubscribe function is stored (line 116, line 164)
  • Properly cleaned up in disconnect() (lines 279-282)

This prevents memory leaks when remote app instances are disposed or disconnected.

Also applies to: 164-171, 279-282


44-47: LGTM - Proper typing for scope parameter.

The ScopeWithMcpClient interface provides proper typing for the scope parameter in getOrCreateMcpClientService(), addressing the past review comment about avoiding any types.

Also applies to: 309-324

# Conflicts:
#	plugins/plugin-cache/src/__tests__/cache.plugin.test.ts
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: 5

Caution

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

⚠️ Outside diff range comments (1)
libs/sdk/src/tool/tool.registry.ts (1)

362-370: Potential infinite loop in disambiguate function.

Unlike prompt.registry.ts which has MAX_DISAMBIGUATE_ATTEMPTS = 10000 (line 28 and used in the disambiguate function), this implementation uses while (true) without any upper bound. In pathological cases (e.g., maxLen too small to accommodate incrementing suffixes), this could loop indefinitely.

🔎 Suggested fix to add max attempts
 function disambiguate(candidate: string, pool: Map<string, ToolEntry>, cfg: Required<ExportNameOptions>): string {
   if (!pool.has(candidate)) return candidate;
+  const maxAttempts = 10000;
   let n = 2;
-  while (true) {
+  while (n <= maxAttempts) {
     const withN = ensureMaxLen(`${candidate}${sepFor(cfg.case)}${n}`, cfg.maxLen);
     if (!pool.has(withN)) return withN;
     n++;
   }
+  throw new Error(`Failed to disambiguate name "${candidate}" after ${maxAttempts} attempts`);
 }
🤖 Fix all issues with AI Agents
In @libs/sdk/src/auth/flows/session.verify.flow.ts:
- Around line 262-267: The anonymous user object for transparent-anon mode
includes a scope property while the public anonymous user object does not; make
the shapes consistent by adding a scope field to the public-anon user creation
too (e.g., add scope: 'public' or scope: scopes.join(' ') to the user object
created where the public anonymous user is constructed) so downstream code sees
a uniform user shape across both transparent and public anonymous flows.
- Around line 218-292: handleAnonymousFallback duplicates most of
handlePublicMode; extract the shared session creation into a private helper
(e.g., createAnonymousSession) that accepts options { issuer, authSig,
sessionIdHeader?, scopes? } and encapsulates logic using
this.state.sessionIdHeader, getMachineId(), decryptPublicSession, encryptJson,
detectPlatformFromUserAgent, and crypto.randomUUID() to build the user, payload
and session id; then replace the duplicated blocks in both handlePublicMode and
handleAnonymousFallback to call this.createAnonymousSession(...) and pass scopes
from (this.scope.auth?.options as TransparentAuthOptions)?.anonymousScopes when
handling transparent anon.
- Around line 273-280: The payload currently sets isPublic: true for
transparent-anon sessions (in session.verify.flow.ts), which conflates public
and transparent-anonymous modes; modify the payload construction (the object
assigned to payload containing uuid, nodeId, authSig, iat, isPublic, ...) to
distinguish modes explicitly: set isPublic only for true public sessions and add
either an authMode field (e.g. 'public' | 'transparent-anon' | 'anonymous') or a
boolean isAnonymous flag for transparent-anon sessions so downstream checks can
reliably differentiate; update the branch that builds authSig for
transparent-anon to include authMode: 'transparent-anon' (or isAnonymous: true)
and ensure public session construction sets authMode: 'public' (or isAnonymous:
false).

In @libs/sdk/src/transport/adapters/transport.local.adapter.ts:
- Around line 87-96: Replace the hardcoded remoteCapabilities object (symbol:
remoteCapabilities, conditional: hasRemoteApps) with capability retrieval via
getCapabilities() on the appropriate registry/registries; e.g., call
this.scope.remoteApps.getCapabilities() if a remote app registry exists, or
merge this.scope.tools.getCapabilities(),
this.scope.resources.getCapabilities(), and this.scope.prompts.getCapabilities()
into the server options capabilities so transport.local.adapter no longer
hardcodes tools/resources/prompts capabilities but delegates to each registry's
getCapabilities() method.
🧹 Nitpick comments (8)
libs/sdk/src/auth/flows/auth.verify.flow.ts (1)

211-230: Code duplication and inconsistent configuration handling.

This method duplicates most of the logic from handlePublicMode (lines 180-199) with subtle inconsistencies:

  1. TTL: handlePublicMode uses this.parseTtl(authOptions?.['sessionTtl']) allowing configuration, while this method hardcodes 3600000.
  2. Issuer: handlePublicMode respects authOptions?.['issuer'], while this always uses baseUrl.

Consider extracting a shared helper to ensure consistent behavior and reduce duplication:

🔎 Proposed refactor
+  /**
+   * Create anonymous authorization from auth options
+   */
+  private createAnonymousAuthorization(authOptions: Record<string, unknown> | undefined): PublicAuthorization {
+    const publicAccess = authOptions?.['publicAccess'] as Record<string, unknown> | undefined;
+    return PublicAuthorization.create({
+      scopes: (authOptions?.['anonymousScopes'] as string[] | undefined) ?? ['anonymous'],
+      ttlMs: this.parseTtl(authOptions?.['sessionTtl'] as string | number | undefined),
+      issuer: (authOptions?.['issuer'] as string | undefined) ?? this.state.required.baseUrl,
+      allowedTools: (publicAccess?.['tools'] as string[] | 'all' | undefined) ?? 'all',
+      allowedPrompts: (publicAccess?.['prompts'] as string[] | 'all' | undefined) ?? 'all',
+    });
+  }

Then use in both stages:

  async handlePublicMode() {
    const authOptions = this.scope.auth?.options as Record<string, unknown> | undefined;
-   const publicAccess = authOptions?.['publicAccess'] as Record<string, unknown> | undefined;
-   const authorization = PublicAuthorization.create({
-     scopes: (authOptions?.['anonymousScopes'] as string[] | undefined) ?? ['anonymous'],
-     ttlMs: this.parseTtl(authOptions?.['sessionTtl'] as string | number | undefined),
-     issuer: (authOptions?.['issuer'] as string | undefined) ?? this.state.required.baseUrl,
-     allowedTools: (publicAccess?.['tools'] as string[] | 'all' | undefined) ?? 'all',
-     allowedPrompts: (publicAccess?.['prompts'] as string[] | 'all' | undefined) ?? 'all',
-   });
+   const authorization = this.createAnonymousAuthorization(authOptions);
    // ...
  }

  async handleAnonymousFallback() {
    const authOptions = this.scope.auth?.options as Record<string, unknown> | undefined;
-   const publicAccess = authOptions?.['publicAccess'] as Record<string, unknown> | undefined;
-   const authorization = PublicAuthorization.create({
-     scopes: (authOptions?.['anonymousScopes'] as string[] | undefined) ?? ['anonymous'],
-     ttlMs: 3600000,
-     issuer: this.state.required.baseUrl,
-     allowedTools: (publicAccess?.['tools'] as string[] | 'all' | undefined) ?? 'all',
-     allowedPrompts: (publicAccess?.['prompts'] as string[] | 'all' | undefined) ?? 'all',
-   });
+   const authorization = this.createAnonymousAuthorization(authOptions);
    // ...
  }
libs/sdk/src/tool/tool.utils.ts (1)

89-113: Consider validating all content items, not just the first.

The isCallToolResult helper only validates the first item in the content array. If subsequent items have invalid types, they'll pass through unchecked. While this is likely sufficient as a heuristic to detect remote tool results (since malformed remote results are the remote server's responsibility), it could cause unexpected behavior if a partially valid result is passed through.

If this is intentional for performance or because remote servers are trusted, a clarifying comment would help.

🔎 Optional: Validate all content items
-  if (content.length > 0) {
-    const firstItem = content[0] as Record<string, unknown>;
-    if (typeof firstItem !== 'object' || firstItem === null) return false;
-    if (typeof firstItem['type'] !== 'string') return false;
-    // Valid content types: 'text', 'image', 'audio', 'resource', 'resource_link'
-    const validTypes = ['text', 'image', 'audio', 'resource', 'resource_link'];
-    if (!validTypes.includes(firstItem['type'])) return false;
-  }
+  const validTypes = ['text', 'image', 'audio', 'resource', 'resource_link'];
+  for (const item of content) {
+    if (typeof item !== 'object' || item === null) return false;
+    const block = item as Record<string, unknown>;
+    if (typeof block['type'] !== 'string') return false;
+    if (!validTypes.includes(block['type'])) return false;
+  }
libs/sdk/src/resource/flows/read-resource.flow.ts (1)

113-159: Consider extracting shared ensureRemoteCapabilities logic.

This method is duplicated across multiple flows (ReadResourceFlow, ResourceTemplatesListFlow, GetPromptFlow, ResourcesListFlow, ToolsListFlow) with nearly identical implementation. Consider extracting this to a shared utility or base class method to reduce duplication.

The implementation itself is correct:

  • Parallel execution with Promise.all for efficiency
  • Individual error handling prevents one failing app from blocking others
  • Duck typing check is appropriate for the loosely typed app interface
🔎 Example: Extract to FlowBase or utility
// In FlowBase or a shared utility
protected async loadRemoteAppCapabilities(): Promise<void> {
  const appRegistries = this.scope.providers.getRegistries('AppRegistry');
  const remoteApps: Array<{ id: string; ensureCapabilitiesLoaded?: () => Promise<void> }> = [];

  for (const appRegistry of appRegistries) {
    for (const app of appRegistry.getApps()) {
      if (app.isRemote) remoteApps.push(app);
    }
  }

  if (remoteApps.length === 0) return;

  await Promise.all(
    remoteApps.map(async (app) => {
      if ('ensureCapabilitiesLoaded' in app && typeof app.ensureCapabilitiesLoaded === 'function') {
        try {
          await app.ensureCapabilitiesLoaded();
        } catch (error) {
          this.logger.warn(`Failed to load capabilities for remote app ${app.id}: ${(error as Error).message}`);
        }
      }
    })
  );
}
libs/sdk/src/prompt/prompt.registry.ts (1)

502-547: Potential memory leak: subscription not cleaned up on registry disposal.

The adoptPromptsFromRemoteApp method subscribes to the remote registry (lines 221-224) but doesn't store the unsubscribe function. If this registry is disposed or the remote app disconnects, the subscription remains active, potentially causing:

  1. Memory leaks from retained closure references
  2. Unexpected adoptRemotePrompts() calls after disposal

Consider storing unsubscribe functions in a class field and calling them during cleanup.

Additionally, the cast on line 516 (prompt as PromptInstance) is similar to the previous type safety concern, though the subsequent validation ensures required properties exist.

🔎 Suggested approach for subscription cleanup
+  /** Subscriptions to remote app registries for cleanup */
+  private remoteSubscriptions = new Map<string, () => void>();
+
   private adoptPromptsFromRemoteApp(app: AppEntry, scope: Scope): void {
     // ... existing code ...
     
     if (remoteRegistry && typeof remoteRegistry.subscribe === 'function') {
-      remoteRegistry.subscribe({ immediate: false }, () => {
+      // Clean up any existing subscription for this app
+      this.remoteSubscriptions.get(app.id)?.();
+      
+      const unsubscribe = remoteRegistry.subscribe({ immediate: false }, () => {
         adoptRemotePrompts();
       });
+      this.remoteSubscriptions.set(app.id, unsubscribe);
     }
   }
libs/sdk/src/resource/resource.registry.ts (1)

584-629: Validation is thorough but consider unifying with prompt registry.

The validation pattern is identical to registerPromptInstance in prompt.registry.ts. Consider extracting a shared validation helper to reduce duplication across registries.

libs/sdk/src/auth/flows/session.verify.flow.ts (1)

295-306: Filter logic correctly coordinates auth modes.

The updated filter properly handles all three scenarios:

  1. Authorization header already present
  2. Public mode (handled by handlePublicMode)
  3. Transparent mode with anonymous fallback (handled by handleAnonymousFallback)
Optional: Simplify for readability

Consider extracting the transparent anonymous check into a helper for clarity:

+const isTransparentAnonymous = (authOptions: AuthOptions | undefined): boolean => {
+  return authOptions !== undefined 
+    && isTransparentMode(authOptions) 
+    && (authOptions as TransparentAuthOptions).allowAnonymous === true;
+};

 @Stage('requireAuthorizationHeader', {
   filter: ({ state, scope }) => {
     if (state.authorizationHeader) return false;
     const authOptions = scope.auth?.options;
     if (authOptions && isPublicMode(authOptions)) return false;
-    if (authOptions && isTransparentMode(authOptions)) {
-      if ((authOptions as TransparentAuthOptions).allowAnonymous === true) return false;
-    }
+    if (isTransparentAnonymous(authOptions)) return false;
     return true;
   },
 })
libs/sdk/src/transport/adapters/transport.local.adapter.ts (1)

70-77: Strengthen type safety for remote app detection.

The remote app detection uses runtime type checks (typeof app === 'object' and 'urlType' in app) without proper TypeScript type guards. This approach is not type-safe and could lead to runtime errors if the shape of scope.metadata.apps changes.

Consider defining a type predicate function or using a Zod schema to validate remote app structure, ensuring compile-time type safety and runtime validation.

🔎 Proposed type-safe approach using type predicate
+// Add helper function before connectServer method
+private isRemoteApp(app: unknown): app is { urlType: string; standalone?: boolean } {
+  return (
+    app !== null &&
+    typeof app === 'object' &&
+    'urlType' in app &&
+    typeof (app as { urlType?: unknown }).urlType === 'string'
+  );
+}
+
 connectServer() {
   const { info, apps } = this.scope.metadata;

-  // Check if there are remote apps configured (they will have tools/resources/prompts that load later)
-  // Remote apps have 'urlType' property indicating they're external MCP servers
-  const hasRemoteApps = apps?.some(
-    (app) =>
-      app && typeof app === 'object' && 'urlType' in app && (app as { standalone?: boolean }).standalone !== true,
-  );
+  const hasRemoteApps = apps?.some((app) => this.isRemoteApp(app) && app.standalone !== true);

Alternatively, if AppMetadata types are available, use those directly with proper type guards.

plugins/plugin-cache/src/cache.plugin.ts (1)

141-165: Consider parameter usage clarity.

The _flowCtx parameter is prefixed with an underscore (indicating it's intentionally unused), but the method doesn't use it—instead accessing context via this.get(FrontMcpContextStorage). While the bypass header logic itself is correct (lines 158-160), the unused parameter might be confusing.

If this parameter is required by the hook signature or for future extensibility, consider adding a comment explaining why it's present but unused. Otherwise, evaluate whether it can be removed for clarity.

📜 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 2fbd8db and ca171ad.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (23)
  • apps/e2e/demo-e2e-remote/src/main.ts
  • docs/draft/docs/plugins/cache-plugin.mdx
  • libs/sdk/src/auth/flows/auth.verify.flow.ts
  • libs/sdk/src/auth/flows/session.verify.flow.ts
  • libs/sdk/src/prompt/flows/get-prompt.flow.ts
  • libs/sdk/src/prompt/flows/prompts-list.flow.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/resource/flows/read-resource.flow.ts
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/tool/tool.instance.ts
  • libs/sdk/src/tool/tool.registry.ts
  • libs/sdk/src/tool/tool.utils.ts
  • libs/sdk/src/transport/adapters/transport.local.adapter.ts
  • libs/uipack/package.json
  • package.json
  • plugins/plugin-cache/src/__tests__/cache.plugin.test.ts
  • plugins/plugin-cache/src/cache.plugin.ts
  • plugins/plugin-cache/src/cache.types.ts
  • plugins/plugin-codecall/package.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • libs/sdk/src/tool/flows/call-tool.flow.ts
  • apps/e2e/demo-e2e-remote/src/main.ts
🧰 Additional context used
📓 Path-based instructions (12)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Enable and use strict TypeScript settings - no any types without strong justification, use unknown for generic type defaults instead
Avoid non-null assertions (!) - use proper error handling and throw specific errors when values are missing instead
Always use @frontmcp/utils for cryptographic operations - use hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode instead of node:crypto
Always use @frontmcp/utils for file system operations - use readFile, writeFile, mkdir, rename, unlink, stat, etc. instead of fs/promises or node:fs
Use constrained generic type parameters with Record<string, string> or similar constraints instead of unconstrained any defaults

Files:

  • libs/sdk/src/resource/flows/read-resource.flow.ts
  • plugins/plugin-cache/src/cache.types.ts
  • libs/sdk/src/auth/flows/session.verify.flow.ts
  • plugins/plugin-cache/src/cache.plugin.ts
  • libs/sdk/src/tool/tool.instance.ts
  • libs/sdk/src/transport/adapters/transport.local.adapter.ts
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/prompt/flows/get-prompt.flow.ts
  • plugins/plugin-cache/src/__tests__/cache.plugin.test.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/auth/flows/auth.verify.flow.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/prompt/flows/prompts-list.flow.ts
  • libs/sdk/src/tool/tool.utils.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/tool/tool.registry.ts
**/libs/{sdk,adapters,plugins}/**/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/libs/{sdk,adapters,plugins}/**/src/**/*.ts: MCP response types must use strictly typed MCP protocol definitions, not unknown - use Promise<GetPromptResult>, Promise<ReadResourceResult>, and similar MCP-defined types for execute() and read() methods
Validation flow pattern - execute/read methods return strictly typed MCP responses, parseOutput normalizes various input shapes, flows finalize using entry's parse methods

Files:

  • libs/sdk/src/resource/flows/read-resource.flow.ts
  • libs/sdk/src/auth/flows/session.verify.flow.ts
  • libs/sdk/src/tool/tool.instance.ts
  • libs/sdk/src/transport/adapters/transport.local.adapter.ts
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/prompt/flows/get-prompt.flow.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/auth/flows/auth.verify.flow.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/prompt/flows/prompts-list.flow.ts
  • libs/sdk/src/tool/tool.utils.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/tool/tool.registry.ts
**/libs/sdk/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Create shared base classes for common functionality - use ExecutionContextBase as parent class for ToolContext, ResourceContext, and similar context classes

Files:

  • libs/sdk/src/resource/flows/read-resource.flow.ts
  • libs/sdk/src/auth/flows/session.verify.flow.ts
  • libs/sdk/src/tool/tool.instance.ts
  • libs/sdk/src/transport/adapters/transport.local.adapter.ts
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/prompt/flows/get-prompt.flow.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/auth/flows/auth.verify.flow.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/prompt/flows/prompts-list.flow.ts
  • libs/sdk/src/tool/tool.utils.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/tool/tool.registry.ts
**/libs/{sdk,adapters}/**/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use getCapabilities() method in registries for dynamic capability exposure - do not hardcode capabilities in transport adapters

Files:

  • libs/sdk/src/resource/flows/read-resource.flow.ts
  • libs/sdk/src/auth/flows/session.verify.flow.ts
  • libs/sdk/src/tool/tool.instance.ts
  • libs/sdk/src/transport/adapters/transport.local.adapter.ts
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/prompt/flows/get-prompt.flow.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/auth/flows/auth.verify.flow.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/prompt/flows/prompts-list.flow.ts
  • libs/sdk/src/tool/tool.utils.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/tool/tool.registry.ts
**/*.{ts,tsx,md}

📄 CodeRabbit inference engine (CLAUDE.md)

Document known limitations clearly in code comments and README files

Files:

  • libs/sdk/src/resource/flows/read-resource.flow.ts
  • plugins/plugin-cache/src/cache.types.ts
  • libs/sdk/src/auth/flows/session.verify.flow.ts
  • plugins/plugin-cache/src/cache.plugin.ts
  • libs/sdk/src/tool/tool.instance.ts
  • libs/sdk/src/transport/adapters/transport.local.adapter.ts
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/prompt/flows/get-prompt.flow.ts
  • plugins/plugin-cache/src/__tests__/cache.plugin.test.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/auth/flows/auth.verify.flow.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/prompt/flows/prompts-list.flow.ts
  • libs/sdk/src/tool/tool.utils.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/tool/tool.registry.ts
**/libs/sdk/src/**/*flow*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Do not mutate rawInput in flows - use state.set() for managing flow state instead of modifying input parameters

Files:

  • libs/sdk/src/resource/flows/read-resource.flow.ts
  • libs/sdk/src/auth/flows/session.verify.flow.ts
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/prompt/flows/get-prompt.flow.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/auth/flows/auth.verify.flow.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/prompt/flows/prompts-list.flow.ts
**/libs/**/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add backwards compatibility exports in new libraries - avoid legacy prefixes like PT-001 in test names

Files:

  • libs/sdk/src/resource/flows/read-resource.flow.ts
  • libs/sdk/src/auth/flows/session.verify.flow.ts
  • libs/sdk/src/tool/tool.instance.ts
  • libs/sdk/src/transport/adapters/transport.local.adapter.ts
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/prompt/flows/get-prompt.flow.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/auth/flows/auth.verify.flow.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/prompt/flows/prompts-list.flow.ts
  • libs/sdk/src/tool/tool.utils.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/tool/tool.registry.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/resource/flows/read-resource.flow.ts
  • libs/uipack/package.json
  • libs/sdk/src/auth/flows/session.verify.flow.ts
  • libs/sdk/src/tool/tool.instance.ts
  • libs/sdk/src/transport/adapters/transport.local.adapter.ts
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/prompt/flows/get-prompt.flow.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/auth/flows/auth.verify.flow.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/prompt/flows/prompts-list.flow.ts
  • libs/sdk/src/tool/tool.utils.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/tool/tool.registry.ts
libs/uipack/**/{package.json,*.ts,*.tsx,*.js,*.jsx}

📄 CodeRabbit inference engine (libs/uipack/CLAUDE.md)

Do not add React dependencies to @frontmcp/uipack - it must remain React-free. Use @frontmcp/ui for React components.

Files:

  • libs/uipack/package.json
docs/draft/docs/**

⚙️ CodeRabbit configuration file

docs/draft/docs/**: This folder holds the draft/source docs that humans are expected to edit. When authors want to add or change documentation, they should do it here. The Codex workflow uses these drafts, together with the code diff, to generate the latest docs under docs/docs/. As a reviewer: - Encourage contributors to add/update content here instead of docs/docs/. - It is fine to do structural/content feedback here (clarity, examples, etc).

Files:

  • docs/draft/docs/plugins/cache-plugin.mdx
docs/**

⚙️ CodeRabbit configuration file

docs/**: Repository documentation for the SDK, using MDX and hosted by Mintlify. See more specific rules for: - docs/docs/** (latest rendered docs, automation-only) - docs/v/** (archived versions, read-only) - docs/draft/docs/** (human-editable drafts) - docs/blogs/** (blogs, human edited) - docs/docs.json (Mintlify navigation)

Files:

  • docs/draft/docs/plugins/cache-plugin.mdx
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Test all code paths including error conditions - include constructor validation tests, error class instanceof checks, and edge cases

Files:

  • plugins/plugin-cache/src/__tests__/cache.plugin.test.ts
🧠 Learnings (26)
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters}/**/src/**/*.ts : Use getCapabilities() method in registries for dynamic capability exposure - do not hardcode capabilities in transport adapters

Applied to files:

  • libs/sdk/src/resource/flows/read-resource.flow.ts
  • libs/sdk/src/transport/adapters/transport.local.adapter.ts
  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/prompt/flows/get-prompt.flow.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/prompt/flows/prompts-list.flow.ts
  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/tool/tool.registry.ts
📚 Learning: 2026-01-06T02:34:55.680Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.680Z
Learning: Applies to libs/plugins/**/*.ts : Extend tool metadata using `declare global` pattern to allow tools to specify plugin-specific options in their decorators

Applied to files:

  • plugins/plugin-cache/src/cache.types.ts
  • plugins/plugin-cache/src/cache.plugin.ts
  • plugins/plugin-cache/src/__tests__/cache.plugin.test.ts
  • libs/sdk/src/tool/tool.utils.ts
📚 Learning: 2026-01-06T02:34:55.680Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.680Z
Learning: Applies to libs/plugins/**/*.ts : RememberPlugin scopes include: `session` (default, current session only), `user` (persists across sessions), `tool` (tied to specific tool + session), and `global` (shared across all sessions/users)

Applied to files:

  • plugins/plugin-cache/src/cache.types.ts
📚 Learning: 2026-01-04T14:35:18.353Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.353Z
Learning: Applies to libs/uipack/**/{package.json,*.ts,*.tsx,*.js,*.jsx} : Do not add React dependencies to frontmcp/uipack - it must remain React-free. Use frontmcp/ui for React components.

Applied to files:

  • libs/uipack/package.json
📚 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/package.json : The frontmcp/ui package requires React as a peer dependency (^18.0.0 || ^19.0.0)

Applied to files:

  • libs/uipack/package.json
📚 Learning: 2026-01-06T02:34:55.680Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.680Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules

Applied to files:

  • libs/uipack/package.json
📚 Learning: 2026-01-06T02:34:55.680Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.680Z
Learning: Applies to libs/plugins/**/*.ts : Always use `frontmcp/utils` for cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead of `node:crypto`

Applied to files:

  • libs/uipack/package.json
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/*.{ts,tsx} : Always use frontmcp/utils for cryptographic operations - use hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode instead of node:crypto

Applied to files:

  • libs/uipack/package.json
📚 Learning: 2026-01-06T02:34:55.680Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.680Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support

Applied to files:

  • libs/uipack/package.json
📚 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/package.json : Entry points must match the documented paths: frontmcp/ui/react, frontmcp/ui/renderers, frontmcp/ui/render, frontmcp/ui/universal, frontmcp/ui/bundler, frontmcp/ui/bridge, frontmcp/ui/components, frontmcp/ui/layouts, frontmcp/ui/web-components

Applied to files:

  • libs/uipack/package.json
📚 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/uipack/package.json
📚 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/uipack/package.json
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/*.{ts,tsx} : Always use frontmcp/utils for file system operations - use readFile, writeFile, mkdir, rename, unlink, stat, etc. instead of fs/promises or node:fs

Applied to files:

  • libs/uipack/package.json
📚 Learning: 2026-01-06T02:34:55.680Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.680Z
Learning: Applies to libs/plugins/**/*plugin.ts : Use module augmentation for context properties via `declare module 'frontmcp/sdk'` combined with runtime plugin metadata `contextExtensions`, not module-level side effects

Applied to files:

  • plugins/plugin-cache/src/cache.plugin.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/plugins/**/src/**/*.ts : Use module augmentation to extend ExecutionContextBase with new properties - declare module 'frontmcp/sdk' and define interface extensions for plugin context properties

Applied to files:

  • plugins/plugin-cache/src/cache.plugin.ts
📚 Learning: 2026-01-06T02:34:55.680Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.680Z
Learning: Applies to libs/plugins/**/*plugin.ts : Extend ExecutionContextBase with plugin-specific properties using module declaration (`declare module 'frontmcp/sdk'`) combined with `contextExtensions` in plugin metadata

Applied to files:

  • plugins/plugin-cache/src/cache.plugin.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters,plugins}/**/src/**/*.ts : Validation flow pattern - execute/read methods return strictly typed MCP responses, parseOutput normalizes various input shapes, flows finalize using entry's parse methods

Applied to files:

  • libs/sdk/src/resource/flows/resource-templates-list.flow.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/auth/flows/auth.verify.flow.ts
📚 Learning: 2026-01-06T02:34:55.680Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.680Z
Learning: Applies to libs/plugins/**/*plugin.ts : Plugins should extend `DynamicPlugin<Options, OptionsInput>` for configurable behavior, with `Plugin` decorator specifying name, description, and static providers

Applied to files:

  • plugins/plugin-cache/src/__tests__/cache.plugin.test.ts
📚 Learning: 2026-01-06T02:34:55.680Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.680Z
Learning: Applies to libs/plugins/**/*.test.ts : Tests must achieve 95%+ code coverage across all metrics; use `MockStore` implementing `RememberStoreInterface` for RememberPlugin testing

Applied to files:

  • plugins/plugin-cache/src/__tests__/cache.plugin.test.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Test all code paths including error conditions - include constructor validation tests, error class instanceof checks, and edge cases

Applied to files:

  • plugins/plugin-cache/src/__tests__/cache.plugin.test.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/sdk/src/**/*flow*.ts : Do not mutate rawInput in flows - use state.set() for managing flow state instead of modifying input parameters

Applied to files:

  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
📚 Learning: 2025-11-05T15:00:47.800Z
Learnt from: frontegg-david
Repo: agentfront/frontmcp PR: 11
File: libs/core/src/auth/jwks/jwks.service.ts:62-0
Timestamp: 2025-11-05T15:00:47.800Z
Learning: In the FrontMCP codebase (libs/core/src/auth/jwks/jwks.service.ts), the verifyGatewayToken method intentionally skips signature verification when running in development-only no-auth mode, using decodeJwtPayloadSafe instead of jwtVerify. This is a deliberate design for local development convenience and should not be flagged as a security issue when the PR or code context indicates development/no-auth mode.

Applied to files:

  • libs/sdk/src/auth/flows/auth.verify.flow.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters,plugins}/**/src/**/*.ts : MCP response types must use strictly typed MCP protocol definitions, not `unknown` - use `Promise<GetPromptResult>`, `Promise<ReadResourceResult>`, and similar MCP-defined types for execute() and read() methods

Applied to files:

  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/resource/resource.registry.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/sdk/src/common/records/**/*.ts : Centralize record types in common/records directory - import AnyResourceRecord and similar types from common/records, not from module-specific files

Applied to files:

  • libs/sdk/src/prompt/prompt.registry.ts
  • libs/sdk/src/resource/resource.registry.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters,plugins}/**/src/**/*event*.ts : Use `changeScope` instead of `scope` property name in change event types to avoid confusion with Scope class

Applied to files:

  • libs/sdk/src/resource/resource.registry.ts
  • libs/sdk/src/tool/tool.registry.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/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/tool/tool.registry.ts
🧬 Code graph analysis (7)
libs/sdk/src/tool/tool.instance.ts (2)
libs/sdk/src/common/interfaces/tool.interface.ts (2)
  • input (68-70)
  • input (72-79)
libs/cli/src/templates/3rd-party-integration/src/tools/example.list.ts (1)
  • inputSchema (12-17)
libs/sdk/src/transport/adapters/transport.local.adapter.ts (2)
libs/sdk/src/scope/scope.instance.ts (1)
  • apps (329-331)
libs/sdk/src/agent/agent.scope.ts (2)
  • apps (245-247)
  • apps (322-324)
libs/sdk/src/resource/flows/resource-templates-list.flow.ts (3)
libs/sdk/src/agent/hooks/agent.hooks.ts (1)
  • Stage (54-54)
libs/sdk/src/scope/scope.instance.ts (1)
  • apps (329-331)
libs/sdk/src/common/interfaces/execution-context.interface.ts (1)
  • error (188-190)
libs/sdk/src/prompt/flows/get-prompt.flow.ts (3)
libs/sdk/src/agent/hooks/agent.hooks.ts (1)
  • Stage (54-54)
libs/sdk/src/scope/scope.instance.ts (1)
  • apps (329-331)
libs/sdk/src/common/interfaces/prompt.interface.ts (1)
  • error (118-120)
libs/sdk/src/resource/flows/resources-list.flow.ts (4)
libs/sdk/src/agent/hooks/agent.hooks.ts (1)
  • Stage (54-54)
libs/sdk/src/scope/scope.instance.ts (2)
  • apps (329-331)
  • resources (341-343)
libs/sdk/src/common/interfaces/execution-context.interface.ts (1)
  • error (188-190)
libs/sdk/src/app/instances/app.local.instance.ts (1)
  • resources (85-87)
libs/sdk/src/auth/flows/auth.verify.flow.ts (1)
libs/sdk/src/agent/hooks/agent.hooks.ts (1)
  • Stage (54-54)
libs/sdk/src/tool/tool.utils.ts (1)
libs/sdk/src/common/entries/tool.entry.ts (1)
  • ParsedToolResult (28-28)
⏰ 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: Build Libraries
  • GitHub Check: Lint & Format Checks
🔇 Additional comments (46)
libs/sdk/src/auth/flows/auth.verify.flow.ts (3)

76-85: LGTM!

The flow plan ordering is logical: handleAnonymousFallback is correctly positioned after handlePublicMode to handle the transparent mode fallback case before requireAuthorizationHeader enforces the auth header requirement.


235-243: LGTM!

The filter logic correctly handles all cases:

  • Skips for public mode (handled by handlePublicMode)
  • Skips when auth header exists (will proceed to verifyToken)
  • Skips for transparent mode with allowAnonymous (handled by handleAnonymousFallback)

The comment on line 239 accurately documents the relationship with handleAnonymousFallback.


204-210: LGTM!

The stage filter correctly gates handleAnonymousFallback to only execute when:

  1. In transparent mode
  2. No token is provided
  3. allowAnonymous is explicitly enabled

This ensures the fallback only triggers under the intended conditions.

libs/sdk/src/tool/tool.instance.ts (2)

138-141: LGTM! Good exhaustive switch handling.

Adding a default case that throws for unknown ToolKind values ensures the switch is exhaustive and will fail fast if a new kind is added without handling it here.


144-158: LGTM! Passthrough for remote tools is appropriate.

Using .passthrough() for remote tools allows arguments to flow through without stripping unknown keys, since validation happens on the remote server. The annotation check metadata.annotations?.['frontmcp:remote'] === true is correctly guarded with optional chaining.

One minor observation: the inputSchema variable on line 151 shadows the class property this.inputSchema referenced on the same line. While functionally correct, consider using a different variable name like passthroughSchema for clarity.

libs/sdk/src/tool/tool.utils.ts (2)

82-87: LGTM! Switch relies on TypeScript exhaustiveness.

The switch implicitly returns undefined for any unhandled cases, which TypeScript will catch if ToolKind is extended without updating this function (assuming strict return type checking).


115-120: LGTM! Early return prevents double-wrapping.

Short-circuiting when raw is already a valid CallToolResult prevents MCP-formatted responses from being wrapped again. This is correct behavior for remote tool results.

libs/sdk/src/resource/flows/read-resource.flow.ts (1)

51-51: LGTM! Stage ordering is correct.

Adding ensureRemoteCapabilities before findResource ensures remote app capabilities are loaded before resource lookup.

libs/sdk/src/resource/flows/resource-templates-list.flow.ts (1)

40-40: LGTM! Stage added to pre phase.

Remote capabilities are loaded before findTemplates.

libs/sdk/src/prompt/flows/get-prompt.flow.ts (1)

45-45: LGTM! Stage added before findPrompt.

Ensures remote capabilities are available for prompt lookup.

libs/sdk/src/resource/flows/resources-list.flow.ts (2)

36-36: LGTM! Pre phase updated correctly.


149-167: LGTM! Resource deduplication by URI.

Deduplicating resources by their URI (with fallback to metadata.name) prevents duplicate entries when the same resource is registered from multiple sources (e.g., local and remote). The logging clearly shows the deduplication effect.

libs/sdk/src/tool/flows/tools-list.flow.ts (2)

47-47: LGTM! Pre phase updated correctly.


182-196: LGTM! Tool deduplication logic.

Deduplicating tools by fullName || metadata.id || metadata.name is appropriate and matches the key hierarchy used elsewhere in tool resolution. The logging clearly indicates deduplication occurred.

libs/sdk/src/prompt/flows/prompts-list.flow.ts (3)

37-41: LGTM! Flow plan extended with remote capabilities stage.

The ensureRemoteCapabilities stage is correctly added to the pre array, ensuring remote app capabilities are loaded before prompt discovery begins.


97-143: Well-structured remote capability loading with proper error isolation.

The implementation correctly:

  • Scans all app registries to find remote apps
  • Uses parallel execution with Promise.all for efficiency
  • Isolates failures per-app to prevent one failing remote from blocking others
  • Uses duck-typing check for ensureCapabilitiesLoaded method presence

One minor observation: the type annotation on line 109 uses an inline object type with optional ensureCapabilitiesLoaded. Consider extracting this to a named type or interface for better readability if this pattern is reused elsewhere.


145-177: Deduplication logic is sound but consider edge case.

The deduplication using fullName || metadata.name works well for preventing duplicates. However, if two different prompts legitimately have the same metadata.name but different fullName values (both truthy), they would be correctly distinguished. But if one has fullName and another has only metadata.name matching the first's fullName, they'd be treated as duplicates.

Given the logging at line 166 shows the deduplication count, this is likely intentional behavior for the remote MCP scenario where the same prompt may appear via multiple paths.

libs/sdk/src/prompt/prompt.registry.ts (5)

118-131: LGTM! Clear separation of remote vs local app adoption.

The initialization flow now properly distinguishes between remote and local apps using the isRemote flag, addressing previous feedback about fragile duck-typing. Each path has its own dedicated adoption method.


178-226: Good implementation addressing past review concerns.

This implementation correctly addresses the issues previously raised:

  • ✅ Uses Symbol.for() for stable, deterministic tokens (line 205)
  • ✅ Cleans up stale entries before re-adoption (lines 190-194)
  • ✅ Validates remote prompt structure before processing
  • ✅ Has try-catch with proper error logging
  • ✅ Subscribes to remote registry for lazy-loaded prompts

The closure-based adoptRemotePrompts helper enables clean re-adoption on subscription updates.


228-239: LGTM! Local app adoption delegates to existing mechanism.

The local adoption path correctly filters by owner kind and delegates to the existing adoptFromChild method, maintaining consistency with the hierarchical registry pattern.


437-452: Good type widening to PromptEntry.

The makeRow signature now accepts PromptEntry instead of PromptInstance, which correctly supports both local PromptInstance and remote ProxyPromptEntry types. This addresses the type safety concerns from past reviews.


475-495: LGTM! Provider ID extraction handles PromptEntry safely.

The method now accepts PromptEntry and uses defensive access patterns with proper type guards. The cast through unknown is appropriate for accessing potentially-dynamic metadata properties.

libs/sdk/src/resource/resource.registry.ts (3)

128-141: LGTM! Consistent remote/local separation pattern.

The initialization flow correctly separates remote and local app adoption, consistent with the prompt registry implementation.


246-257: LGTM! Local adoption delegates to existing mechanism.

Consistent with the prompt registry pattern.


491-514: LGTM! makeRow correctly accepts ResourceEntry.

The signature change allows both ResourceInstance and ProxyResourceEntry to be used, addressing the past review concern about type safety.

libs/sdk/src/tool/tool.registry.ts (4)

109-116: Good fix for duck-typing concern.

The code now uses the explicit isRemote flag instead of checking for getMcpClient method presence, addressing the previous review feedback about fragile duck-typing.


217-228: LGTM! Consistent local adoption pattern.

Matches the pattern established in prompt and resource registries.


405-411: LGTM! makeRow correctly accepts ToolEntry.

This addresses the past review concern about type safety. Both ToolInstance and ProxyToolEntry can now be passed without unsafe casting.


434-453: LGTM! Provider ID extraction updated for ToolEntry.

The method now accepts ToolEntry and uses defensive patterns consistent with the other registries.

libs/sdk/src/auth/flows/session.verify.flow.ts (1)

61-61: LGTM: Stage ordering is correct.

The new handleAnonymousFallback stage is correctly positioned after handlePublicMode and before requireAuthorizationHeader, ensuring proper precedence for different auth modes.

libs/sdk/src/transport/adapters/transport.local.adapter.ts (3)

81-83: LGTM - Capability flags correctly include remote apps.

The logic to enable prompts, resources, and tools capabilities when remote apps are present is correct. This ensures the MCP server advertises these capabilities even before remote app entries are fully loaded.


117-117: LGTM - Helpful observability enhancement.

Adding hasRemoteApps to the logging output improves debugging and monitoring of remote app integration.


100-100: The capability spread order and override behavior is correct and intentional. Remote capabilities are pre-advertised as defaults when remote apps exist, and local registries either match or supersede these values with their own capability flags. No data loss occurs since local registries return the same capability structures when items are registered.

plugins/plugin-cache/src/cache.types.ts (1)

23-60: Excellent documentation and API design.

The new optional properties (defaultTTL, toolPatterns, bypassHeader) are well-documented with comprehensive JSDoc, clear examples, and maintain backward compatibility. The distinction between tool-level ttl (in CachePluginToolOptions) and global defaultTTL is properly established.

The security note about the x-frontmcp-* prefix requirement for headers is a good practice that helps ensure headers are properly captured.

plugins/plugin-cache/src/__tests__/cache.plugin.test.ts (3)

9-17: Good type safety improvements.

The helper types ValueProvider and FactoryProvider properly formalize the provider shapes returned by dynamicProviders, improving type safety throughout the test suite while maintaining compatibility with the generic ProviderFactoryType return type.


259-273: LGTM - Constructor tests for new options.

The tests properly verify that toolPatterns and bypassHeader options are accepted and stored correctly in the plugin configuration.


276-368: Excellent test coverage for pattern matching.

The isCacheable test suite is comprehensive and covers all essential scenarios:

  • No patterns configured
  • Exact tool name matches
  • Various wildcard patterns (namespace, prefix, suffix, middle)
  • Multiple pattern combinations
  • Special regex character escaping

This provides strong confidence in the pattern matching implementation.

libs/uipack/package.json (1)

62-62: Dependency is valid and secure.

Version 2.3.0 of enclave-vm is available on npm (confirmed in yarn.lock) and is actively used throughout the codebase. No known security vulnerabilities were found in public CVE databases.

docs/draft/docs/plugins/cache-plugin.mdx (3)

210-220: LGTM! Configuration parameters are well-documented.

The toolPatterns and bypassHeader configuration options are clearly documented with appropriate examples and defaults that match the implementation. The bypass header correctly uses the required x-frontmcp-* prefix.


274-340: Excellent guidance for remote tool caching.

This section provides comprehensive coverage of pattern-based caching for remote tools, including clear examples, pattern syntax reference, and priority rules. The guidance aligns with the implementation and addresses the use case where tool metadata cannot be directly modified.

As per coding guidelines, this properly documents the remote integration patterns for the cache plugin.


343-364: Clear bypass documentation with correct examples.

The cache bypass section provides practical examples and properly uses the x-frontmcp-disable-cache header name. The custom header configuration example is clear and the use case guidance is helpful.

plugins/plugin-cache/src/cache.plugin.ts (5)

19-23: LGTM! Well-documented default header.

The default bypass header constant is clearly defined with a helpful comment explaining the prefix requirement for header extraction.


126-139: Correct priority logic for cache eligibility.

The shouldCacheTool method properly implements the documented priority rules: tool metadata takes precedence (line 134), then falls back to pattern matching (lines 137-138). This matches the behavior described in the documentation.


167-175: LGTM! Clean TTL resolution logic.

The getTtl method correctly implements the priority chain: tool-specific TTL (lines 171-173) takes precedence over the plugin default (line 174). The fallback to 86400 seconds matches the documented default.


177-249: Past type safety issue is properly resolved.

The updated cache hit handling (lines 217-237) now correctly addresses the type safety concern from the previous review:

  • Line 221 checks if the cached value is a plain object before attempting to augment it with _meta
  • Lines 224-233 only spread object properties when safe to do so
  • Lines 235-236 return primitives and arrays as-is without modification

Additionally, the dual checks for tool.fullName and tool.name (line 192) properly support pattern matching for both namespaced and non-namespaced tool identifiers.


25-42: Pattern matching logic is correct and well-tested.

The matchesToolPattern function properly handles both exact matches and glob-style wildcard patterns. The regex escaping correctly preserves special characters, and the wildcard conversion to .* is properly implemented with anchoring. The test suite comprehensively validates all documented pattern types: namespace wildcards (mintlify:*), prefix patterns (api:get-*), suffix patterns (*-readonly), middle patterns (api:*:list), and special character escaping (api.v1:get-users, api[2]:*). No changes needed.

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: 2

Caution

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

⚠️ Outside diff range comments (1)
libs/sdk/src/tool/tool.registry.ts (1)

489-523: Unsafe cast and type mismatch in validation logic.

Three issues:

  1. Line 492: Casts tool: ToolEntry to ToolInstance without validation. If a ProxyToolEntry or other ToolEntry subclass is passed (which the method signature allows), this cast is unsafe and bypasses TypeScript's type checking.

  2. Lines 495-503: The validation accesses instance.record assuming ToolInstance structure, but ToolEntry may not guarantee this property exists. This creates a runtime risk if non-ToolInstance entries are passed.

  3. Type signature vs. implementation mismatch: The signature accepts ToolEntry but the implementation assumes ToolInstance. This violates the Liskov Substitution Principle.

🔎 Recommended fix: narrow the parameter type or use type guards

Option 1 (preferred): Narrow the parameter type to only accept valid subclasses

- registerToolInstance(tool: ToolEntry): void {
+ registerToolInstance(tool: ToolInstance | RemoteToolInstance): void {
    // Validate that we have a proper instance with required properties
-   // Accept both ToolInstance and RemoteToolInstance (or any ToolEntry subclass with valid record)
-   const instance = tool as ToolInstance;
+   const instance = tool;

Option 2: Add a type guard to validate before casting

  registerToolInstance(tool: ToolEntry): void {
+   // Type guard to check if tool is a registerable instance
+   if (!isRegisterableToolInstance(tool)) {
+     throw new Error('Tool must be a ToolInstance or RemoteToolInstance with required properties');
+   }
    const instance = tool as ToolInstance;
    
+   function isRegisterableToolInstance(t: ToolEntry): t is ToolInstance {
+     const candidate = t as ToolInstance;
+     return !!(candidate.record?.provide && candidate.record?.kind && candidate.record?.metadata);
+   }
🤖 Fix all issues with AI Agents
In @libs/sdk/src/tool/tool.registry.ts:
- Line 172: The direct cast const remoteRegistry = app.tools as ToolRegistry is
unsafe; add a runtime validation before using it: check that app.tools is
non-null and implements the ToolRegistry contract (e.g., has the expected
methods/properties used later such as registerTool, getTool, getAllTools or
whichever ToolRegistry-specific methods the code calls) and only then assign to
remoteRegistry or wrap it; if validation fails either adapt to the minimal
interface, create an adapter that implements ToolRegistry, or throw a
descriptive error. Ensure you update usages that assume ToolRegistry to handle
the validated/adapter type rather than relying on a blind cast.
🧹 Nitpick comments (1)
libs/sdk/src/tool/tool.registry.ts (1)

210-214: Duck-typing check for subscribe method.

Checking typeof remoteRegistry.subscribe === 'function' is defensive but fragile. If the object has a subscribe property that isn't callable or has a different signature, this could fail silently or cause runtime errors.

Consider using a type guard or interface check to validate the registry implements the expected subscription pattern, or document that ToolRegistryInterface guarantees the subscribe method.

📜 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 095bfda and ab058ab.

📒 Files selected for processing (3)
  • libs/sdk/src/auth/flows/auth.verify.flow.ts
  • libs/sdk/src/tool/tool.registry.ts
  • libs/sdk/src/transport/adapters/transport.local.adapter.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • libs/sdk/src/transport/adapters/transport.local.adapter.ts
  • libs/sdk/src/auth/flows/auth.verify.flow.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Enable and use strict TypeScript settings - no any types without strong justification, use unknown for generic type defaults instead
Avoid non-null assertions (!) - use proper error handling and throw specific errors when values are missing instead
Always use @frontmcp/utils for cryptographic operations - use hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode instead of node:crypto
Always use @frontmcp/utils for file system operations - use readFile, writeFile, mkdir, rename, unlink, stat, etc. instead of fs/promises or node:fs
Use constrained generic type parameters with Record<string, string> or similar constraints instead of unconstrained any defaults

Files:

  • libs/sdk/src/tool/tool.registry.ts
**/libs/{sdk,adapters,plugins}/**/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/libs/{sdk,adapters,plugins}/**/src/**/*.ts: MCP response types must use strictly typed MCP protocol definitions, not unknown - use Promise<GetPromptResult>, Promise<ReadResourceResult>, and similar MCP-defined types for execute() and read() methods
Validation flow pattern - execute/read methods return strictly typed MCP responses, parseOutput normalizes various input shapes, flows finalize using entry's parse methods

Files:

  • libs/sdk/src/tool/tool.registry.ts
**/libs/sdk/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Create shared base classes for common functionality - use ExecutionContextBase as parent class for ToolContext, ResourceContext, and similar context classes

Files:

  • libs/sdk/src/tool/tool.registry.ts
**/libs/{sdk,adapters}/**/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use getCapabilities() method in registries for dynamic capability exposure - do not hardcode capabilities in transport adapters

Files:

  • libs/sdk/src/tool/tool.registry.ts
**/*.{ts,tsx,md}

📄 CodeRabbit inference engine (CLAUDE.md)

Document known limitations clearly in code comments and README files

Files:

  • libs/sdk/src/tool/tool.registry.ts
**/libs/**/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add backwards compatibility exports in new libraries - avoid legacy prefixes like PT-001 in test names

Files:

  • libs/sdk/src/tool/tool.registry.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/tool/tool.registry.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters,plugins}/**/src/**/*error*.ts : Use specific MCP error classes with MCP error codes instead of generic errors - define error classes with `mcpErrorCode` property and `toJsonRpcError()` method
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters,plugins}/**/src/**/*event*.ts : Use `changeScope` instead of `scope` property name in change event types to avoid confusion with Scope class

Applied to files:

  • libs/sdk/src/tool/tool.registry.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters}/**/src/**/*.ts : Use getCapabilities() method in registries for dynamic capability exposure - do not hardcode capabilities in transport adapters

Applied to files:

  • libs/sdk/src/tool/tool.registry.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/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/tool/tool.registry.ts
⏰ 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 (5)
libs/sdk/src/tool/tool.registry.ts (5)

109-116: Good improvement: using app.isRemote instead of duck-typing.

The conditional logic now uses a proper property check instead of the fragile getMcpClient method detection from earlier iterations. This is a more robust approach for discriminating remote vs. local apps.


282-289: Excellent: consistent ToolEntry widening across the API.

The systematic change from ToolInstance to ToolEntry across return types and parameters (listAllInstances, listByOwner, exportResolvedNames, getExported, subscribe, makeRow) enables the registry to handle both local instances and remote proxy entries uniformly. This resolves the type cast issues flagged in previous reviews.

Also applies to: 300-300, 376-376, 384-384, 407-407


364-371: Good defensive practice: disambiguation loop guard.

Adding a maxAttempts guard prevents infinite loops during export name disambiguation. The 10,000 iteration limit is reasonable for practical scenarios.


436-455: LGTM: safe type casting with proper guards.

The providerIdOf implementation correctly:

  • Casts through unknown before narrowing to Record<string, unknown>
  • Validates types before property access
  • Uses try-catch for defensive error handling

This follows the coding guideline: "Enable and use strict TypeScript settings."


193-193: Good fix: stable symbols with Symbol.for().

Using Symbol.for() with a composite key (tool:${app.id}:${remoteTool.name}) ensures stable, reusable symbols across registry operations and capability refreshes. This resolves the symbol duplication issue flagged in previous reviews.

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: 2

🤖 Fix all issues with AI Agents
In @libs/sdk/src/tool/tool.registry.ts:
- Around line 299-304: Widen the public API types from ToolInstance to the
broader ToolEntry to support ProxyToolEntry and avoid unsafe casts: change the
return/type annotations for listAllInstances, listByOwner, exportResolvedNames,
getExported, subscribe, and makeRow to use ToolEntry (and readonly ToolEntry[]
where applicable), update any internal mappings (e.g., listAllIndexed().map(...)
usage) and call sites to treat results as ToolEntry (handling instance-specific
access via its instance property when needed), and remove previous casts to
ToolInstance so implementations like ProxyToolEntry are accepted.
- Around line 197-200: The current cleanup in adoptRemoteTools relies on
symbol.description string matching which is fragile; introduce a dedicated
Map<string, IndexedTool[]> named remoteAppTools as a class field to track rows
added per remote app, then in adoptRemoteTools use remoteAppTools.get(app.id) to
obtain previousTools and remove them from this.localRows (filtering out
previousTools.includes(row)); when creating new rows with makeRow and pushing
into this.localRows, also collect them into newRows and call
remoteAppTools.set(app.id, newRows). Ensure you reference localRows,
adoptRemoteTools, makeRow, and remoteAppTools when implementing these changes.
🧹 Nitpick comments (2)
libs/sdk/src/resource/flows/resources-list.flow.ts (1)

74-75: Avoid any type for params.

Per coding guidelines, prefer unknown over any when the type is not fully known. The params variable can be typed more safely.

🔎 Suggested fix
   let method!: string;
-  let params: any;
+  let params: unknown;

Then access cursor with proper type narrowing:

-  const cursor = params?.cursor;
+  const cursor = (params as { cursor?: string } | undefined)?.cursor;
libs/sdk/src/tool/tool.registry.ts (1)

170-175: Consider using interface marker instead of duck-typing.

While the duck-typing check for the subscribe method has safe fallback behavior (if not present, subscription is skipped), a more robust approach would be to use an interface marker property (similar to app.isRemote) to explicitly indicate remote registry capabilities.

This is optional since the current implementation safely handles cases where subscribe is absent or has an unexpected signature.

📜 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 ab058ab and c9a7ab9.

📒 Files selected for processing (4)
  • libs/sdk/src/prompt/flows/prompts-list.flow.ts
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
  • libs/sdk/src/tool/tool.registry.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • libs/sdk/src/prompt/flows/prompts-list.flow.ts
  • libs/sdk/src/tool/flows/tools-list.flow.ts
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Enable and use strict TypeScript settings - no any types without strong justification, use unknown for generic type defaults instead
Avoid non-null assertions (!) - use proper error handling and throw specific errors when values are missing instead
Always use @frontmcp/utils for cryptographic operations - use hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode instead of node:crypto
Always use @frontmcp/utils for file system operations - use readFile, writeFile, mkdir, rename, unlink, stat, etc. instead of fs/promises or node:fs
Use constrained generic type parameters with Record<string, string> or similar constraints instead of unconstrained any defaults

Files:

  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/tool/tool.registry.ts
**/libs/{sdk,adapters,plugins}/**/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/libs/{sdk,adapters,plugins}/**/src/**/*.ts: MCP response types must use strictly typed MCP protocol definitions, not unknown - use Promise<GetPromptResult>, Promise<ReadResourceResult>, and similar MCP-defined types for execute() and read() methods
Validation flow pattern - execute/read methods return strictly typed MCP responses, parseOutput normalizes various input shapes, flows finalize using entry's parse methods

Files:

  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/tool/tool.registry.ts
**/libs/sdk/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Create shared base classes for common functionality - use ExecutionContextBase as parent class for ToolContext, ResourceContext, and similar context classes

Files:

  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/tool/tool.registry.ts
**/libs/{sdk,adapters}/**/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use getCapabilities() method in registries for dynamic capability exposure - do not hardcode capabilities in transport adapters

Files:

  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/tool/tool.registry.ts
**/*.{ts,tsx,md}

📄 CodeRabbit inference engine (CLAUDE.md)

Document known limitations clearly in code comments and README files

Files:

  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/tool/tool.registry.ts
**/libs/sdk/src/**/*flow*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Do not mutate rawInput in flows - use state.set() for managing flow state instead of modifying input parameters

Files:

  • libs/sdk/src/resource/flows/resources-list.flow.ts
**/libs/**/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add backwards compatibility exports in new libraries - avoid legacy prefixes like PT-001 in test names

Files:

  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/tool/tool.registry.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/resource/flows/resources-list.flow.ts
  • libs/sdk/src/tool/tool.registry.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters,plugins}/**/src/**/*error*.ts : Use specific MCP error classes with MCP error codes instead of generic errors - define error classes with `mcpErrorCode` property and `toJsonRpcError()` method
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters}/**/src/**/*.ts : Use getCapabilities() method in registries for dynamic capability exposure - do not hardcode capabilities in transport adapters

Applied to files:

  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/tool/tool.registry.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters,plugins}/**/src/**/*.ts : Validation flow pattern - execute/read methods return strictly typed MCP responses, parseOutput normalizes various input shapes, flows finalize using entry's parse methods

Applied to files:

  • libs/sdk/src/resource/flows/resources-list.flow.ts
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters,plugins}/**/src/**/*event*.ts : Use `changeScope` instead of `scope` property name in change event types to avoid confusion with Scope class

Applied to files:

  • libs/sdk/src/tool/tool.registry.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/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/tool/tool.registry.ts
🧬 Code graph analysis (1)
libs/sdk/src/resource/flows/resources-list.flow.ts (4)
libs/sdk/src/agent/hooks/agent.hooks.ts (1)
  • Stage (54-54)
libs/sdk/src/scope/scope.instance.ts (2)
  • apps (329-331)
  • resources (341-343)
libs/sdk/src/common/interfaces/execution-context.interface.ts (1)
  • error (188-190)
libs/sdk/src/app/instances/app.local.instance.ts (1)
  • resources (85-87)
⏰ 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: Build Libraries
  • GitHub Check: Lint & Format Checks
🔇 Additional comments (10)
libs/sdk/src/resource/flows/resources-list.flow.ts (3)

128-137: Verify silent error handling is intentional for resilience.

Errors during capability loading are logged as warnings and swallowed. This provides resilience (partial failures don't block the entire list operation), but could mask systematic issues with remote server connectivity.

Consider whether aggregate failure tracking would be helpful (e.g., emit a single warning if all remote apps failed to load, vs. some succeeding).


149-168: LGTM on deduplication logic.

The composite key ${resource.owner.id}:${resource.uri || resource.metadata.name} correctly handles:

  • Same resource registered multiple times by the same owner (deduped)
  • Different owners with resources of the same name (preserved for conflict resolution)
  • Resources without URI (falls back to name)

The logging clearly reflects the deduplication outcome.


95-141: New stage integrates well with the flow pattern.

The ensureRemoteCapabilities method correctly:

  • Discovers remote apps across all AppRegistry instances
  • Loads capabilities in parallel with Promise.all
  • Uses state management properly (no rawInput mutation)
  • Provides clear logging for debugging
libs/sdk/src/tool/tool.registry.ts (7)

15-16: LGTM! Necessary imports for remote app support.

The new imports for Scope and AppEntry enable the enhanced adoption logic that distinguishes remote from local apps.


105-116: Excellent improvement: metadata-based branching replaces duck-typing.

The use of app.isRemote to distinguish remote from local apps is a robust replacement for the previous duck-typed getMcpClient check. The clean separation of adoption paths improves maintainability.


210-211: Excellent fix: stable symbols and type-safe row creation.

The use of Symbol.for() creates stable, deterministic tokens that resolve the previous symbol duplication issue. Combined with the type widening of makeRow to accept ToolEntry, this eliminates the unsafe type cast that was previously flagged.


234-245: LGTM! Clean delegation to existing adoption mechanism.

The local app adoption correctly filters for app-owned registries and delegates to the existing adoptFromChild method, maintaining consistency with the established pattern.


379-389: Good defensive programming: bounded disambiguation loop.

Adding a maxAttempts limit (10,000) with an explicit error when exceeded prevents potential infinite loops while providing clear diagnostics if an unexpected naming collision occurs.


483-499: Good defensive validation with type narrowing.

The isRegisterableTool type guard validates that tool entries have the required properties (name, record.provide, record.kind, record.metadata) before registration. This provides runtime safety while narrowing the type for subsequent use.


529-566: Enhanced validation with detailed error diagnostics.

The improved registerToolInstance method validates tool structure using the type guard and provides specific error messages for each validation failure. This significantly improves debuggability when registering remote or agent-scoped tools.

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: 0

🧹 Nitpick comments (2)
libs/sdk/src/tool/tool.registry.ts (2)

185-242: Excellent improvements—validation and error handling are now robust.

The method now includes:

  • ✅ Validation of app.tools interface before use
  • ✅ Error handling with logging
  • ✅ Stable symbols using Symbol.for()
  • ✅ Efficient cleanup via remoteAppTools Map
  • ✅ Conditional subscription with type guard

This addresses all critical concerns from previous reviews.

Optional: Validate getTools() return type

For additional robustness, validate that getTools() returns an array:

  try {
-   const remoteTools = app.tools.getTools();
+   const remoteTools = app.tools.getTools();
+   if (!Array.isArray(remoteTools)) {
+     scope.logger.warn(`Remote app ${app.id} getTools() did not return an array`);
+     return;
+   }
    const newRows: IndexedTool[] = [];

309-424: Essential refactor—type widening enables remote tool support.

The systematic widening of types from ToolInstance to ToolEntry across the public API is essential for supporting ProxyToolEntry and remote tool instances. This eliminates unsafe type casts and provides a flexible, type-safe foundation for both local and remote tools.

Methods updated: listAllInstances, listByOwner, exportResolvedNames, getExported, subscribe, and internal helpers.

Optional: Improve disambiguate error message

The error on line 398 could be more actionable:

- throw new Error(`Failed to disambiguate name "${candidate}" after ${maxAttempts} attempts`);
+ throw new Error(`Failed to disambiguate tool name "${candidate}" after ${maxAttempts} attempts. This indicates too many tools with similar names—consider adjusting naming or maxLen configuration.`);

Also applies to: 327-327, 346-346, 389-399, 403-403, 411-411

📜 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 c9a7ab9 and d7a5fa9.

📒 Files selected for processing (2)
  • libs/sdk/src/resource/flows/resources-list.flow.ts
  • libs/sdk/src/tool/tool.registry.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • libs/sdk/src/resource/flows/resources-list.flow.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Enable and use strict TypeScript settings - no any types without strong justification, use unknown for generic type defaults instead
Avoid non-null assertions (!) - use proper error handling and throw specific errors when values are missing instead
Always use @frontmcp/utils for cryptographic operations - use hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode instead of node:crypto
Always use @frontmcp/utils for file system operations - use readFile, writeFile, mkdir, rename, unlink, stat, etc. instead of fs/promises or node:fs
Use constrained generic type parameters with Record<string, string> or similar constraints instead of unconstrained any defaults

Files:

  • libs/sdk/src/tool/tool.registry.ts
**/libs/{sdk,adapters,plugins}/**/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/libs/{sdk,adapters,plugins}/**/src/**/*.ts: MCP response types must use strictly typed MCP protocol definitions, not unknown - use Promise<GetPromptResult>, Promise<ReadResourceResult>, and similar MCP-defined types for execute() and read() methods
Validation flow pattern - execute/read methods return strictly typed MCP responses, parseOutput normalizes various input shapes, flows finalize using entry's parse methods

Files:

  • libs/sdk/src/tool/tool.registry.ts
**/libs/sdk/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Create shared base classes for common functionality - use ExecutionContextBase as parent class for ToolContext, ResourceContext, and similar context classes

Files:

  • libs/sdk/src/tool/tool.registry.ts
**/libs/{sdk,adapters}/**/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use getCapabilities() method in registries for dynamic capability exposure - do not hardcode capabilities in transport adapters

Files:

  • libs/sdk/src/tool/tool.registry.ts
**/*.{ts,tsx,md}

📄 CodeRabbit inference engine (CLAUDE.md)

Document known limitations clearly in code comments and README files

Files:

  • libs/sdk/src/tool/tool.registry.ts
**/libs/**/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not add backwards compatibility exports in new libraries - avoid legacy prefixes like PT-001 in test names

Files:

  • libs/sdk/src/tool/tool.registry.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/tool/tool.registry.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters,plugins}/**/src/**/*error*.ts : Use specific MCP error classes with MCP error codes instead of generic errors - define error classes with `mcpErrorCode` property and `toJsonRpcError()` method
📚 Learning: 2026-01-06T02:34:41.820Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:41.820Z
Learning: Applies to **/libs/{sdk,adapters}/**/src/**/*.ts : Use getCapabilities() method in registries for dynamic capability exposure - do not hardcode capabilities in transport adapters

Applied to files:

  • libs/sdk/src/tool/tool.registry.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/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/tool/tool.registry.ts
🧬 Code graph analysis (1)
libs/sdk/src/tool/tool.registry.ts (7)
libs/sdk/src/tool/tool.types.ts (1)
  • IndexedTool (6-19)
libs/sdk/src/scope/scope.instance.ts (2)
  • apps (329-331)
  • Scope (37-384)
libs/sdk/src/agent/agent.scope.ts (2)
  • apps (245-247)
  • apps (322-324)
libs/sdk/src/common/entries/base.entry.ts (1)
  • EntryLineage (6-6)
libs/sdk/src/common/interfaces/index.ts (1)
  • Token (10-10)
libs/sdk/src/auth/session/record/session.base.ts (1)
  • id (151-153)
libs/sdk/src/tool/tool.instance.ts (2)
  • getMetadata (99-101)
  • ToolInstance (37-184)
⏰ 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: Build Libraries
  • GitHub Check: Lint & Format Checks
🔇 Additional comments (8)
libs/sdk/src/tool/tool.registry.ts (8)

38-39: Good solution for remote tool tracking.

The dedicated remoteAppTools Map efficiently tracks tools by app ID, enabling robust cleanup during re-adoption. This addresses the fragile string-based filtering flagged in previous reviews.


173-178: LGTM—duck-typing is appropriate for remote registries.

The type guard validates that subscribe exists and is a function. While it doesn't verify the exact signature (options and callback parameters), this level of checking is reasonable for remote registries that may have varying implementations.


248-255: LGTM—clean delegation to existing adoption logic.

The method correctly delegates local app tool adoption to the existing adoptFromChild mechanism, maintaining consistency with the hierarchical registry pattern.


434-434: Type safety improvements align with ToolEntry widening.

Both makeRow and providerIdOf now accept ToolEntry, maintaining consistency with the broader refactoring. The defensive type narrowing in providerIdOf (casting through unknown) follows TypeScript best practices.

Also applies to: 463-482

Also applies to: 463-482


582-591: Correct dynamic capability exposure.

The method properly implements the registry pattern of exposing capabilities only when tools are registered, following coding guidelines. Based on learnings, this prevents hardcoding capabilities in transport adapters.


108-133: No changes needed. The code is already safe.

getActiveScope() has return type Scope (non-optional), not Scope | null | undefined. The underlying get() method throws an error ("Provider Scope is not available in local or parent registries") if Scope cannot be resolved, so there is no null/undefined case to defend against. The suggested defensive check would never execute.

Likely an incorrect or invalid review comment.


493-509: Type guard correctly validates registration requirements, but fix incorrect reference.

The isRegisterableTool guard properly validates that entries have the required name and record properties (with provide, kind, metadata) before registration. However, the original comment references ProxyToolEntry, which does not exist—remote tools use regular ToolInstance created through the createRemoteToolInstance() factory. The type guard is correct for all ToolEntry subclasses.

Likely an incorrect or invalid review comment.


538-576: The type cast is safe and justified. ProxyToolEntry does not exist in the codebase, and "RemoteToolInstance" is only mentioned in comments—remote tools are instantiated via createRemoteToolInstance(), which returns a standard ToolInstance object.

The pattern is correct:

  • registerToolInstance(tool: ToolEntry) accepts the abstract type for API flexibility
  • All actual values passed are ToolInstance objects (either created directly or via createRemoteToolInstance)
  • The instances map correctly stores only ToolInstance instances
  • The type guard validates required properties exist
  • The only usage of the map (getInlineTools()) returns values as ToolEntry[] anyway, so the cast poses no risk

No changes needed.

Likely an incorrect or invalid review comment.

@frontegg-david frontegg-david merged commit 3852787 into main Jan 6, 2026
21 of 22 checks passed
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