-
Notifications
You must be signed in to change notification settings - Fork 4
feat: Refactor authentication module structure and add new utilities #206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
📝 WalkthroughWalkthroughAdds a new publishable package Changes
Sequence Diagram(s)sequenceDiagram
participant SDK as SDK (consumer)
participant Auth as `@frontmcp/auth` (JwksService)
participant KP as `@frontmcp/utils` (KeyPersistence)
participant FS as FileSystemStorageAdapter
SDK->>Auth: request orchestrator signing key
Auth->>Auth: shouldEnablePersistence()
alt persistence enabled
Auth->>KP: getAsymmetric("jwks-orchestrator")
KP-->>Auth: AsymmetricKeyData | null
alt persisted key found
Auth-->>SDK: return persisted key
else
Auth->>Auth: generate new key
Auth->>KP: set(asymmetricKeyData)
KP->>FS: write key to disk
FS-->>KP: ack
KP-->>Auth: ack
Auth-->>SDK: return new key
end
else persistence disabled
Auth->>Auth: generate ephemeral key
Auth-->>SDK: return ephemeral key
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 15
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🤖 Fix all issues with AI agents
In @libs/auth/jest.config.cts:
- Around line 13-20: The coverage thresholds in jest.config.cts are set too low
(statements: 65, branches: 60, functions: 40, lines: 65); update the
coverageThreshold global values for statements, branches, functions, and lines
to 95 (or 95+) to meet the coding guidelines, and if tests currently cannot meet
that target add a TODO or create a tracking issue referenced in the repo (e.g.,
update the coverageThreshold block and add an issue link) so the team can
incrementally raise test coverage to 95% across all metrics.
In @libs/auth/src/jwks/__tests__/jwks.utils.test.ts:
- Around line 100-106: The decodeJwtPayloadSafe implementation currently treats
any typeof === 'object' as valid, which lets arrays through; update
decodeJwtPayloadSafe to explicitly reject arrays by adding an Array.isArray(obj)
check and return undefined when true before casting to Record<string, unknown>,
ensuring the function respects the Record<string, unknown> return shape; then
update the test in jwks.utils.test.ts that constructs an array payload to expect
undefined instead of [1,2,3] (reference decodeJwtPayloadSafe and the test case
that builds arrayPayload).
In @libs/auth/src/session/authorization-vault.ts:
- Around line 34-42: The enum credentialTypeSchema currently omits three
credential types present in the discriminated union (ssh_key, service_account,
oauth_pkce), causing validation mismatches; update the z.enum([...]) in
credentialTypeSchema to include 'ssh_key', 'service_account', and 'oauth_pkce'
(with appropriate inline comments if desired) so it matches the variants used in
the credentialSchema discriminated union and avoid runtime validation errors.
- Around line 845-872: In refreshOAuthCredential, the early return currently
skips credentials whose credential.type !== 'oauth', so oauth_pkce entries
(which also carry accessToken/refreshToken/expiresAt) are ignored; change the
type check in refreshOAuthCredential (and any related comment) to accept both
'oauth' and 'oauth_pkce' (e.g., credential.credential.type === 'oauth' ||
credential.credential.type === 'oauth_pkce') or use a predicate like
startsWith('oauth') before returning, then proceed to update accessToken,
refreshToken, expiresAt, isValid, invalidReason, and entry.lastAccessAt as
currently implemented.
In @libs/auth/src/session/storage/storage-token-store.ts:
- Around line 55-63: The constructor currently creates a namespaced view when
given a NamespacedStorage but key() determines namespacing by inspecting
this.storage.raw, causing double-prefixing; change the logic to track whether
the original storage argument was namespaced (e.g., set a boolean like
storageIsNamespaced = this.isNamespacedStorage(storage) in the constructor) and
use that flag in key() instead of inspecting this.storage.raw so keys are only
namespaced once; update references to isNamespacedStorage, namespacedStorage,
this.storage and key() accordingly.
In @libs/auth/src/session/token.vault.ts:
- Around line 69-75: Replace the custom Buffer-based helpers by using the shared
utilities: remove or stop using the private methods toBase64Url and
fromBase64Url and instead import base64urlEncode and base64urlDecode from
@frontmcp/utils (add them to the existing import list) and call
base64urlEncode(...) where toBase64Url(...) was used and base64urlDecode(...)
where fromBase64Url(...) was used; ensure any references to the private methods
are updated and delete the now-unused private helper methods to avoid dead code.
In @libs/auth/src/session/vault-encryption.ts:
- Around line 188-189: The call to encryptAesGcm uses the wrong argument order;
change the invocation to match the signature encryptAesGcm(key: Uint8Array,
plaintext: Uint8Array, iv: Uint8Array) by passing key, new
TextEncoder().encode(plaintext), iv (i.e., swap the iv and plaintext args),
ensuring plaintext is a Uint8Array and iv remains a Uint8Array.
- Around line 227-229: The call to decryptAesGcm in vault-encryption.ts passes
arguments in the wrong order; update the invocation of decryptAesGcm(key,
ivBuffer, ciphertext, tagBuffer) to match the signature decryptAesGcm(key:
Uint8Array, ciphertext: Uint8Array, iv: Uint8Array, tag: Uint8Array) by swapping
the ivBuffer and ciphertext arguments so it becomes decryptAesGcm(key,
ciphertext, ivBuffer, tagBuffer); keep the same variable names and ensure types
remain Uint8Array.
In @libs/auth/src/ui/__tests__/templates.test.ts:
- Around line 1-606: Tests lack required coverage and missing XSS/edge-case/unit
tests; update jest.config.cts coverage thresholds to 95%+ and add focused tests:
add XSS assertions for buildConsentPage (clientName, app description, scope),
buildIncrementalAuthPage (app.description), buildFederatedLoginPage
(providerName/providerUrl/appIds), buildToolConsentPage (tool descriptions, app
fields, userName, userEmail), and buildLoginPage (scope); add tests for empty
arrays and missing optional fields (apps/providers/tools, iconUrl, description,
userName, userEmail, providerUrl), invalid/malformed inputs, and unit tests
exercising the internal buildAppCardHtml function and error/boundary paths so
branches/functions/lines all reach 95%+ coverage.
In @libs/auth/src/ui/base-layout.ts:
- Around line 165-173: The file defines a local escapeHtml function duplicate to
the shared implementation; remove the local export function escapeHtml and
instead import the canonical escapeHtml from @frontmcp/utils (use the same
symbol or align with how templates.ts uses baseEscapeHtml) so callers use the
single shared implementation; update any local references to escapeHtml to use
the imported symbol and remove the redundant code block to prevent divergence.
In @libs/sdk/src/auth/session/authorization-vault.ts:
- Around line 197-218: The pkceOAuthCredentialSchema currently includes a
persistent codeVerifier field which violates RFC 7636 and is unnecessary since
code verifiers are single-use and only needed by verifyPkce() during the initial
authorization code exchange; remove the codeVerifier property from
pkceOAuthCredentialSchema (delete the codeVerifier: z.string().optional() line
and its misleading comment), update any surrounding comment that claims it's
"stored for refresh", search for and remove any vault persistence or loading
logic that expects codeVerifier (e.g., in modules interacting with the vault or
token.refresh.ts) and ensure verifyPkce() continues to use the transient code
verifier passed at runtime rather than relying on stored credentials, then run
tests and update docs/comments accordingly.
In @libs/sdk/src/auth/vault/auth-providers.types.ts:
- Around line 188-201: The z.function() usages in credentialProviderConfigSchema
(fields factory, refresh, toHeaders) are incompatible with Zod 4; update
credentialProviderConfigSchema to define each function with explicit
input/output schemas matching the TS function types—e.g., make factory and
refresh accept the CredentialFactoryContext type (use an appropriate
z.instanceof/Object surrogate or schema) and return a Promise of unknown|null,
and make toHeaders accept an unknown and return z.record(z.string());
alternatively, if strict validation isn't needed, mark those fields as
z.unknown()/.passthrough() or remove them from strict validation and validate
functions separately.
In @libs/sdk/src/auth/vault/auth-providers.vault.ts:
- Around line 249-265: In buildVaultKey, avoid silently falling back to session
when scope === 'user' and userId is missing; instead validate and throw a clear
error (e.g., Error or a specific VaultAuthError) when userId is undefined so
callers must provide a userId for 'user' scope; update any callers of
buildVaultKey (or the public API that accepts scope/userId) to handle/propagate
this error and adjust tests to expect the new validation behavior.
In @libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts:
- Around line 139-149: The extractExpiry method is duplicated in eager-loader.ts
and lazy-loader.ts; create a shared helper: add a new credential-helpers.ts
exporting a function extractCredentialExpiry(credential: Credential): number |
undefined (same switch logic), update eager-loader.ts and lazy-loader.ts to
import and call extractCredentialExpiry instead of their local extractExpiry
implementations, remove the duplicate extractExpiry methods, and add an export
for extractCredentialExpiry in the credential-loaders index.ts barrel.
🟡 Minor comments (13)
libs/auth/src/session/storage/__tests__/storage-token-store.test.ts-170-181 (1)
170-181: Weak test assertion - no meaningful verification.This test stores an expired blob but has no assertion verifying the expected behavior. The comment "May or may not exist depending on timing" indicates the test is non-deterministic. Either assert that the record exists immediately after
put(before the 1s minimum TTL expires), or remove this test if the behavior is intentionally undefined.Suggested fix - add deterministic assertion
it('should use minimum TTL for expired blobs', async () => { const id = tokenStore.allocId(); const pastExp = Math.floor(Date.now() / 1000) - 100; // Expired 100 seconds ago const blob = createTestBlob(pastExp); await tokenStore.put(id, blob); - // The record should still be stored (TTL set to 1 second minimum) - // but will expire very soon + // The record should still be stored immediately after put + // (TTL is set to minimum 1 second for expired blobs) const record = await tokenStore.get(id); - // May or may not exist depending on timing + expect(record).toBeDefined(); + expect(record?.blob.exp).toBe(pastExp); });libs/utils/src/crypto/key-persistence/key-persistence.ts-160-165 (1)
160-165: Cache is cleared even if storage deletion fails.The
deletemethod removes from cache before callingstorage.delete, but if the storage operation fails, the cache is already cleared, leaving an inconsistent state.🐛 Proposed fix
async delete(kid: string): Promise<boolean> { - // Remove from cache - this.cache.delete(kid); - - return this.storage.delete(kid); + const deleted = await this.storage.delete(kid); + // Only clear cache after successful deletion + this.cache.delete(kid); + return deleted; }libs/auth/src/session/storage/storage-token-store.ts-118-126 (1)
118-126: TTL calculation returns 1 for already-expired tokens.When
expis in the past (ttl <= 0), the code returns1second instead ofundefined. This stores expired tokens briefly rather than rejecting them or letting them expire immediately.Consider whether storing already-expired tokens is intended behavior, or if
put()should reject them with an error or return early.libs/auth/src/ui/base-layout.ts-293-295 (1)
293-295: Rawcontentinjection creates XSS risk if caller passes unsanitized user input.The
contentparameter is inserted directly into the HTML without escaping. While the caller is expected to provide safe HTML, this creates an implicit contract that could be violated.Consider documenting this clearly or providing a safe variant:
/** * @param content - Pre-sanitized HTML content. Caller is responsible for escaping user input. */Alternatively, provide a text-only variant that auto-escapes content for simple use cases.
package.json-87-87 (1)
87-87: Prettier v3 has breaking changes—verify formatting after upgrade, particularly for text wrapping and CJK spacing.Prettier v3 introduced breaking changes compared to v2, including modified trailing comma behavior, Markdown/CJK spacing rules, and text unwrapping logic. Your
.prettierrcalready sets"trailingComma": "all"(Prettier v3's default) and"proseWrap": "preserve", which mitigates some breaking changes. However, other formatting differences may still occur, especially in Markdown or edge cases where text wrapping behavior changed. Sincelint-staged(line 25) runs prettier on all staged files, test the formatting across the codebase to ensure the output matches expectations before merging.libs/auth/src/jwks/__tests__/jwks.utils.test.ts-81-84 (1)
81-84: Test may not validate intended behavior.The test on line 83 expects
'header.no-signature'to return undefined for having "less than 2 parts," but this string contains a dot and would be split into 2 parts:['header', 'no-signature']. This would pass the length checkparts.length < 2and proceed to attempt decoding, likely failing later due to invalid base64. Consider using a token without any dots (e.g.,'only-one-part'alone) or one with exactly 1 dot to properly test this condition, or update the test expectation.🧪 Proposed fix for the test
- it('should return undefined for token with less than 2 parts', () => { - expect(decodeJwtPayloadSafe('only-one-part')).toBeUndefined(); - expect(decodeJwtPayloadSafe('header.no-signature')).toBeUndefined(); - }); + it('should return undefined for token with less than 2 parts', () => { + expect(decodeJwtPayloadSafe('only-one-part')).toBeUndefined(); + }); + + it('should return undefined for token with invalid payload decoding', () => { + expect(decodeJwtPayloadSafe('header.invalid-payload')).toBeUndefined(); + });libs/auth/src/jwks/jwks.service.ts-75-83 (1)
75-83: Potential race condition in lazy initialization.The
getKeyPersistencemethod has a race condition:if (!this.keyPersistence) { this.keyPersistence = await createKeyPersistence({...}); }If multiple concurrent calls occur before the first
createKeyPersistencecompletes, multiple instances could be created. While this may not cause data corruption (KeyPersistence uses the same underlying storage), it could lead to:
- Wasted resources creating multiple instances
- Cache inconsistencies between instances
🔒 Proposed fix using promise guard pattern
Similar to the
keyInitPromisepattern used inensureOrchestratorKey:+ private keyPersistencePromise?: Promise<KeyPersistence>; + private async getKeyPersistence(): Promise<KeyPersistence | null> { if (!this.shouldEnablePersistence()) return null; - if (!this.keyPersistence) { + + if (this.keyPersistence) return this.keyPersistence; + + if (!this.keyPersistencePromise) { - this.keyPersistence = await createKeyPersistence({ + this.keyPersistencePromise = createKeyPersistence({ baseDir: '.frontmcp/keys', }); } + + this.keyPersistence = await this.keyPersistencePromise; return this.keyPersistence; }libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts-33-53 (1)
33-53: Potential type safety issue with concurrent loads of different credential types.The deduplication logic casts the in-flight promise to match the caller's generic type
T, but this could lead to type mismatches if two concurrent callers request the same provider with different credential type expectations.For example:
// Thread 1: load<OAuthCredential>('github', context) // Thread 2: load<ApiKeyCredential>('github', context) // Thread 2 gets Thread 1's promise but expects wrong typeThis scenario is unlikely in practice (providers typically produce one credential type), but the type system doesn't enforce this constraint.
💡 Consider one of these approaches
Option 1: Document the assumption
/** * Load a credential lazily. * If already loading, returns the in-flight promise (deduplication). + * + * Note: Assumes each provider name maps to a single credential type. + * Concurrent loads with different type parameters will share the same promise. * * @param config - Provider configuration * @param context - Factory context * @returns Resolved credential or null */Option 2: Add type checking (runtime overhead)
// Store provider name -> credential type mapping private readonly typeMap = new Map<string, string>(); async load<T extends Credential>(config: NormalizedProviderConfig<T>, context: CredentialFactoryContext) { const existing = this.loading.get(config.name); if (existing) { const expectedType = this.typeMap.get(config.name); if (expectedType && expectedType !== config.factory.name) { throw new Error(`Type mismatch for provider ${config.name}`); } // ... } this.typeMap.set(config.name, config.factory.name); // ... }libs/auth/src/ui/__tests__/templates.test.ts-24-147 (1)
24-147: Add XSS tests for all user-provided fields.While XSS testing is present for
clientName(lines 100-108), similar tests are missing for other user-provided fields that are rendered in the HTML:
appName(lines 53-57)description(lines 59-63)requiredScopes(lines 65-70)iconUrl(lines 110-125)Each field that renders user content should have explicit XSS injection tests.
Based on learnings about comprehensive XSS testing for all user-provided content in templates.
libs/auth/src/ui/__tests__/templates.test.ts-321-441 (1)
321-441: Add XSS injection tests.This suite is missing XSS escaping tests for user-provided fields:
toolName,description,appName,userName, anduserEmail. All user-provided content rendered in HTML must be tested against injection attacks.Based on learnings about XSS testing requirements.
libs/auth/src/ui/__tests__/templates.test.ts-214-319 (1)
214-319: Add XSS escaping tests for user-provided fields.Missing XSS injection tests for fields like
providerName,providerUrl, andclientNamethat are rendered in the HTML output. These should be tested with malicious payloads to ensure proper escaping.Based on learnings about comprehensive XSS testing for templates.
libs/auth/src/ui/templates.ts-254-342 (1)
254-342: UnusedcsrfTokenparameter inbuildFederatedLoginPage.The
csrfTokenis accepted as a parameter but never rendered in the form. This form usesmethod="GET"which doesn't require CSRF protection, so either:
- Remove the unused parameter for clarity, or
- If this should be a POST form for security, add the CSRF token field
🔧 Option 1: Remove unused parameter
export function buildFederatedLoginPage(params: { providers: ProviderCard[]; clientName: string; pendingAuthId: string; - csrfToken: string; callbackPath: string; }): string { - const { providers, clientName, pendingAuthId, csrfToken, callbackPath } = params; + const { providers, clientName, pendingAuthId, callbackPath } = params;libs/auth/src/session/storage/storage-authorization-vault.ts-121-131 (1)
121-131: Silent failure onupdate()when entry doesn't exist.The method silently returns without indication when the entry is not found. Callers cannot distinguish between "update succeeded" and "entry didn't exist." Consider throwing an error or returning a boolean indicating success.
🔧 Suggested improvement
- async update(id: string, updates: Partial<AuthorizationVaultEntry>): Promise<void> { + async update(id: string, updates: Partial<AuthorizationVaultEntry>): Promise<boolean> { const entry = await this.get(id); - if (!entry) return; + if (!entry) return false; const updated = { ...entry, ...updates, lastAccessAt: Date.now(), }; await this.storage.set(this.key(id), updated); + return true; }
🧹 Nitpick comments (47)
libs/auth/src/session/utils/tiny-ttl-cache.ts (1)
34-36:size()includes expired entries that haven't been cleaned up.The
size()method returnsmap.size, which includes expired entries that haven't been accessed or explicitly cleaned. Consider either documenting this behavior or callingcleanup()internally for accurate counts.Option 1: Document the behavior
+ /** + * Returns the number of entries (including expired but not-yet-cleaned entries). + * Call cleanup() first for an accurate count of live entries. + */ size(): number { return this.map.size; }Option 2: Return accurate count
+ /** + * Returns the number of live (non-expired) entries. + */ size(): number { + this.cleanup(); return this.map.size; }libs/utils/src/storage/adapters/__tests__/filesystem.test.ts (1)
246-249: Consider asserting specific error type or message.The test asserts that incrementing a non-integer throws with message containing "not an integer", but doesn't verify the error type. Consider using
toThrow(SomeSpecificError)if there's a dedicated error class.libs/auth/src/session/vault-encryption.ts (2)
106-110: Consider documenting that pepper should come from environment variables.Per learnings, encryption keys should not be hardcoded. While the config allows passing a pepper, the example at Line 89 shows
process.env.VAULT_PEPPER, which is good. Consider adding a note in the JSDoc that the pepper should be sourced from environment variables for production use.
254-257: Unsafe type assertion in decryptObject.
JSON.parse(plaintext) as Tperforms an unsafe cast without validation. If the decrypted JSON doesn't match the expected typeT, callers will get runtime errors.Consider accepting an optional Zod schema for validation:
♻️ Suggested improvement
- async decryptObject<T>(encrypted: EncryptedData, key: Uint8Array): Promise<T> { + async decryptObject<T>(encrypted: EncryptedData, key: Uint8Array, schema?: z.ZodType<T>): Promise<T> { const plaintext = await this.decrypt(encrypted, key); - return JSON.parse(plaintext) as T; + const parsed = JSON.parse(plaintext); + if (schema) { + return schema.parse(parsed); + } + return parsed as T; }libs/auth/src/jwks/dev-key-persistence.ts (1)
22-26: Direct use ofnode:cryptoin deprecated module.Line 23 imports
cryptodirectly, and Line 254 usescrypto.randomBytes. While this violates the coding guideline to use@frontmcp/utilsfor cryptographic operations, since this module is deprecated and scheduled for removal, the effort to fix may not be justified.Consider adding a comment noting this deviation or prioritizing removal of this module. Based on learnings, prefer
@frontmcp/utilsfor cryptographic operations.libs/auth/src/ui/base-layout.ts (1)
35-44: Consider using theme.cdn configuration instead of hardcoded CDN URLs.Based on learnings, the
libs/uipackguidelines specify not to hard-code CDN URLs and instead usetheme.cdnconfiguration. This would allow consumers to override CDN endpoints for different environments or self-hosted scenarios.💡 Suggested approach
+export interface CdnConfig { + tailwind?: string; + fonts?: { + preconnect?: string[]; + stylesheet?: string; + }; +} export const CDN = { /** Tailwind CSS v4 Browser CDN - generates styles on-the-fly with @theme support */ tailwind: 'https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4', /** Google Fonts - Inter for modern UI */ fonts: { preconnect: ['https://fonts.googleapis.com', 'https://fonts.gstatic.com'], stylesheet: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap', }, } as const;Then accept an optional
cdnoverride inBaseLayoutOptionsto merge with defaults.libs/auth/src/session/authorization-vault.ts (3)
1071-1074: Rediscleanup()is a placeholder - add implementation or TODO tracking.The in-memory implementation properly cleans up expired pending auths, but the Redis version is empty. For production use, this should implement SCAN-based cleanup or document that external cleanup (e.g., Redis TTL) is expected.
💡 Suggested approach
async cleanup(): Promise<void> { // TODO: Implement Redis cleanup using SCAN // Consider: // 1. Use SCAN to iterate vault keys // 2. Parse each entry and filter expired pendingAuths // 3. Update entries with cleaned pendingAuths array // Alternatively, document that Redis TTL should be used for automatic expiration }Do you want me to generate a full Redis SCAN-based cleanup implementation?
884-889:anytype for Redis client - consider a minimal interface.While the eslint-disable comment acknowledges this, defining a minimal interface would improve type safety without requiring a specific Redis client library.
💡 Suggested interface
interface RedisClient { get(key: string): Promise<string | null>; set(key: string, value: string): Promise<unknown>; del(key: string): Promise<number>; } export class RedisAuthorizationVault implements AuthorizationVault { constructor( private readonly redis: RedisClient, private readonly namespace = 'vault:', ) {}
927-929: No TTL set on Redis keys - vault entries could accumulate indefinitely.Unlike the in-memory implementation which can be garbage collected, Redis entries persist. Consider adding TTL support based on session lifetime or implementing periodic cleanup.
Also applies to: 1000-1001
libs/sdk/src/auth/flows/session.verify.flow.ts (2)
188-194: Refactor nested ternary for readability.The deeply nested ternary operator makes the sessionProtocol assignment logic difficult to follow. Consider using if-else statements or extracting to a helper function.
♻️ Proposed refactor using if-else statements
- const sessionProtocol = httpTransportHeader - ? 'http' - : sessionIdRawHeader - ? 'streamable-http' - : sessionIdQuery - ? 'sse' - : undefined; + let sessionProtocol: string | undefined; + if (httpTransportHeader) { + sessionProtocol = 'http'; + } else if (sessionIdRawHeader) { + sessionProtocol = 'streamable-http'; + } else if (sessionIdQuery) { + sessionProtocol = 'sse'; + } else { + sessionProtocol = undefined; + }
302-302: Address the TODO comment.The TODO indicates that provider handling needs to be fixed. This should be resolved before merging to ensure the JwksService is properly integrated with the dependency injection system.
Do you want me to help generate a solution for proper provider integration, or should this be tracked in a separate issue?
libs/auth/tsconfig.spec.json (1)
6-6: Use modern module resolution instead of deprecated "node10".The
"node10"module resolution strategy is deprecated. Use"node"for backward compatibility or"node16"/"nodenext"for modern Node.js ESM/CJS resolution.♻️ Recommended fix
- "moduleResolution": "node10", + "moduleResolution": "node",libs/auth/package.json (1)
47-59: Consider adding zod to devDependencies.While
zodis correctly declared as a peer dependency, it should also be included indevDependenciesto support local development and testing. Without it, developers working on this package may encounter import errors when running tests or building the package.📦 Add zod to devDependencies
"devDependencies": { "@vercel/kv": "^3.0.0", "ioredis": "^5.8.0", - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "zod": "^4.0.0" }libs/utils/src/crypto/key-persistence/factory.ts (1)
97-106: Consider validating storage adapter connection state.The documentation states "The storage adapter must already be connected" (line 82), but the function doesn't verify this assumption. If a caller passes a disconnected adapter, it may lead to runtime errors when KeyPersistence attempts operations.
Consider either:
- Adding a connection state check (if StorageAdapter exposes this)
- Calling
await storage.connect()to ensure connectivity- Strengthening the JSDoc to emphasize this is a strict precondition
🛡️ Proposed validation check
If StorageAdapter has an
isConnected()method:export function createKeyPersistenceWithStorage( storage: StorageAdapter, options?: Pick<CreateKeyPersistenceOptions, 'throwOnInvalid' | 'enableCache'>, ): KeyPersistence { + // Verify storage is connected + if (typeof storage.isConnected === 'function' && !storage.isConnected()) { + throw new Error('Storage adapter must be connected before creating KeyPersistence'); + } + return new KeyPersistence({ storage, throwOnInvalid: options?.throwOnInvalid ?? false, enableCache: options?.enableCache ?? true, }); }libs/auth/src/ui/__tests__/base-layout.test.ts (1)
349-361: Consider expanding authLayout test coverage.While the current tests verify that
authLayoutis a function and generates valid HTML, consider adding tests for:
- How it handles different
bodyClassconfigurations (since it's acreateLayoutwith a default bodyClass)- Theme merging behavior specific to authLayout
- Interaction with other options (headExtra, description, etc.)
This would ensure parity with the coverage depth of other layout functions.
libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (2)
91-98: Improve error logging to include provider name on rejection.When a promise rejects, the provider name is lost because it's only captured in the fulfilled result. Consider restructuring to preserve the provider name in error cases.
♻️ Suggested improvement
const promises = providerNames.map(async (name) => { - const result = await this.get(name, options); - return { name, result }; + try { + const result = await this.get(name, options); + return { name, result, error: null }; + } catch (error) { + return { name, result: null, error }; + } }); - const settled = await Promise.allSettled(promises); + const settled = await Promise.all(promises); - for (const outcome of settled) { - if (outcome.status === 'fulfilled') { - results.set(outcome.value.name, outcome.value.result); - } else { - // Find the provider name from the error context if possible - this.logger?.warn('Failed to load credential:', outcome.reason); - } + for (const outcome of settled) { + if (outcome.error) { + this.logger?.warn(`Failed to load credential "${outcome.name}":`, outcome.error); + } + results.set(outcome.name, outcome.result); }
117-126: Consider parallel header retrieval for better performance.The current implementation processes providers sequentially. Since
getManyalready supports parallel loading,headersManycould benefit from the same approach.♻️ Parallel implementation
async headersMany(providerNames: string[]): Promise<Record<string, string>> { - const allHeaders: Record<string, string> = {}; - - for (const name of providerNames) { - const headers = await this.headers(name); - Object.assign(allHeaders, headers); - } - - return allHeaders; + const headerPromises = providerNames.map((name) => this.headers(name)); + const headerResults = await Promise.all(headerPromises); + return Object.assign({}, ...headerResults); }libs/auth/src/session/storage/__tests__/typed-storage.test.ts (1)
129-156: Consider adding test formsetwith per-entry TTL options.The
TypedSetEntry<T>type likely supports per-entry options including TTL. Adding a test case that exercisesmsetwith different TTL values per entry would improve coverage.💚 Suggested test addition
it('should support TTL option per entry in mset', async () => { const users: TestUser[] = [ { id: '1', name: 'Alice' }, { id: '2', name: 'Bob' }, ]; await storage.mset([ { key: 'user:1', value: users[0], options: { ttlSeconds: 3600 } }, { key: 'user:2', value: users[1], options: { ttlSeconds: 7200 } }, ]); const ttl1 = await storage.ttl('user:1'); const ttl2 = await storage.ttl('user:2'); expect(ttl1).toBeLessThanOrEqual(3600); expect(ttl1).toBeGreaterThan(0); expect(ttl2).toBeLessThanOrEqual(7200); expect(ttl2).toBeGreaterThan(3600); // Should be higher than ttl1 });libs/sdk/src/auth/vault/auth-providers.context-extension.ts (1)
60-66: Consider extracting the error message to avoid duplication.The error message is duplicated between
authProvidersContextExtension.errorMessageandgetAuthProviders. Extracting to a shared constant would improve maintainability.♻️ DRY suggestion
+const AUTH_PROVIDERS_NOT_CONFIGURED_MESSAGE = + 'AuthProviders vault is not configured. ' + + 'Ensure AuthProvidersVault is enabled in your FrontMcp configuration with registered providers.'; + export const authProvidersContextExtension: ContextExtension = { property: 'authProviders', token: AUTH_PROVIDERS_ACCESSOR, - errorMessage: - 'AuthProviders vault is not configured. ' + - 'Ensure AuthProvidersVault is enabled in your FrontMcp configuration with registered providers.', + errorMessage: AUTH_PROVIDERS_NOT_CONFIGURED_MESSAGE, }; export function getAuthProviders(ctx: { get: <T>(token: unknown) => T }): AuthProvidersAccessor { try { return ctx.get(AUTH_PROVIDERS_ACCESSOR); } catch (error) { - throw new Error( - 'AuthProviders vault is not configured. ' + - 'Ensure AuthProvidersVault is enabled in your FrontMcp configuration with registered providers.', - { cause: error }, - ); + throw new Error(AUTH_PROVIDERS_NOT_CONFIGURED_MESSAGE, { cause: error }); } }Also applies to: 80-90
libs/sdk/src/auth/vault/auth-providers.vault.ts (3)
98-98: Unsafe type assertion without runtime validation.The
as Tcast at line 98 bypasses TypeScript's type checking without validating that the credential actually matches typeT. If the stored credential doesn't match the expected type, this could cause runtime errors in calling code.Consider adding runtime validation or returning the credential with its actual type:
Proposed fix
- return appCredential.credential as T; + // Return the credential - caller should validate if specific type is needed + return appCredential.credential as T; + // Alternative: Add schema validation here if T's shape is known
121-127: Inconsistent error handling across credential operations.
removeCredentialandinvalidateCredentialcatch and log errors but don't rethrow them, whilestoreCredential(line 57-60) andrefreshOAuthCredential(line 176-179) do rethrow. This inconsistency could make debugging difficult and lead to silent failures.Consider either:
- Rethrowing errors consistently across all methods
- Documenting why some operations intentionally swallow errors
Proposed fix for consistent error handling
async removeCredential( sessionId: string, providerId: string, scope: CredentialScope, userId?: string, ): Promise<void> { const vaultKey = this.buildVaultKey(sessionId, scope, userId); try { await this.baseVault.removeAppCredential(vaultKey, this.namespace, providerId); this.logger?.debug(`Removed credential "${providerId}"`); } catch (error) { this.logger?.warn(`Failed to remove credential "${providerId}":`, error); + throw error; } }Also applies to: 144-153
195-196: Empty catch blocks silently swallow errors.Multiple empty catch blocks in
getAllCredentialssilently discard errors with just// Ignorecomments. While aggregating credentials across scopes may warrant continuing on partial failures, consider logging these errors at debug level to aid troubleshooting.Proposed improvement
} catch { - // Ignore + // Continue on partial failures - log for debugging + this.logger?.debug('Failed to get session scope credentials'); }Also applies to: 210-212, 222-224, 234-236
libs/auth/src/session/storage/__tests__/storage-authorization-vault.test.ts (1)
702-724: Potential flaky test due to timing sensitivity.The cleanup test uses a 1ms TTL and waits only 10ms before asserting. While this works most of the time, it could be flaky in slow CI environments or under high load. Consider using a more robust approach.
Suggested improvement
- // Create a pending auth with very short TTL - await vault.createPendingAuth(entry.id, { - appId: 'app1', - authUrl: 'https://auth.example.com/authorize', - ttlMs: 1, // 1ms TTL - }); - - // Wait for it to expire - await new Promise((resolve) => setTimeout(resolve, 10)); + // Create a pending auth with short TTL + await vault.createPendingAuth(entry.id, { + appId: 'app1', + authUrl: 'https://auth.example.com/authorize', + ttlMs: 50, // 50ms TTL - short but not too aggressive + }); + + // Wait for it to expire with buffer + await new Promise((resolve) => setTimeout(resolve, 100));libs/sdk/src/auth/vault/auth-providers.providers.ts (1)
103-112: Consider safer access to optional auth claims.The code accesses
ctx.authInfo?.extra?.['sub']etc. with type assertions tostring | undefined. Ifextracontains non-string values for these fields, this could cause issues downstream.Consider adding runtime type guards:
Proposed improvement
const factoryContext: CredentialFactoryContext = { sessionId: ctx.sessionId, - userSub: ctx.authInfo?.extra?.['sub'] as string | undefined, - userEmail: ctx.authInfo?.extra?.['email'] as string | undefined, - userName: ctx.authInfo?.extra?.['name'] as string | undefined, + userSub: typeof ctx.authInfo?.extra?.['sub'] === 'string' ? ctx.authInfo.extra['sub'] : undefined, + userEmail: typeof ctx.authInfo?.extra?.['email'] === 'string' ? ctx.authInfo.extra['email'] : undefined, + userName: typeof ctx.authInfo?.extra?.['name'] === 'string' ? ctx.authInfo.extra['name'] : undefined,libs/auth/src/session/token.store.ts (3)
44-50: Consider using a proper Redis client type instead ofany.The comment suggests replacing
anywith "your redis client type", but for a published SDK library, this isn't ideal. Consider:
- Using a generic type parameter with a minimal interface
- Defining a minimal
RedisClientinterface that covers the needed methodsAs per coding guidelines, avoid
anytypes without strong justification.Proposed improvement
+/** Minimal Redis client interface for TokenStore */ +export interface RedisClientLike { + set(key: string, value: string, options?: { EX?: number }): Promise<unknown>; + get(key: string): Promise<string | null>; + del(key: string): Promise<number>; +} + /** Redis token store - replace `any` with your redis client type. */ export class RedisTokenStore implements TokenStore { - // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor( - private readonly redis: any, + private readonly redis: RedisClientLike, private readonly ns = 'tok:', ) {}
58-62: Consider implementing Redis TTL based on blob expiry.The comment mentions setting
EXbyblob.expfor Redis eviction, but this isn't implemented. If tokens have expiry times, leveraging Redis TTL would automatically clean up expired tokens.Proposed implementation
async put(id: string, blob: EncBlob) { const rec = JSON.stringify({ id, blob, updatedAt: Date.now() }); - // Optional: set EX by blob.exp if you want Redis eviction at token expiry - await this.redis.set(this.key(id), rec); + if (blob.exp) { + const ttlSeconds = Math.max(0, blob.exp - Math.floor(Date.now() / 1000)); + if (ttlSeconds > 0) { + await this.redis.set(this.key(id), rec, { EX: ttlSeconds }); + return; + } + } + await this.redis.set(this.key(id), rec); }
64-72: Add error handling for Redis operations.Redis operations can fail due to network issues, connection problems, etc. Consider adding try-catch blocks or letting errors propagate with meaningful context.
Proposed improvement
async get(id: string) { - const raw = await this.redis.get(this.key(id)); - if (!raw) return undefined; - return JSON.parse(raw) as SecretRecord; + const raw = await this.redis.get(this.key(id)); + if (!raw) return undefined; + try { + return JSON.parse(raw) as SecretRecord; + } catch { + // Corrupted data - treat as missing + return undefined; + } }libs/auth/src/session/token.vault.ts (2)
37-40: Add parameter validation torotateTomethod.The
rotateTomethod doesn't validate its input parameter. It should verify that the key parameter is defined and has validkidandkeyproperties before updating internal state.♻️ Proposed fix
rotateTo(k: VaultKey) { + if (!k || !k.kid || !k.key) { + throw new Error('Invalid VaultKey: kid and key are required'); + } this.active = k; this.keys.set(k.kid, k.key); }
57-67: Consider checking expiration before decrypting.The
decryptmethod decrypts the blob before checking if it has expired. For better performance and user experience, consider validatingblob.exp(if present) before performing the decryption operation.♻️ Proposed enhancement
async decrypt(blob: EncBlob): Promise<string> { + // Check expiration first to avoid unnecessary decryption + if (blob.exp && Date.now() > blob.exp * 1000) { + throw new Error(`vault_expired:${blob.kid}`); + } + const key = this.keys.get(blob.kid); if (!key) throw new Error(`vault_unknown_kid:${blob.kid}`); const iv = this.fromBase64Url(blob.iv); const tag = this.fromBase64Url(blob.tag); const data = this.fromBase64Url(blob.data); const plaintext = await decryptAesGcm(key, iv, data, tag); return new TextDecoder().decode(plaintext); }libs/sdk/src/auth/vault/auth-providers.registry.ts (2)
39-47: Add validation for options.providers array.The constructor iterates over
options.providerswithout validating that it's an array. If a malformed configuration is passed, the error message may be unclear.♻️ Proposed enhancement
constructor(options?: AuthProvidersVaultOptions) { this.defaultCacheTtl = options?.defaultCacheTtl ?? 3600000; // 1 hour if (options?.providers) { + if (!Array.isArray(options.providers)) { + throw new Error('AuthProvidersVaultOptions.providers must be an array'); + } for (const provider of options.providers) { this.register(provider); } } }
55-63: Consider validating provider name is non-empty.The
registermethod doesn't validate thatconfig.nameis a non-empty string. Allowing empty or whitespace-only names could lead to confusion and lookup issues.♻️ Proposed enhancement
register<T extends Credential = Credential>(config: CredentialProviderConfig<T>): void { + if (!config.name || !config.name.trim()) { + throw new Error('Provider name must be a non-empty string'); + } if (this.providers.has(config.name)) { throw new Error(`Credential provider "${config.name}" is already registered`); } const normalized = this.normalize(config); // Cast to base type for storage - generic is preserved on retrieval via get<T>() this.providers.set(config.name, normalized as unknown as NormalizedProviderConfig); }libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (2)
58-94: Minor timestamp inconsistency in credential metadata.Lines 62 and 79 both call
Date.now(), resulting in slightly different timestamps. TheacquiredAtfield (line 79) should represent when acquisition started or completed. Consider usingstartTimefor consistency and accuracy.♻️ Proposed fix
private async doLoad<T extends Credential>( config: NormalizedProviderConfig<T>, context: CredentialFactoryContext, ): Promise<ResolvedCredential<T> | null> { const startTime = Date.now(); this.logger?.debug(`Loading lazy credential: ${config.name}`); try { const credential = await config.factory({ ...context, metadata: config.metadata, }); if (!credential) { this.logger?.debug(`Lazy credential "${config.name}" returned null`); return null; } const resolved: ResolvedCredential<T> = { credential, providerId: config.name, - acquiredAt: Date.now(), + acquiredAt: startTime, expiresAt: this.extractExpiry(credential), isValid: true, scope: config.scope, }; const duration = Date.now() - startTime; this.logger?.debug(`Loaded lazy credential "${config.name}" in ${duration}ms`); return resolved; } catch (error) { const duration = Date.now() - startTime; this.logger?.warn(`Failed to load lazy credential "${config.name}" after ${duration}ms:`, error); throw error; } }
103-144: Same timestamp inconsistency in refresh method.Similar to
doLoad, therefreshmethod has a timestamp inconsistency whereacquiredAt(line 129) uses a newDate.now()call instead ofstartTime(line 107).♻️ Proposed fix
async refresh<T extends Credential>( config: NormalizedProviderConfig<T>, context: CredentialFactoryContext & { existingCredential: T }, ): Promise<ResolvedCredential<T> | null> { const startTime = Date.now(); this.logger?.debug(`Refreshing credential: ${config.name}`); try { let credential: T | null; if (config.refresh) { // Use dedicated refresh function credential = await config.refresh(context); } else { // Fall back to factory credential = await config.factory(context); } if (!credential) { this.logger?.debug(`Credential refresh "${config.name}" returned null`); return null; } const resolved: ResolvedCredential<T> = { credential, providerId: config.name, - acquiredAt: Date.now(), + acquiredAt: startTime, expiresAt: this.extractExpiry(credential), isValid: true, scope: config.scope, }; const duration = Date.now() - startTime; this.logger?.debug(`Refreshed credential "${config.name}" in ${duration}ms`); return resolved; } catch (error) { const duration = Date.now() - startTime; this.logger?.warn(`Failed to refresh credential "${config.name}" after ${duration}ms:`, error); throw error; } }libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts (3)
47-97: Consider logging when provider name cannot be determined from error.Line 86 falls back to
'unknown'when the provider name can't be extracted from the error. This could make debugging difficult if multiple providers fail. Consider logging a warning when this fallback occurs.♻️ Proposed enhancement
} else { const providerName = (result.reason as { providerName?: string })?.providerName || 'unknown'; + if (providerName === 'unknown') { + this.logger?.warn('Failed to determine provider name from error:', result.reason); + } const error = result.reason instanceof Error ? result.reason : new Error(String(result.reason)); failed.set(providerName, error); this.logger?.warn(`Failed to load eager credential "${providerName}":`, error.message); }
102-116: Consider using a structured error wrapper instead of mutating error objects.Lines 112-113 mutate the error object to add
providerName. This approach can fail if the error is frozen/sealed and TypeScript doesn't track the added property. Consider wrapping in a structured error class.♻️ Proposed enhancement
First, define an error class at the top of the file:
class ProviderLoadError extends Error { constructor( message: string, public readonly providerName: string, public readonly cause?: Error ) { super(message); this.name = 'ProviderLoadError'; } }Then update the
loadOnemethod:private async loadOne<T extends Credential>( config: NormalizedProviderConfig<T>, context: CredentialFactoryContext, ): Promise<T | null> { try { return await config.factory({ ...context, metadata: config.metadata, }); } catch (error) { - // Add provider name to error for identification - (error as { providerName?: string }).providerName = config.name; - throw error; + throw new ProviderLoadError( + `Failed to load credential for provider "${config.name}"`, + config.name, + error instanceof Error ? error : new Error(String(error)) + ); } }Then update the error extraction in
loadForSession:} else { - const providerName = (result.reason as { providerName?: string })?.providerName || 'unknown'; + const providerName = result.reason instanceof ProviderLoadError + ? result.reason.providerName + : 'unknown'; const error = result.reason instanceof Error ? result.reason : new Error(String(result.reason)); failed.set(providerName, error); this.logger?.warn(`Failed to load eager credential "${providerName}":`, error.message); }
121-134: Consider validating credential expiry when settingisValid.Line 131 hardcodes
isValid: true, but if the credential factory returns an already-expired credential (e.g.,expiresAtis in the past), the resolved credential should reflect that. Consider checking expiry when setting theisValidflag.♻️ Proposed fix
private wrapCredential<T extends Credential>( providerId: string, credential: T, config: NormalizedProviderConfig<T>, ): ResolvedCredential<T> { + const expiresAt = this.extractExpiry(credential); + const isValid = !expiresAt || Date.now() < expiresAt; + return { credential, providerId, acquiredAt: Date.now(), - expiresAt: this.extractExpiry(credential), - isValid: true, + expiresAt, + isValid, scope: config.scope, }; }libs/utils/src/crypto/key-persistence/schemas.ts (4)
28-32: Consider adding.strict()to prevent unexpected properties.The
baseKeyDataSchemadoesn't use.strict(), which allows additional properties to pass validation. Per learnings, all Zod schemas should use.strict()for validation to prevent unexpected data from being accepted.♻️ Add .strict() to schema
const baseKeyDataSchema = z.object({ kid: z.string().min(1), createdAt: z.number().positive().int(), version: z.number().positive().int(), -}); +}).strict();Based on learnings about using
.strict()on all Zod schemas.
60-64: Consider adding.strict()to secretKeyDataSchema.The schema extends
baseKeyDataSchemabut doesn't add.strict(). While it inherits from base, explicitly adding.strict()would ensure no unexpected properties are accepted at this level.♻️ Add .strict() for stricter validation
export const secretKeyDataSchema = baseKeyDataSchema.extend({ type: z.literal('secret'), secret: z.string().min(1), bytes: z.number().positive().int(), -}); +}).strict();Based on learnings about using
.strict()on Zod schemas.
69-76: Consider adding.strict()to asymmetricKeyDataSchema.Similar to
secretKeyDataSchema, consider adding.strict()to ensure no unexpected properties are accepted.♻️ Add .strict() for stricter validation
export const asymmetricKeyDataSchema = baseKeyDataSchema.extend({ type: z.literal('asymmetric'), alg: asymmetricAlgSchema, privateKey: jsonWebKeySchema, publicJwk: z.object({ keys: z.array(jsonWebKeySchema).min(1), }), -}); +}).strict();Based on learnings about using
.strict()on Zod schemas.
137-143: Non-null assertion without verification.Line 142 uses non-null assertion (
validation.data!) after checkingvalidation.valid. While this is logically safe, the coding guidelines discourage non-null assertions in favor of proper error handling.♻️ Remove non-null assertion
export function parseKeyData(data: unknown): AnyKeyData | null { const validation = validateKeyData(data); - if (!validation.valid) { - return null; - } - return validation.data!; + return validation.valid ? validation.data ?? null : null; }Or use a type guard approach:
export function parseKeyData(data: unknown): AnyKeyData | null { const validation = validateKeyData(data); - if (!validation.valid) { + if (!validation.valid || !validation.data) { return null; } - return validation.data!; + return validation.data; }As per coding guidelines to avoid non-null assertions.
libs/sdk/src/auth/vault/credential-cache.ts (2)
140-147: Map modification during iteration is safe in JavaScript but consider consistency.The
invalidateByScopemethod correctly iterates and deletes entries. In JavaScript, deleting keys from a Map duringfor...ofiteration is safe (deleted keys won't be visited again, and the iteration continues correctly). However, there's no eviction stat increment here unlikecleanup().For consistency, consider incrementing
evictionswhen invalidating by scope, or clarify in documentation thatevictionsonly tracks TTL-based removals.💡 Optional: Track evictions consistently
invalidateByScope(scope: CredentialScope): void { + let removed = 0; for (const [key, entry] of this.cache) { if (entry.resolved.scope === scope) { this.cache.delete(key); + removed++; } } this.stats.size = this.cache.size; + // Optionally track: this.stats.evictions += removed; }
48-67: Duplicate credential expiration check.The
get()method checks credential expiration twice: once viaisExpired()(line 49) which internally checksentry.resolved.expiresAt(line 210), and again explicitly at lines 58-64. The second check is redundant sinceisExpiredAt()already handles this case.♻️ Remove redundant expiration check
get<T extends Credential = Credential>(providerId: string): ResolvedCredential<T> | undefined { const entry = this.cache.get(providerId); if (!entry) { this.stats.misses++; return undefined; } - // Check TTL expiration + // Check TTL and credential expiration if (this.isExpired(entry)) { this.cache.delete(providerId); this.stats.size = this.cache.size; this.stats.misses++; this.stats.evictions++; return undefined; } - // Check credential expiration - if (entry.resolved.expiresAt && Date.now() >= entry.resolved.expiresAt) { - this.cache.delete(providerId); - this.stats.size = this.cache.size; - this.stats.misses++; - this.stats.evictions++; - return undefined; - } - this.stats.hits++; return entry.resolved as ResolvedCredential<T>; }libs/auth/src/session/storage/encrypted-typed-storage.ts (1)
273-285: Consider documenting key cleanup strategy.The
rotateKeymethod correctly adds new keys while retaining old ones for decryption. However, there's no method to remove deprecated keys from thekeyMap. This may be intentional for backward compatibility, but consider:
- Documenting that old keys accumulate in memory
- Optionally adding a
removeKey(kid)method for explicit cleanup after re-encryption is completelibs/utils/src/storage/adapters/filesystem.ts (1)
130-134: Broad error suppression may hide configuration issues.The catch block silently ignores all errors from
mkdir, but the comment only mentions "Directory already exists". This could mask permission denied (EPERM), disk full (ENOSPC), or other filesystem errors that indicate misconfiguration.♻️ Only ignore EEXIST errors
// Try to set directory permissions (may not work on all platforms) try { await mkdir(this.baseDir, { recursive: true, mode: this.dirMode }); - } catch { - // Directory already exists, ignore error + } catch (e) { + const error = e as NodeJS.ErrnoException; + // Only ignore if directory already exists + if (error.code !== 'EEXIST') { + // Log or handle other errors (permission issues, disk full, etc.) + // For now, we proceed as ensureDir already succeeded + } }libs/auth/src/ui/templates.ts (1)
560-566: Consider documentingrenderToHtmlwrapper purpose.The
_optionsparameter is unused. If this is intentionally a no-op wrapper for API compatibility, consider adding a brief JSDoc note explaining when it might be removed or enhanced.libs/auth/src/session/storage/storage-authorization-vault.ts (1)
429-454: Cleanup method has scalability concerns for large datasets.The
keys('*')call loads all keys into memory, which could cause issues with large vault datasets. The comment acknowledges this, but consider:
- Adding a
limitparameter to process in batches- Documenting the expected use case (e.g., "for small to medium datasets only")
- Implementing cursor-based iteration for production use
libs/auth/src/index.ts (1)
42-47: Add@deprecatedJSDoc annotations to dev-key persistence exports in the barrel file.These functions are marked as deprecated in the
jwksmodule but lack deprecation notices at the final re-export level. IDE tooling reads JSDoc from the barrel export, so users importing from@frontmcp/authwon't receive deprecation warnings without annotations here. Add deprecation notices above the export block on lines 42-47.
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In @libs/auth/jest.config.cts:
- Around line 12-20: Update the Jest coverageThreshold block so the global
thresholds for statements, branches, functions, and lines are all set to 95;
modify the coverageThreshold/global entries (statements, branches, functions,
lines) in libs/auth/jest.config.cts to 95 to meet the documented requirement
before release.
In @libs/auth/src/session/authorization-vault.ts:
- Around line 1021-1030: The pending auth status update in completePendingAuth
is never persisted because authorizeApp reloads/writes the vault entry from
Redis; modify completePendingAuth (which calls get, updates entry.pendingAuths
and then authorizeApp) to persist the updated entry back to storage before
calling authorizeApp—e.g., after setting pendingAuth.status = 'completed' call
the vault save method (set/saveEntry/updateEntry) so the 'completed' status is
written to Redis, then call authorizeApp(vaultId, pendingAuth.appId).
In @libs/auth/src/session/storage/storage-authorization-vault.ts:
- Around line 381-389: The code uses a non-null assertion on entry.consent
(entry.consent!.selectedToolIds); instead add a proper guard: retrieve const
consent = entry.consent; if (!consent) return false (or handle as appropriate),
then replace both uses of entry.consent! with consent.selectedToolIds so
selectedToolIds is accessed only after the null check; ensure this guard covers
the branch that checks toolIds.some(...) and the later consentedToolIds logic.
In @libs/auth/src/session/token.vault.ts:
- Around line 28-35: The TokenVault constructor currently accepts any VaultKey
array and blindly inserts entries into this.keys and sets this.active; update it
to validate each VaultKey: ensure key.key is a Buffer/Uint8Array of exactly 32
bytes (throw an Error mentioning the offending kid if not), and check for
duplicate kid values before inserting into this.keys (throw if a duplicate kid
is encountered rather than silently overwriting). Also validate that the chosen
active key (this.active = keys[0]) itself passes the 32-byte check and exists in
the map after insertion.
- Around line 57-67: The decrypt method must enforce blob.exp: before decoding
or decrypting, check if blob.exp is present, parse/validate it as a numeric
timestamp, compare it to the current time, and if it is expired throw a clear
error (e.g. throw new Error(`vault_expired:${blob.kid}`) or similar). Place this
check at the start of decrypt (in function decrypt) so expired blobs are
rejected immediately; if blob.exp is missing treat it as non-expiring. Ensure
the error distinguishes expired vs unknown kid (keep the existing
vault_unknown_kid behavior).
🧹 Nitpick comments (17)
libs/auth/src/session/token.vault.ts (1)
37-40: Consider validating the key parameter for consistency.The
rotateTomethod doesn't validate the input key, which could allow invalid keys (wrong size, missing properties) to be set as active. This is inconsistent with the validation proposed for the constructor.♻️ Proposed validation for rotateTo
rotateTo(k: VaultKey) { + if (!k.kid || typeof k.kid !== 'string') { + throw new Error('rotateTo: key must have a non-empty string kid'); + } + if (!(k.key instanceof Uint8Array) || k.key.length !== 32) { + throw new Error(`rotateTo: key ${k.kid} must be a 32-byte Uint8Array for AES-256-GCM`); + } this.active = k; this.keys.set(k.kid, k.key); }libs/auth/src/session/vault-encryption.ts (4)
125-132: Consider using delimited concatenation for IKM to prevent collisions.Joining IKM parts without a delimiter can cause collisions. For example,
jti="ab", vaultKey="cd"produces the same IKM asjti="abc", vaultKey="d". Using a delimiter (e.g., null byte\0) ensures distinct inputs produce distinct keys.♻️ Proposed fix
const ikmParts = [ claims.jti, claims.vaultKey ?? '', claims.sub, claims.iat.toString(), new TextDecoder().decode(this.pepper), ]; - const ikm = new TextEncoder().encode(ikmParts.join('')); + const ikm = new TextEncoder().encode(ikmParts.join('\0'));
157-165: Apply the same delimiter fix toderiveKeyFromToken.This method has the same concatenation pattern without delimiters, sharing the collision risk noted for
deriveKey.♻️ Proposed fix
const ikmParts = [ claims.jti, claims.vaultKey ?? '', claims.sub, claims.iat.toString(), signature, new TextDecoder().decode(this.pepper), ]; - const ikm = new TextEncoder().encode(ikmParts.join('')); + const ikm = new TextEncoder().encode(ikmParts.join('\0'));
33-44: Consider adding.strict()to Zod schemas for stricter validation.The
encryptedDataSchemaandencryptedVaultEntrySchemadon't use.strict(), which allows extra properties to pass validation silently. Based on learnings for uipack validation schemas, consider using.strict()for tighter input validation, especially for security-sensitive data structures.♻️ Proposed fix for encryptedDataSchema
export const encryptedDataSchema = z.object({ /** Version for future algorithm changes */ v: z.literal(1), /** Algorithm identifier */ alg: z.literal('aes-256-gcm'), /** Initialization vector (base64) */ iv: z.string(), /** Ciphertext (base64) */ ct: z.string(), /** Authentication tag (base64) */ tag: z.string(), -}); +}).strict();Also applies to: 281-304
254-257: Unsafe cast onJSON.parseresult.The
decryptObjectmethod casts the parsed JSON directly toTwithout validation. If the encrypted data was tampered with before encryption or the wrong key is used (producing garbage that happens to be valid JSON), this could return malformed data.♻️ Consider accepting an optional schema for validation
- async decryptObject<T>(encrypted: EncryptedData, key: Uint8Array): Promise<T> { + async decryptObject<T>(encrypted: EncryptedData, key: Uint8Array, schema?: z.ZodType<T>): Promise<T> { const plaintext = await this.decrypt(encrypted, key); - return JSON.parse(plaintext) as T; + const parsed = JSON.parse(plaintext); + return schema ? schema.parse(parsed) : (parsed as T); }libs/sdk/src/auth/vault/auth-providers.vault.ts (4)
98-102: Unsafe type cast without validation.The cast
appCredential.credential as Tassumes the stored credential matches the generic type, but this isn't validated. If the stored credential type doesn't match the expected type, this could cause runtime issues downstream.♻️ Consider documenting the assumption or adding runtime validation
- return appCredential.credential as T; + // Type safety: caller is responsible for ensuring T matches the stored credential type + // Consider passing a Zod schema for runtime validation in type-sensitive contexts + return appCredential.credential as T;
121-127: Inconsistent error handling: errors silently swallowed.
removeCredentiallogs a warning but doesn't throw or return a failure indicator, unlikestoreCredentialandrefreshOAuthCredentialwhich rethrow. This inconsistency could mask storage failures.♻️ Consider consistent error handling
async removeCredential( sessionId: string, providerId: string, scope: CredentialScope, userId?: string, ): Promise<void> { const vaultKey = this.buildVaultKey(sessionId, scope, userId); try { await this.baseVault.removeAppCredential(vaultKey, this.namespace, providerId); this.logger?.debug(`Removed credential "${providerId}"`); } catch (error) { this.logger?.warn(`Failed to remove credential "${providerId}":`, error); + throw error; } }Alternatively, if silent failures are intentional (e.g., idempotent deletes), document this behavior in the JSDoc.
258-262: Default case duplicates session scope logic.The
defaultcase on lines 260-261 repeats thesessioncase behavior. This is defensive but could be simplified or made explicit with an exhaustive type check.♻️ Use exhaustive check for type safety
case 'session': return `${this.namespace}session:${sessionId}`; - default: - return `${this.namespace}session:${sessionId}`; + default: { + // Exhaustive check - should never reach here with valid CredentialScope + const _exhaustive: never = scope; + return `${this.namespace}session:${sessionId}`; + } }
268-278: Code duplication withextractCredentialExpiryin credential-helpers.ts.This
extractExpirymethod duplicates the logic inlibs/sdk/src/auth/vault/credential-loaders/credential-helpers.ts. Consider importing and reusing that helper to maintain DRY principles.♻️ Proposed fix
+import { extractCredentialExpiry } from './credential-loaders/credential-helpers'; // ... in class - /** - * Extract expiry time from credential - */ - private extractExpiry(credential: Credential): number | undefined { - switch (credential.type) { - case 'oauth': - case 'oauth_pkce': - case 'bearer': - case 'service_account': - return credential.expiresAt; - default: - return undefined; - } - } + /** + * Extract expiry time from credential + */ + private extractExpiry(credential: Credential): number | undefined { + return extractCredentialExpiry(credential); + }libs/auth/src/session/storage/storage-authorization-vault.ts (2)
121-126: Silent failure on missing vault entry.The
updatemethod logs a warning but silently returns when the entry is not found. This could mask bugs where callers expect an error on invalid IDs. Consider throwing or returning a boolean to indicate success/failure.♻️ Consider throwing for explicit failure
async update(id: string, updates: Partial<AuthorizationVaultEntry>): Promise<void> { const entry = await this.get(id); if (!entry) { - console.warn(`[StorageAuthorizationVault] Update failed: vault entry not found for id ${id}`); - return; + throw new Error(`[StorageAuthorizationVault] Update failed: vault entry not found for id ${id}`); }Alternatively, document this as intentional idempotent behavior if that's the design intent.
432-457: Scalability concern acknowledged in comments, but consider additional safeguards.The cleanup implementation iterates all keys with
keys('*'), which is noted as expensive. Consider adding a limit or cursor-based pagination to prevent memory issues with large datasets.♻️ Consider adding a batch limit
async cleanup(): Promise<void> { // For storage-based implementation, cleanup would need to iterate over all keys // This is expensive for large datasets, so we rely on in-document expiration checks // For production use, consider a scheduled cleanup job with cursor-based iteration const keys = await this.storage.keys('*'); const now = Date.now(); + + // Process in batches to limit memory usage + const BATCH_SIZE = 100; + const keysToProcess = keys.slice(0, BATCH_SIZE); + if (keys.length > BATCH_SIZE) { + console.warn(`[StorageAuthorizationVault] cleanup: processing first ${BATCH_SIZE} of ${keys.length} keys`); + } - for (const key of keys) { + for (const key of keysToProcess) {libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (1)
157-162:cancelAll()clears the map but doesn't reject pending promises.Clearing the in-flight map allows new loads to start, but existing callers awaiting the old promises will still complete normally. If true cancellation is intended, consider also rejecting the pending promises or documenting the current behavior.
📝 Consider documenting the behavior
/** * Cancel all in-flight loads (for cleanup) + * + * Note: This clears the deduplication map, allowing new loads to start. + * Existing callers awaiting previous loads will still complete normally. + * For true cancellation with AbortController, a more complex implementation would be needed. */ cancelAll(): void { this.loading.clear(); }libs/sdk/src/auth/session/authorization-vault.ts (1)
169-170: Consider deprecating DSA key type.DSA keys are deprecated in modern SSH implementations (OpenSSH disabled them by default in version 7.0). While this may be needed for legacy compatibility, consider adding a JSDoc comment noting that DSA is deprecated, or remove it if legacy support isn't required.
libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts (1)
86-91: Fragile provider name extraction from rejected promises.The provider name extraction relies on the error having been mutated by
loadOne(line 114). If an error is thrown before reaching that mutation (e.g., during argument evaluation),providerNamewill be undefined and default to'unknown'. This makes debugging harder.Consider capturing the provider name before the async operation to ensure it's always available:
♻️ Proposed fix
// Load all in parallel const results = await Promise.allSettled( eagerProviders.map(async (config) => { - const credential = await this.loadOne(config, context); - return { name: config.name, credential, config }; + try { + const credential = await this.loadOne(config, context); + return { name: config.name, credential, config }; + } catch (error) { + // Re-throw with provider name always attached + const err = error instanceof Error ? error : new Error(String(error)); + (err as { providerName?: string }).providerName = config.name; + throw err; + } }), ); // Process results for (const result of results) { if (result.status === 'fulfilled') { const { name, credential, config } = result.value; if (credential) { const resolved = this.wrapCredential(name, credential, config); loaded.set(name, resolved); this.cache.set(name, resolved, config.cacheTtl); this.logger?.debug(`Loaded eager credential: ${name}`); } else if (config.required) { failed.set(name, new Error(`Required credential "${name}" returned null`)); this.logger?.warn(`Required eager credential "${name}" returned null`); } } else { - const providerName = (result.reason as { providerName?: string })?.providerName || 'unknown'; + const providerName = (result.reason as { providerName?: string })?.providerName ?? 'unknown'; const error = result.reason instanceof Error ? result.reason : new Error(String(result.reason)); failed.set(providerName, error); this.logger?.warn(`Failed to load eager credential "${providerName}":`, error.message); } }libs/auth/src/session/authorization-vault.ts (3)
886-891: Consider typing the Redis client parameter.The
anytype for the Redis client reduces type safety. Consider defining a minimal interface for the Redis operations used:♻️ Proposed interface
+/** Minimal Redis client interface for vault storage */ +interface RedisVaultClient { + get(key: string): Promise<string | null>; + set(key: string, value: string): Promise<unknown>; + del(key: string): Promise<unknown>; +} + export class RedisAuthorizationVault implements AuthorizationVault { constructor( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private readonly redis: any, + private readonly redis: RedisVaultClient, private readonly namespace = 'vault:', ) {}This would work with both
ioredisand@vercel/kv(both listed as peerDependencies inlibs/auth/package.json) while maintaining type safety.
1072-1075: Redis cleanup method is a placeholder.The
cleanup()method lacks implementation for the Redis vault. Consider adding a TODO comment with more detail or implementing the SCAN-based cleanup.Would you like me to help implement the Redis cleanup using SCAN, or should I open an issue to track this?
932-938: Consider validating parsed JSON against schema.The
getmethod parses JSON and casts directly toAuthorizationVaultEntrywithout validation. If the stored data is malformed or has been tampered with, this could cause runtime errors or security issues.♻️ Proposed fix with schema validation
async get(id: string): Promise<AuthorizationVaultEntry | null> { const data = await this.redis.get(this.key(id)); if (!data) return null; - const entry = JSON.parse(data) as AuthorizationVaultEntry; - return entry; + try { + const parsed = JSON.parse(data); + return authorizationVaultEntrySchema.parse(parsed); + } catch { + // Log error and return null for corrupted entries + return null; + } }This adds defense-in-depth for data integrity and would catch schema drift or corruption issues early.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (22)
libs/auth/jest.config.ctslibs/auth/src/jwks/__tests__/jwks.utils.test.tslibs/auth/src/jwks/jwks.utils.tslibs/auth/src/session/authorization-vault.tslibs/auth/src/session/storage/__tests__/storage-token-store.test.tslibs/auth/src/session/storage/storage-authorization-vault.tslibs/auth/src/session/storage/storage-token-store.tslibs/auth/src/session/token.vault.tslibs/auth/src/session/vault-encryption.tslibs/auth/src/ui/__tests__/base-layout.advanced.test.tslibs/auth/src/ui/__tests__/base-layout.test.tslibs/auth/src/ui/__tests__/templates.advanced.test.tslibs/auth/src/ui/__tests__/templates.test.tslibs/auth/src/ui/base-layout.tslibs/auth/src/ui/templates.tslibs/sdk/src/auth/session/authorization-vault.tslibs/sdk/src/auth/vault/auth-providers.types.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/sdk/src/auth/vault/credential-loaders/credential-helpers.tslibs/sdk/src/auth/vault/credential-loaders/eager-loader.tslibs/sdk/src/auth/vault/credential-loaders/index.tslibs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- libs/auth/src/session/storage/tests/storage-token-store.test.ts
- libs/auth/src/jwks/tests/jwks.utils.test.ts
- libs/auth/src/ui/tests/base-layout.advanced.test.ts
- libs/auth/src/session/storage/storage-token-store.ts
- libs/auth/src/ui/tests/templates.test.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
libs/auth/src/ui/__tests__/base-layout.test.tslibs/sdk/src/auth/vault/credential-loaders/credential-helpers.tslibs/sdk/src/auth/vault/credential-loaders/index.tslibs/auth/src/ui/__tests__/templates.advanced.test.tslibs/sdk/src/auth/vault/credential-loaders/eager-loader.tslibs/auth/src/session/vault-encryption.tslibs/sdk/src/auth/vault/credential-loaders/lazy-loader.tslibs/auth/src/session/token.vault.tslibs/auth/src/ui/base-layout.tslibs/sdk/src/auth/vault/auth-providers.types.tslibs/sdk/src/auth/session/authorization-vault.tslibs/auth/src/session/storage/storage-authorization-vault.tslibs/auth/src/jwks/jwks.utils.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/auth/src/ui/templates.tslibs/auth/src/session/authorization-vault.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Do not use prefixes like 'PT-001' in test names
Do not skip constructor validation tests for error classes and types
Includeinstanceofchecks in tests for error classes to verify proper error hierarchy
Files:
libs/auth/src/ui/__tests__/base-layout.test.tslibs/auth/src/ui/__tests__/templates.advanced.test.ts
libs/**
⚙️ CodeRabbit configuration file
libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).
Files:
libs/auth/src/ui/__tests__/base-layout.test.tslibs/sdk/src/auth/vault/credential-loaders/credential-helpers.tslibs/auth/jest.config.ctslibs/sdk/src/auth/vault/credential-loaders/index.tslibs/auth/src/ui/__tests__/templates.advanced.test.tslibs/sdk/src/auth/vault/credential-loaders/eager-loader.tslibs/auth/src/session/vault-encryption.tslibs/sdk/src/auth/vault/credential-loaders/lazy-loader.tslibs/auth/src/session/token.vault.tslibs/auth/src/ui/base-layout.tslibs/sdk/src/auth/vault/auth-providers.types.tslibs/sdk/src/auth/session/authorization-vault.tslibs/auth/src/session/storage/storage-authorization-vault.tslibs/auth/src/jwks/jwks.utils.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/auth/src/ui/templates.tslibs/auth/src/session/authorization-vault.ts
libs/{sdk,adapters}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
UsegetCapabilities()for dynamic capability exposure instead of hardcoding capabilities
Files:
libs/sdk/src/auth/vault/credential-loaders/credential-helpers.tslibs/sdk/src/auth/vault/credential-loaders/index.tslibs/sdk/src/auth/vault/credential-loaders/eager-loader.tslibs/sdk/src/auth/vault/credential-loaders/lazy-loader.tslibs/sdk/src/auth/vault/auth-providers.types.tslibs/sdk/src/auth/session/authorization-vault.tslibs/sdk/src/auth/vault/auth-providers.vault.ts
libs/sdk/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/sdk/**/*.{ts,tsx}: Preferinterfacefor defining object shapes in TypeScript, avoidanytypes
Use type parameters with constraints instead of unconstrained generics withanydefaults
Validate URIs per RFC 3986 at metadata level usingisValidMcpUrirefinement
UsechangeScopeinstead ofscopein change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g.,Promise<GetPromptResult>) instead ofPromise<unknown>for MCP entry methods
Useunknowninstead ofanyfor generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows
Files:
libs/sdk/src/auth/vault/credential-loaders/credential-helpers.tslibs/sdk/src/auth/vault/credential-loaders/index.tslibs/sdk/src/auth/vault/credential-loaders/eager-loader.tslibs/sdk/src/auth/vault/credential-loaders/lazy-loader.tslibs/sdk/src/auth/vault/auth-providers.types.tslibs/sdk/src/auth/session/authorization-vault.tslibs/sdk/src/auth/vault/auth-providers.vault.ts
🧠 Learnings (30)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.)
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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:
libs/auth/src/ui/__tests__/base-layout.test.tslibs/auth/jest.config.ctslibs/auth/src/ui/__tests__/templates.advanced.test.tslibs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{renderers,handlebars,utils}/**/*.{test,spec}.{ts,tsx,js,jsx} : Test HTML escaping for user-provided content to prevent XSS vulnerabilities
Applied to files:
libs/auth/src/ui/__tests__/base-layout.test.tslibs/auth/src/ui/__tests__/templates.advanced.test.tslibs/auth/src/ui/base-layout.tslibs/auth/src/ui/templates.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/*.{test,spec}.{ts,tsx,js,jsx} : Every component and utility must test invalid inputs and edge cases
Applied to files:
libs/auth/src/ui/__tests__/base-layout.test.tslibs/auth/src/ui/__tests__/templates.advanced.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/**/*.test.{ts,tsx} : SSR components must be tested for correct HTML output and client-side hydration must be tested separately
Applied to files:
libs/auth/src/ui/__tests__/base-layout.test.tslibs/auth/src/ui/__tests__/templates.advanced.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/**/*.test.{ts,tsx} : Use React Testing Library for component tests and include SSR/hydration tests for all interactive components
Applied to files:
libs/auth/src/ui/__tests__/base-layout.test.tslibs/auth/jest.config.ctslibs/auth/src/ui/__tests__/templates.advanced.test.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{src,tests}/**/*.{ts,tsx,js,jsx} : Maintain 95%+ code coverage across statements, branches, functions, and lines
Applied to files:
libs/auth/src/ui/__tests__/base-layout.test.tslibs/auth/jest.config.cts
📚 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} : Test coverage must be 95%+ across statements, branches, functions, and lines
Applied to files:
libs/auth/src/ui/__tests__/base-layout.test.tslibs/auth/jest.config.ctslibs/auth/src/ui/__tests__/templates.advanced.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/**/*.test.{ts,tsx} : Do not skip SSR/hydration testing for components; all interactive widgets must be tested in both rendering contexts
Applied to files:
libs/auth/src/ui/__tests__/base-layout.test.tslibs/auth/src/ui/__tests__/templates.advanced.test.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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/auth/src/ui/__tests__/base-layout.test.tslibs/sdk/src/auth/vault/credential-loaders/index.tslibs/auth/src/ui/base-layout.tslibs/auth/src/ui/templates.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Applied to files:
libs/auth/jest.config.cts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Applied to files:
libs/auth/jest.config.cts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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:
libs/auth/jest.config.cts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/*/src/index.ts : Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases
Applied to files:
libs/sdk/src/auth/vault/credential-loaders/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files
Applied to files:
libs/sdk/src/auth/vault/credential-loaders/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management
Applied to files:
libs/auth/src/session/vault-encryption.tslibs/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/auth/src/session/vault-encryption.tslibs/auth/src/session/token.vault.tslibs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Applied to files:
libs/auth/src/session/vault-encryption.tslibs/auth/src/session/token.vault.tslibs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Applied to files:
libs/auth/src/session/token.vault.tslibs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly
Applied to files:
libs/auth/src/session/token.vault.tslibs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/auth/src/session/token.vault.tslibs/auth/src/ui/base-layout.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/auth/src/session/token.vault.tslibs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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:
libs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{renderers,build}/**/*.{ts,tsx} : Use mdxClientRenderer for CDN-based MDX rendering without bundled React
Applied to files:
libs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{renderers,handlebars,base-template}/**/*.{ts,tsx,js,jsx} : Never skip HTML escaping for user-provided content in renderers and templates
Applied to files:
libs/auth/src/ui/base-layout.tslibs/auth/src/ui/templates.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/components/**/*.{ts,tsx} : HTML string component functions (button, card, alert, badge) must accept component-specific props and return HTML strings
Applied to files:
libs/auth/src/ui/base-layout.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{validation,**}/**/*.{ts,tsx} : Use `.strict()` on all Zod schemas for validation
Applied to files:
libs/sdk/src/auth/vault/auth-providers.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/auth/session/authorization-vault.tslibs/auth/src/jwks/jwks.utils.tslibs/sdk/src/auth/vault/auth-providers.vault.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{renderers,build}/**/*.{ts,tsx} : Use htmlRenderer for HTML template rendering without React dependency
Applied to files:
libs/auth/src/ui/templates.ts
🧬 Code graph analysis (7)
libs/sdk/src/auth/vault/credential-loaders/credential-helpers.ts (2)
libs/auth/src/session/authorization-vault.ts (1)
Credential(247-247)libs/sdk/src/auth/session/authorization-vault.ts (1)
Credential(247-247)
libs/auth/src/ui/__tests__/templates.advanced.test.ts (1)
libs/auth/src/ui/templates.ts (9)
buildConsentPage(97-123)AppAuthCard(32-43)buildIncrementalAuthPage(188-249)buildFederatedLoginPage(254-341)ProviderCard(48-61)buildToolConsentPage(346-466)ToolCard(66-77)buildLoginPage(471-526)buildErrorPage(531-553)
libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts (4)
libs/sdk/src/auth/vault/auth-providers.types.ts (1)
ResolvedCredential(71-84)libs/sdk/src/auth/vault/auth-providers.registry.ts (2)
AuthProvidersRegistry(35-186)NormalizedProviderConfig(21-30)libs/sdk/src/auth/vault/credential-cache.ts (1)
CredentialCache(25-242)libs/sdk/src/auth/session/authorization-vault.ts (1)
Credential(247-247)
libs/sdk/src/auth/vault/auth-providers.types.ts (1)
libs/sdk/src/auth/session/authorization-vault.ts (2)
Credential(247-247)AuthorizationVault(386-534)
libs/sdk/src/auth/vault/auth-providers.vault.ts (3)
libs/sdk/src/auth/vault/index.ts (3)
AuthProvidersVault(81-81)CredentialScope(46-46)AUTH_PROVIDERS_VAULT(81-81)libs/sdk/src/auth/session/authorization-vault.ts (3)
AuthorizationVault(386-534)Credential(247-247)AppCredential(285-285)libs/sdk/src/auth/vault/auth-providers.types.ts (1)
CredentialScope(22-22)
libs/auth/src/ui/templates.ts (1)
libs/auth/src/ui/base-layout.ts (4)
escapeHtml(157-157)wideLayout(357-366)centeredCardLayout(340-352)extraWideLayout(371-380)
libs/auth/src/session/authorization-vault.ts (3)
libs/sdk/src/auth/session/authorization-vault.ts (15)
credentialTypeSchema(34-45)oauthCredentialSchema(56-70)apiKeyCredentialSchema(75-85)basicAuthCredentialSchema(90-98)bearerCredentialSchema(103-109)privateKeyCredentialSchema(114-128)mtlsCredentialSchema(133-143)customCredentialSchema(148-156)sshKeyCredentialSchema(161-175)serviceAccountCredentialSchema(180-198)pkceOAuthCredentialSchema(203-219)credentialSchema(224-235)OAuthCredential(237-237)Credential(247-247)key(893-895)libs/utils/src/crypto/index.ts (1)
randomUUID(48-50)libs/auth/src/session/storage/storage-authorization-vault.ts (1)
key(467-469)
⏰ 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). (4)
- GitHub Check: Lint & Format Checks
- GitHub Check: Build Libraries
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (47)
libs/auth/jest.config.cts (1)
1-11: LGTM - Jest configuration is well-structured.The basic Jest setup is appropriate for a Node.js library, with correct ts-jest transform configuration and proper handling of the
joseESM module viatransformIgnorePatterns.libs/auth/src/jwks/jwks.utils.ts (1)
21-21: Good improvement to type safety!The added
!Array.isArray(obj)check ensures the runtime behavior correctly matches theRecord<string, unknown>return type. JWT payloads are typically objects (per RFC 7519), so filtering out arrays aligns with standards and prevents type mismatches. The comprehensive test suite, including a dedicated test case for array payload rejection (lines 100-105), confirms this change is intentional and safe.libs/auth/src/session/token.vault.ts (2)
8-8: LGTM! Correct use of@frontmcp/utilsfor cryptographic operations.The imports follow the coding guidelines and learnings by using
@frontmcp/utilsinstead ofnode:cryptodirectly.
42-55: LGTM! Encryption logic is correct.The implementation correctly:
- Generates a 12-byte IV for AES-GCM mode
- Uses the active key for encryption
- Constructs a well-formed EncBlob with proper base64url encoding
libs/auth/src/session/vault-encryption.ts (1)
23-24: LGTM! Proper use of@frontmcp/utilsfor cryptographic operations.Correctly imports cryptographic primitives from the centralized utils package rather than using
node:cryptodirectly, aligning with the codebase guidelines.libs/sdk/src/auth/vault/auth-providers.types.ts (3)
56-62: LGTM! Schema uses.strict()for validation.The
getCredentialOptionsSchemaproperly uses.strict()to reject unknown properties, aligning with validation best practices.
188-202: Reasonable use ofz.any()for function schemas with documented justification.The comment on line 195 explains the rationale for using
z.any()for function properties (Zod 4 compatibility). While coding guidelines discourageanytypes, runtime validation of function signatures is not practical with Zod, and the justification is documented. Consider adding a runtime type check in the consuming code if stricter validation is needed.
296-305: LGTM! Well-structured options schema with sensible defaults.The
authProvidersVaultOptionsSchemauses.strict()and provides reasonable defaults for optional configuration fields.libs/auth/src/session/storage/storage-authorization-vault.ts (1)
22-23: LGTM! Proper use of@frontmcp/utilsfor UUID generation.Correctly imports
randomUUIDfrom the centralized utils package per coding guidelines.libs/sdk/src/auth/vault/credential-loaders/credential-helpers.ts (1)
15-25: LGTM! Clean, focused utility function.The
extractCredentialExpiryhelper is well-documented and handles the credential type switch appropriately. This is the canonical implementation that should be reused by other files (likeauth-providers.vault.ts).libs/sdk/src/auth/vault/credential-loaders/index.ts (1)
5-7: LGTM! Clean barrel exports.Properly exports public APIs through a single entry point, following the codebase pattern for barrel exports.
libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (2)
34-54: LGTM! Well-implemented in-flight deduplication.The load method correctly handles concurrent requests for the same provider by returning the existing promise, and properly cleans up the in-flight map in the
finallyblock regardless of success or failure.
77-84: LGTM! Consistent use ofextractCredentialExpiryhelper.Both
doLoadandrefreshmethods correctly use the sharedextractCredentialExpiryhelper to populate theexpiresAtfield, maintaining consistency with the credential-helpers module.Also applies to: 127-134
libs/sdk/src/auth/session/authorization-vault.ts (2)
42-44: LGTM! New credential types are well-integrated.The new credential types (
ssh_key,service_account,oauth_pkce) are properly added to the enum and included in the discriminated union. The schema definitions follow consistent patterns with existing credential types.Also applies to: 232-234
857-873: LGTM! OAuth PKCE refresh support is correctly implemented.The
refreshOAuthCredentialmethod now properly handles bothoauthandoauth_pkcecredential types, which share the same token structure. The type narrowing is sufficient since both schemas defineaccessToken,refreshToken, andexpiresAtfields.libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts (2)
103-117: LGTM with the suggested refactor above.The
loadOnemethod correctly calls the factory with merged context and metadata. The error mutation here becomes unnecessary if the refactor above is applied.
119-135: LGTM!The
wrapCredentialmethod correctly constructs aResolvedCredentialobject with proper metadata. The use ofextractCredentialExpiryensures consistent expiry extraction across credential types.libs/auth/src/session/authorization-vault.ts (1)
1-1211: Remove duplicateauthorization-vault.tsimplementation from SDK and re-export from@frontmcp/auth.The file
libs/sdk/src/auth/session/authorization-vault.tsduplicateslibs/auth/src/session/authorization-vault.ts(~1200 lines). The@frontmcp/authpackage already exportsInMemoryAuthorizationVault,RedisAuthorizationVault, and all credential/vault schemas as its public API.The SDK should import and re-export these from
@frontmcp/authinlibs/sdk/src/auth/session/index.tsinstead of maintaining a duplicate implementation. This ensures a single source of truth and prevents divergence between the auth package and SDK.⛔ Skipped due to learnings
Learnt from: CR Repo: agentfront/frontmcp PR: 0 File: libs/plugins/CLAUDE.md:0-0 Timestamp: 2026-01-06T02:34:55.689Z Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modulesLearnt from: CR Repo: agentfront/frontmcp PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-01-06T17:16:04.304Z Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific filesLearnt from: CR Repo: agentfront/frontmcp PR: 0 File: libs/plugins/CLAUDE.md:0-0 Timestamp: 2026-01-06T02:34:55.689Z 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`Learnt from: CR Repo: agentfront/frontmcp PR: 0 File: libs/plugins/CLAUDE.md:0-0 Timestamp: 2026-01-06T02:34:55.689Z Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic supportlibs/auth/src/ui/base-layout.ts (7)
37-46: LGTM! Centralized CDN configuration.The CDN configuration is appropriately exported as a public constant, allowing consumers to reference or override these URLs if needed. This aligns with the pattern of centralizing external resource URLs.
56-102: LGTM! Well-structured theme configuration types.The theme interfaces provide good extensibility through index signatures while defining common color and font properties. All properties are appropriately optional, allowing flexible theme customization.
107-120: LGTM! Sensible default theme.The default theme provides a well-considered color palette and font stack with appropriate fallbacks.
162-189: LGTM! Clean CSS theme builder.The internal
buildThemeCssfunction correctly transforms theme configuration into CSS custom properties, filtering out undefined values and maintaining proper formatting.
215-281: LGTM! Comprehensive HTML document builder with proper escaping.The
baseLayoutfunction correctly:
- Merges themes with appropriate precedence
- Conditionally includes CDN resources
- Escapes all user-provided text (title, description, bodyClass)
Note: The
contentparameter is intentionally not escaped, as it's expected to contain pre-built HTML from template builders.
303-324: LGTM! Well-designed layout factory.The
createLayoutfactory correctly implements deep theme merging with appropriate nullish coalescing for custom properties.
333-380: LGTM! Useful preset layouts.The preset layout functions provide convenient wrappers for common UI patterns, each with appropriate styling for their intended use cases.
libs/auth/src/ui/__tests__/base-layout.test.ts (6)
16-67: LGTM! Comprehensive HTML escaping tests with XSS coverage.The test suite thoroughly covers HTML special character escaping and includes multiple XSS attack vectors, ensuring robust security testing.
69-79: LGTM! CDN configuration tests.Tests verify the expected CDN URLs are correctly configured.
81-97: LGTM! Default theme tests.Tests verify the default theme has expected color and font definitions.
99-246: LGTM! Comprehensive baseLayout tests.The test suite thoroughly covers:
- HTML document structure validation
- XSS protection through escaping
- Conditional resource inclusion
- Theme merging and customization
- All configuration options
Excellent edge case coverage.
248-299: LGTM! createLayout factory tests.Tests cover the factory pattern, option overriding, and deep theme merging behavior.
301-359: LGTM! Layout wrapper tests.Tests cover all preset layout functions and verify their specific styling characteristics.
libs/auth/src/ui/__tests__/templates.advanced.test.ts (7)
19-105: LGTM! Comprehensive consent page edge case tests.Tests cover important edge cases including:
- Empty/missing scopes
- Multiple apps
- XSS protection
- Long strings
Excellent boundary condition testing.
107-131: LGTM! Incremental auth edge case tests.Tests cover optional fields and XSS protection for the incremental authorization flow.
133-235: LGTM! Comprehensive federated login edge case tests.Tests cover:
- Optional provider fields
- Multiple providers and app IDs
- Correct checkbox pre-selection logic
Excellent coverage of provider selection scenarios.
237-324: LGTM! Comprehensive tool consent edge case tests.Tests cover:
- Optional tool descriptions
- User info display logic
- Multi-app tool grouping
- XSS protection for user data
Excellent edge case coverage.
326-367: LGTM! Login page edge case tests.Tests cover scope parsing with special characters, multiple scopes, and XSS protection for client names.
369-417: LGTM! Error page edge case tests.Tests cover:
- Empty error/description fields
- Long error codes
- Multi-line descriptions
- HTML entity escaping
Comprehensive edge case coverage.
420-476: LGTM! Layout variation tests.Tests verify each template function uses the appropriate layout wrapper by checking for layout-specific CSS classes.
libs/auth/src/ui/templates.ts (9)
32-77: LGTM! Well-defined template data types.The interface definitions clearly separate required and optional fields, with appropriate types for each property.
97-123: LGTM! Consent page builder with proper escaping.The function correctly escapes the client name and uses the appropriate wide layout for multi-app display.
128-182: LGTM! Internal card builder with comprehensive escaping.The
buildAppCardHtmlfunction properly escapes all user-provided data including:
- App metadata (ID, name, description, icon URL)
- Scopes array
- Security tokens and callback paths
Excellent XSS protection.
188-249: LGTM! Incremental auth page with proper escaping.All dynamic content (tool ID, app metadata, session hint, callback path) is correctly escaped, and the centered card layout is appropriate for this single-app authorization flow.
254-341: LGTM! Federated login page with comprehensive escaping.All provider metadata, client information, and form parameters are properly escaped. The wide layout is appropriate for the multi-provider selection interface.
346-466: LGTM! Tool consent page with thorough escaping.All tool metadata, user information, and form parameters are properly escaped. The extra-wide layout is appropriate for displaying multiple tools grouped by app.
471-526: LGTM! Login page with proper scope handling and escaping.Scopes are correctly split and individually escaped, and all other dynamic content (client name, callback path, pending auth ID) is properly escaped. Centered card layout is appropriate for the login form.
531-553: LGTM! Error page with proper escaping.Both error code and description are correctly escaped, and the centered card layout is appropriate for error display.
563-565: LGTM! Simple compatibility wrapper.The
renderToHtmlfunction serves as a no-op compatibility layer, appropriately using underscore prefix for the unused options parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🤖 Fix all issues with AI agents
In @libs/auth/src/session/authorization-vault.ts:
- Around line 1171-1174: The Redis implementation uses a non-null assertion on
entry.consent (in the block that returns toolIds.some(...)) — replace the
assertion with a safe check: ensure entry.consent exists before accessing
selectedToolIds (e.g., grab const consent = entry.consent; if (!consent) return
false; then use consent.selectedToolIds.includes(toolId) or use optional
chaining) so you avoid the bang operator and potential runtime errors in the
authorization-vault Redis path.
- Around line 932-938: The get method parses Redis data with JSON.parse without
handling parse errors or validating the resulting object against the
AuthorizationVaultEntry shape; wrap the JSON.parse(this.redis.get(this.key(id)))
in a try/catch to return null (and optionally log a warning) on parse failure,
then validate the parsed object against the expected schema (e.g., a
validateAuthorizationVaultEntry or isAuthorizationVaultEntry helper) before
casting to AuthorizationVaultEntry and returning it; if validation fails, treat
it like missing data (return null) to avoid runtime exceptions.
- Around line 829-832: The non-null assertion on entry.consent should be
removed; after the existing guard that narrows entry.consent (the check above
line 825), store it in a local const (e.g., const consent = entry.consent) and
then use consent.selectedToolIds.includes(toolId) in the toolIds.some callback
(or, alternatively, use optional chaining like entry.consent?.selectedToolIds
but preferred is the local variable after the guard) so no `!` is needed.
In @libs/auth/src/session/token.vault.ts:
- Around line 57-70: The encrypt method calls encryptAesGcm with the wrong
argument order causing IV/plaintext mixup; update the encrypt function to call
encryptAesGcm(this.active.key, new TextEncoder().encode(plaintext), iv) (i.e.,
pass key, then plaintext bytes, then iv) so the AES-GCM API is used correctly,
keeping the rest of the returned EncBlob (alg, kid, iv, tag, data, exp, meta)
unchanged.
- Around line 52-55: rotateTo currently accepts any VaultKey and sets
this.active and this.keys without validating the key material, risking runtime
encryption errors; update rotateTo(VaultKey) to validate that k.key is a
Uint8Array of exactly 32 bytes (same constraint enforced in the constructor),
throw or reject with a clear error if invalid, and only set this.active and
this.keys.set(k.kid, k.key) after the validation passes; reference the VaultKey
type, rotateTo method, this.active, and this.keys when making the change so
behavior matches the constructor's key validation.
🧹 Nitpick comments (4)
libs/auth/src/session/authorization-vault.ts (3)
592-597: Inconsistent error handling for missing vault entries.The
updatemethod silently returns when the vault entry is not found, butcreatePendingAuththrows an error for the same condition. Consider consistent error handling or explicit documentation of the silent-fail behavior.Option: Throw or return a boolean indicating success
- async update(id: string, updates: Partial<AuthorizationVaultEntry>): Promise<void> { + async update(id: string, updates: Partial<AuthorizationVaultEntry>): Promise<boolean> { const entry = this.vaults.get(id); - if (!entry) return; + if (!entry) return false; Object.assign(entry, updates, { lastAccessAt: Date.now() }); + return true; }
718-731: Unused variable and inconsistent cleanup behavior.
- The
idvariable on line 721 is unused.- The comment on line 727 says "Keep for audit trail" but the filter removes non-pending auths, contradicting the comment.
Fix unused variable and clarify logic
async cleanup(): Promise<void> { const now = Date.now(); - for (const [id, entry] of this.vaults) { + for (const entry of this.vaults.values()) { // Clean up expired pending auths entry.pendingAuths = entry.pendingAuths.filter((p) => { if (now > p.expiresAt && p.status === 'pending') { p.status = 'expired'; } - // Keep for audit trail, or remove completely if desired + // Remove completed/cancelled/expired auths return p.status === 'pending'; }); } }
885-894:anytype used for Redis client.The
anytype for the Redis client is understandable given the variety of Redis client libraries, but consider defining a minimal interface for the required Redis operations (get,set,del) to provide some type safety.Define minimal Redis client interface
+/** Minimal Redis client interface */ +interface RedisClient { + get(key: string): Promise<string | null>; + set(key: string, value: string): Promise<unknown>; + del(key: string): Promise<unknown>; +} export class RedisAuthorizationVault implements AuthorizationVault { constructor( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private readonly redis: any, + private readonly redis: RedisClient, private readonly namespace = 'vault:', ) {}libs/auth/src/session/storage/storage-authorization-vault.ts (1)
293-307: Redundant storage reads in addAppCredential.The method fetches the entry on line 294, but
shouldStoreCredential(called on line 298) fetches the same entry again. Consider refactoring to avoid the duplicate read.Pass entry to avoid duplicate read
async addAppCredential(vaultId: string, credential: AppCredential): Promise<void> { const entry = await this.get(vaultId); if (!entry) return; // Check if we should store based on consent - const shouldStore = await this.shouldStoreCredential(vaultId, credential.appId); + const shouldStore = this.shouldStoreCredentialForEntry(entry, credential.appId); if (!shouldStore) { return; } const key = this.credentialKey(credential.appId, credential.providerId); entry.appCredentials[key] = credential; entry.lastAccessAt = Date.now(); await this.storage.set(this.key(vaultId), entry); } + private shouldStoreCredentialForEntry(entry: AuthorizationVaultEntry, appId: string, toolIds?: string[]): boolean { + const consent = entry.consent; + if (!consent?.enabled) { + return true; + } + if (toolIds && toolIds.length > 0) { + return toolIds.some((toolId) => consent.selectedToolIds.includes(toolId)); + } + return consent.selectedToolIds.some((toolId) => toolId.startsWith(`${appId}:`)); + }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
libs/auth/jest.config.ctslibs/auth/src/session/authorization-vault.tslibs/auth/src/session/storage/storage-authorization-vault.tslibs/auth/src/session/token.vault.tslibs/utils/src/storage/errors.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
libs/utils/src/storage/errors.tslibs/auth/src/session/token.vault.tslibs/auth/src/session/storage/storage-authorization-vault.tslibs/auth/src/session/authorization-vault.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/utils/src/storage/errors.tslibs/auth/src/session/token.vault.tslibs/auth/src/session/storage/storage-authorization-vault.tslibs/auth/jest.config.ctslibs/auth/src/session/authorization-vault.ts
🧠 Learnings (20)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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/utils/src/storage/errors.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Do not expose internal error details in public-facing error messages
Applied to files:
libs/utils/src/storage/errors.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/{sdk,adapters}/**/*.{ts,tsx} : Use specific error classes with MCP error codes instead of generic errors
Applied to files:
libs/utils/src/storage/errors.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management
Applied to files:
libs/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Applied to files:
libs/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Applied to files:
libs/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly
Applied to files:
libs/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/auth/src/session/token.vault.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/auth/src/session/token.vault.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/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Avoid non-null assertions (`!`) - use proper error handling and type guards instead
Applied to files:
libs/auth/src/session/storage/storage-authorization-vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Applied to files:
libs/auth/jest.config.cts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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:
libs/auth/jest.config.cts
📚 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} : Test coverage must be 95%+ across statements, branches, functions, and lines
Applied to files:
libs/auth/jest.config.cts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Applied to files:
libs/auth/jest.config.cts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{src,tests}/**/*.{ts,tsx,js,jsx} : Maintain 95%+ code coverage across statements, branches, functions, and lines
Applied to files:
libs/auth/jest.config.cts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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:
libs/auth/jest.config.cts
📚 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:
libs/auth/jest.config.cts
🧬 Code graph analysis (2)
libs/utils/src/storage/errors.ts (2)
libs/auth/src/session/authorization-vault.ts (1)
key(892-894)libs/auth/src/session/storage/storage-authorization-vault.ts (1)
key(468-470)
libs/auth/src/session/token.vault.ts (1)
libs/utils/src/crypto/index.ts (5)
randomBytes(57-62)encryptAesGcm(113-125)base64urlEncode(182-197)base64urlDecode(202-220)decryptAesGcm(135-146)
⏰ 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). (4)
- GitHub Check: Build Libraries
- GitHub Check: Lint & Format Checks
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Package E2E (Verdaccio)
🔇 Additional comments (16)
libs/utils/src/storage/errors.ts (1)
41-45: LGTM! Formatting improvements enhance readability.The multi-line constructor parameter formatting is consistent across all affected error classes and improves code readability without changing any runtime behavior or public API signatures.
Also applies to: 65-70, 89-93, 114-117, 132-135, 153-156
libs/auth/jest.config.cts (1)
1-20: Rename file tojest.config.tsto match monorepo conventions.The Jest configuration content is well-structured and coverage thresholds at 95% correctly enforce repository standards. However, the file uses
.ctsextension while all 17 other Jest config files in the monorepo use.tsextension. Renamejest.config.ctstojest.config.tsfor consistency.
- Coverage thresholds at 95% align with repository requirements
transformIgnorePatternscorrectly handles thejoseESM module- Test environment
nodeis appropriate for auth utilities⛔ Skipped due to learnings
Learnt from: CR Repo: agentfront/frontmcp PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-01-06T17:16:04.304Z Learning: Applies to **/*.test.{ts,tsx} : Use Jest for testing with 95%+ coverage requirement and 100% test pass rateLearnt from: CR Repo: agentfront/frontmcp PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-01-06T17:16:04.304Z Learning: Applies to **/*.test.{ts,tsx} : Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)Learnt from: CR Repo: agentfront/frontmcp PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-01-06T17:16:04.304Z Learning: Applies to **/*.test.{ts,tsx} : Do not use prefixes like 'PT-001' in test namesLearnt 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} : Test coverage must be 95%+ across statements, branches, functions, and linesLearnt from: CR Repo: agentfront/frontmcp PR: 0 File: libs/uipack/CLAUDE.md:0-0 Timestamp: 2026-01-04T14:35:18.366Z Learning: Applies to libs/uipack/**/{src,tests}/**/*.{ts,tsx,js,jsx} : Maintain 95%+ code coverage across statements, branches, functions, and linesLearnt from: CR Repo: agentfront/frontmcp PR: 0 File: libs/plugins/CLAUDE.md:0-0 Timestamp: 2026-01-06T02:34:55.689Z Learning: Applies to libs/plugins/**/*.test.ts : Tests must achieve 95%+ code coverage across all metrics; use `MockStore` implementing `RememberStoreInterface` for RememberPlugin testingLearnt from: CR Repo: agentfront/frontmcp PR: 0 File: libs/uipack/CLAUDE.md:0-0 Timestamp: 2026-01-04T14:35:18.366Z Learning: Applies to libs/uipack/**/{theme,adapters,bundler}/**/*.{test,spec}.{ts,tsx,js,jsx} : Test behavior across all supported platform configurations (OpenAI, Claude, etc.)Learnt from: CR Repo: agentfront/frontmcp PR: 0 File: libs/plugins/CLAUDE.md:0-0 Timestamp: 2026-01-06T02:34:55.689Z Learning: Applies to libs/plugins/**/*.ts : Configure approval requirements on tools using the `approval` decorator property with fields: `required`, `defaultScope`, `category`, `riskLevel`, and `approvalMessage`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 componentslibs/auth/src/session/token.vault.ts (4)
1-8: LGTM!The imports correctly use
@frontmcp/utilsfor cryptographic operations (encryptAesGcm,decryptAesGcm,randomBytes,base64urlEncode,base64urlDecode), which aligns with the coding guidelines and retrieved learnings.
10-20: LGTM!The
EncBlobandVaultKeytype definitions are well-structured. TheEncBlobenvelope correctly includes all necessary fields for AES-256-GCM: algorithm identifier, key ID for rotation support, IV, authentication tag, ciphertext, and optional expiration/metadata.
28-50: LGTM!The constructor properly validates:
- Non-empty keys array
- Each key is exactly 32 bytes (required for AES-256-GCM)
- No duplicate
kidvaluesThis defensive validation prevents runtime errors during encryption operations.
72-90: LGTM!The decrypt method correctly:
- Checks expiration before attempting decryption (fail-fast)
- Validates the
kidexists in the key map- Uses the correct argument order for
decryptAesGcm(key, ciphertext, iv, tag)- Properly decodes base64url fields before passing to the decryption function
libs/auth/src/session/authorization-vault.ts (4)
24-26: LGTM!Clean imports using
@frontmcp/utilsfor UUID generation as per coding guidelines.
34-248: LGTM! Well-structured credential schemas.Good use of Zod discriminated union for type-safe credential handling. The schema coverage is comprehensive and follows Zod 4 best practices.
344-371: LGTM! Comprehensive vault entry schema.The
authorizationVaultEntrySchemaprovides a well-structured model for session state with appropriate defaults for collections.
386-534: LGTM! Well-designed interface.The
AuthorizationVaultinterface provides a comprehensive API surface with clear documentation for each method.libs/auth/src/session/storage/storage-authorization-vault.ts (6)
22-34: LGTM! Clean imports and type usage.Good use of type imports for interfaces and proper schema import for validation.
69-80: LGTM! Good defensive design.The constructor handles both
StorageAdapterandNamespacedStorageappropriately, with optional schema validation and graceful handling of invalid data.
121-134: Good observability improvement over other implementations.The warning log on line 124 when update fails is a good practice that the other implementations lack. Consider adding similar logging to
InMemoryAuthorizationVaultandRedisAuthorizationVaultfor consistency.
233-250: Good optimization by inlining authorization logic.Unlike the Redis implementation which calls
authorizeAppseparately (requiring another read-write cycle), this implementation inlines the logic to complete the pending auth and authorize the app in a single write. This is more efficient.
464-470: TheTypedStorage.rawproperty exists and is correctly implemented. No issues found.The getter is properly defined at lines 193-195 of
typed-storage.tsand returns the underlyingStorageAdapter | NamespacedStorage. Thekey()method correctly uses this property to determine whether to apply namespace prefixing.
433-458: Thecleanupmethod correctly useskeys()which is supported by all storage backends.The
StorageAdapterinterface requires all implementations to provide thekeys()method with glob-pattern support. All storage adapters (memory, Redis, Vercel KV, Upstash) implement this method. The performance concern for large datasets is already documented in the code comments.
There was a problem hiding this 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/auth/src/session/authorization-vault.ts:
- Around line 718-731: In cleanup() the loop currently destructures [id, entry]
from this.vaults but never uses id; change the loop to iterate over the map
values instead (e.g., for (const entry of this.vaults.values())) so only entry
is declared, then keep the existing logic that filters entry.pendingAuths by
p.expiresAt and p.status; alternatively, if you prefer to keep the key for
future logging, rename id to _id to mark it as intentionally unused.
- Around line 932-943: In get(id: string) you currently JSON.parse and cast to
AuthorizationVaultEntry, which skips runtime checks; replace the cast with Zod
validation: parse the string, run authorizationVaultEntrySchema.safeParse (or
parse with try/catch) to validate the shape, return the parsed value on success,
and on validation failure treat it as a missing/corrupted entry (return null)
and optionally remove the key with this.redis.del(this.key(id)); keep using
this.redis.get and this.key(id) to locate the data and ensure the
authorizationVaultEntrySchema is imported where get is defined.
🧹 Nitpick comments (3)
libs/auth/src/session/authorization-vault.ts (3)
592-597: Consider returning success status or throwing on missing entries.The
updatemethod (and similar methods likeupdateConsent,authorizeApp) silently returns when the entry is not found. While this provides idempotent behavior, it can mask bugs where callers operate on non-existent vaults. Consider either:
- Returning a boolean indicating success
- Throwing an error for missing entries (consistent with
createPendingAuth)This would improve debuggability without changing the current interface if you opt for the return value approach.
885-890: Define a minimal Redis interface instead ofany.Per coding guidelines,
anytypes should be avoided. Define a minimal interface for the required Redis operations to maintain type safety while allowing different Redis client implementations:♻️ Suggested interface
export interface RedisLike { get(key: string): Promise<string | null>; set(key: string, value: string): Promise<unknown>; del(key: string): Promise<unknown>; } export class RedisAuthorizationVault implements AuthorizationVault { constructor( private readonly redis: RedisLike, private readonly namespace = 'vault:', ) {} // ... }
1082-1085: Consider implementing or documenting the cleanup placeholder.The
cleanup()method is a stub. If this is intentional for the initial release, consider adding a TODO comment or documentation explaining the expected behavior and limitations. Redis SCAN-based cleanup requires careful implementation to avoid blocking operations.Would you like me to help draft a SCAN-based cleanup implementation for expired pending auths?
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
libs/auth/src/session/authorization-vault.tslibs/auth/src/session/token.vault.tslibs/plugins/project.jsonlibs/plugins/tsconfig.lib.json
🧰 Additional context used
📓 Path-based instructions (2)
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/plugins/tsconfig.lib.jsonlibs/auth/src/session/token.vault.tslibs/plugins/project.jsonlibs/auth/src/session/authorization-vault.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
libs/auth/src/session/token.vault.tslibs/auth/src/session/authorization-vault.ts
🧠 Learnings (19)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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`
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/plugins/tsconfig.lib.jsonlibs/plugins/project.json
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/plugins/tsconfig.lib.jsonlibs/auth/src/session/token.vault.tslibs/plugins/project.json
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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:
libs/plugins/tsconfig.lib.jsonlibs/plugins/project.json
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/plugins/tsconfig.lib.json
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly
Applied to files:
libs/plugins/tsconfig.lib.jsonlibs/auth/src/session/token.vault.tslibs/plugins/project.json
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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/plugins/tsconfig.lib.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/plugins/tsconfig.lib.json
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/plugins/tsconfig.lib.jsonlibs/auth/src/session/token.vault.tslibs/plugins/project.json
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Applied to files:
libs/plugins/tsconfig.lib.jsonlibs/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/plugins/**/*.{ts,tsx} : Use module augmentation and runtime prototype modification to extend ExecutionContextBase with plugin properties
Applied to files:
libs/plugins/tsconfig.lib.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/plugins/tsconfig.lib.json
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management
Applied to files:
libs/auth/src/session/token.vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Applied to files:
libs/auth/src/session/token.vault.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/auth/src/session/token.vault.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/auth/src/session/token.vault.tslibs/auth/src/session/authorization-vault.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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/plugins/project.json
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Nx-based monorepo build system - use commands like `nx build sdk`, `nx test ast-guard`, `nx run-many -t test`
Applied to files:
libs/plugins/project.json
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Avoid non-null assertions (`!`) - use proper error handling and type guards instead
Applied to files:
libs/auth/src/session/authorization-vault.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). (4)
- GitHub Check: Build Libraries
- GitHub Check: Lint & Format Checks
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (11)
libs/plugins/tsconfig.lib.json (1)
13-13: LGTM!The new path mapping for
@frontmcp/plugin-configfollows the established pattern and is correctly positioned alphabetically among the other plugin mappings.libs/plugins/project.json (2)
7-7: LGTM!The
plugin-configimplicit dependency is correctly added alongside the existing plugin dependencies. This ensures proper build ordering.
29-29: LGTM! The plugin-config project exists with expected structure.The
@frontmcp/plugin-configexternal entry is correctly added to both CJS and ESM build configurations (lines 29 and 55), following the established pattern for other plugins. Verification confirms the plugin-config directory has the required project.json, package.json, and src/index.ts entry point.libs/auth/src/session/token.vault.ts (4)
1-9: LGTM! Proper use of@frontmcp/utilsfor cryptographic operations.The imports correctly use
@frontmcp/utilsfor crypto primitives (encryptAesGcm,decryptAesGcm,randomBytes,base64urlEncode,base64urlDecode) instead ofnode:crypto, aligning with the project's cross-platform cryptographic abstraction. Based on learnings and coding guidelines.
28-50: LGTM! Thorough key validation in constructor.The constructor properly validates:
- At least one key is required
- Each key is a 32-byte
Uint8Array(required for AES-256-GCM)- No duplicate
kidvaluesThe convention of using the first key as active is documented and reasonable for key rotation scenarios.
52-59: LGTM!Key rotation correctly validates the new key size and allows replacing existing keys (by
kid) in the key store, which is appropriate for rotation scenarios.
61-94: LGTM! Correct AES-256-GCM implementation.The encryption/decryption implementation follows best practices:
- 12-byte (96-bit) IV for AES-GCM is the standard recommendation
- Expiration is correctly handled in epoch seconds
- Unknown
kidand expired tokens throw descriptive errors- Base64url encoding ensures URL-safe serialization
libs/auth/src/session/authorization-vault.ts (4)
24-25: LGTM! Correct imports.Uses
randomUUIDfrom@frontmcp/utilsas required by coding guidelines, and Zod import aligns with the v4 library context.
34-248: LGTM! Well-structured Zod v4 schemas.The credential type system is comprehensive and correctly uses:
z.discriminatedUnionfor efficient type discriminationz.literalfor discriminator valuesz.inferfor type derivation- Appropriate defaults with
.default()
945-951: Verify concurrent access requirements for read-modify-write operations.The Redis implementation uses read-modify-write patterns (e.g.,
get→ modify →set) that aren't atomic. Under concurrent access, this could lead to lost updates. If high concurrency is expected, consider:
- Redis WATCH/MULTI/EXEC for optimistic locking
- Lua scripts for atomic operations
- Document single-writer assumptions
Is this vault expected to handle concurrent modifications to the same entry from multiple processes?
846-874: LGTM! OAuth credential refresh handles both credential types.The
refreshOAuthCredentialmethod correctly handles bothoauthandoauth_pkcecredential types, properly updates tokens, resets validity state, and maintains thelastAccessAttimestamp.
There was a problem hiding this 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 (3)
libs/sdk/src/auth/session/encrypted-authorization-vault.ts (1)
85-90: Type mismatch:keyshould beUint8Arrayinstead ofBuffer.Per the
VaultEncryption.deriveKeymethod (andderiveKeyFromToken), keys are returned asUint8Array. TheEncryptionContext.keytype should match to maintain type consistency across the codebase.🔧 Suggested fix
export interface EncryptionContext { /** Encryption key derived from JWT */ - key: Buffer; + key: Uint8Array; /** Vault ID (from JWT jti claim) */ vaultId: string; }libs/sdk/src/auth/session/record/session.stateful.ts (1)
51-54: Avoidas anycast and use specific error classes.Two issues in this stub constructor:
ctx as anyviolates strict TypeScript guidelines—use proper type narrowing or fix the type hierarchy- Per SDK guidelines, use specific error classes with MCP error codes instead of generic
Error🔧 Suggested fix
+import { InternalMcpError } from '../../../errors/mcp.error'; + constructor(ctx: StatefulCreateCtx) { - super(ctx as any); - throw new Error('Method not implemented.'); + super(ctx as BaseCreateCtx); + throw new InternalMcpError('StatefulSession not yet implemented', 'NOT_IMPLEMENTED'); }Note: The
StatelessSessionin the companion file already usesInternalMcpErrorcorrectly.libs/sdk/src/auth/session/token.refresh.ts (1)
111-116: Replace custombase64urlDecodefunction with import from@frontmcp/utils.The custom
base64urlDecodeimplementation duplicates functionality already available in@frontmcp/utils. Per the project guidelines, cryptographic and encoding operations should use@frontmcp/utilsfor consistency and cross-platform support.♻️ Suggested fix
+import { base64urlDecode } from '@frontmcp/utils'; + // ... existing imports ... -function base64urlDecode(input: string): string { - // pad - const pad = input.length % 4 === 2 ? '==' : input.length % 4 === 3 ? '=' : ''; - const s = input.replace(/-/g, '+').replace(/_/g, '/') + pad; - return Buffer.from(s, 'base64').toString('utf8'); -}Remove the local function and import from
@frontmcp/utils.
🤖 Fix all issues with AI agents
In @libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts:
- Line 42: The test declares encryptionKey as Uint8Array while the
implementation's EncryptionContext.key is typed as Buffer, risking a TS
mismatch; update EncryptionContext.key's type to Uint8Array (to match
VaultEncryption.deriveKey and the test) and adjust any related uses/overrides to
accept Uint8Array (or a union Buffer | Uint8Array if necessary), ensuring
imports/types referencing EncryptionContext reflect the new type.
In @libs/sdk/src/auth/session/record/session.stateless.ts:
- Line 5: The type name StatefulCreateCtx in this stateless session file is
misleading; rename the type to StatelessCreateCtx (or an equivalent
stateless-sounding name) and update any references in the StatelessSession
implementation—specifically the constructor parameter type for class
StatelessSession—to use the new StatelessCreateCtx name so the type accurately
reflects its usage.
In @libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts:
- Around line 314-327: The extractExpiry method duplicates logic already
implemented in extractCredentialExpiry; remove the duplicate logic in
extractExpiry and instead import and call extractCredentialExpiry from
credential-helpers (ensure the import is added to the top of the file),
returning its result (ensuring the return type remains number | undefined) and
remove any local special-casing; update usages of extractExpiry to continue to
work with the Credential type by forwarding the same Credential parameter into
extractCredentialExpiry.
In @libs/sdk/src/auth/vault/auth-providers.vault.ts:
- Around line 265-278: The extractExpiry method in auth-providers.vault.ts
duplicates logic found in credential-helpers.ts and
auth-providers.accessor.impl.ts; remove the local private
extractExpiry(credential: Credential) implementation and instead import and use
the shared helper from credential-helpers.ts (e.g., the exported extractExpiry
or appropriately named helper function) wherever extractExpiry is called within
the class, updating references to call the imported function; ensure the
Credential type usage remains compatible and adjust imports/exports so there’s a
single source of truth for expiry extraction.
In @libs/sdk/src/common/metadata/tool.metadata.ts:
- Around line 16-25: The TypeScript interface ToolAuthProviderMapping declares
required?: boolean but the runtime schema applies .default(true), so align them
by making the property non-optional (required: boolean) to reflect that after
parsing it will always be true/false; apply the same change to the other
affected interface referenced around lines 32-42 (the similar mapping interface
found there). Alternatively, if you prefer undefined to be a valid runtime
value, remove .default(true) from the corresponding schema instead—choose one
approach and make the interface and schema consistent.
🧹 Nitpick comments (17)
libs/auth/src/session/storage/in-memory-authorization-vault.ts (2)
60-62: Consider handling the connect() promise explicitly.The fire-and-forget
void connect()pattern could silently swallow errors if the adapter's behavior ever changes. For a memory adapter this is low-risk, but the pattern is fragile.💡 Alternative: use a static factory method
- constructor(options: InMemoryAuthorizationVaultOptions = {}) { + private constructor( + memoryAdapter: MemoryStorageAdapter, + options: InMemoryAuthorizationVaultOptions = {} + ) { const memoryAdapter = new MemoryStorageAdapter(); super(memoryAdapter, { namespace: options.namespace ?? 'vault', pendingAuthTtlMs: options.pendingAuthTtlMs, }); this.memoryAdapter = memoryAdapter; - - // Connect the adapter (memory adapter connect is instant and synchronous) - void this.memoryAdapter.connect(); } + + static async create(options: InMemoryAuthorizationVaultOptions = {}): Promise<InMemoryAuthorizationVault> { + const memoryAdapter = new MemoryStorageAdapter(); + await memoryAdapter.connect(); + return new InMemoryAuthorizationVault(memoryAdapter, options); + }Alternatively, if synchronous construction is required, the current approach is acceptable given the documented assumption.
68-73: Sequential deletion could be parallelized.The
forloop withawaitinside performs deletions sequentially. For testing with small datasets this is fine, butPromise.all()would be more efficient.💡 Proposed optimization
async clear(): Promise<void> { const keys = await this.memoryAdapter.keys('*'); - for (const key of keys) { - await this.memoryAdapter.delete(key); - } + await Promise.all(keys.map((key) => this.memoryAdapter.delete(key))); }libs/sdk/src/auth/session/encrypted-authorization-vault.ts (3)
110-116: Consider typing the Redis client more strictly.The
redis: anytype violates the strict TypeScript guidelines. Consider defining a minimal interface for the Redis client methods used by this class.♻️ Suggested interface for Redis client
+/** + * Minimal Redis client interface for vault storage + */ +interface RedisClient { + get(key: string): Promise<string | null>; + set(key: string, value: string): Promise<void>; + del(key: string): Promise<void>; +} + export class EncryptedRedisVault implements AuthorizationVault { constructor( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private readonly redis: any, + private readonly redis: RedisClient, private readonly encryption: VaultEncryption, private readonly namespace = 'vault:', ) {}
536-542: Non-null assertion onentry.consentafter optional check.Line 537 uses
entry.consent!.selectedToolIdswith a non-null assertion. While the code checksentry.consent?.enabledon line 532, the current structure still requires the assertion. Consider restructuring to avoid it.♻️ Suggested refactor to avoid non-null assertion
async shouldStoreCredential(vaultId: string, appId: string, toolIds?: string[]): Promise<boolean> { const entry = await this.loadEntry(vaultId); if (!entry) return false; - if (!entry.consent?.enabled) { + const consent = entry.consent; + if (!consent?.enabled) { return true; } if (toolIds && toolIds.length > 0) { - return toolIds.some((toolId) => entry.consent!.selectedToolIds.includes(toolId)); + return toolIds.some((toolId) => consent.selectedToolIds.includes(toolId)); } - const consentedToolIds = entry.consent.selectedToolIds; + const consentedToolIds = consent.selectedToolIds; return consentedToolIds.some((toolId) => toolId.startsWith(`${appId}:`)); }
594-606: Apply same Redis client typing to factory function.If you add the
RedisClientinterface as suggested above, update the factory function parameter type as well for consistency.♻️ Suggested fix
export function createEncryptedVault( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - redis: any, + redis: RedisClient, config: { pepper?: string; namespace?: string; } = {}, ): { vault: EncryptedRedisVault; encryption: VaultEncryption } {libs/sdk/src/auth/session/token.store.ts (2)
40-44: Avoid untypedanyfor the Redis client parameter.Per coding guidelines,
anytypes require strong justification. The comment acknowledges this is a sketch, but consider defining a minimal interface for the required Redis operations to maintain type safety.♻️ Suggested interface for Redis client
+/** Minimal Redis client interface for token storage. */ +export interface RedisClientLike { + get(key: string): Promise<string | null>; + set(key: string, value: string): Promise<unknown>; + del(key: string): Promise<unknown>; +} + /** Redis (sketch) — replace `any` with your redis client type. */ export class RedisTokenStore implements TokenStore { constructor( - private readonly redis: any, + private readonly redis: RedisClientLike, private readonly ns = 'tok:', ) {}
58-62: Consider adding runtime validation afterJSON.parse.The type assertion
as SecretRecordbypasses runtime checks. If stored data is corrupted or tampered with, this could cause subtle bugs downstream. Consider validating the parsed structure or at minimum checking for required fields.♻️ Optional: Add basic validation
async get(id: string) { const raw = await this.redis.get(this.key(id)); if (!raw) return undefined; - return JSON.parse(raw) as SecretRecord; + const parsed = JSON.parse(raw); + if (typeof parsed?.id !== 'string' || !parsed?.blob) { + return undefined; // or throw + } + return parsed as SecretRecord; }libs/sdk/src/auth/session/record/session.stateful.ts (2)
56-58: Use specific MCP error class for unimplemented method.Per SDK coding guidelines, prefer specific error classes with MCP error codes.
🔧 Suggested fix
override getToken(providerId?: string): Promise<string> | string { - throw new Error('Method not implemented.'); + throw new InternalMcpError('getToken not yet implemented for StatefulSession', 'NOT_IMPLEMENTED'); }
60-91: Consider removing or documenting the commented-out code block.Large commented-out implementations can create maintenance burden. If this is intentional scaffolding for future work, consider adding a brief comment explaining the purpose, or track it via a TODO/issue reference.
libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (2)
104-145: Consider adding deduplication to refresh similar to load.The
refreshmethod doesn't have deduplication likeloaddoes. If multiple callers trigger refresh concurrently for the same provider, this could result in redundant refresh calls and potential race conditions where one refresh overwrites another's result.♻️ Suggested approach
Consider tracking in-flight refreshes similarly to loads, or documenting that callers are responsible for preventing concurrent refreshes.
export class LazyCredentialLoader { /** In-flight loading promises for deduplication */ private readonly loading = new Map<string, Promise<ResolvedCredential | null>>(); + /** In-flight refresh promises for deduplication */ + private readonly refreshing = new Map<string, Promise<ResolvedCredential | null>>(); private readonly logger?: FrontMcpLogger;
157-162:cancelAll()clears tracking but doesn't abort in-flight operations.The method name suggests cancellation, but it only clears the map. In-flight promises will still complete and their results will be lost (not cached). Consider renaming to
clearPendingTracking()or documenting this behavior.📝 Documentation suggestion
/** - * Cancel all in-flight loads (for cleanup) + * Clear all in-flight load tracking (for cleanup). + * Note: This doesn't abort running operations - they will complete but their results won't be deduplicated. */ cancelAll(): void { this.loading.clear(); }libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts (1)
103-117: Consider wrapping errors instead of mutating them.Mutating the error object at line 114 is a code smell that can lead to unexpected behavior. Consider wrapping the error in a custom error class instead.
♻️ Suggested approach
+class ProviderLoadError extends Error { + constructor( + public readonly providerName: string, + public readonly cause: unknown, + ) { + super(`Failed to load provider "${providerName}": ${cause instanceof Error ? cause.message : String(cause)}`); + this.name = 'ProviderLoadError'; + } +} private async loadOne<T extends Credential>( config: NormalizedProviderConfig<T>, context: CredentialFactoryContext, ): Promise<T | null> { try { return await config.factory({ ...context, metadata: config.metadata, }); } catch (error) { - // Add provider name to error for identification - (error as { providerName?: string }).providerName = config.name; - throw error; + throw new ProviderLoadError(config.name, error); } }Then update the result processing:
} else { - const providerName = (result.reason as { providerName?: string })?.providerName || 'unknown'; - const error = result.reason instanceof Error ? result.reason : new Error(String(result.reason)); + const reason = result.reason; + const providerName = reason instanceof ProviderLoadError ? reason.providerName : 'unknown'; + const error = reason instanceof Error ? reason : new Error(String(reason)); failed.set(providerName, error);libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (2)
77-101: Consider preserving provider names for rejected promises ingetMany.When a promise is rejected in
getMany, the provider name is lost because it's not captured in the rejection. This makes it impossible to populate the results map with null for failed providers, leaving them absent from the result entirely.♻️ Suggested approach
async getMany( providerNames: string[], options?: GetCredentialOptions, ): Promise<Map<string, ResolvedCredential | null>> { const results = new Map<string, ResolvedCredential | null>(); // Load all in parallel const promises = providerNames.map(async (name) => { - const result = await this.get(name, options); - return { name, result }; + try { + const result = await this.get(name, options); + return { name, result, error: null }; + } catch (error) { + return { name, result: null, error }; + } }); - const settled = await Promise.allSettled(promises); + const settled = await Promise.all(promises); - for (const outcome of settled) { - if (outcome.status === 'fulfilled') { - results.set(outcome.value.name, outcome.value.result); - } else { - // Find the provider name from the error context if possible - this.logger?.warn('Failed to load credential:', outcome.reason); - } + for (const { name, result, error } of settled) { + results.set(name, result); + if (error) { + this.logger?.warn(`Failed to load credential "${name}":`, error); + } } return results; }
117-126: Consider parallel fetching inheadersMany.The current sequential loop could be slow with many providers. Consider using
Promise.allfor parallel fetching, similar togetMany.♻️ Suggested approach
async headersMany(providerNames: string[]): Promise<Record<string, string>> { - const allHeaders: Record<string, string> = {}; - - for (const name of providerNames) { - const headers = await this.headers(name); - Object.assign(allHeaders, headers); - } - - return allHeaders; + const headerResults = await Promise.all( + providerNames.map((name) => this.headers(name)), + ); + return Object.assign({}, ...headerResults); }libs/sdk/src/auth/vault/auth-providers.vault.ts (2)
189-239: Consider returning partial results vs empty array on errors ingetAllCredentials.The empty catch blocks silently swallow errors, which may hide underlying storage issues. Consider logging at debug level or returning a result that indicates partial failure.
📝 Suggested improvement
// Session scope try { const sessionCreds = await this.baseVault.getAppCredentials( this.buildVaultKey(sessionId, 'session'), this.namespace, ); credentials.push(...sessionCreds); - } catch { - // Ignore + } catch (error) { + this.logger?.debug('Failed to get session-scoped credentials:', error); }
249-263: Consider exhaustive switch handling forCredentialScope.The default case silently falls back to session scope, which could mask bugs if new scopes are added. Consider using an exhaustive check pattern.
♻️ Suggested approach
private buildVaultKey(sessionId: string, scope: CredentialScope, userId?: string): string { switch (scope) { case 'global': return `${this.namespace}global`; case 'user': if (!userId) { throw new Error(`userId is required for user-scoped credentials (namespace: ${this.namespace})`); } return `${this.namespace}user:${userId}`; case 'session': return `${this.namespace}session:${sessionId}`; - default: - return `${this.namespace}session:${sessionId}`; + default: { + // Exhaustive check - this should never happen + const _exhaustive: never = scope; + throw new Error(`Unknown credential scope: ${_exhaustive}`); + } } }libs/sdk/src/auth/vault/auth-providers.providers.ts (1)
32-40: Consider usingunknowninstead ofanyfor factory function types.Per coding guidelines, prefer
unknownoverany. The eslint-disable comment acknowledges this but a better approach would be to useunknownwith proper type narrowing in consumers.♻️ Suggested approach
interface AuthProvidersProviderDef { provide: Token; scope: ProviderScope; name?: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - useFactory?: (...args: any[]) => any; + useFactory?: (...args: unknown[]) => unknown; useValue?: unknown; inject?: () => readonly Token[]; }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (27)
libs/auth/src/index.tslibs/auth/src/session/authorization-vault.tslibs/auth/src/session/index.tslibs/auth/src/session/storage/in-memory-authorization-vault.tslibs/auth/src/session/storage/index.tslibs/sdk/src/auth/session/__tests__/authorization-vault.test.tslibs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.tslibs/sdk/src/auth/session/__tests__/vault-encryption.test.tslibs/sdk/src/auth/session/authorization-vault.tslibs/sdk/src/auth/session/encrypted-authorization-vault.tslibs/sdk/src/auth/session/record/session.stateful.tslibs/sdk/src/auth/session/record/session.stateless.tslibs/sdk/src/auth/session/token.refresh.tslibs/sdk/src/auth/session/token.store.tslibs/sdk/src/auth/session/token.vault.tslibs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/auth-providers.accessor.tslibs/sdk/src/auth/vault/auth-providers.providers.tslibs/sdk/src/auth/vault/auth-providers.registry.tslibs/sdk/src/auth/vault/auth-providers.types.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/sdk/src/auth/vault/credential-cache.tslibs/sdk/src/auth/vault/credential-loaders/credential-helpers.tslibs/sdk/src/auth/vault/credential-loaders/eager-loader.tslibs/sdk/src/auth/vault/credential-loaders/lazy-loader.tslibs/sdk/src/common/metadata/tool.metadata.tslibs/sdk/src/common/tokens/tool.tokens.ts
💤 Files with no reviewable changes (4)
- libs/sdk/src/auth/session/tests/vault-encryption.test.ts
- libs/sdk/src/auth/session/tests/authorization-vault.test.ts
- libs/sdk/src/auth/session/token.vault.ts
- libs/sdk/src/auth/session/authorization-vault.ts
🚧 Files skipped from review as they are similar to previous changes (8)
- libs/sdk/src/auth/vault/credential-loaders/credential-helpers.ts
- libs/sdk/src/common/tokens/tool.tokens.ts
- libs/sdk/src/auth/vault/auth-providers.accessor.ts
- libs/sdk/src/auth/vault/auth-providers.registry.ts
- libs/sdk/src/auth/vault/credential-cache.ts
- libs/auth/src/session/storage/index.ts
- libs/auth/src/session/index.ts
- libs/auth/src/index.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
libs/sdk/src/auth/session/token.refresh.tslibs/sdk/src/common/metadata/tool.metadata.tslibs/auth/src/session/storage/in-memory-authorization-vault.tslibs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.tslibs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/sdk/src/auth/vault/auth-providers.providers.tslibs/sdk/src/auth/session/encrypted-authorization-vault.tslibs/sdk/src/auth/session/record/session.stateful.tslibs/sdk/src/auth/session/token.store.tslibs/sdk/src/auth/session/record/session.stateless.tslibs/sdk/src/auth/vault/credential-loaders/eager-loader.tslibs/sdk/src/auth/vault/credential-loaders/lazy-loader.tslibs/sdk/src/auth/vault/auth-providers.types.tslibs/auth/src/session/authorization-vault.ts
libs/{sdk,adapters}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
UsegetCapabilities()for dynamic capability exposure instead of hardcoding capabilities
Files:
libs/sdk/src/auth/session/token.refresh.tslibs/sdk/src/common/metadata/tool.metadata.tslibs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.tslibs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/sdk/src/auth/vault/auth-providers.providers.tslibs/sdk/src/auth/session/encrypted-authorization-vault.tslibs/sdk/src/auth/session/record/session.stateful.tslibs/sdk/src/auth/session/token.store.tslibs/sdk/src/auth/session/record/session.stateless.tslibs/sdk/src/auth/vault/credential-loaders/eager-loader.tslibs/sdk/src/auth/vault/credential-loaders/lazy-loader.tslibs/sdk/src/auth/vault/auth-providers.types.ts
libs/sdk/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/sdk/**/*.{ts,tsx}: Preferinterfacefor defining object shapes in TypeScript, avoidanytypes
Use type parameters with constraints instead of unconstrained generics withanydefaults
Validate URIs per RFC 3986 at metadata level usingisValidMcpUrirefinement
UsechangeScopeinstead ofscopein change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g.,Promise<GetPromptResult>) instead ofPromise<unknown>for MCP entry methods
Useunknowninstead ofanyfor generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows
Files:
libs/sdk/src/auth/session/token.refresh.tslibs/sdk/src/common/metadata/tool.metadata.tslibs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.tslibs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/sdk/src/auth/vault/auth-providers.providers.tslibs/sdk/src/auth/session/encrypted-authorization-vault.tslibs/sdk/src/auth/session/record/session.stateful.tslibs/sdk/src/auth/session/token.store.tslibs/sdk/src/auth/session/record/session.stateless.tslibs/sdk/src/auth/vault/credential-loaders/eager-loader.tslibs/sdk/src/auth/vault/credential-loaders/lazy-loader.tslibs/sdk/src/auth/vault/auth-providers.types.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/auth/session/token.refresh.tslibs/sdk/src/common/metadata/tool.metadata.tslibs/auth/src/session/storage/in-memory-authorization-vault.tslibs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.tslibs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/sdk/src/auth/vault/auth-providers.providers.tslibs/sdk/src/auth/session/encrypted-authorization-vault.tslibs/sdk/src/auth/session/record/session.stateful.tslibs/sdk/src/auth/session/token.store.tslibs/sdk/src/auth/session/record/session.stateless.tslibs/sdk/src/auth/vault/credential-loaders/eager-loader.tslibs/sdk/src/auth/vault/credential-loaders/lazy-loader.tslibs/sdk/src/auth/vault/auth-providers.types.tslibs/auth/src/session/authorization-vault.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Do not use prefixes like 'PT-001' in test names
Do not skip constructor validation tests for error classes and types
Includeinstanceofchecks in tests for error classes to verify proper error hierarchy
Files:
libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts
🧠 Learnings (12)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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`
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/auth/session/token.refresh.tslibs/sdk/src/auth/session/record/session.stateful.tslibs/sdk/src/auth/session/token.store.tslibs/sdk/src/auth/session/record/session.stateless.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Applied to files:
libs/sdk/src/auth/session/token.refresh.tslibs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.tslibs/sdk/src/auth/session/encrypted-authorization-vault.tslibs/sdk/src/auth/session/record/session.stateful.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/sdk/src/auth/session/token.refresh.tslibs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/common/metadata/tool.metadata.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/tool.metadata.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management
Applied to files:
libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.tslibs/sdk/src/auth/session/encrypted-authorization-vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Applied to files:
libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.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/vault/auth-providers.vault.tslibs/auth/src/session/authorization-vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files
Applied to files:
libs/sdk/src/auth/session/record/session.stateful.tslibs/sdk/src/auth/session/record/session.stateless.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{validation,**}/**/*.{ts,tsx} : Use `.strict()` on all Zod schemas for validation
Applied to files:
libs/sdk/src/auth/vault/auth-providers.types.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Avoid non-null assertions (`!`) - use proper error handling and type guards instead
Applied to files:
libs/auth/src/session/authorization-vault.ts
🧬 Code graph analysis (5)
libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts (1)
libs/auth/src/session/vault-encryption.ts (1)
VaultEncryption(102-268)
libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (6)
libs/sdk/src/auth/vault/auth-providers.accessor.ts (1)
AuthProvidersAccessor(30-159)libs/sdk/src/auth/vault/auth-providers.registry.ts (2)
AuthProvidersRegistry(35-186)NormalizedProviderConfig(21-30)libs/sdk/src/auth/vault/auth-providers.vault.ts (1)
AuthProvidersVault(16-279)libs/sdk/src/auth/vault/credential-cache.ts (1)
CredentialCache(25-242)libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (1)
LazyCredentialLoader(17-163)libs/sdk/src/auth/vault/auth-providers.types.ts (3)
CredentialFactoryContext(92-111)GetCredentialOptions(46-53)ResolvedCredential(70-83)
libs/sdk/src/auth/vault/auth-providers.vault.ts (2)
libs/sdk/src/auth/vault/index.ts (3)
AuthProvidersVault(81-81)CredentialScope(46-46)AUTH_PROVIDERS_VAULT(81-81)libs/sdk/src/auth/vault/auth-providers.types.ts (1)
CredentialScope(21-21)
libs/sdk/src/auth/vault/auth-providers.providers.ts (7)
libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (1)
LazyCredentialLoader(17-163)libs/sdk/src/auth/vault/auth-providers.types.ts (2)
AuthProvidersVaultOptions(275-293)CredentialFactoryContext(92-111)libs/sdk/src/auth/vault/auth-providers.registry.ts (2)
AUTH_PROVIDERS_REGISTRY(191-191)AuthProvidersRegistry(35-186)libs/sdk/src/auth/vault/auth-providers.vault.ts (2)
AUTH_PROVIDERS_VAULT(284-284)AuthProvidersVault(16-279)libs/sdk/src/auth/vault/credential-cache.ts (2)
CREDENTIAL_CACHE(247-247)CredentialCache(25-242)libs/sdk/src/auth/vault/auth-providers.accessor.ts (1)
AUTH_PROVIDERS_ACCESSOR(169-169)libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (1)
AuthProvidersAccessorImpl(20-328)
libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (3)
libs/sdk/src/auth/vault/auth-providers.types.ts (2)
ResolvedCredential(70-83)CredentialFactoryContext(92-111)libs/sdk/src/auth/vault/auth-providers.registry.ts (1)
NormalizedProviderConfig(21-30)libs/sdk/src/auth/vault/credential-loaders/credential-helpers.ts (1)
extractCredentialExpiry(15-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). (4)
- GitHub Check: Build Libraries
- GitHub Check: Lint & Format Checks
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (39)
libs/auth/src/session/storage/in-memory-authorization-vault.ts (2)
1-21: LGTM!Good documentation explaining the development/testing purpose with clear guidance to use persistent adapters in production. Import from
@frontmcp/utilsfollows the project conventions.
23-38: LGTM!Interface is well-documented with sensible defaults for a testing-focused implementation.
libs/auth/src/session/authorization-vault.ts (4)
24-26: LGTM - Proper use of@frontmcp/utilsfor UUID generation.Correctly imports
randomUUIDfrom@frontmcp/utilsinstead of usingnode:cryptodirectly, in compliance with the coding guidelines.
34-47: Well-structured credential type enumeration.The credential type schema provides a comprehensive set of authentication methods with clear documentation. The enum values are self-descriptive and cover common auth patterns.
224-247: Good use of Zod discriminated union for type-safe credential handling.The discriminated union on the
typefield enables TypeScript to narrow credential types correctly. The type exports align well with the schemas.
386-534: ComprehensiveAuthorizationVaultinterface with clear contracts.The interface defines a complete API surface for vault operations including CRUD, consent management, credential lifecycle, and OAuth token refresh. Method signatures are well-typed and documented.
libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts (3)
51-56: LGTM - Properly updated to async key derivation.The
beforeEachhook is correctly made async to accommodate the now-asyncderiveKeymethod, ensuring the encryption key is properly derived before tests run.
179-210: Good test for decryption failure with wrong key.This test properly validates the security property that decryption fails when using an incorrect key, ensuring encrypted data cannot be accessed without the proper credentials.
600-647: Excellent zero-knowledge property validation.This test comprehensively verifies that:
- Metadata remains accessible without decryption
- Sensitive data is encrypted and unreadable
- Decryption fails with incorrect keys
This is crucial for validating the security guarantees of the vault.
libs/sdk/src/auth/session/encrypted-authorization-vault.ts (4)
29-41: LGTM - Imports consolidated from@frontmcp/auth.The imports are properly sourced from the new public auth package, aligning with the module refactor objectives.
169-178: LGTM - Encryption methods properly converted to async.The
encryptSensitiveanddecryptSensitivemethods are correctly updated to return Promises, matching the async nature of the underlyingVaultEncryptionmethods.
183-227: LGTM - Entry transformation methods properly handle async encryption.Both
toVaultEntryandtoRedisEntrycorrectly await the encryption/decryption operations before returning their results.
232-251: LGTM - Save and load operations properly await async transformations.The
saveEntryandloadEntrymethods correctly await the asynctoRedisEntryandtoVaultEntrycalls respectively.libs/sdk/src/auth/session/token.store.ts (1)
3-3: LGTM!Import path correctly updated to use
@frontmcp/authfor theEncBlobtype, aligning with the broader refactor centralizing auth types in the external package.libs/sdk/src/common/metadata/tool.metadata.ts (2)
263-305: Well-documented authProviders API with comprehensive examples.The JSDoc examples clearly demonstrate all supported usage patterns (shorthand, full mapping, multiple providers). The schema integration at line 349 properly validates against
toolAuthProviderMappingSchema.Also applies to: 349-349
176-181: Pre-existinganyusage inStructuredOutputType.Lines 178 and 180-181 use
z.ZodObject<any>. Per coding guidelines,anytypes require justification. This appears to be pre-existing code, but if touched in this PR, consider usingz.ZodObject<z.ZodRawShape>or a more constrained type.#!/bin/bash # Check if these lines were modified in this PR or are pre-existing git log --oneline -1 -- libs/sdk/src/common/metadata/tool.metadata.ts git diff HEAD~1 -- libs/sdk/src/common/metadata/tool.metadata.ts | grep -A2 -B2 "ZodObject<any>"libs/sdk/src/auth/session/record/session.stateless.ts (1)
2-2: LGTM!Import correctly updated to use
@frontmcp/authforTokenVault, consistent with the auth module refactoring.libs/sdk/src/auth/session/record/session.stateful.ts (1)
4-4: LGTM!Import correctly updated to use
@frontmcp/authfor theTokenVaulttype.libs/sdk/src/auth/session/token.refresh.ts (1)
4-4: LGTM!Import correctly updated to source
TokenVaultfrom@frontmcp/auth, consistent with the auth module consolidation.libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (3)
1-13: LGTM on imports and module structure.The lazy credential loader is well-structured with proper type imports from
@frontmcp/authand internal modules. The use ofextractCredentialExpiryfrom credential-helpers promotes code reuse.
17-24: LGTM on class setup and logger initialization.The class properly uses a child logger for scoped logging and maintains the in-flight map as a readonly private member.
34-54: LGTM on the deduplication pattern.The in-flight deduplication logic correctly prevents concurrent loads for the same provider. The
finallyblock ensures proper cleanup even on failure.libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts (2)
15-25: LGTM on the EagerLoadResult interface.The interface clearly documents the result structure with loaded credentials, failed providers, and timing information.
48-98: LGTM on parallel loading with Promise.allSettled.The implementation correctly uses
Promise.allSettledto handle both successful and failed loads without short-circuiting, and properly categorizes results including required-null as failures.libs/sdk/src/auth/vault/auth-providers.types.ts (5)
1-9: LGTM on module structure and imports.Clean separation of type definitions with proper Zod imports for runtime validation.
55-61: Good use of.strict()on Zod schemas.The schema correctly uses
.strict()which aligns with validation best practices. Based on learnings, this is the expected pattern for Zod schemas in this codebase.
222-232: LGTM on the union schema with default.The
authProviderMappingSchemacorrectly uses Zod 4's union pattern with.default(true)for the required field, allowing both string shorthand and object configurations.
295-304: LGTM on the vault options schema.The schema properly uses
.optional().default()pattern from Zod 4 for sensible defaults while maintaining strict validation.
187-201: The original comment should be removed. The suggestion to usez.function()is incorrect for Zod v4. In Zod v4,z.function()is a function factory for creating validated functions with.implement()or.implementAsync(), not a schema validator for checking if a value is a function. The current implementation usingz.any()is the correct approach for validating that these properties are functions at runtime, and the existing code comment accurately explains this Zod 4 compatibility decision.Likely an incorrect or invalid review comment.
libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (2)
1-16: LGTM on imports and class structure.The implementation properly imports all required types and follows the interface contract from
AuthProvidersAccessor.
34-75: LGTM on the credential retrieval flow.The
getmethod correctly implements the cache → vault → factory fallback pattern with proper cache population at each level.libs/sdk/src/auth/vault/auth-providers.vault.ts (3)
1-12: LGTM on module structure and namespace isolation approach.The vault correctly wraps the base
AuthorizationVaultwith a dedicated namespace to prevent key collisions.
36-61: LGTM on credential storage with proper error handling.The
storeCredentialmethod correctly builds the vault key, constructs theAppCredentialshape, and re-throws errors after logging for proper error propagation.
72-103: LGTM on credential retrieval with validity checks.The
getCredentialmethod properly validatesisValidflag and expiry time before returning credentials, returningnullon failure to avoid propagating vault errors.libs/sdk/src/auth/vault/auth-providers.providers.ts (5)
1-22: LGTM on imports and module structure.The DI provider module correctly imports all necessary tokens and types for wiring the AuthProviders system.
55-71: LGTM on GLOBAL scope providers.Registry and Vault are correctly scoped as singletons at GLOBAL level.
73-88: LGTM on CONTEXT scope providers.CredentialCache and LazyCredentialLoader are correctly scoped per-request/session at CONTEXT level, ensuring isolation between requests.
90-125: LGTM on AuthProvidersAccessor factory wiring.The factory correctly builds the
CredentialFactoryContextfromFrontMcpContextand properly injects all dependencies. The comment at lines 109-110 clarifies thatappIdandtoolIdare set by tool flows.
130-144: LGTM on the enable check logic.The
isAuthProvidersEnabledfunction correctly implements the enable logic: explicit false → disabled, explicit true → enabled, otherwise auto-enable based on registered providers.
libs/sdk/src/auth/session/__tests__/encrypted-authorization-vault.test.ts
Show resolved
Hide resolved
…ld and type declaration handling
There was a problem hiding this 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/auth/project.json:
- Line 6: Add an API reference MDX documenting the public exports of the new
@frontmcp/auth package: create docs/draft/docs/authentication/api-reference.mdx
(matching other libs) and include sections for JwksService, the authorization
vault, exported credential types, session management functions/classes, and
available UI templates with short usage examples and import paths; ensure the
MDX mirrors the structure and metadata style used by existing api-reference
files so it’s picked up by the docs pipeline and references the exact exported
symbols from @frontmcp/auth.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (22)
apps/e2e/demo-e2e-agents/jest.e2e.config.tsapps/e2e/demo-e2e-cache/jest.e2e.config.tsapps/e2e/demo-e2e-codecall/jest.e2e.config.tsapps/e2e/demo-e2e-config/jest.e2e.config.tsapps/e2e/demo-e2e-direct/jest.e2e.config.tsapps/e2e/demo-e2e-errors/jest.e2e.config.tsapps/e2e/demo-e2e-hooks/jest.e2e.config.tsapps/e2e/demo-e2e-multiapp/jest.e2e.config.tsapps/e2e/demo-e2e-notifications/jest.e2e.config.tsapps/e2e/demo-e2e-openapi/jest.e2e.config.tsapps/e2e/demo-e2e-providers/jest.e2e.config.tsapps/e2e/demo-e2e-public/jest.e2e.config.tsapps/e2e/demo-e2e-redis/jest.e2e.config.tsapps/e2e/demo-e2e-remember/jest.e2e.config.tsapps/e2e/demo-e2e-serverless/jest.e2e.config.tsapps/e2e/demo-e2e-standalone/jest.e2e.config.tsapps/e2e/demo-e2e-transparent/jest.e2e.config.tsapps/e2e/demo-e2e-transport-recreation/jest.e2e.config.tsapps/e2e/demo-e2e-ui/jest.e2e.config.tslibs/auth/project.jsonlibs/auth/tsconfig.lib.jsonlibs/sdk/tsconfig.lib.json
💤 Files with no reviewable changes (19)
- apps/e2e/demo-e2e-ui/jest.e2e.config.ts
- apps/e2e/demo-e2e-redis/jest.e2e.config.ts
- apps/e2e/demo-e2e-serverless/jest.e2e.config.ts
- apps/e2e/demo-e2e-public/jest.e2e.config.ts
- apps/e2e/demo-e2e-hooks/jest.e2e.config.ts
- apps/e2e/demo-e2e-transparent/jest.e2e.config.ts
- apps/e2e/demo-e2e-direct/jest.e2e.config.ts
- apps/e2e/demo-e2e-providers/jest.e2e.config.ts
- apps/e2e/demo-e2e-openapi/jest.e2e.config.ts
- apps/e2e/demo-e2e-errors/jest.e2e.config.ts
- apps/e2e/demo-e2e-standalone/jest.e2e.config.ts
- apps/e2e/demo-e2e-cache/jest.e2e.config.ts
- apps/e2e/demo-e2e-agents/jest.e2e.config.ts
- apps/e2e/demo-e2e-notifications/jest.e2e.config.ts
- apps/e2e/demo-e2e-config/jest.e2e.config.ts
- apps/e2e/demo-e2e-remember/jest.e2e.config.ts
- apps/e2e/demo-e2e-multiapp/jest.e2e.config.ts
- apps/e2e/demo-e2e-codecall/jest.e2e.config.ts
- apps/e2e/demo-e2e-transport-recreation/jest.e2e.config.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- libs/auth/tsconfig.lib.json
- libs/sdk/tsconfig.lib.json
🧰 Additional context used
📓 Path-based instructions (1)
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/auth/project.json
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.)
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Nx-based monorepo build system - use commands like `nx build sdk`, `nx test ast-guard`, `nx run-many -t test`
Applied to files:
libs/auth/project.json
⏰ 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). (4)
- GitHub Check: Build Libraries
- GitHub Check: Lint & Format Checks
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
libs/auth/project.json (4)
1-6: Well-structured project metadata for the new auth library.The project configuration correctly identifies this as a publishable library with synchronized versioning. The tags align with the monorepo conventions.
8-55: Build configuration follows best practices for dual CJS/ESM output.The build pipeline is well-structured:
- CJS build generates declarations and runs type-checking.
- ESM build correctly depends on CJS, skips redundant type-checking, and uses
.mjsextension.- External dependencies are consistent between both formats.
56-63: Test configuration looks good.The Jest configuration is standard. Note that
passWithNoTests: trueis appropriate for initial setup but consider removing it once tests are established to catch accidental test file deletion.
49-78: No issues found. All referenced scripts exist and are properly configured:scripts/strip-dist-from-pkg.jsandscripts/publish-alpha.share both present and executable.
…ptedTypedStorage implementations
There was a problem hiding this 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/auth/src/session/vault-encryption.ts:
- Around line 194-196: Replace the Node-only encodings for iv, ciphertext, and
tag with the cross-platform helper: import base64urlEncode from
'@frontmcp/utils' (or the named export used elsewhere) and change iv:
Buffer.from(iv).toString('base64'), ct:
Buffer.from(ciphertext).toString('base64'), tag:
Buffer.from(tag).toString('base64') to iv: base64urlEncode(iv), ct:
base64urlEncode(ciphertext), tag: base64urlEncode(tag); ensure the import is
added to this module so the same RFC4648 base64url encoding used in
libs/utils/src/crypto/encrypted-blob.ts is applied here.
🧹 Nitpick comments (24)
libs/utils/src/storage/__tests__/typed-storage.test.ts (4)
72-82: Consider adding a test for TTL expiration behavior.The TTL tests verify that TTL is set correctly, but there's no test verifying that values become inaccessible after TTL expires. This would require either mocking time or using a very short TTL with a small delay.
💡 Example test for TTL expiration
it('should return null after TTL expires', async () => { jest.useFakeTimers(); const user: TestUser = { id: '1', name: 'Alice' }; await storage.set('user:1', user, { ttlSeconds: 1 }); // Fast-forward time past TTL jest.advanceTimersByTime(1500); const result = await storage.get('user:1'); expect(result).toBeNull(); jest.useRealTimers(); });
158-175: Consider adding a test for partial mdelete.Testing
mdeletewith a mix of existing and non-existing keys would verify correct count behavior in edge cases.💡 Example test
it('should return correct count when some keys do not exist', async () => { const user: TestUser = { id: '1', name: 'Alice' }; await storage.set('user:1', user); const deleted = await storage.mdelete(['user:1', 'user:non-existent']); expect(deleted).toBe(1); });
255-265: Addinstanceofcheck for the thrown error.Per coding guidelines, error tests should include
instanceofchecks to verify proper error hierarchy. This helps ensure the correct error type is thrown.♻️ Proposed enhancement
it('should throw for invalid data when throwOnInvalid is true', async () => { const validatedStorage = new TypedStorage<TestUser>(adapter, { schema: testUserSchema, throwOnInvalid: true, }); // Store invalid data directly await adapter.set('user:invalid', JSON.stringify({ invalid: 'data' })); - await expect(validatedStorage.get('user:invalid')).rejects.toThrow(); + await expect(validatedStorage.get('user:invalid')).rejects.toThrow(); + + // Verify error type + try { + await validatedStorage.get('user:invalid'); + } catch (error) { + expect(error).toBeInstanceOf(z.ZodError); + } });
310-318: Addinstanceofcheck for the JSON parse error.Similar to the schema validation test, this error test should verify the error type to ensure consistent error handling behavior.
♻️ Proposed enhancement
it('should throw for invalid JSON when throwOnInvalid is true', async () => { const throwingStorage = new TypedStorage<TestUser>(adapter, { throwOnInvalid: true, }); await adapter.set('invalid:json', 'not valid json'); - await expect(throwingStorage.get('invalid:json')).rejects.toThrow(); + await expect(throwingStorage.get('invalid:json')).rejects.toThrow(SyntaxError); });libs/utils/src/storage/__tests__/encrypted-typed-storage.test.ts (4)
62-68: Add error type verification alongside message check.Per coding guidelines, include
instanceofchecks in tests for error classes to verify proper error hierarchy. This test only verifies the error message but not the error type.Suggested improvement
it('should throw if key is not 32 bytes', () => { - expect(() => { - new EncryptedTypedStorage<TestSecret>(adapter, { - keys: [{ kid: 'short', key: randomBytes(16) }], - }); - }).toThrow('must be 32 bytes'); + const createWithShortKey = () => { + new EncryptedTypedStorage<TestSecret>(adapter, { + keys: [{ kid: 'short', key: randomBytes(16) }], + }); + }; + expect(createWithShortKey).toThrow(EncryptedStorageError); + expect(createWithShortKey).toThrow('must be 32 bytes'); });
297-303: Add error type verification for consistency.Similar to the constructor test, this should verify both the error type and message for complete error hierarchy validation.
Suggested improvement
it('should throw when rotating to invalid key', () => { const storage = new EncryptedTypedStorage<TestSecret>(adapter, { keys: [key1] }); - expect(() => { - storage.rotateKey({ kid: 'bad', key: randomBytes(16) }); - }).toThrow('must be 32 bytes'); + const rotateToInvalidKey = () => { + storage.rotateKey({ kid: 'bad', key: randomBytes(16) }); + }; + expect(rotateToInvalidKey).toThrow(EncryptedStorageError); + expect(rotateToInvalidKey).toThrow('must be 32 bytes'); });
336-351: Consider verifying error type in addition to message.For consistency with other error tests and per coding guidelines about
instanceofchecks, consider also verifying the error class.Suggested improvement
- await expect(storage.get('secret:invalid')).rejects.toThrow('Schema validation failed'); + await expect(storage.get('secret:invalid')).rejects.toThrow(EncryptedStorageError); + await expect(storage.get('secret:invalid')).rejects.toThrow('Schema validation failed');Note: The duplicate
await expectcan be avoided by capturing the promise in a variable or using a try/catch block if preferred.
367-403: Add error type assertions for throwOnError=true tests.The error handling tests at lines 375 and 402 only verify error messages. Adding error type verification would ensure proper error hierarchy per coding guidelines.
Suggested improvement for lines 374-375
- await expect(storage.get('secret:corrupted')).rejects.toThrow('Failed to parse'); + await expect(storage.get('secret:corrupted')).rejects.toThrow(EncryptedStorageError);Suggested improvement for lines 401-402
- await expect(storage.get('secret:unknown')).rejects.toThrow('Unknown encryption key'); + await expect(storage.get('secret:unknown')).rejects.toThrow(EncryptedStorageError);libs/auth/src/session/storage/storage-token-store.ts (1)
104-107: Minor: Redundant nullish coalescing withundefined.
record ?? undefinedis equivalent to justrecordwhenrecordis alreadyT | undefined. The storage'sgetmethod already returnsundefinedfor missing keys.Suggested simplification
async get(id: string): Promise<SecretRecord | undefined> { const record = await this.storage.get(this.key(id)); - return record ?? undefined; + return record; }libs/auth/src/jwks/jwks.service.ts (2)
58-62: Consider extracting environment check to a testable utility.The
shouldEnablePersistencemethod directly readsprocess.env['NODE_ENV'], making it harder to unit test different environment scenarios. Consider accepting this as a constructor option or using a utility that can be mocked.
68-76: Potential issue: hardcoded persistence directory.The
baseDir: '.frontmcp/keys'is hardcoded. Consider making this configurable viaJwksServiceOptionsto allow flexibility in different deployment scenarios.Suggested enhancement
+ // In JwksServiceOptions type: + /** Base directory for key persistence (development mode). Default: '.frontmcp/keys' */ + persistenceDir?: string; private async getKeyPersistence(): Promise<KeyPersistence | null> { if (!this.shouldEnablePersistence()) return null; if (!this.keyPersistence) { this.keyPersistence = await createKeyPersistence({ - baseDir: '.frontmcp/keys', + baseDir: this.opts.persistenceDir ?? '.frontmcp/keys', }); } return this.keyPersistence; }libs/auth/src/jwks/__tests__/jwks.service.advanced.test.ts (3)
248-259: Test relies on external service - consider mocking.This test depends on
httpstat.us, an external service. This makes the test flaky and dependent on network availability. Consider mocking the fetch call or using a local server setup for more reliable CI/CD execution.Alternative approach using fetch mock
it('should use custom network timeout', async () => { const service = new JwksService({ networkTimeoutMs: 1 }); // Mock fetch to simulate slow response const originalFetch = global.fetch; global.fetch = jest.fn().mockImplementation(() => new Promise((resolve) => setTimeout(resolve, 5000)) ); try { const result = await service.getJwksForProvider({ id: 'slow-provider', issuerUrl: 'https://example.com', jwksUri: 'https://example.com/.well-known/jwks.json', }); expect(result).toBeUndefined(); } finally { global.fetch = originalFetch; } });
261-278: TTL test may be flaky due to timing sensitivity.The test uses a 1ms TTL and 10ms delay, which can be unreliable across different environments. Consider using Jest's fake timers for deterministic behavior.
Using fake timers
it('should use custom provider JWKS TTL', async () => { jest.useFakeTimers(); const service = new JwksService({ providerJwksTtlMs: 1 }); const jwks1 = { keys: [{ kty: 'RSA', kid: 'v1', alg: 'RS256', use: 'sig' }] }; service.setProviderJwks('provider1', jwks1 as any); // Advance time past TTL jest.advanceTimersByTime(10); const result = await service.getJwksForProvider({ id: 'provider1', issuerUrl: 'https://example.com', }); expect(result).toEqual(jwks1); jest.useRealTimers(); });
63-71: Consider adding verification that cached JWKS has correct structure.The test sets a minimal JWKS with
as any. While acceptable for testing caching behavior, consider also testing that the cached result maintains proper structure, not just equality.libs/auth/src/session/vault-encryption.ts (2)
125-137: Inefficient pepper handling - unnecessary encode/decode cycle.The pepper is stored as
Uint8Arraybut then decoded back to string inderiveKey. This round-trip is unnecessary - you could concatenate the bytes directly.More efficient approach
async deriveKey(claims: VaultKeyDerivationClaims): Promise<Uint8Array> { - const ikmParts = [ - claims.jti, - claims.vaultKey ?? '', - claims.sub, - claims.iat.toString(), - new TextDecoder().decode(this.pepper), - ]; - const ikm = new TextEncoder().encode(ikmParts.join('')); + const encoder = new TextEncoder(); + const claimsPart = encoder.encode( + [claims.jti, claims.vaultKey ?? '', claims.sub, claims.iat.toString()].join('') + ); + // Concatenate claims bytes with pepper bytes directly + const ikm = new Uint8Array(claimsPart.length + this.pepper.length); + ikm.set(claimsPart, 0); + ikm.set(this.pepper, claimsPart.length); const infoBytes = new TextEncoder().encode(this.hkdfInfo); const key = await hkdfSha256(ikm, new Uint8Array(0), infoBytes, 32); return key; }
157-170: Same inefficiency inderiveKeyFromToken.This method has the same pepper decode/encode cycle. Apply the same optimization for consistency.
libs/auth/src/ui/templates.ts (3)
293-302: Consider accessibility: Ensure checkbox state is programmatically accessible.The "Select all" checkbox uses inline JavaScript that modifies other checkboxes. For screen reader users, consider adding
aria-controlsto indicate which elements are controlled, or use a more semantic pattern.
407-416: Inline script injection risk mitigated but consider CSP compatibility.The inline
<script>block works for runtime rendering but may conflict with strict Content Security Policy (CSP) headers that disallow inline scripts. If CSP is enforced, consider usingnonceattributes or moving logic to an external script.
557-559:renderToHtmlappears unused - consider removing or documenting purpose.The function just returns the input unchanged. If it's for API compatibility, add documentation explaining the legacy pattern it supports.
Add clarifying documentation
/** * Simple wrapper for compatibility - just returns the HTML string * (Templates are already complete HTML documents) + * + * @deprecated This function is a no-op kept for backward compatibility. + * Template builders already return complete HTML documents. */ export function renderToHtml(html: string, _options?: { title?: string }): string { return html; }libs/auth/src/session/authorization-vault.ts (2)
24-26: Remove or update the dangling comment aboutrandomUUID.Line 25 contains a comment suggesting
randomUUIDcan be imported from@frontmcp/utils, but this file defines only types and schemas with no runtime code requiring UUID generation. Consider removing this comment to avoid confusion, or relocate it to an implementation file where it's applicable.Suggested fix
import { z } from 'zod'; -// randomUUID can be imported from @frontmcp/utils if needed // ============================================
320-339: Consider documenting the TTL behavior for pending incremental auth.The
pendingIncrementalAuthSchemaincludescreatedAtandexpiresAtfields, but the relationship between them (e.g., default TTL) is only documented in implementation classes. Adding a brief JSDoc note about expected TTL handling would improve API clarity for consumers.libs/utils/src/index.ts (1)
114-130: Consider adding@deprecatedJSDoc tags to legacy secret persistence exports.The comment "(legacy)" on line 114 is helpful, but consumers using IDE autocompletion won't see the deprecation warning. Adding
@deprecatedannotations in the source files (./crypto) would provide better DX by showing warnings at usage sites.libs/auth/src/session/storage/storage-authorization-vault.ts (2)
120-133: Silent failure on missing vault entry may hide bugs.The
update()method logs a warning and silently returns when the vault entry doesn't exist (lines 122-125). While this prevents errors, it may mask issues where callers expect the entry to exist. Consider:
- Documenting this behavior in the interface JSDoc, or
- Throwing an error for callers who need to handle missing entries explicitly
The same pattern appears in
updateConsent,authorizeApp,completePendingAuth,cancelPendingAuth, and credential methods.
292-306: Consider logging when credential storage is skipped due to consent.In
addAppCredential(), whenshouldStoreCredentialreturns false (lines 297-300), the method silently returns. This could be confusing when debugging why credentials aren't being stored. A debug-level log would aid troubleshooting.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (24)
libs/auth/package.jsonlibs/auth/src/index.tslibs/auth/src/jwks/__tests__/jwks.service.advanced.test.tslibs/auth/src/jwks/jwks.service.tslibs/auth/src/jwks/jwks.types.tslibs/auth/src/session/authorization-vault.tslibs/auth/src/session/index.tslibs/auth/src/session/storage/in-memory-authorization-vault.tslibs/auth/src/session/storage/index.tslibs/auth/src/session/storage/storage-authorization-vault.tslibs/auth/src/session/storage/storage-token-store.tslibs/auth/src/session/token.store.tslibs/auth/src/session/vault-encryption.tslibs/auth/src/ui/__tests__/base-layout.advanced.test.tslibs/auth/src/ui/templates.tslibs/utils/src/index.tslibs/utils/src/storage/__tests__/encrypted-typed-storage.test.tslibs/utils/src/storage/__tests__/typed-storage.test.tslibs/utils/src/storage/encrypted-typed-storage.tslibs/utils/src/storage/encrypted-typed-storage.types.tslibs/utils/src/storage/errors.tslibs/utils/src/storage/index.tslibs/utils/src/storage/typed-storage.tslibs/utils/src/storage/typed-storage.types.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- libs/auth/src/ui/tests/base-layout.advanced.test.ts
- libs/auth/src/session/storage/in-memory-authorization-vault.ts
- libs/auth/package.json
- libs/auth/src/session/storage/index.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
libs/auth/src/jwks/jwks.types.tslibs/auth/src/session/storage/storage-token-store.tslibs/utils/src/storage/__tests__/encrypted-typed-storage.test.tslibs/auth/src/jwks/jwks.service.tslibs/utils/src/storage/__tests__/typed-storage.test.tslibs/auth/src/session/token.store.tslibs/auth/src/jwks/__tests__/jwks.service.advanced.test.tslibs/auth/src/session/index.tslibs/auth/src/session/vault-encryption.tslibs/utils/src/index.tslibs/auth/src/ui/templates.tslibs/auth/src/session/authorization-vault.tslibs/auth/src/index.tslibs/auth/src/session/storage/storage-authorization-vault.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/auth/src/jwks/jwks.types.tslibs/auth/src/session/storage/storage-token-store.tslibs/utils/src/storage/__tests__/encrypted-typed-storage.test.tslibs/auth/src/jwks/jwks.service.tslibs/utils/src/storage/__tests__/typed-storage.test.tslibs/auth/src/session/token.store.tslibs/auth/src/jwks/__tests__/jwks.service.advanced.test.tslibs/auth/src/session/index.tslibs/auth/src/session/vault-encryption.tslibs/utils/src/index.tslibs/auth/src/ui/templates.tslibs/auth/src/session/authorization-vault.tslibs/auth/src/index.tslibs/auth/src/session/storage/storage-authorization-vault.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Do not use prefixes like 'PT-001' in test names
Do not skip constructor validation tests for error classes and types
Includeinstanceofchecks in tests for error classes to verify proper error hierarchy
Files:
libs/utils/src/storage/__tests__/encrypted-typed-storage.test.tslibs/utils/src/storage/__tests__/typed-storage.test.tslibs/auth/src/jwks/__tests__/jwks.service.advanced.test.ts
libs/*/src/index.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases
Files:
libs/utils/src/index.tslibs/auth/src/index.ts
🧠 Learnings (26)
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{src}/**/*.{ts,tsx} : Do not use `any` type without justification in TypeScript code
Applied to files:
libs/auth/src/jwks/jwks.types.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files
Applied to files:
libs/auth/src/jwks/jwks.types.tslibs/auth/src/session/token.store.tslibs/auth/src/session/index.tslibs/auth/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use strict TypeScript mode with no `any` types without strong justification
Applied to files:
libs/auth/src/jwks/jwks.types.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `any` type without justification in TypeScript files
Applied to files:
libs/auth/src/jwks/jwks.types.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Use type parameters with constraints instead of unconstrained generics with `any` defaults
Applied to files:
libs/auth/src/jwks/jwks.types.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management
Applied to files:
libs/utils/src/storage/__tests__/encrypted-typed-storage.test.tslibs/auth/src/session/vault-encryption.tslibs/utils/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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:
libs/utils/src/storage/__tests__/encrypted-typed-storage.test.tslibs/utils/src/storage/__tests__/typed-storage.test.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/auth/src/jwks/jwks.service.tslibs/auth/src/session/authorization-vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Applied to files:
libs/auth/src/jwks/jwks.service.tslibs/auth/src/session/index.tslibs/auth/src/session/vault-encryption.tslibs/utils/src/index.tslibs/auth/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Applied to files:
libs/auth/src/jwks/jwks.service.tslibs/auth/src/session/vault-encryption.tslibs/utils/src/index.tslibs/auth/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/auth/src/jwks/jwks.service.tslibs/auth/src/session/vault-encryption.tslibs/utils/src/index.tslibs/auth/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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:
libs/utils/src/storage/__tests__/typed-storage.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/**/*.test.{ts,tsx} : Use React Testing Library for component tests and include SSR/hydration tests for all interactive components
Applied to files:
libs/utils/src/storage/__tests__/typed-storage.test.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Applied to files:
libs/utils/src/storage/__tests__/typed-storage.test.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/*/src/index.ts : Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases
Applied to files:
libs/auth/src/session/index.tslibs/auth/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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/auth/src/session/index.tslibs/auth/src/ui/templates.tslibs/auth/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{renderers,build}/**/*.{ts,tsx} : Use htmlRenderer for HTML template rendering without React dependency
Applied to files:
libs/auth/src/ui/templates.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{renderers,handlebars,base-template}/**/*.{ts,tsx,js,jsx} : Never skip HTML escaping for user-provided content in renderers and templates
Applied to files:
libs/auth/src/ui/templates.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{renderers,handlebars,utils}/**/*.{test,spec}.{ts,tsx,js,jsx} : Test HTML escaping for user-provided content to prevent XSS vulnerabilities
Applied to files:
libs/auth/src/ui/templates.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Avoid non-null assertions (`!`) - use proper error handling and type guards instead
Applied to files:
libs/auth/src/session/authorization-vault.tslibs/auth/src/session/storage/storage-authorization-vault.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/auth/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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/auth/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/auth/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/**/*.{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/auth/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly
Applied to files:
libs/auth/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/universal/**/*.{ts,tsx} : Universal app shell (UniversalApp, FrontMCPProvider) must provide platform-agnostic React context and initialization
Applied to files:
libs/auth/src/index.ts
🧬 Code graph analysis (5)
libs/utils/src/storage/__tests__/typed-storage.test.ts (1)
libs/utils/src/storage/typed-storage.ts (5)
raw(193-195)ttl(165-167)exists(95-97)keys(175-177)count(185-187)
libs/auth/src/session/token.store.ts (1)
libs/auth/src/session/token.vault.ts (1)
EncBlob(10-18)
libs/auth/src/jwks/__tests__/jwks.service.advanced.test.ts (1)
libs/auth/src/jwks/jwks.service.ts (1)
JwksService(15-506)
libs/auth/src/ui/templates.ts (1)
libs/auth/src/ui/base-layout.ts (4)
escapeHtml(157-157)wideLayout(357-366)centeredCardLayout(340-352)extraWideLayout(371-380)
libs/auth/src/session/authorization-vault.ts (3)
libs/auth/src/index.ts (29)
credentialTypeSchema(88-88)CredentialType(127-127)oauthCredentialSchema(89-89)apiKeyCredentialSchema(90-90)basicAuthCredentialSchema(91-91)bearerCredentialSchema(92-92)privateKeyCredentialSchema(93-93)mtlsCredentialSchema(94-94)customCredentialSchema(95-95)sshKeyCredentialSchema(96-96)serviceAccountCredentialSchema(97-97)pkceOAuthCredentialSchema(98-98)credentialSchema(99-99)OAuthCredential(128-128)ApiKeyCredential(129-129)BasicAuthCredential(130-130)BearerCredential(131-131)PrivateKeyCredential(132-132)MtlsCredential(133-133)CustomCredential(134-134)SshKeyCredential(135-135)ServiceAccountCredential(136-136)PkceOAuthCredential(137-137)Credential(138-138)AppCredential(139-139)VaultConsentRecord(140-140)VaultFederatedRecord(141-141)PendingIncrementalAuth(142-142)AuthorizationVault(144-144)libs/auth/src/session/index.ts (29)
credentialTypeSchema(10-10)CredentialType(30-30)oauthCredentialSchema(11-11)apiKeyCredentialSchema(12-12)basicAuthCredentialSchema(13-13)bearerCredentialSchema(14-14)privateKeyCredentialSchema(15-15)mtlsCredentialSchema(16-16)customCredentialSchema(17-17)sshKeyCredentialSchema(18-18)serviceAccountCredentialSchema(19-19)pkceOAuthCredentialSchema(20-20)credentialSchema(21-21)OAuthCredential(31-31)ApiKeyCredential(32-32)BasicAuthCredential(33-33)BearerCredential(34-34)PrivateKeyCredential(35-35)MtlsCredential(36-36)CustomCredential(37-37)SshKeyCredential(38-38)ServiceAccountCredential(39-39)PkceOAuthCredential(40-40)Credential(41-41)AppCredential(42-42)VaultConsentRecord(43-43)VaultFederatedRecord(44-44)PendingIncrementalAuth(45-45)AuthorizationVault(47-47)libs/sdk/src/auth/session/record/session.base.ts (1)
id(151-153)
⏰ 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). (4)
- GitHub Check: Build Libraries
- GitHub Check: Lint & Format Checks
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (36)
libs/utils/src/storage/__tests__/typed-storage.test.ts (9)
1-36: Well-structured test setup.The test infrastructure is clean with proper lifecycle management (connect/disconnect) and a well-defined test schema that matches the TypeScript interface.
38-56: LGTM!Constructor tests properly verify instantiation with various configuration options and include
instanceofchecks as required by the coding guidelines.
96-112: LGTM!Delete tests comprehensively cover both successful deletion and non-existent key scenarios with proper verification.
114-127: LGTM!Exists tests cover both positive and negative cases appropriately.
129-156: LGTM!Bulk operation tests cover the essential scenarios including mixed results (present/missing keys) and empty array edge cases.
177-199: LGTM!Expire/TTL tests properly cover both existing and non-existent key scenarios with appropriate assertions.
201-227: LGTM!Keys/count tests cover pattern matching well, and using
sort()for comparison is a good practice for non-deterministic ordering.
268-294: LGTM!Custom serialization test effectively verifies that user-provided serializer/deserializer functions are invoked and data integrity is maintained through the round-trip.
296-300: LGTM!Raw access test correctly verifies the underlying adapter is exposed via the
rawproperty, aligning with the implementation shown in the relevant code snippets.libs/utils/src/storage/__tests__/encrypted-typed-storage.test.ts (4)
1-40: LGTM!Test setup is well-structured with proper initialization and cleanup. Using
randomBytes(32)for key generation is appropriate for test scenarios. Based on learnings, this approach avoids hardcoding encryption keys.
79-203: LGTM!Comprehensive coverage of CRUD operations including encryption verification, null handling for non-existent keys, TTL support, and batch operations. The test at lines 86-91 that verifies raw storage contents is a good practice for validating actual encryption.
428-530: LGTM!Good coverage of utility methods (
keys,count,expire,ttl,raw) and complex data type handling. The nested object test at lines 498-518 properly validates that encryption/decryption preserves complex structures.
532-789: Excellent client-side key binding test coverage!This section thoroughly tests critical security features including:
- Session isolation (lines 740-772) ensuring different sessions cannot access each other's data
- Domain separation via
infoparameter (lines 637-653)- Key rotation with client binding (lines 700-738)
- Error type verification at line 787 correctly uses
.toThrow(EncryptedStorageError)The multi-session isolation test is particularly valuable for validating the security model.
libs/auth/src/session/token.store.ts (1)
1-41: LGTM!Clean interface definition with proper TypeScript types. The
TokenStoreabstraction is well-documented with example usage, and theSecretRecordtype appropriately wraps the encrypted blob with metadata.libs/auth/src/session/storage/storage-token-store.ts (1)
124-132: Verify TTL behavior for already-expired tokens.When
expis in the past,calculateTtlreturns1(minimum TTL). This means an already-expired token blob will persist for 1 second before the storage backend expires it. Consider whether storing expired tokens at all is the intended behavior, or ifputshould reject/skip them.libs/auth/src/jwks/jwks.types.ts (1)
33-34: Good improvement: Stricter typing for JWT header and payload.Changing from
anytoRecord<string, unknown>aligns with the strict TypeScript guidelines. Note this may require consumers to add type guards or assertions when accessing specific claims.libs/auth/src/ui/templates.ts (2)
77-81: LGTM: Proper HTML escaping re-exported.Good practice to re-export
escapeHtmlfrom the base layout, ensuring consistent XSS protection across all templates.
122-176: Good XSS protection in app card template.All dynamic content (appId, appName, description, iconUrl, scopes, csrfToken, pendingAuthId, callbackPath) is properly escaped using
escapeHtml(). This prevents XSS attacks from malicious app names or URLs.libs/auth/src/jwks/jwks.service.ts (1)
449-453: Remove concern aboutcreatePrivateKey— no utility alternative exists in@frontmcp/utils.
@frontmcp/utilsdoes not export a wrapper forcreatePrivateKeyor other key object constructors. While it providesgenerateRsaKeyPair()for key generation, it lacks utilities for reconstructing KeyObjects from persisted JWK data, which is the specific use case here. This directnode:cryptousage is a necessary exception.libs/auth/src/session/authorization-vault.ts (5)
1-26: LGTM on module structure and imports.The module header clearly documents the purpose, supported credential types, and stateful vs stateless modes. The Zod import is appropriate for schema definitions.
34-48: LGTM on credential type enum schema.The credential type enum comprehensively covers common authentication mechanisms including OAuth, API keys, certificates, and cloud service accounts. The inline comments aid readability.
56-235: LGTM on credential schemas.The individual credential schemas are well-structured with appropriate validation constraints (e.g.,
z.string().min(1)for required fields). The discriminated union using thetypefield enables type-safe credential handling.
256-285: LGTM on app credential schema.The
appCredentialSchemaproperly composes the credential union with metadata fields for lifecycle tracking (acquiredAt, lastUsedAt, validity, expiration). The optionaluserInfoobject is appropriately scoped.
386-534: LGTM on AuthorizationVault interface.The interface is comprehensive with clear method signatures for:
- Core CRUD operations
- Consent management
- Incremental authorization lifecycle
- Credential storage with consent-aware filtering
- OAuth token refresh support
Method signatures are consistent with Zod-inferred types.
libs/auth/src/session/index.ts (1)
1-92: LGTM on session module barrel exports.The file correctly re-exports all public APIs from submodules with clear logical groupings:
- Authorization vault schemas and types
- Vault encryption utilities
- Token vault and store
- Storage-backed implementations
The separation of value exports from type exports follows TypeScript best practices. As per coding guidelines, no backwards compatibility aliases are introduced.
libs/utils/src/index.ts (2)
131-152: LGTM on unified key persistence exports.The new key persistence API is well-structured with:
- Base types for different key data variants
- Validation utilities (
validateKeyData,parseKeyData)- Type guards (
isSecretKeyData,isAsymmetricKeyData)- Factory functions with storage integration
The "(recommended)" annotation clearly guides users toward the newer API.
217-239: LGTM on storage exports expansion.The additions appropriately expose:
EncryptedStorageErrorfor typed error handlingFileSystemStorageAdapterfor Node.js file-based storageTypedStorageandEncryptedTypedStoragewrappers with their configuration typesThis aligns with the PR's goal of providing storage-backed vault implementations.
libs/auth/src/session/storage/storage-authorization-vault.ts (4)
1-32: LGTM on module documentation and imports.Clear documentation with usage example. Imports correctly use
@frontmcp/utilsforrandomUUIDandTypedStorage, following the coding guidelines.
180-214: LGTM on pending auth creation.Properly generates UUID, calculates expiration from TTL, and stores the pending auth with the vault entry. The default TTL fallback to
this.pendingAuthTtlMsis appropriate.
467-476: Potential double-prefixing issue in key generation.The
key()method (lines 467-469) adds the namespace prefix only when storage is NOT namespaced. However, the constructor (line 73) already creates a namespaced view ifisNamespacedStorageis true. This means:
- If storage is
NamespacedStorage: namespace is applied at line 73, andkey()returns justid- If storage is
StorageAdapter: namespace is applied at line 469This logic appears correct, but verify the
isNamespacedStoragecheck operates on the original storage, not the wrapped TypedStorage.
68-79: The concern in this review comment is not valid.TypedStorage<T>correctly exposes arawgetter property (lines 193–195 oftyped-storage.ts) that returns the underlyingStorageAdapter | NamespacedStorage. The line 468 usage (this.storage.raw) is correct and will work without errors.Likely an incorrect or invalid review comment.
libs/auth/src/index.ts (5)
1-32: LGTM on package documentation.Excellent module-level documentation with practical usage examples covering JWKS management, auth UI templates, and credential vault operations. This provides a clear entry point for library consumers.
50-82: LGTM on UI module exports.Clean separation of layout utilities, template builders, and render functions. The type exports for theming configuration enable consumers to customize the auth UI.
83-167: LGTM on session module exports.Comprehensive export of credential schemas, vault implementations, encryption utilities, and storage types. The grouping with inline comments aids discoverability.
169-192: LGTM on utils module exports.WWW-Authenticate helpers and audience validation utilities are properly exposed with their associated types.
34-48: No action required—the dev-key functions are already properly marked with@deprecatedJSDoc annotations in the source module (src/jwks/dev-key-persistence.ts) and re-exported in the barrel file. Consumers will see deprecation warnings directing them to usecreateKeyPersistencefrom@frontmcp/utilsinstead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In @libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts:
- Around line 161-175: The has() method currently returns true if
this.cache.has(providerName) without validating the cached credential; change it
to retrieve the cached entry (this.cache.get(providerName)), run
this.isValid(cached) and only return true if valid; if invalid/expired, remove
it from the cache (e.g., this.cache.delete(providerName)) and fall through to
the existing registry/vault check that uses this.loadFromVault(providerName,
config) and this.isValid(fromVault).
- Around line 294-298: Replace the direct Buffer base64 encoding with the
utility from @frontmcp/utils: in the 'basic' case (where credential.encodedValue
is used), import the base64 encoder (e.g., encodeBase64) from '@frontmcp/utils'
and use encodeBase64(`${credential.username}:${credential.password}`) instead of
Buffer.from(...).toString('base64'); keep the same fallback logic
(credential.encodedValue ?? ...) and return { Authorization: `Basic ${encoded}`
} as before.
In @libs/sdk/src/auth/vault/auth-providers.vault.ts:
- Around line 250-264: In buildVaultKey, replace the generic throw new
Error(...) in the 'user' case with the project-specific MCP error class (e.g.,
throw new McpError({ code: 'MCP_INVALID_ARGUMENT', message: `userId is required
for user-scoped credentials (namespace: ${this.namespace})` })) and add the
corresponding import; also remove or tighten the redundant default branch
(either delete the default and let TypeScript exhaustiveness check handle
unknown CredentialScope or make default explicitly throw an McpError for an
unknown scope) so the intent is explicit.
🧹 Nitpick comments (12)
libs/sdk/src/auth/session/record/session.stateful.ts (1)
52-55: Redundant type cast.
StatefulCreateCtxis defined asBaseCreateCtx & {}(line 7), which is equivalent toBaseCreateCtx. The type assertion is unnecessary.Suggested fix
constructor(ctx: StatefulCreateCtx) { - super(ctx as BaseCreateCtx); + super(ctx); throw new InternalMcpError('StatefulSession not yet implemented', 'NOT_IMPLEMENTED'); }libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (3)
78-102: Lost provider name context on rejection.When a promise is rejected in
getMany, the provider name associated with that failure is lost becausePromise.allSettledonly provides the rejection reason inoutcome.reason. The comment on line 96 acknowledges this limitation.Consider capturing the name in the rejection path:
Suggested improvement
const promises = providerNames.map(async (name) => { - const result = await this.get(name, options); - return { name, result }; + try { + const result = await this.get(name, options); + return { name, result }; + } catch (error) { + throw { name, error }; + } }); const settled = await Promise.allSettled(promises); for (const outcome of settled) { if (outcome.status === 'fulfilled') { results.set(outcome.value.name, outcome.value.result); } else { - // Find the provider name from the error context if possible - this.logger?.warn('Failed to load credential:', outcome.reason); + const { name, error } = outcome.reason as { name: string; error: unknown }; + this.logger?.warn(`Failed to load credential "${name}":`, error); + results.set(name, null); } }
118-127: Sequential header fetching inheadersManycould be parallelized.The method fetches headers sequentially using a
for...ofloop. Sincethis.headers()is async, this could be parallelized for better performance when fetching headers for multiple providers.Suggested parallelization
async headersMany(providerNames: string[]): Promise<Record<string, string>> { const allHeaders: Record<string, string> = {}; - for (const name of providerNames) { - const headers = await this.headers(name); - Object.assign(allHeaders, headers); - } + const headerResults = await Promise.all( + providerNames.map((name) => this.headers(name)), + ); + for (const headers of headerResults) { + Object.assign(allHeaders, headers); + } return allHeaders; }
140-144: Type assertionas Credentialbypasses type safety.The
existingCredentialis cast toCredentialwithout validation. Ifexisting?.credentialhas an incompatible shape, this could cause runtime issues in the loader's refresh logic.Consider using a type guard or removing the assertion if the types are already compatible:
const refreshed = await this.loader.refresh(config, { ...this.context, - existingCredential: existingCredential as Credential, + existingCredential, });If the type mismatch is due to interface definitions, ensure
CredentialFactoryContext.existingCredentialacceptsCredential | undefined.libs/sdk/src/auth/vault/auth-providers.vault.ts (1)
190-240: Empty catch blocks silently swallow errors.The
getAllCredentialsmethod has multiple empty catch blocks (lines 196-198, 211-213, 223-225, 235-237) that silently ignore errors. While this provides resilience, it makes debugging difficult if one scope's retrieval fails.Consider at minimum logging at debug level:
Suggested improvement
// Session scope try { const sessionCreds = await this.baseVault.getAppCredentials( this.buildVaultKey(sessionId, 'session'), this.namespace, ); credentials.push(...sessionCreds); - } catch { - // Ignore + } catch (error) { + this.logger?.debug('Failed to get session-scoped credentials:', error); }libs/auth/src/session/vault-encryption.ts (3)
110-116: Consider storing pepper as string to avoid redundant encoding/decoding.The pepper is converted from string to
Uint8Arrayhere (line 115), but then decoded back to string when building IKM (lines 137, 170). Consider keeping it as a string internally since it's always used in string concatenation.♻️ Suggested simplification
export class VaultEncryption { - private readonly pepper: Uint8Array; + private readonly pepper: string; private readonly hkdfInfo: string; constructor(config: VaultEncryptionConfig = {}) { - // Convert pepper to Uint8Array, use empty if not provided - this.pepper = new TextEncoder().encode(config.pepper ?? ''); + this.pepper = config.pepper ?? ''; this.hkdfInfo = config.hkdfInfo ?? 'frontmcp-vault-v1'; }Then in
deriveKeyandderiveKeyFromToken, usethis.pepperdirectly instead ofnew TextDecoder().decode(this.pepper).
132-144: Add delimiters to IKM construction to prevent potential collisions.Joining IKM parts with an empty separator could theoretically cause collisions if claim values contain overlapping characters at boundaries. For example:
jti="ab" + vaultKey="cd"produces the same IKM prefix asjti="abc" + vaultKey="d".While the practical risk is low given structured claim formats (UUIDs, user IDs), adding a delimiter like
\x00(null byte) between parts is a cryptographic best practice.♻️ Suggested improvement
const ikmParts = [ claims.jti, claims.vaultKey ?? '', claims.sub, claims.iat.toString(), - new TextDecoder().decode(this.pepper), + this.pepper, ]; - const ikm = new TextEncoder().encode(ikmParts.join('')); + const ikm = new TextEncoder().encode(ikmParts.join('\x00'));
143-143: Remove unnecessaryasync/awaitfrom methods using synchronous crypto functions.The
@frontmcp/utilscrypto functions (hkdfSha256,encryptAesGcm,decryptAesGcm) are all synchronous and return values directly, not Promises. MethodsderiveKey(line 143),deriveKeyFromToken(line 176),encrypt(line 196), anddecrypt(line 235) can be refactored to remove theasynckeyword and unnecessaryawaitcalls, improving performance and code clarity.libs/sdk/src/auth/session/encrypted-authorization-vault.ts (4)
110-116: Consider defining a minimal Redis interface instead ofany.While the
eslint-disablecomment is present, defining a typed interface for the required Redis methods (get,set,del) would improve type safety and documentation of the expected contract.♻️ Suggested interface
interface RedisClient { get(key: string): Promise<string | null>; set(key: string, value: string): Promise<void>; del(key: string): Promise<void>; }export class EncryptedRedisVault implements AuthorizationVault { constructor( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private readonly redis: any, + private readonly redis: RedisClient, private readonly encryption: VaultEncryption, private readonly namespace = 'vault:', ) {}
183-201: Type assertions on decrypted data bypass runtime safety.The
ascasts on lines 194-197 assume the decrypted data matches the expected types. IfdecryptObjectreturns unvalidated data, corrupted or tampered encrypted blobs could cause runtime issues. Consider validating the decrypted sensitive data against a schema.♻️ Suggested validation approach
// Define a schema for VaultSensitiveData if not already exported const vaultSensitiveDataSchema = z.object({ appCredentials: z.record(z.string(), appCredentialSchema), consent: vaultConsentRecordSchema.optional(), federated: vaultFederatedRecordSchema.optional(), pendingAuths: z.array(pendingIncrementalAuthSchema), }); // In toVaultEntry: const sensitive = vaultSensitiveDataSchema.parse( await this.decryptSensitive(redisEntry.encrypted) );
240-251: Consider preserving error cause for better debugging.The error handling extracts only the message, losing the original stack trace. Using the
causeoption preserves the error chain for debugging.♻️ Preserve error cause
} catch (error) { // Could be decryption failure (wrong key) or corrupt data - throw new Error(`Failed to load vault ${id}: ${error instanceof Error ? error.message : 'Unknown error'}`); + throw new Error(`Failed to load vault ${id}`, { cause: error }); }
528-542: Avoid non-null assertion on line 537.While the logic ensures
entry.consentexists at this point (checked on line 532), using!violates coding guidelines. A type guard or restructuring would be cleaner.♻️ Remove non-null assertion
async shouldStoreCredential(vaultId: string, appId: string, toolIds?: string[]): Promise<boolean> { const entry = await this.loadEntry(vaultId); if (!entry) return false; - if (!entry.consent?.enabled) { + const consent = entry.consent; + if (!consent?.enabled) { return true; } if (toolIds && toolIds.length > 0) { - return toolIds.some((toolId) => entry.consent!.selectedToolIds.includes(toolId)); + return toolIds.some((toolId) => consent.selectedToolIds.includes(toolId)); } - const consentedToolIds = entry.consent.selectedToolIds; + const consentedToolIds = consent.selectedToolIds; return consentedToolIds.some((toolId) => toolId.startsWith(`${appId}:`)); }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
libs/auth/src/session/vault-encryption.tslibs/sdk/src/auth/flows/auth.verify.flow.tslibs/sdk/src/auth/session/encrypted-authorization-vault.tslibs/sdk/src/auth/session/record/session.stateful.tslibs/sdk/src/auth/session/record/session.stateless.tslibs/sdk/src/auth/session/token.refresh.tslibs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/auth-providers.vault.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- libs/sdk/src/auth/flows/auth.verify.flow.ts
- libs/sdk/src/auth/session/token.refresh.ts
- libs/sdk/src/auth/session/record/session.stateless.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
libs/sdk/src/auth/vault/auth-providers.vault.tslibs/auth/src/session/vault-encryption.tslibs/sdk/src/auth/session/record/session.stateful.tslibs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/session/encrypted-authorization-vault.ts
libs/{sdk,adapters}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
UsegetCapabilities()for dynamic capability exposure instead of hardcoding capabilities
Files:
libs/sdk/src/auth/vault/auth-providers.vault.tslibs/sdk/src/auth/session/record/session.stateful.tslibs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/session/encrypted-authorization-vault.ts
libs/sdk/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/sdk/**/*.{ts,tsx}: Preferinterfacefor defining object shapes in TypeScript, avoidanytypes
Use type parameters with constraints instead of unconstrained generics withanydefaults
Validate URIs per RFC 3986 at metadata level usingisValidMcpUrirefinement
UsechangeScopeinstead ofscopein change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g.,Promise<GetPromptResult>) instead ofPromise<unknown>for MCP entry methods
Useunknowninstead ofanyfor generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows
Files:
libs/sdk/src/auth/vault/auth-providers.vault.tslibs/sdk/src/auth/session/record/session.stateful.tslibs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/session/encrypted-authorization-vault.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/auth/vault/auth-providers.vault.tslibs/auth/src/session/vault-encryption.tslibs/sdk/src/auth/session/record/session.stateful.tslibs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/session/encrypted-authorization-vault.ts
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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`
📚 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/vault/auth-providers.vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Applied to files:
libs/auth/src/session/vault-encryption.tslibs/sdk/src/auth/session/encrypted-authorization-vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/auth/src/session/vault-encryption.tslibs/sdk/src/auth/session/encrypted-authorization-vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Applied to files:
libs/auth/src/session/vault-encryption.tslibs/sdk/src/auth/session/encrypted-authorization-vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management
Applied to files:
libs/auth/src/session/vault-encryption.tslibs/sdk/src/auth/session/encrypted-authorization-vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly
Applied to files:
libs/auth/src/session/vault-encryption.ts
🧬 Code graph analysis (2)
libs/auth/src/session/vault-encryption.ts (1)
libs/utils/src/crypto/index.ts (6)
hkdfSha256(96-104)randomBytes(57-62)encryptAesGcm(113-125)base64urlEncode(182-197)base64urlDecode(202-220)decryptAesGcm(135-146)
libs/sdk/src/auth/session/record/session.stateful.ts (1)
libs/sdk/src/auth/session/record/session.base.ts (1)
BaseCreateCtx(8-29)
⏰ 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). (4)
- GitHub Check: Build Libraries
- GitHub Check: Lint & Format Checks
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (24)
libs/sdk/src/auth/session/record/session.stateful.ts (2)
4-5: LGTM! Imports align with architecture goals.The switch to
@frontmcp/authforTokenVaultfollows the package extraction pattern, and usingInternalMcpErrorwith MCP error codes aligns with the SDK error handling guidelines.
57-59: LGTM!Correct use of underscore prefix for unused parameter and proper MCP-specific error handling.
libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (4)
21-33: LGTM - Well-structured constructor with dependency injection.The constructor properly encapsulates dependencies and creates a child logger for tracing. The use of readonly ensures immutability of injected dependencies.
35-76: Consider handling the case where loader returns a non-null value but caching fails.The
getmethod has a good layered approach (cache → vault → loader). However, ifstoreInVaultfails silently at line 72 (it catches and logs), the credential is still cached and returned. This is acceptable defensive behavior, but worth noting that vault and cache can become inconsistent.One minor concern: at line 75, the type assertion
as ResolvedCredential<T>assumesloadedmatchesT, but the loader returns a genericResolvedCredential. This should be safe if the registry and loader are consistent, but verify the type flow.
207-238: LGTM - Robust vault loading with proper error handling.The
loadFromVaultmethod correctly catches errors and returns null as a fallback. The reconstructedResolvedCredentialshape is reasonable, though theacquiredAt: Date.now()comment correctly notes the limitation of not persisting acquisition time.
264-274: LGTM - Clean validity check.The
isValidmethod properly checks both the explicitisValidflag and expiry time.libs/sdk/src/auth/vault/auth-providers.vault.ts (6)
17-26: LGTM - Clean constructor with sensible defaults.The namespace default
'authproviders:'and child logger pattern are well-designed.
37-62: LGTM - Proper credential storage with error propagation.The
storeCredentialmethod correctly constructs theAppCredentialshape with expiry extraction and re-throws on failure, which is appropriate for write operations where callers need to know about failures.
73-104: LGTM - Defensive credential retrieval with validity checks.The method properly checks both the
isValidflag and expiry time before returning credentials. Returningnullon errors is appropriate for read operations.
114-128: Inconsistent error handling:removeCredentialswallows errors.Unlike
storeCredentialwhich re-throws,removeCredentialonly logs the error. If removal fails, the caller won't know and may assume the credential was successfully removed. Consider either re-throwing or documenting this as intentional "best-effort" removal.
165-181: LGTM - OAuth refresh with proper error propagation.The method correctly re-throws on failure, consistent with
storeCredentialbehavior for write operations.
267-270: PreferToken.for()pattern if available in@frontmcp/di.The DI token is created using
Symbol.for()with a cast. Verify this matches the project's DI token conventions. If@frontmcp/diprovides aToken.for()or similar factory method, that would be more type-safe.libs/auth/src/session/vault-encryption.ts (6)
24-31: LGTM! Correctly uses@frontmcp/utilsfor cryptographic operations.This aligns with the coding guidelines and learnings that require using
@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly, ensuring cross-platform compatibility.
158-178: LGTM! Including JWT signature provides additional entropy.The method correctly includes the JWT signature as additional entropy for key derivation. Note: The same delimiter suggestion from
deriveKeyapplies to the IKM construction here (line 172).
187-205: LGTM! Encryption implementation is correct.
- Proper 32-byte key validation
- Correct 12-byte IV for AES-GCM
- Base64url encoding for safe storage
215-241: LGTM! Decryption implementation is secure.
- Proper schema validation before decryption
- Generic error message prevents leaking cryptographic details
- Correct use of
decryptAesGcmwith tag authentication
250-264: LGTM! Generic encryption helpers are convenient.The
as Tcast indecryptObjectis a common pattern. Consider documenting that callers should validate the returned object if type safety is critical.
40-51: LGTM! Encrypted data schema is well-designed.Version field (
v: z.literal(1)) enables future algorithm migrations. The structure properly separates IV, ciphertext, and authentication tag.libs/sdk/src/auth/session/encrypted-authorization-vault.ts (6)
26-41: LGTM! Import consolidation aligns with the new @frontmcp/auth package structure.The imports correctly use
@frontmcp/utilsforrandomUUIDand consolidate vault-related types from@frontmcp/auth. Based on learnings, this follows the project convention of using@frontmcp/utilsfor common utilities.
143-150: LGTM! Clear fail-fast behavior with helpful error message.The method correctly retrieves the key from AsyncLocalStorage and provides a descriptive error when the context is not set, guiding users to the correct usage pattern.
206-227: LGTM! Clean async transformation for Redis entry conversion.The method correctly awaits the encryption operation and constructs the Redis entry with proper separation of encrypted and unencrypted fields.
594-606: Factory function mirrors constructor'sanytype for redis.Same suggestion applies here - consider using the Redis interface type if defined.
169-178: LGTM! Async transformation of encryption methods.The async conversion is correct and necessary—
VaultEncryption.encryptObject()anddecryptObject()both returnPromise<EncryptedData>andPromise<T>respectively. The methods properly delegate to these Promise-based operations and correctly preserve generic typing withVaultSensitiveData.
85-90:Uint8Arrayfor encryption keys is correct and properly used throughout the codebase.The type change from
BuffertoUint8Arrayimproves portability and aligns with the@frontmcp/utilscryptographic operations. All key derivation methods (deriveKey,deriveKeyFromToken) and encryption/decryption operations correctly return and acceptUint8Array. SinceEncryptionContextis an internal implementation detail not exported from the public SDK API, this is not a breaking change for consumers.
There was a problem hiding this 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/auth/vault/auth-providers.accessor.impl.ts:
- Around line 232-239: The comment in loadFromVault is misleading about
acquiredAt; either clarify that acquiredAt reflects the load time by changing
the comment in auth-providers.accessor.impl.ts to "// acquiredAt is set to load
time; original acquisition time is not preserved from vault", or if you intended
to preserve original acquisition time, update getCredential in
auth-providers.vault.ts to return the stored AppCredential metadata (including
acquiredAt) and have loadFromVault use that stored acquiredAt instead of
Date.now(); reference storeCredential, getCredential, loadFromVault and
acquiredAt when making the change.
🧹 Nitpick comments (3)
libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (3)
93-100: Incomplete error context for rejected promises.When a credential load fails, the provider name is lost because the rejection handler can't reliably extract it from
outcome.reason. Consider restructuring to preserve the provider name.♻️ Suggested improvement
for (const outcome of settled) { if (outcome.status === 'fulfilled') { results.set(outcome.value.name, outcome.value.result); } else { - // Find the provider name from the error context if possible - this.logger?.warn('Failed to load credential:', outcome.reason); + // Note: provider name context is lost in rejection + // Consider wrapping errors in loader to include provider name + this.logger?.warn('Failed to load credential for a provider:', outcome.reason); } }Alternatively, wrap errors in the promise map to preserve context:
const promises = providerNames.map(async (name) => { try { const result = await this.get(name, options); return { name, result }; } catch (error) { throw { name, error }; // Preserve name in rejection } });
119-128: Sequential header fetching inheadersMany.The method fetches headers sequentially. If multiple providers are typically requested together, parallel fetching could improve performance. However, the current sequential approach is simpler and avoids header key collision complexity.
♻️ Optional parallel optimization
async headersMany(providerNames: string[]): Promise<Record<string, string>> { - const allHeaders: Record<string, string> = {}; - - for (const name of providerNames) { - const headers = await this.headers(name); - Object.assign(allHeaders, headers); - } - - return allHeaders; + const headerPromises = providerNames.map((name) => this.headers(name)); + const headerResults = await Promise.all(headerPromises); + return Object.assign({}, ...headerResults); }
249-265: Silent vault storage failure may cause credential loss on restart.If
storeInVaultfails (e.g., Redis unavailable), the credential exists only in the in-memory cache and will be lost on restart. This resilience trade-off is reasonable but worth documenting or monitoring.Consider tracking vault write failures via metrics or structured logs with a specific code for observability.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
libs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/utils/src/crypto/index.tslibs/utils/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- libs/utils/src/crypto/index.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
libs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/utils/src/index.ts
libs/{sdk,adapters}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
UsegetCapabilities()for dynamic capability exposure instead of hardcoding capabilities
Files:
libs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/auth-providers.vault.ts
libs/sdk/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/sdk/**/*.{ts,tsx}: Preferinterfacefor defining object shapes in TypeScript, avoidanytypes
Use type parameters with constraints instead of unconstrained generics withanydefaults
Validate URIs per RFC 3986 at metadata level usingisValidMcpUrirefinement
UsechangeScopeinstead ofscopein change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g.,Promise<GetPromptResult>) instead ofPromise<unknown>for MCP entry methods
Useunknowninstead ofanyfor generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows
Files:
libs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/auth-providers.vault.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/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/utils/src/index.ts
libs/*/src/index.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases
Files:
libs/utils/src/index.ts
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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`
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/utils/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Applied to files:
libs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/utils/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Applied to files:
libs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/utils/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly
Applied to files:
libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/auth/vault/auth-providers.accessor.impl.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/{sdk,adapters}/**/*.{ts,tsx} : Use specific error classes with MCP error codes instead of generic errors
Applied to files:
libs/sdk/src/auth/vault/auth-providers.vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Return strictly typed MCP protocol responses (e.g., `Promise<GetPromptResult>`) instead of `Promise<unknown>` for MCP entry methods
Applied to files:
libs/sdk/src/auth/vault/auth-providers.vault.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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/utils/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management
Applied to files:
libs/utils/src/index.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). (4)
- GitHub Check: Lint & Format Checks
- GitHub Check: Build Libraries
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (18)
libs/utils/src/index.ts (3)
84-85: Good addition for completeness.Adding standard base64 encoding/decoding alongside the existing base64url variants provides a complete encoding surface for consumers.
219-240: Comprehensive storage API expansion.The new storage exports are well-organized:
FileSystemStorageAdapterfollows the established adapter pattern (consistent with Redis, VercelKv, Upstash adapters).TypedStorageandEncryptedTypedStorageprovide progressive type safety and encryption layers.- Associated types (
EncryptionKey,StoredEncryptedBlob,ClientKeyBinding) properly support the encryption features.This aligns with the coding guidelines recommendation to use
@frontmcp/utilsfor file system operations.
116-153: Add documentation for the new KeyPersistence API todocs/draft/docs/**.The new
KeyPersistenceclass and factory functions (createKeyPersistence,createKeyPersistenceWithStorage) are public API additions to the utils library. Per coding guidelines, public API changes require matching documentation updates indocs/draft/docs/**. Currently, no documentation exists for this new API surface.libs/sdk/src/auth/vault/auth-providers.vault.ts (9)
1-27: LGTM! Clean module setup and constructor.The class is well-structured with proper dependency injection, optional logger with child context, and clear documentation. The use of
InvalidInputError(imported from../../errors/mcp.error) aligns with the coding guideline to use specific error classes with MCP error codes.
38-63: LGTM!The
storeCredentialmethod correctly builds the vault key, constructs theAppCredentialwith proper metadata (acquiredAt, expiresAt), and appropriately re-throws errors after logging to allow callers to handle failures.
74-105: LGTM!The
getCredentialmethod properly validates credential state (checking bothisValidflag and expiration) before returning. The error handling approach of logging and returningnullis appropriate for a retrieval operation where absence is a valid outcome.
115-129: LGTM!The silent error handling for
removeCredentialis acceptable for a cleanup operation where a best-effort approach is appropriate. The warning log ensures visibility while not disrupting the caller flow.
140-155: LGTM!The invalidation method correctly marks credentials as invalid while preserving them for audit/debugging purposes. The reason parameter provides good traceability.
166-182: LGTM!The
refreshOAuthCredentialmethod correctly re-throws errors since token refresh is a critical operation where failures should propagate to the caller for proper handling.
191-241: LGTM!The
getAllCredentialsmethod correctly aggregates credentials from all applicable scopes. The silent error handling for individual scope queries is appropriate since partial results are better than failing entirely.
251-268: LGTM!Excellent use of the exhaustive switch pattern with
nevertype to ensure compile-time safety when new scopes are added. The key construction is well-documented and uses the appropriateInvalidInputErrorclass per coding guidelines.
271-274: LGTM!The DI token follows the established pattern using
Symbol.forfor cross-module consistency with proper type casting.libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (6)
1-34: LGTM! Good use of@frontmcp/utilsfor cryptographic operations.The import of
base64Encodefrom@frontmcp/utils(line 9) follows the coding guideline to use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly. The constructor properly injects all required dependencies.
36-77: LGTM!The
getmethod implements a correct multi-tier lookup strategy (cache → vault → loader) with proper handling of theforceRefreshoption. The flow correctly caches results after loading from vault or factory.
130-160: LGTM!The
refreshmethod correctly retrieves the existing credential for context, delegates to the loader, and updates both vault and cache on success. Error handling with warning logs is appropriate.
162-211: LGTM!The
hasmethod correctly validates and invalidates expired cache entries. The utility methods (isRegistered,invalidate,invalidateAll,listProviders,listAvailable) properly delegate to the appropriate underlying components.
270-280: LGTM!The
isValidhelper correctly validates both the validity flag and expiration timestamp.
285-319: LGTM! Proper use of@frontmcp/utilsfor encoding.The
defaultHeadersmethod correctly usesbase64Encodefrom@frontmcp/utilsfor Basic auth encoding, adhering to the coding guideline about cryptographic operations. The OAuth token type is properly taken from the credential rather than hardcoding "Bearer".
…om @frontmcp/auth
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In @libs/auth/src/session/session-crypto.ts:
- Around line 106-118: decryptAesGcmSync currently requires Node's crypto at
runtime and uses CommonJS require; replace that with the same ES-module or
injected crypto usage as encryptAesGcmSync to avoid direct node:crypto require
calls—remove the inline require('crypto') in decryptAesGcmSync, use the
shared/imported crypto API (e.g., the createDecipheriv function from
'node:crypto' or the passed-in crypto instance used elsewhere), keep the
existing logic (setAuthTag, update/final, Buffer.concat) and ensure types remain
correct for key/iv/tag/data and the function signature.
In @libs/sdk/src/auth/vault/auth-providers.vault.ts:
- Around line 100-103: The catch in getCredential currently logs and returns
null, hiding storage failures; update getCredential to re-throw storage errors
using the same pattern as addAppCredential/refreshOAuthCredential by wrapping or
converting the caught error into an InternalMcpError (or re-throwing if already
that type) so callers can distinguish "not found" from storage failure; keep
logging but throw InternalMcpError with context including providerId and
original error instead of returning null for non-not-found errors.
🧹 Nitpick comments (14)
libs/sdk/src/auth/machine-id.ts (2)
53-56: Strengthen UUID validation regex.The current regex
/^[0-9a-f-]{32,36}$/iis too permissive and would match invalid patterns like"------------------------------------"(36 dashes). Consider using a proper UUID v4 pattern or at minimum enforce the 8-4-4-4-12 structure.Suggested stricter UUID regex
- // Validate UUID format (loose check) - if (/^[0-9a-f-]{32,36}$/i.test(content)) { + // Validate UUID format (8-4-4-4-12 structure) + if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(content)) { return content; }
85-86: Use type guard for error handling consistency.Same issue as in
loadMachineIdSync- use a type guard instead of type assertion.Suggested fix
} catch (error) { - console.warn(`[MachineId] Failed to save to ${machineIdPath}: ${(error as Error).message}`); + const message = error instanceof Error ? error.message : String(error); + console.warn(`[MachineId] Failed to save to ${machineIdPath}: ${message}`); }libs/auth/src/vault/credential-cache.ts (2)
47-63: Redundant credential expiration check.The
isExpired()call on line 48 already checks credential expiration viaisExpiredAt()(see lines 208-210). The second check on lines 57-63 is redundant and can be removed.♻️ Suggested simplification
// Check TTL expiration if (this.isExpired(entry)) { this.cache.delete(providerId); this.stats.size = this.cache.size; this.stats.misses++; this.stats.evictions++; return undefined; } - // Check credential expiration - if (entry.resolved.expiresAt && Date.now() >= entry.resolved.expiresAt) { - this.cache.delete(providerId); - this.stats.size = this.cache.size; - this.stats.misses++; - this.stats.evictions++; - return undefined; - } - this.stats.hits++;
139-146: Consider using a separate array for keys to delete when iterating.Modifying a
Mapwhile iterating over it withfor...ofis technically safe in modern JavaScript (the iterator will see items deleted during iteration), but collecting keys first is more explicit and avoids potential confusion.♻️ Suggested clarification
invalidateByScope(scope: CredentialScope): void { + const keysToDelete: string[] = []; for (const [key, entry] of this.cache) { if (entry.resolved.scope === scope) { - this.cache.delete(key); + keysToDelete.push(key); } } + for (const key of keysToDelete) { + this.cache.delete(key); + } this.stats.size = this.cache.size; }libs/auth/src/vault/auth-providers.types.ts (1)
319-327: Consider whetherErrortype inCredentialEventshould be serializable.The
error?: Errorfield may not serialize well if events are logged or transmitted. Consider using a structured error type withmessage,code, and optionalstackfields for better observability.libs/auth/src/authorization/authorization.types.ts (1)
1-256: Well-structured authorization types with comprehensive runtime validation.The types and schemas are well-documented and follow a consistent pattern. A few observations:
AuthorizationCreateCtx(lines 227-256) lacks a corresponding Zod schema, unlike all other public types in this file. If runtime validation is needed for this type at API boundaries, consider adding anauthorizationCreateCtxSchema.
AuthorizedToolandAuthorizedPrompt(and their schemas) are structurally identical. If this is intentional for semantic distinction, consider a shared base type to reduce duplication.💡 Optional: Add schema for AuthorizationCreateCtx if needed at runtime boundaries
export const authorizationCreateCtxSchema = z.object({ id: z.string(), isAnonymous: z.boolean(), user: authUserSchema, claims: z.record(z.string(), z.unknown()).optional(), expiresAt: z.number().optional(), scopes: z.array(z.string()).optional(), token: z.string().optional(), authorizedApps: z.record(z.string(), z.object({ id: z.string(), toolIds: z.array(z.string()) })).optional(), authorizedAppIds: z.array(z.string()).optional(), authorizedTools: z.record(z.string(), authorizedToolSchema).optional(), authorizedToolIds: z.array(z.string()).optional(), authorizedPrompts: z.record(z.string(), authorizedPromptSchema).optional(), authorizedPromptIds: z.array(z.string()).optional(), authorizedResources: z.array(z.string()).optional(), });libs/sdk/src/auth/session/transport-session.manager.ts (1)
16-16: Using deprecated sync crypto functions.The
encryptAesGcmSyncanddecryptAesGcmSyncfunctions are marked as deprecated insession-crypto.ts(with a note to use async variants instead). Consider refactoringencodeSessionJwtanddecryptSessionJwtto be async methods, which would allow using the preferred async crypto functions and improve cross-platform compatibility.If async refactoring is not feasible now, please verify that the sync crypto path (which uses
node:cryptodirectly) meets your cross-platform requirements, as per the coding guidelines recommending@frontmcp/utilsfor cross-platform crypto support.libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (1)
123-132: Consider parallelizingheadersManylikegetMany.The sequential
awaitin the loop could be parallelized for better performance when fetching headers for multiple providers.♻️ Optional parallel implementation
async headersMany(providerNames: string[]): Promise<Record<string, string>> { const allHeaders: Record<string, string> = {}; - for (const name of providerNames) { - const headers = await this.headers(name); - Object.assign(allHeaders, headers); - } + const headerResults = await Promise.all( + providerNames.map((name) => this.headers(name)) + ); + + for (const headers of headerResults) { + Object.assign(allHeaders, headers); + } return allHeaders; }libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (3)
33-53: In-flight deduplication returns stale promise on generic type mismatch.The
loadmethod stores the promise in aMap<string, Promise<ResolvedCredential | null>>(line 18), but retrieves and casts it toPromise<ResolvedCredential<T> | null>at line 41. If two callers request the same provider with different generic type parametersT, the second caller receives a promise typed incorrectly.This is safe at runtime since the credential type is determined by the provider config, but the cast at line 41 (
as Promise<ResolvedCredential<T> | null>) bypasses TypeScript's type checking. Consider documenting this behavior or storing provider-specific type information.
156-161:cancelAll()only clears the map without canceling pending operations.The method name implies cancellation, but it only clears the in-flight map. The actual promises continue executing, and their results are discarded. This could lead to:
- Wasted resources from completing operations that are no longer tracked
- Potential state inconsistencies if factory functions have side effects
Consider renaming to
clearPending()or adding a comment clarifying the behavior does not actually abort in-flight operations.📝 Suggested documentation improvement
/** - * Cancel all in-flight loads (for cleanup) + * Clear all in-flight load tracking (for cleanup). + * Note: This does not abort executing promises - they will complete + * but their results will not be tracked or deduplicated. */ cancelAll(): void { this.loading.clear(); }
103-144: Refresh operation lacks in-flight deduplication.Unlike
load(), therefresh()method does not deduplicate concurrent refresh calls for the same provider. If multiple callers refresh the same credential simultaneously, they will all execute the refresh function independently, potentially causing race conditions or wasted API calls.Consider applying the same deduplication pattern used in
load()if concurrent refreshes are expected.libs/sdk/src/auth/vault/auth-providers.vault.ts (1)
190-240: Silent error handling ingetAllCredentialsmay mask storage issues.The method catches and ignores errors when fetching credentials from each scope (lines 196-198, 211-213, 223-225, 235-237). While this allows partial results, it could hide systematic storage failures.
Consider logging these errors at debug level to aid troubleshooting, or returning a result object that indicates which scopes succeeded/failed.
📝 Suggested improvement for observability
// Session scope try { const sessionCreds = await this.baseVault.getAppCredentials( this.buildVaultKey(sessionId, 'session'), this.namespace, ); credentials.push(...sessionCreds); } catch { - // Ignore + this.logger?.debug('Failed to fetch session-scoped credentials'); }libs/auth/src/index.ts (2)
180-201: Mixed export syntax: inlinetypekeyword and separateexport type.The authorization module exports mix inline type exports (
type AuthMode,type AuthUser, etc.) with value exports (AppAuthState, schemas) in the same block. While valid TypeScript, this differs from other sections that use separateexport type { ... }blocks.Consider using the same pattern as other sections for consistency:
📝 Suggested consistency improvement
export { - // Types - type AuthMode, - type AuthUser, - type AuthorizedTool, - type AuthorizedPrompt, - type LLMSafeAuthContext, - type AppAuthorizationRecord, - type ProgressiveAuthState, - type AuthorizationCreateCtx, // Enums AppAuthState, // Schemas authModeSchema, authUserSchema, authorizedToolSchema, authorizedPromptSchema, llmSafeAuthContextSchema, appAuthStateSchema, appAuthorizationRecordSchema, progressiveAuthStateSchema, } from './authorization'; +export type { + AuthMode, + AuthUser, + AuthorizedTool, + AuthorizedPrompt, + LLMSafeAuthContext, + AppAuthorizationRecord, + ProgressiveAuthState, + AuthorizationCreateCtx, +} from './authorization';
228-260: Vault module exports also use mixed inlinetypesyntax.Similar to the authorization section, the vault module exports use inline
typekeywords. Consider aligning with the separateexport type { ... }pattern used elsewhere for consistency across the file.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (38)
libs/auth/docs/AUTH.mdlibs/auth/docs/FLOW_CHART.mdlibs/auth/docs/REQUIREMENTS.mdlibs/auth/docs/ROADMAP.mdlibs/auth/src/authorization/authorization.types.tslibs/auth/src/authorization/index.tslibs/auth/src/index.tslibs/auth/src/session/index.tslibs/auth/src/session/session-crypto.tslibs/auth/src/vault/auth-providers.types.tslibs/auth/src/vault/credential-cache.tslibs/auth/src/vault/credential-helpers.tslibs/auth/src/vault/index.tslibs/sdk/src/auth/authorization/authorization.types.tslibs/sdk/src/auth/machine-id.tslibs/sdk/src/auth/oauth/flows/oauth.authorize.flow.tslibs/sdk/src/auth/oauth/flows/oauth.device-authorization.flow.tslibs/sdk/src/auth/oauth/flows/oauth.introspect.flow.tslibs/sdk/src/auth/oauth/flows/oauth.par.flow.tslibs/sdk/src/auth/oauth/flows/oauth.revoke.flow.tslibs/sdk/src/auth/oauth/flows/oauth.token.flow.tslibs/sdk/src/auth/oauth/flows/oauth.userinfo.flow.tslibs/sdk/src/auth/oauth/flows/oidc.logout.flow.tslibs/sdk/src/auth/session/record/session.stateful.tslibs/sdk/src/auth/session/session.crypto.tslibs/sdk/src/auth/session/token.refresh.tslibs/sdk/src/auth/session/token.store.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/auth-providers.accessor.tslibs/sdk/src/auth/vault/auth-providers.providers.tslibs/sdk/src/auth/vault/auth-providers.registry.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/sdk/src/auth/vault/credential-cache.tslibs/sdk/src/auth/vault/credential-loaders/eager-loader.tslibs/sdk/src/auth/vault/credential-loaders/index.tslibs/sdk/src/auth/vault/credential-loaders/lazy-loader.tslibs/sdk/src/auth/vault/index.ts
💤 Files with no reviewable changes (11)
- libs/sdk/src/auth/oauth/flows/oauth.userinfo.flow.ts
- libs/sdk/src/auth/oauth/flows/oauth.token.flow.ts
- libs/sdk/src/auth/session/session.crypto.ts
- libs/sdk/src/auth/oauth/flows/oauth.par.flow.ts
- libs/sdk/src/auth/oauth/flows/oauth.introspect.flow.ts
- libs/sdk/src/auth/oauth/flows/oauth.device-authorization.flow.ts
- libs/sdk/src/auth/oauth/flows/oidc.logout.flow.ts
- libs/sdk/src/auth/oauth/flows/oauth.authorize.flow.ts
- libs/sdk/src/auth/session/token.store.ts
- libs/sdk/src/auth/oauth/flows/oauth.revoke.flow.ts
- libs/auth/docs/REQUIREMENTS.md
✅ Files skipped from review due to trivial changes (1)
- libs/auth/docs/FLOW_CHART.md
🚧 Files skipped from review as they are similar to previous changes (6)
- libs/sdk/src/auth/vault/auth-providers.providers.ts
- libs/sdk/src/auth/session/record/session.stateful.ts
- libs/sdk/src/auth/vault/credential-loaders/eager-loader.ts
- libs/sdk/src/auth/vault/auth-providers.registry.ts
- libs/sdk/src/auth/session/token.refresh.ts
- libs/sdk/src/auth/vault/index.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
libs/sdk/src/auth/vault/credential-cache.tslibs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/credential-loaders/index.tslibs/auth/src/vault/credential-helpers.tslibs/sdk/src/auth/machine-id.tslibs/auth/src/session/index.tslibs/sdk/src/auth/vault/auth-providers.accessor.tslibs/auth/src/authorization/index.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/auth/src/vault/index.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/auth/src/vault/credential-cache.tslibs/auth/src/session/session-crypto.tslibs/sdk/src/auth/authorization/authorization.types.tslibs/sdk/src/auth/vault/credential-loaders/lazy-loader.tslibs/auth/src/vault/auth-providers.types.tslibs/auth/src/authorization/authorization.types.tslibs/auth/src/index.ts
libs/{sdk,adapters}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
UsegetCapabilities()for dynamic capability exposure instead of hardcoding capabilities
Files:
libs/sdk/src/auth/vault/credential-cache.tslibs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/credential-loaders/index.tslibs/sdk/src/auth/machine-id.tslibs/sdk/src/auth/vault/auth-providers.accessor.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/authorization/authorization.types.tslibs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts
libs/sdk/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/sdk/**/*.{ts,tsx}: Preferinterfacefor defining object shapes in TypeScript, avoidanytypes
Use type parameters with constraints instead of unconstrained generics withanydefaults
Validate URIs per RFC 3986 at metadata level usingisValidMcpUrirefinement
UsechangeScopeinstead ofscopein change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g.,Promise<GetPromptResult>) instead ofPromise<unknown>for MCP entry methods
Useunknowninstead ofanyfor generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows
Files:
libs/sdk/src/auth/vault/credential-cache.tslibs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/credential-loaders/index.tslibs/sdk/src/auth/machine-id.tslibs/sdk/src/auth/vault/auth-providers.accessor.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/authorization/authorization.types.tslibs/sdk/src/auth/vault/credential-loaders/lazy-loader.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/auth/vault/credential-cache.tslibs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/credential-loaders/index.tslibs/auth/src/vault/credential-helpers.tslibs/sdk/src/auth/machine-id.tslibs/auth/src/session/index.tslibs/sdk/src/auth/vault/auth-providers.accessor.tslibs/auth/src/authorization/index.tslibs/auth/docs/AUTH.mdlibs/sdk/src/auth/vault/auth-providers.vault.tslibs/auth/src/vault/index.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/auth/src/vault/credential-cache.tslibs/auth/src/session/session-crypto.tslibs/sdk/src/auth/authorization/authorization.types.tslibs/sdk/src/auth/vault/credential-loaders/lazy-loader.tslibs/auth/src/vault/auth-providers.types.tslibs/auth/src/authorization/authorization.types.tslibs/auth/src/index.ts
libs/*/src/index.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases
Files:
libs/auth/src/index.ts
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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`
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.)
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/machine-id.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/auth/src/session/session-crypto.tslibs/auth/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Applied to files:
libs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/machine-id.tslibs/auth/src/session/index.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/auth/src/session/session-crypto.tslibs/auth/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Applied to files:
libs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/machine-id.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/auth/src/session/session-crypto.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly
Applied to files:
libs/sdk/src/auth/vault/auth-providers.accessor.impl.tslibs/auth/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/auth/vault/auth-providers.accessor.impl.tslibs/sdk/src/auth/vault/credential-loaders/index.tslibs/sdk/src/auth/machine-id.tslibs/sdk/src/auth/authorization/authorization.types.tslibs/auth/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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/auth/vault/credential-loaders/index.tslibs/auth/src/session/index.tslibs/auth/src/authorization/index.tslibs/auth/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/*/src/index.ts : Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases
Applied to files:
libs/sdk/src/auth/vault/credential-loaders/index.tslibs/auth/src/session/index.tslibs/auth/src/authorization/index.tslibs/auth/src/vault/index.tslibs/auth/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files
Applied to files:
libs/sdk/src/auth/vault/credential-loaders/index.tslibs/auth/src/session/index.tslibs/auth/src/authorization/index.tslibs/auth/src/vault/index.tslibs/sdk/src/auth/authorization/authorization.types.tslibs/auth/src/vault/auth-providers.types.tslibs/auth/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/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/auth/vault/credential-loaders/index.tslibs/auth/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/{sdk,adapters}/**/*.{ts,tsx} : Use specific error classes with MCP error codes instead of generic errors
Applied to files:
libs/sdk/src/auth/vault/auth-providers.vault.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Return strictly typed MCP protocol responses (e.g., `Promise<GetPromptResult>`) instead of `Promise<unknown>` for MCP entry methods
Applied to files:
libs/sdk/src/auth/vault/auth-providers.vault.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management
Applied to files:
libs/sdk/src/auth/session/transport-session.manager.tslibs/auth/src/session/session-crypto.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/{sdk,adapters}/**/*.{ts,tsx} : Use `getCapabilities()` for dynamic capability exposure instead of hardcoding capabilities
Applied to files:
libs/sdk/src/auth/authorization/authorization.types.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{validation,**}/**/*.{ts,tsx} : Use `.strict()` on all Zod schemas for validation
Applied to files:
libs/auth/src/vault/auth-providers.types.tslibs/auth/src/authorization/authorization.types.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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/auth/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/**/*.{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/auth/src/index.ts
🧬 Code graph analysis (3)
libs/sdk/src/auth/vault/credential-cache.ts (2)
libs/sdk/src/auth/vault/index.ts (2)
CREDENTIAL_CACHE(87-87)CredentialCache(71-71)libs/auth/src/vault/credential-cache.ts (1)
CredentialCache(24-241)
libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (4)
libs/auth/src/vault/auth-providers.types.ts (2)
ResolvedCredential(71-84)CredentialFactoryContext(93-112)libs/auth/src/session/authorization-vault.ts (1)
Credential(247-247)libs/sdk/src/auth/vault/auth-providers.registry.ts (1)
NormalizedProviderConfig(21-30)libs/auth/src/vault/credential-helpers.ts (1)
extractCredentialExpiry(15-25)
libs/auth/src/authorization/authorization.types.ts (1)
libs/auth/src/authorization/index.ts (17)
AuthMode(9-9)authModeSchema(24-24)AuthUser(10-10)authUserSchema(25-25)AuthorizedTool(11-11)authorizedToolSchema(26-26)AuthorizedPrompt(12-12)authorizedPromptSchema(27-27)LLMSafeAuthContext(13-13)llmSafeAuthContextSchema(28-28)AppAuthState(20-20)appAuthStateSchema(29-29)AppAuthorizationRecord(14-14)appAuthorizationRecordSchema(30-30)ProgressiveAuthState(15-15)progressiveAuthStateSchema(31-31)AuthorizationCreateCtx(16-16)
⏰ 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). (15)
- GitHub Check: E2E Tests (demo-e2e-multiapp)
- GitHub Check: E2E Tests (demo-e2e-config)
- GitHub Check: E2E Tests (demo-e2e-openapi)
- GitHub Check: E2E Tests (demo-e2e-hooks)
- GitHub Check: E2E Tests (demo-e2e-cache)
- GitHub Check: E2E Tests (demo-e2e-redis)
- GitHub Check: E2E Tests (demo-e2e-public)
- GitHub Check: E2E Tests (demo-e2e-serverless)
- GitHub Check: E2E Tests (demo-e2e-notifications)
- GitHub Check: E2E Tests (demo-e2e-transparent)
- GitHub Check: E2E Tests (demo-e2e-errors)
- GitHub Check: E2E Tests (demo-e2e-ui)
- GitHub Check: E2E Tests (demo-e2e-codecall)
- GitHub Check: E2E Tests (demo-e2e-providers)
- GitHub Check: Unit Tests
🔇 Additional comments (32)
libs/sdk/src/auth/machine-id.ts (7)
17-18: Good use of@frontmcp/utilsfor utilities.The imports correctly use
@frontmcp/utilsforrandomUUID,mkdir, andwriteFile, which aligns with coding guidelines for cross-platform support.
25-27: LGTM!Simple and clear implementation for detecting development mode.
32-35: LGTM!Path resolution logic handles both absolute and relative paths correctly.
72-89: LGTM - Fire-and-forget pattern is appropriate here.Good use of
@frontmcp/utilsformkdirandwriteFile. The async IIFE with try-catch correctly handles errors without blocking startup, and the permission modes (0o700 for directory, 0o600 for file) are appropriately restrictive for storing sensitive identifiers.
103-123: Clear resolution priority with well-documented fallback chain.The IIFE correctly implements the documented priority order (env var → file → random generation) and the fire-and-forget persistence maintains non-blocking startup.
125-131: LGTM!Public API surface remains unchanged as noted in the summary.
47-66: The suggested fix referencingreadFileSyncfrom@frontmcp/utilsis incorrect—that export does not exist.The guideline to prefer
@frontmcp/utilsfor file system operations is valid, but@frontmcp/utilsexports only async file operations (readFile,writeFile,mkdir, etc.), not synchronous alternatives. TheloadMachineIdSync()function requires synchronous I/O during bootstrap to load the machine ID before async initialization can proceed.However, the type assertions in error handling should be replaced with proper type guards:
Use type guards for safer error handling
+function isNodeError(error: unknown): error is NodeJS.ErrnoException { + return error instanceof Error && 'code' in error; +} + try { // ... existing code ... } catch (error: unknown) { // ENOENT is expected on first run - if ((error as NodeJS.ErrnoException).code !== 'ENOENT') { - console.warn(`[MachineId] Failed to load from ${machineIdPath}: ${(error as Error).message}`); + if (isNodeError(error) && error.code === 'ENOENT') { + // Expected on first run, no warning needed + } else if (isNodeError(error)) { + console.warn(`[MachineId] Failed to load from ${machineIdPath}: ${error.message}`); + } else { + console.warn(`[MachineId] Failed to load from ${machineIdPath}: ${String(error)}`); } return null; }For the synchronous file read itself, consider if the bootstrap initialization can be refactored to support async loading in a future update, or document this as an intentional exception to the file system utilities guideline for critical bootstrap paths.
libs/auth/src/vault/credential-helpers.ts (1)
15-25: LGTM - Clean utility function for credential expiry extraction.The switch statement correctly handles credential types that have expiry timestamps and returns
undefinedfor types without expiry semantics. The function is well-documented with clear JSDoc.libs/auth/src/vault/credential-cache.ts (1)
24-31: LGTM - Well-designed cache implementation.The
CredentialCacheclass provides a clean API for session-scoped credential caching with appropriate TTL handling, eviction policy, and statistics tracking. The implementation is straightforward and suitable for the expected cache sizes.libs/auth/src/vault/index.ts (1)
1-40: LGTM - Well-organized barrel exports.The vault index consolidates types, schemas, helpers, and cache utilities following the project convention of exporting through barrel files. The grouping with comments improves discoverability.
libs/sdk/src/auth/authorization/authorization.types.ts (2)
3-27: LGTM - Clean separation of portable and SDK-specific types.The file correctly re-exports portable types from
@frontmcp/authfor downstream consumers while importing the same types locally for use in SDK-specific interfaces. This pattern maintains API consistency while allowing the SDK to extend the portable types.
38-135: Well-designed Authorization interface with comprehensive accessor methods.The interface properly marks all properties as
readonlyand provides useful helper methods (hasScope,canAccessTool, etc.) that align with SDK usage patterns. The separation of ID arrays from record types (e.g.,authorizedToolIdsalongsideauthorizedTools) enables efficient lookups and iteration.libs/auth/src/vault/auth-providers.types.ts (2)
188-202: Acceptable use ofz.any()for function types with proper justification.The comment on line 195 explains that functions are validated at runtime rather than through Zod, which is a valid approach since Zod cannot meaningfully validate function signatures. This justifies the
z.any()usage per the coding guidelines requiring strong justification foranytypes.
56-62: Good use of.strict()on Zod schemas.The schemas correctly apply
.strict()to reject unknown properties, aligning with the coding guidelines for validation schemas.libs/sdk/src/auth/vault/auth-providers.accessor.ts (2)
29-158: LGTM - Excellent interface design with comprehensive JSDoc.The
AuthProvidersAccessorinterface provides a clean API for credential management with appropriate async/sync method signatures:
- Async for operations that may trigger loading (
get,has,listAvailable)- Sync for registry checks and cache invalidation (
isRegistered,invalidate)The JSDoc examples demonstrate practical usage patterns.
160-168: The DI token naming convention is consistent across the codebase.AUTH_PROVIDERS_ACCESSORcorrectly follows the establishedSymbol.for('frontmcp:TOKEN_NAME')pattern used throughout the SDK for dependency injection tokens. No changes needed.libs/auth/src/authorization/index.ts (1)
1-32: LGTM!Clean barrel exports with proper separation of types, enums, and schemas. The use of
export typefor type-only exports is correct for optimal tree-shaking. Based on learnings, this follows the pattern of exporting all public APIs through appropriate entry points.libs/sdk/src/auth/vault/credential-cache.ts (1)
1-14: LGTM!Clean DI token definition following the established pattern. The use of
Symbol.for()ensures consistent identity across module boundaries, and the type import from@frontmcp/authmaintains proper separation between the SDK's DI wiring and the auth library's implementation.libs/sdk/src/auth/session/transport-session.manager.ts (2)
156-162: LGTM - Key derivation implementation.The HKDF key derivation is correctly implemented with proper parameters: 32-byte output for AES-256, descriptive salt and info strings, and efficient reuse of the
TextEncoderinstance.
340-345: Verify the blob format compatibility.The
decryptAesGcmSyncexpects aSessionEncBlobwith base64url-encoded strings, but the JWT format stored is${iv}.${tag}.${ct}(line 313, 318). Ensure the decryption correctly handles this format - currently it passes the split parts directly which should work, but consider adding a comment documenting this format transformation for maintainability.libs/sdk/src/auth/vault/auth-providers.accessor.impl.ts (1)
289-323: LGTM - Comprehensive header generation.The
defaultHeadersmethod properly handles all credential types with appropriate header formats. Good use ofbase64Encodefrom@frontmcp/utilsas per coding guidelines.libs/sdk/src/auth/vault/credential-loaders/index.ts (1)
8-9: Verify the intent of re-exporting from@frontmcp/auth.Re-exporting
extractCredentialExpiryfrom@frontmcp/authcreates a secondary import path. Consumers might import from either location, potentially causing confusion. If this is intentional for convenience within the credential-loaders context, consider adding a comment explaining the re-export. Otherwise, consumers should import directly from@frontmcp/auth.libs/auth/src/session/session-crypto.ts (2)
36-62: LGTM - Async crypto functions.The async
encryptAesGcmanddecryptAesGcmfunctions correctly use@frontmcp/utilsprimitives as per coding guidelines, with proper base64url encoding for the output blob.
73-75: LGTM - HKDF wrapper.Clean passthrough wrapper for the
@frontmcp/utilsHKDF function.libs/sdk/src/auth/vault/credential-loaders/lazy-loader.ts (1)
1-162: LGTM on overall structure and implementation.The class correctly implements lazy loading with in-flight deduplication, proper logging with duration tracking, and clean error propagation. The use of
extractCredentialExpiryfrom@frontmcp/authaligns with the library's centralized credential helpers. Based on learnings, the imports from@frontmcp/authinstead of internal paths is the correct approach for this refactored architecture.libs/sdk/src/auth/vault/auth-providers.vault.ts (3)
8-12: Good use of specific MCP error class.The import of
InvalidInputErrorfrom the SDK's error module aligns with the coding guidelines requiring specific error classes with MCP error codes instead of generic errors. Based on learnings, this is the correct pattern forlibs/sdk/**/*.tsfiles.
250-267: Robust exhaustive scope checking with proper error handling.The
buildVaultKeymethod correctly:
- Validates
userIdrequirement for user-scoped credentials- Uses the exhaustive
nevercheck pattern to ensure allCredentialScopevalues are handled- Throws
InvalidInputErrorwith descriptive messagesThis ensures compile-time safety when new scopes are added.
270-273: DI token correctly uses Symbol.for for cross-package compatibility.Using
Symbol.for('frontmcp:AUTH_PROVIDERS_VAULT')ensures the same symbol is used across different module instances, which is essential for DI to work correctly across package boundaries. The type assertion toToken<AuthProvidersVault>properly types the token.libs/auth/src/session/index.ts (2)
67-69: Crypto exports align with centralized utility pattern.The session-crypto exports (
encryptAesGcm,decryptAesGcm,hkdfSha256,encryptAesGcmSync,decryptAesGcmSync) provide the recommended crypto utilities. Based on learnings, these should be used instead ofnode:cryptodirectly throughout the codebase.
1-96: Well-organized barrel exports with clear module groupings.The file follows the guideline to export everything users need through barrel exports without backwards compatibility aliases. The exports are logically grouped by domain (Authorization Vault, Vault Encryption, Token Vault, etc.) with clear comments. The separation of runtime values (
export { ... }) from types (export type { ... }) is consistent throughout.libs/auth/src/index.ts (2)
1-32: Excellent documentation with practical usage example.The module-level JSDoc provides clear context and a working example demonstrating three key use cases (JWKS, UI templates, credential vault). This helps developers quickly understand how to use the package.
34-260: Comprehensive public API surface with logical organization.The barrel file successfully consolidates the
@frontmcp/authpackage's public API across all submodules (JWKS, UI, Session, Authorization, Utils, Vault). Each section is clearly demarcated with comments, and exports are complete based on the AI summary. This aligns with the guideline to export everything users need through barrel exports.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
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/auth/session/transport-session.manager.ts (1)
329-359:encodeSessionJwtmethod is not integrated into the session creation flow and appears to be incomplete/dead code.While
encodeSessionJwt(lines 290-320) encryptsadditionalState.stateandadditionalState.tokenswithencryptValue, it is never called in the codebase. ThecreateSessionmethod returns aTransportSessionobject directly without invokingencodeSessionJwtfor stateless mode, anddecryptSessionJwtnever attempts to recover the inner-encrypted state/tokens fields.The stateless session encoding/decoding path appears incomplete:
encodeSessionJwtis never invoked, so inner state/tokens are never stored in the JWTdecryptSessionJwtcasts the decrypted payload asSessionJwtPayload(base type) rather thanStatelessSessionJwtPayload, losing access to optionalstateandtokensfields even if they existedTransportSessionhas no fields to carry these decrypted valuesEither complete the stateless session implementation by integrating
encodeSessionJwtintocreateSessionand updatingdecryptSessionJwtto handleStatelessSessionJwtPayload, or remove the unused encryption code if stateless mode doesn't require carrying state/tokens in the session JWT.
🤖 Fix all issues with AI agents
In @libs/sdk/src/auth/session/record/session.mcp.ts:
- Around line 13-14: The constructor in McpSession currently calls super(ctx as
any); remove the unsafe any cast by making the types compatible: either update
the base Session constructor signature to accept McpSessionCreateCtx (or a
common BaseCreateCtx that McpSessionCreateCtx extends), replace the cast with a
narrower type assertion only if structurally compatible (e.g., ctx as
BaseCreateCtx), or explicitly map ctx properties into the shape Session expects
before calling super; reference the constructor, McpSessionCreateCtx, and
Session when locating and applying the change.
- Around line 3-5: McpSessionCreateCtx currently declares apps: string[] but
that property is never propagated to BaseCreateCtx or stored, forcing an unsafe
ctx as any cast; either remove apps from McpSessionCreateCtx if MCP sessions
don't need it, or add a corresponding class property (e.g., this.apps) and/or
map it into the parent create context (BaseCreateCtx.authorizedAppIds) in the
constructor where McpSessionCreateCtx is used so the types align; then delete
the ctx as any cast and update signatures so McpSessionCreateCtx is assignable
to BaseCreateCtx without casting.
In @libs/sdk/src/auth/session/session.service.ts:
- Line 78: The session creation is ignoring the optional authorizedResources
from CreateSessionArgs by hardcoding authorizedResources: [], so update the
session builder in session.service.ts to use the passed-in value (e.g.,
args.authorizedResources ?? []) instead of an empty array; locate the code in
the create session function (where authorizedTools/authorizedPrompts are
handled) and replace the hardcoded array with args.authorizedResources ?? [] so
provided values are respected.
🧹 Nitpick comments (9)
libs/sdk/src/auth/session/session-crypto.ts (1)
44-60: Consider hardening the development fallback behavior.The insecure default secret fallback (line 56) relies on
NODE_ENV !== 'production'to determine safety. IfNODE_ENVis unset or misconfigured in a production-like environment, the insecure default will be used silently after a warning.Consider also checking for common production indicators like cloud environment variables or requiring explicit opt-in for the insecure default:
💡 Optional: More defensive environment check
if (!secret) { - if (process.env['NODE_ENV'] === 'production') { + const isProduction = process.env['NODE_ENV'] === 'production' || + process.env['VERCEL_ENV'] === 'production' || + process.env['AWS_EXECUTION_ENV']; + if (isProduction) { throw new Error( '[SessionCrypto] MCP_SESSION_SECRET is required in production for session signing. ' + 'Set this environment variable to a secure random string.', ); }libs/auth/src/index.ts (2)
128-136: Consider documenting the re-export rationale for crypto utilities.Re-exporting
hkdfSha256,encryptValue,decryptValue,encryptAesGcm, anddecryptAesGcmfrom@frontmcp/utilscreates a convenience API but also couples consumers to this package for crypto operations. This is fine if intentional, but a brief comment explaining why these are re-exported (e.g., "for use with vault encryption") would clarify the design intent.
42-47: Verify if dev-key-persistence exports should be marked as deprecated.The exports
isDevKeyPersistenceEnabled,resolveKeyPath,loadDevKey,saveDevKey, anddeleteDevKeyappear to be development-only utilities. Consider adding JSDoc@deprecatedtags or grouping them under a separate comment indicating they're for development use only to prevent accidental production usage.libs/sdk/src/auth/session/session.types.ts (2)
41-41: Replaceanywithunknownin claims type.Per coding guidelines, use
unknowninstead ofanyfor generic type parameter defaults. Theclaimsproperty should useRecord<string, unknown>for type safety.♻️ Suggested fix
- claims: Record<string, any>; + claims: Record<string, unknown>;
51-54: Replaceanywithunknownin details types.The
detailsproperties inauthorizedToolsandauthorizedPromptsshould also useRecord<string, unknown>instead ofRecord<string, any>.♻️ Suggested fix
- authorizedTools?: Record<string, { executionPath: [string, string]; details?: Record<string, any> }>; + authorizedTools?: Record<string, { executionPath: [string, string]; details?: Record<string, unknown> }>; authorizedToolIds?: string[]; - authorizedPrompts?: Record<string, { executionPath: [string, string]; details?: Record<string, any> }>; + authorizedPrompts?: Record<string, { executionPath: [string, string]; details?: Record<string, unknown> }>;libs/utils/src/crypto/hmac-signing.ts (2)
154-161: Consider strengthening the signed data detection.The function only checks for the presence of
sigandvproperties. Objects that coincidentally have these properties could produce false positives. Consider also checking thatvis a number (specifically1for the current version) and thatdataexists.♻️ Optional: Stricter detection
export function isSignedData(json: string): boolean { try { const parsed = JSON.parse(json); - return parsed && typeof parsed === 'object' && 'sig' in parsed && 'v' in parsed; + return ( + parsed && + typeof parsed === 'object' && + 'sig' in parsed && + 'v' in parsed && + 'data' in parsed && + typeof parsed.sig === 'string' && + parsed.v === 1 + ); } catch { return false; } }
171-183: Type assertion on unsigned data bypasses runtime validation.The
as Tcast on line 179 is inherently unsafe since there's no schema validation for legacy unsigned data. The inline comment (line 177) is a good warning, but callers might miss it.Consider adding a Zod schema overload or a generic validator parameter for scenarios where type safety matters:
export function verifyOrParseData<T>( json: string, config: HmacSigningConfig, schema?: z.ZodType<T> ): T | null;This would allow callers to opt into validation for unsigned data during migration.
libs/sdk/src/auth/session/session.service.ts (2)
39-52: Reduceanytype assertions.Multiple
as anycasts here and on lines 74-75 work around type mismatches. Per coding guidelines, avoidanytypes without strong justification.The
claimsparameter is already typed asRecord<string, any>inCreateSessionArgs, so the cast on line 40 is redundant. For the provider snapshot casts, consider aligning theProviderSnapshottype with the constructed object shape, or use a type assertion to the specific expected type rather thanany.♻️ Suggested improvement for claims access
const expClaim = - args.claims && typeof (args.claims as any)['exp'] === 'number' - ? Number((args.claims as any)['exp']) + args.claims && typeof args.claims['exp'] === 'number' + ? Number(args.claims['exp']) : undefined;
65-85: Finalas anycast bypasses McpSession type checking.The constructor call ends with
as any, hiding any type mismatches between the provided object andMcpSessionCreateCtx. This makes it easy for bugs to slip through if the context type changes.Consider defining the context object with explicit typing, or updating
McpSessionCreateCtxto match the constructed shape. If the cast is unavoidable due to base class constraints (as suggested byMcpSessionusingsuper(ctx as any)), document why.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
libs/auth/src/index.tslibs/auth/src/session/authorization.store.tslibs/auth/src/session/index.tslibs/sdk/src/auth/flows/__tests__/oauth.authorize.flow.test.tslibs/sdk/src/auth/instances/instance.local-primary-auth.tslibs/sdk/src/auth/session/index.tslibs/sdk/src/auth/session/record/session.mcp.tslibs/sdk/src/auth/session/record/session.stateful.tslibs/sdk/src/auth/session/record/session.stateless.tslibs/sdk/src/auth/session/record/session.transparent.tslibs/sdk/src/auth/session/session-crypto.tslibs/sdk/src/auth/session/session.schema.tslibs/sdk/src/auth/session/session.service.tslibs/sdk/src/auth/session/session.types.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/vault/auth-providers.vault.tslibs/utils/src/crypto/hmac-signing.tslibs/utils/src/crypto/index.tslibs/utils/src/index.ts
💤 Files with no reviewable changes (3)
- libs/sdk/src/auth/session/record/session.transparent.ts
- libs/sdk/src/auth/session/record/session.stateless.ts
- libs/sdk/src/auth/session/record/session.stateful.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- libs/sdk/src/auth/instances/instance.local-primary-auth.ts
- libs/sdk/src/auth/vault/auth-providers.vault.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
libs/sdk/src/auth/flows/__tests__/oauth.authorize.flow.test.tslibs/sdk/src/auth/session/session.schema.tslibs/sdk/src/auth/session/record/session.mcp.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/session/session.types.tslibs/utils/src/crypto/hmac-signing.tslibs/auth/src/session/index.tslibs/sdk/src/auth/session/session-crypto.tslibs/sdk/src/auth/session/index.tslibs/sdk/src/auth/session/session.service.tslibs/utils/src/crypto/index.tslibs/auth/src/index.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Do not use prefixes like 'PT-001' in test names
Do not skip constructor validation tests for error classes and types
Includeinstanceofchecks in tests for error classes to verify proper error hierarchy
Files:
libs/sdk/src/auth/flows/__tests__/oauth.authorize.flow.test.ts
libs/{sdk,adapters}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
UsegetCapabilities()for dynamic capability exposure instead of hardcoding capabilities
Files:
libs/sdk/src/auth/flows/__tests__/oauth.authorize.flow.test.tslibs/sdk/src/auth/session/session.schema.tslibs/sdk/src/auth/session/record/session.mcp.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/session/session.types.tslibs/sdk/src/auth/session/session-crypto.tslibs/sdk/src/auth/session/index.tslibs/sdk/src/auth/session/session.service.ts
libs/sdk/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/sdk/**/*.{ts,tsx}: Preferinterfacefor defining object shapes in TypeScript, avoidanytypes
Use type parameters with constraints instead of unconstrained generics withanydefaults
Validate URIs per RFC 3986 at metadata level usingisValidMcpUrirefinement
UsechangeScopeinstead ofscopein change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g.,Promise<GetPromptResult>) instead ofPromise<unknown>for MCP entry methods
Useunknowninstead ofanyfor generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows
Files:
libs/sdk/src/auth/flows/__tests__/oauth.authorize.flow.test.tslibs/sdk/src/auth/session/session.schema.tslibs/sdk/src/auth/session/record/session.mcp.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/session/session.types.tslibs/sdk/src/auth/session/session-crypto.tslibs/sdk/src/auth/session/index.tslibs/sdk/src/auth/session/session.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/auth/flows/__tests__/oauth.authorize.flow.test.tslibs/sdk/src/auth/session/session.schema.tslibs/sdk/src/auth/session/record/session.mcp.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/session/session.types.tslibs/utils/src/crypto/hmac-signing.tslibs/auth/src/session/index.tslibs/sdk/src/auth/session/session-crypto.tslibs/sdk/src/auth/session/index.tslibs/sdk/src/auth/session/session.service.tslibs/utils/src/crypto/index.tslibs/auth/src/index.ts
libs/*/src/index.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases
Files:
libs/auth/src/index.ts
🧠 Learnings (16)
📓 Common learnings
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.
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Return strictly typed MCP protocol responses (e.g., `Promise<GetPromptResult>`) instead of `Promise<unknown>` for MCP entry methods
Applied to files:
libs/sdk/src/auth/session/session.schema.tslibs/sdk/src/auth/session/record/session.mcp.tslibs/sdk/src/auth/session/session.types.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files
Applied to files:
libs/sdk/src/auth/session/session.schema.tslibs/auth/src/session/index.tslibs/sdk/src/auth/session/index.tslibs/auth/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Use `unknown` instead of `any` for generic type parameter defaults (not for MCP protocol types)
Applied to files:
libs/sdk/src/auth/session/session.schema.tslibs/sdk/src/auth/session/session.types.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/{sdk,adapters}/**/*.{ts,tsx} : Use specific error classes with MCP error codes instead of generic errors
Applied to files:
libs/sdk/src/auth/session/session.schema.tslibs/sdk/src/auth/session/record/session.mcp.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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:
libs/sdk/src/auth/session/record/session.mcp.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management
Applied to files:
libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/sdk/src/auth/session/transport-session.manager.tslibs/utils/src/crypto/hmac-signing.tslibs/sdk/src/auth/session/session-crypto.tslibs/utils/src/crypto/index.tslibs/auth/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Applied to files:
libs/sdk/src/auth/session/transport-session.manager.tslibs/utils/src/crypto/hmac-signing.tslibs/auth/src/session/index.tslibs/sdk/src/auth/session/session-crypto.tslibs/sdk/src/auth/session/index.tslibs/utils/src/crypto/index.tslibs/auth/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Applied to files:
libs/sdk/src/auth/session/transport-session.manager.tslibs/utils/src/crypto/hmac-signing.tslibs/sdk/src/auth/session/session-crypto.tslibs/utils/src/crypto/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly
Applied to files:
libs/utils/src/crypto/hmac-signing.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/*/src/index.ts : Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases
Applied to files:
libs/auth/src/session/index.tslibs/sdk/src/auth/session/index.tslibs/utils/src/crypto/index.tslibs/auth/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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/auth/src/session/index.tslibs/sdk/src/auth/session/index.tslibs/utils/src/crypto/index.tslibs/auth/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/auth/session/index.tslibs/auth/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/bundler/**/*.{ts,tsx} : The bundler module must re-export utilities from frontmcp/uipack/bundler and provide SSR component bundling functionality
Applied to files:
libs/auth/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/**/*.{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/auth/src/index.ts
🧬 Code graph analysis (5)
libs/sdk/src/auth/session/session.schema.ts (1)
libs/sdk/src/auth/session/record/session.mcp.ts (1)
McpSession(11-20)
libs/sdk/src/auth/session/record/session.mcp.ts (1)
libs/sdk/src/auth/session/record/session.base.ts (1)
BaseCreateCtx(8-29)
libs/sdk/src/auth/session/transport-session.manager.ts (3)
libs/utils/src/crypto/index.ts (3)
hkdfSha256(96-104)encryptValue(270-270)decryptValue(271-271)libs/utils/src/index.ts (3)
hkdfSha256(77-77)encryptValue(94-94)decryptValue(95-95)libs/sdk/src/auth/session/transport-session.types.ts (1)
SessionJwtPayload(124-137)
libs/utils/src/crypto/hmac-signing.ts (2)
libs/utils/src/crypto/index.ts (8)
SignedData(332-332)HmacSigningConfig(333-333)hmacSha256(81-83)base64urlEncode(182-197)base64urlDecode(202-220)timingSafeEqual(154-159)isSignedData(337-337)verifyOrParseData(338-338)libs/utils/src/index.ts (8)
SignedData(134-134)HmacSigningConfig(135-135)hmacSha256(76-76)base64urlEncode(82-82)base64urlDecode(83-83)timingSafeEqual(80-80)isSignedData(138-138)verifyOrParseData(139-139)
libs/sdk/src/auth/session/session.service.ts (4)
libs/sdk/src/auth/session/record/session.base.ts (1)
scope(106-108)libs/sdk/src/scope/scope.instance.ts (1)
Scope(37-392)libs/sdk/src/auth/session/session.types.ts (1)
CreateSessionArgs(38-55)libs/sdk/src/auth/session/record/session.mcp.ts (1)
McpSession(11-20)
⏰ 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). (4)
- GitHub Check: Build Libraries
- GitHub Check: Lint & Format Checks
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (19)
libs/sdk/src/auth/flows/__tests__/oauth.authorize.flow.test.ts (1)
15-15: LGTM! Import correctly migrated to the new package.The import path update from local modules to
@frontmcp/authaligns with the PR's objective to consolidate authentication APIs into a unified package surface.libs/sdk/src/auth/session/session-crypto.ts (2)
12-19: LGTM! Correctly uses@frontmcp/utilsfor cryptographic operations.The migration to generic HMAC signing utilities from
@frontmcp/utilsaligns with the coding guidelines requiring use of@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly.
85-86: LGTM! Clean delegation to utility functions.The session signing and verification functions correctly delegate to the generic utilities while providing session-specific typing and configuration resolution.
libs/auth/src/index.ts (1)
1-48: Well-structured barrel exports for the new package.The module organization with clear section comments makes the API surface discoverable. The example in the JSDoc provides helpful guidance for consumers.
libs/sdk/src/auth/session/session.types.ts (1)
5-6: LGTM! SessionMode consolidation to 'mcp'.The simplification from a three-variant union to a single
'mcp'literal aligns with the PR's objective to unify session handling under the MCP model.libs/sdk/src/auth/session/session.schema.ts (1)
1-4: LGTM! Clean schema simplification.The schema consolidation from a three-type union to a single
McpSessioninstanceof check aligns with the broader MCP session unification. This simplifies validation logic and reduces maintenance burden.libs/sdk/src/auth/session/index.ts (1)
24-44: LGTM! Clean re-export structure from @frontmcp/auth.The migration to re-exporting authorization store components from
@frontmcp/authproperly centralizes the auth API surface while maintaining backwards compatibility for SDK consumers. The separation between SDK-specific session utilities (transport, rate limiting, crypto) and the shared auth package components is well-structured.libs/utils/src/crypto/hmac-signing.ts (4)
1-25: LGTM! Well-documented module with correct imports.The module documentation is comprehensive with a clear example. Imports correctly use internal crypto utilities from
./indexrather thannode:cryptodirectly, following coding guidelines.
26-48: LGTM! Clean interface definitions with forward-compatible versioning.The
SignedData<T>interface with version field (v: 1) enables future algorithm changes without breaking existing data. The comment recommending at least 32 bytes for the secret is helpful guidance.
50-88: LGTM! Correct signing implementation.The signature computation over the JSON-stringified data and the wrapper structure are sound. Using base64url encoding for the signature is appropriate for storage/transport.
109-145: LGTM! Robust verification with proper timing-safe comparison.Good defensive implementation:
- Validates structure before processing
- Uses timing-safe comparison to prevent timing attacks
- Length check before
timingSafeEqualavoids the exception it would throw on mismatched lengths- Catches all exceptions and returns null for invalid inputs
libs/utils/src/crypto/index.ts (2)
222-249: LGTM! Standard base64 utilities complement existing base64url functions.Clean implementation following the established cross-platform pattern. The JSDoc correctly references RFC 4648 Section 4 and notes the use case (HTTP Basic auth).
328-367: LGTM! Well-organized re-exports expand the crypto API surface.The re-exports from
./hmac-signingand./key-persistenceare clearly sectioned with comments distinguishing types, schemas, classes, and factory functions. This makes the barrel export discoverable and maintainable.libs/auth/src/session/index.ts (1)
1-125: LGTM! Comprehensive barrel export for the session module.The file consolidates session-related exports into a single public entry point with clear section comments. Re-exporting crypto utilities from
@frontmcp/utils(lines 90-98) centralizes dependencies for consumers of the auth package. This follows the guideline to export everything users need through barrel exports.libs/sdk/src/auth/session/transport-session.manager.ts (5)
3-3: LGTM!The imports correctly use
@frontmcp/utilsfor cryptographic operations (encryptValue,decryptValue,hkdfSha256) as required by the coding guidelines.
122-122: LGTM!The migration from
BuffertoUint8Arrayaligns with the broader refactor toward portable, Web Crypto-compatible primitives. SinceencryptionKeyisprivate readonly, this change has no external API impact.
154-160: LGTM!The HKDF key derivation correctly uses
TextEncoderfor string-to-Uint8Arrayconversion before passing tohkdfSha256. The 32-byte output is appropriate for AES-256-GCM operations used downstream.
309-317: LGTM!The stateless encoding correctly uses
encryptValuefrom@frontmcp/utilsand formats the encrypted output asiv.tag.data, which matches the expected format indecryptSessionJwt. The symmetric handling of bothstateandtokensis consistent.
337-341: > Likely an incorrect or invalid review comment.
There was a problem hiding this 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/plugins/src/index.ts:
- Around line 11-12: Update the barrel file comment that currently notes
"ConfigPlugin is now built into @frontmcp/sdk" to include explicit migration
guidance: mention that the `@frontmcp/plugin-config` re-export has been removed
and instruct users to import `ConfigPlugin` (or equivalent) directly from
`@frontmcp/sdk` (e.g., "import { ConfigPlugin } from '@frontmcp/sdk'"), and add
a one-line pointer to the existing migration docs at
`docs/draft/docs/extensibility/config-yaml.mdx` for more details; ensure the
comment references the symbol name `ConfigPlugin`, the removed package
`@frontmcp/plugin-config`, and the new package `@frontmcp/sdk`.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
apps/e2e/demo-e2e-config/jest.e2e.config.tslibs/plugins/package.jsonlibs/plugins/src/index.tsplugins/plugin-config/jest.config.tsplugins/plugin-config/src/__tests__/config.plugin.test.tsplugins/plugin-config/src/__tests__/config.service.test.tsplugins/plugin-config/src/__tests__/env-loader.test.tsplugins/plugin-config/src/index.tsplugins/plugin-config/tsconfig.spec.jsontsconfig.base.json
💤 Files with no reviewable changes (2)
- libs/plugins/package.json
- apps/e2e/demo-e2e-config/jest.e2e.config.ts
🧰 Additional context used
📓 Path-based instructions (5)
libs/plugins/**/*.ts
📄 CodeRabbit inference engine (libs/plugins/CLAUDE.md)
libs/plugins/**/*.ts: Extend tool metadata usingdeclare globalpattern to allow tools to specify plugin-specific options in their decorators
Always use@frontmcp/utilsfor cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead ofnode:crypto
RememberPlugin automatically providesthis.rememberandthis.approvalon execution contexts when installed; use helper functionsgetRemember()ortryGetRemember()for graceful degradation
RememberPlugin scopes include:session(default, current session only),user(persists across sessions),tool(tied to specific tool + session), andglobal(shared across all sessions/users)
Configure approval requirements on tools using theapprovaldecorator property with fields:required,defaultScope,category,riskLevel, andapprovalMessage
Avoid usingnode:cryptodirectly; always use@frontmcp/utilsfor cross-platform cryptographic support
Use proper ES module imports instead ofrequire()for SDK imports; avoid dynamic require of@frontmcp/sdkmodules
Always use theoverridekeyword when inheriting and overriding methods from parent classes
Avoid usinganytype without justification in TypeScript files
Never hardcode encryption keys; use environment variables for key management
Do not expose internal error details in public-facing error messages
Files:
libs/plugins/src/index.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
libs/plugins/src/index.ts
libs/*/src/index.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases
Files:
libs/plugins/src/index.ts
libs/plugins/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use module augmentation and runtime prototype modification to extend ExecutionContextBase with plugin properties
Files:
libs/plugins/src/index.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/plugins/src/index.ts
🧠 Learnings (14)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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`
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/plugins/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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:
libs/plugins/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/bundler/**/*.{ts,tsx} : The bundler module must re-export utilities from frontmcp/uipack/bundler and provide SSR component bundling functionality
Applied to files:
libs/plugins/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/**/*.{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/plugins/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/*/src/index.ts : Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases
Applied to files:
libs/plugins/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Applied to files:
libs/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*plugin.ts : Avoid module-level side effects for context extensions as they cause circular dependencies; install extensions in plugin constructor instead
Applied to files:
libs/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : RememberPlugin automatically provides `this.remember` and `this.approval` on execution contexts when installed; use helper functions `getRemember()` or `tryGetRemember()` for graceful degradation
Applied to files:
libs/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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:
libs/plugins/src/index.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). (4)
- GitHub Check: Build Libraries
- GitHub Check: Lint & Format Checks
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
libs/plugins/src/index.ts (1)
15-18: Exports structure is correct.The barrel exports follow the guidelines—re-exporting from individual plugin packages without backward compatibility aliases.
There was a problem hiding this 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
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/auth/session/transport-session.manager.ts (1)
309-323: Remove unusedadditionalStateparameter or implement proper decryption flow.The
additionalStateparameter inencodeSessionJwtis never passed by callers (line 206 only passessession). Additionally, even if it were passed, the double encryption at lines 313–319 would create encryptedstateandtokensfields that are never decrypted—decryptSessionJwtonly decrypts the outerencryptJsonlayer and discards these fields during the return mapping (lines 350–356).Either remove this unused parameter and code path, or implement the complete decryption and retrieval flow for stateless sessions that include additional state.
🤖 Fix all issues with AI agents
In @libs/sdk/src/auth/session/transport-session.manager.ts:
- Around line 340-344: The encryptJson implementation is using node:crypto
directly which violates project guidelines; replace its low-level AES-256-GCM
logic with the shared helper encryptValue from @frontmcp/utils so its output
matches decryptValue's expected base64url {alg,iv,tag,data} shape; update the
session-id.utils.ts import to pull in encryptValue and remove direct node:crypto
usage inside encryptJson, call encryptValue with the same encryptionKey and JSON
payload, and ensure the returned object still exposes iv, tag, and data in
base64url so decryptValue(payload, this.encryptionKey) continues to work
unchanged.
🧹 Nitpick comments (3)
libs/sdk/src/auth/session/session.service.ts (2)
11-13: Remove unnecessaryasynckeyword.
createSessionis markedasyncbutcreateMcpSessionreturns synchronously. Theasynckeyword adds unnecessary overhead by wrapping the result in a Promise.♻️ Suggested fix
- async createSession(scope: Scope, args: CreateSessionArgs) { + createSession(scope: Scope, args: CreateSessionArgs) { return this.createMcpSession(scope, args); }
26-31: Silent catch may hide legitimate errors.The empty
catchblock silently swallows all errors fromapp.tools.getTools(), defaulting to an empty array. This could mask configuration issues or bugs in tool registration.♻️ Consider logging the error
try { const toolNames = app.tools.getTools().map((t) => String(t.metadata.name)); authorizedApps[app.id] = { id: app.id, toolIds: toolNames }; - } catch { + } catch (err) { + // Log at debug level to aid troubleshooting without breaking session creation + scope.logger?.debug?.('Failed to retrieve tools for app', { appId: app.id, error: err }); authorizedApps[app.id] = { id: app.id, toolIds: [] }; }libs/sdk/src/auth/session/transport-session.manager.ts (1)
359-361: Consider adding debug-level logging for decryption failures.The empty catch block silently returns
nullfor any decryption error. While this is appropriate for security (avoiding information disclosure), it may make debugging difficult.Consider adding a debug-level log when
NODE_ENV !== 'production'to aid troubleshooting invalid session tokens during development.💡 Optional: Add debug logging
- } catch { + } catch (err) { + if (process.env['NODE_ENV'] !== 'production') { + console.debug('[TransportSessionManager] Failed to decrypt session JWT:', err); + } return null; }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
.claude/skills/fix-pr/SKILL.mdlibs/plugins/src/index.tslibs/sdk/src/auth/session/record/session.base.tslibs/sdk/src/auth/session/record/session.mcp.tslibs/sdk/src/auth/session/session.service.tslibs/sdk/src/auth/session/session.types.tslibs/sdk/src/auth/session/transport-session.manager.ts
✅ Files skipped from review due to trivial changes (1)
- .claude/skills/fix-pr/SKILL.md
🚧 Files skipped from review as they are similar to previous changes (1)
- libs/sdk/src/auth/session/record/session.mcp.ts
🧰 Additional context used
📓 Path-based instructions (7)
libs/plugins/**/*.ts
📄 CodeRabbit inference engine (libs/plugins/CLAUDE.md)
libs/plugins/**/*.ts: Extend tool metadata usingdeclare globalpattern to allow tools to specify plugin-specific options in their decorators
Always use@frontmcp/utilsfor cryptographic operations (hkdfSha256, encryptAesGcm, decryptAesGcm, randomBytes, sha256, sha256Hex, base64urlEncode, base64urlDecode) instead ofnode:crypto
RememberPlugin automatically providesthis.rememberandthis.approvalon execution contexts when installed; use helper functionsgetRemember()ortryGetRemember()for graceful degradation
RememberPlugin scopes include:session(default, current session only),user(persists across sessions),tool(tied to specific tool + session), andglobal(shared across all sessions/users)
Configure approval requirements on tools using theapprovaldecorator property with fields:required,defaultScope,category,riskLevel, andapprovalMessage
Avoid usingnode:cryptodirectly; always use@frontmcp/utilsfor cross-platform cryptographic support
Use proper ES module imports instead ofrequire()for SDK imports; avoid dynamic require of@frontmcp/sdkmodules
Always use theoverridekeyword when inheriting and overriding methods from parent classes
Avoid usinganytype without justification in TypeScript files
Never hardcode encryption keys; use environment variables for key management
Do not expose internal error details in public-facing error messages
Files:
libs/plugins/src/index.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
libs/plugins/src/index.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/session/record/session.base.tslibs/sdk/src/auth/session/session.types.tslibs/sdk/src/auth/session/session.service.ts
libs/*/src/index.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases
Files:
libs/plugins/src/index.ts
libs/plugins/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use module augmentation and runtime prototype modification to extend ExecutionContextBase with plugin properties
Files:
libs/plugins/src/index.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/plugins/src/index.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/session/record/session.base.tslibs/sdk/src/auth/session/session.types.tslibs/sdk/src/auth/session/session.service.ts
libs/{sdk,adapters}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
UsegetCapabilities()for dynamic capability exposure instead of hardcoding capabilities
Files:
libs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/session/record/session.base.tslibs/sdk/src/auth/session/session.types.tslibs/sdk/src/auth/session/session.service.ts
libs/sdk/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/sdk/**/*.{ts,tsx}: Preferinterfacefor defining object shapes in TypeScript, avoidanytypes
Use type parameters with constraints instead of unconstrained generics withanydefaults
Validate URIs per RFC 3986 at metadata level usingisValidMcpUrirefinement
UsechangeScopeinstead ofscopein change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g.,Promise<GetPromptResult>) instead ofPromise<unknown>for MCP entry methods
Useunknowninstead ofanyfor generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows
Files:
libs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/session/record/session.base.tslibs/sdk/src/auth/session/session.types.tslibs/sdk/src/auth/session/session.service.ts
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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`
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/plugins/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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:
libs/plugins/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/**/*.{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/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Applied to files:
libs/plugins/src/index.tslibs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/*/src/index.ts : Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases
Applied to files:
libs/plugins/src/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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/plugins/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/bundler/**/*.{ts,tsx} : The bundler module must re-export utilities from frontmcp/uipack/bundler and provide SSR component bundling functionality
Applied to files:
libs/plugins/src/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/plugins/src/index.tslibs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly
Applied to files:
libs/plugins/src/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Applied to files:
libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management
Applied to files:
libs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Use `unknown` instead of `any` for generic type parameter defaults (not for MCP protocol types)
Applied to files:
libs/sdk/src/auth/session/record/session.base.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files
Applied to files:
libs/sdk/src/auth/session/record/session.base.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Return strictly typed MCP protocol responses (e.g., `Promise<GetPromptResult>`) instead of `Promise<unknown>` for MCP entry methods
Applied to files:
libs/sdk/src/auth/session/session.types.tslibs/sdk/src/auth/session/session.service.ts
🧬 Code graph analysis (2)
libs/sdk/src/auth/session/transport-session.manager.ts (3)
libs/utils/src/index.ts (3)
hkdfSha256(77-77)encryptValue(94-94)decryptValue(95-95)libs/utils/src/crypto/index.ts (3)
hkdfSha256(96-104)encryptValue(270-270)decryptValue(271-271)libs/sdk/src/auth/session/transport-session.types.ts (1)
SessionJwtPayload(124-137)
libs/sdk/src/auth/session/session.service.ts (3)
libs/sdk/src/scope/scope.instance.ts (1)
Scope(37-392)libs/sdk/src/auth/session/session.types.ts (1)
CreateSessionArgs(38-55)libs/sdk/src/auth/session/record/session.mcp.ts (1)
McpSession(7-16)
⏰ 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). (4)
- GitHub Check: Build Libraries
- GitHub Check: Lint & Format Checks
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (14)
libs/plugins/src/index.ts (2)
18-21: LGTM!The barrel exports correctly re-export the remaining official plugins using proper ES module syntax. The removal of
@frontmcp/plugin-configaligns with the migration to@frontmcp/sdk.
11-15: Migration note is clear and well-documented.The deprecation guidance for ConfigPlugin is properly communicated, and the referenced documentation at
docs/draft/docs/extensibility/config-yaml.mdxexists and comprehensively covers the migration path. Users moving from@frontmcp/pluginsto@frontmcp/sdkwill have clear examples and configuration guidance. The code change and documentation are properly aligned.libs/sdk/src/auth/session/record/session.base.ts (5)
25-28: LGTM! Type safety improvement aligns with guidelines.Changing
detailsfromRecord<string, any>toRecord<string, unknown>enforces proper type narrowing when accessing these values. Based on learnings, this correctly appliesunknownfor generic type defaults.
40-42: LGTM! Proper typing for extensible claims.Using
unknownfor the index signature enforces type narrowing at access sites, which is the correct pattern for JWT claims where values can be of various types.
61-63: LGTM! Clean removal of type assertion with proper type guard.The
expclaim access now correctly handles theunknowntype via thetypeofcheck instead of using an unsafeas anycast. The property type changes maintain consistency withBaseCreateCtx.Also applies to: 82-85
141-143: LGTM!Formatting improvements for readability with no behavioral changes.
Also applies to: 148-151
169-175: Verify:transportIdgetter returns method reference, not result.While reviewing the adjacent
SessionViewclass, I noticed thattransportIdreturnsthis.parent.getTransportSessionId(a method reference) rather than invoking it. This appears to be a pre-existing issue, but given the refactoring context, verify whether this is intentional or should be:get transportId() { return this.parent.getTransportSessionId(); }Or if async handling is needed:
getTransportId() { return this.parent.getTransportSessionId(); }libs/sdk/src/auth/session/session.types.ts (2)
37-54: Type tightening fromanytounknownis correct.The changes to use
Record<string, unknown>forclaims,authorizedTools.details, andauthorizedPrompts.detailsalign with the coding guidelines requiringunknowninstead ofanyfor generic type parameter defaults. This enforces proper type narrowing at consumption sites.
5-6: Address the SessionMode type fragmentation across the SDK.The original concern about downstream consumers is overstated—only
session.base.tsdirectly imports the narrowedSessionMode = 'mcp'definition. However, there's a more serious issue:SessionModeis defined in three separate locations with conflicting values:
libs/sdk/src/auth/session/session.types.ts:6→'mcp'libs/sdk/src/common/types/options/session.options.ts:5→'stateful' | 'stateless'libs/sdk/src/errors/authorization-required.error.ts:24→'stateful' | 'stateless'Per the coding guidelines ("Centralize record types in common/records and import from there"), consolidate these definitions into a single canonical location.
libs/sdk/src/auth/session/session.service.ts (1)
63-81: MCP session construction looks correct.The
McpSessioninstantiation properly passes all required fields from args and computed values. The fallback patterns forauthorizedResources(line 75) and scopes resolution are sensible defaults.libs/sdk/src/auth/session/transport-session.manager.ts (4)
3-3: LGTM!The import consolidation from
@frontmcp/utilscorrectly follows the project's coding guidelines. Based on learnings, using@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly is the expected pattern.
122-122: LGTM!The type change from
BuffertoUint8Arraycorrectly aligns with thehkdfSha256return type and modern crypto APIs. Since the property isprivate readonly, this is not a breaking change.
154-160: LGTM!The HKDF key derivation correctly uses
TextEncoderto convert strings toUint8Arrayfor cross-platform compatibility. The 32-byte key length is appropriate for AES-256-GCM encryption.
204-207: LGTM!The stateless mode correctly encodes the session as an encrypted JWT, using the JWT string itself as the session ID. This enables stateless session handling where the client carries the encrypted session state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
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/auth/session/transport-session.manager.ts (1)
121-160: Fix key derivation mismatch betweenencryptJsonanddecryptSessionJwt.In stateless mode, sessions are encrypted via
encodeSessionJwt→encryptJson, which usesgetKey()(SHA256-derived key), but decrypted viadecryptSessionJwt, which usesthis.encryptionKey(HKDF-derived key). These keys are different despite the same source material, causing all stateless session round-trips to fail. Either align both to use HKDF derivation with the same context/salts, or passthis.encryptionKeytoencryptJsonso it uses the same key for encryption and decryption.
🤖 Fix all issues with AI agents
In @libs/sdk/src/auth/session/session.service.ts:
- Around line 35-50: The code recomputes both authorizedProviders and
authorizedProviderIds whenever either is missing and builds authorizedAppIds
from appIds (scope.apps.getApps()) ignoring args.authorizedAppIds, causing
mismatches; change the logic so you only derive the missing piece: if
authorizedProviderIds is missing derive it from Object.keys(authorizedProviders)
and if authorizedProviders is missing build it from primary/claims as currently
done, and for apps prefer args.authorizedAppIds when present (authorizedAppIds
??= args.authorizedAppIds ?? appIds) and likewise prefer args.authorizedApps for
authorizedApps, falling back to scope.apps.getApps() only when caller values are
absent; ensure the created provider snapshot still uses primary.id, exp from
args.claims, payload args.claims, and apps built from the chosen
authorizedApps/authorizedAppIds.
In @libs/sdk/src/auth/session/transport-session.manager.ts:
- Around line 203-206: decryptSessionJwt() currently sets and returns session.id
= payload.sid (the decrypted UUID) which mismatches createSession() that uses
the JWT token as session.id; change decryptSessionJwt() to return id = the
original JWT token (the encoded string input), not payload.sid, so getSession()
returns the token form callers expect (ensure the function that takes the token
as input—decryptSessionJwt()/getSession()/sessionExists()—preserves and returns
the raw JWT as session.id rather than the decrypted sid).
In @libs/sdk/src/auth/session/utils/session-id.utils.ts:
- Around line 13-23: The getKey function currently falls back to getMachineId()
when MCP_SESSION_SECRET is unset, which is insecure; update getKey to require
MCP_SESSION_SECRET in non-development/test environments and fail fast (throw an
Error) or at minimum log a clear warning via the module logger when
MCP_SESSION_SECRET is missing, only allowing getMachineId() as a fallback for
explicit dev/test modes (detect via NODE_ENV or an isDev/isTest helper).
Reference the cachedKey variable and getKey/getMachineId functions: on first
call, if process.env['MCP_SESSION_SECRET'] is falsy and NODE_ENV is not
'development'/'test', throw an error stating the missing MCP_SESSION_SECRET and
how to set it; keep the existing sha256(TextEncoder().encode(...)) derivation
when the secret is present and still cache to cachedKey. Also add a short
comment and ensure any thrown error message documents the requirement so callers
fail fast.
🧹 Nitpick comments (2)
libs/sdk/src/auth/session/transport-session.manager.ts (1)
291-302: Public API change:encodeSessionJwt()signature change needs call-site + docs alignment.
This is a publishable SDK (libs/**), andencodeSessionJwtis a public method; removing the optionaladditionalStateparameter is a breaking change unless coordinated.Safer API shape (if you need to preserve backward compatibility)
- encodeSessionJwt(session: TransportSession): string { + encodeSessionJwt(session: TransportSession, _additionalState?: unknown): string { const payload: SessionJwtPayload = { sid: session.id, aid: session.authorizationId, proto: session.protocol, nid: session.nodeId, iat: Math.floor(Date.now() / 1000), exp: session.expiresAt ? Math.floor(session.expiresAt / 1000) : undefined, }; return encryptJson(payload); }libs/sdk/src/auth/session/session.service.ts (1)
8-16: Add explicit return type tocreateMcpSession()to keep strictness.Right now
createMcpSessionrelies on inference; since this is a core SDK surface, it’s worth pinning it toMcpSessionexplicitly.Proposed change
createSession(scope: Scope, args: CreateSessionArgs): McpSession { return this.createMcpSession(scope, args); } - private createMcpSession(scope: Scope, args: CreateSessionArgs) { + private createMcpSession(scope: Scope, args: CreateSessionArgs): McpSession { const primary = scope.auth;
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
libs/sdk/src/auth/session/record/session.base.tslibs/sdk/src/auth/session/session.service.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/session/utils/session-id.utils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- libs/sdk/src/auth/session/record/session.base.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
libs/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/session.service.tslibs/sdk/src/auth/session/transport-session.manager.ts
libs/{sdk,adapters}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
UsegetCapabilities()for dynamic capability exposure instead of hardcoding capabilities
Files:
libs/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/session.service.tslibs/sdk/src/auth/session/transport-session.manager.ts
libs/sdk/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/sdk/**/*.{ts,tsx}: Preferinterfacefor defining object shapes in TypeScript, avoidanytypes
Use type parameters with constraints instead of unconstrained generics withanydefaults
Validate URIs per RFC 3986 at metadata level usingisValidMcpUrirefinement
UsechangeScopeinstead ofscopein change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g.,Promise<GetPromptResult>) instead ofPromise<unknown>for MCP entry methods
Useunknowninstead ofanyfor generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows
Files:
libs/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/session.service.tslibs/sdk/src/auth/session/transport-session.manager.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/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/session.service.tslibs/sdk/src/auth/session/transport-session.manager.ts
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Applied to files:
libs/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Applied to files:
libs/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management
Applied to files:
libs/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Return strictly typed MCP protocol responses (e.g., `Promise<GetPromptResult>`) instead of `Promise<unknown>` for MCP entry methods
Applied to files:
libs/sdk/src/auth/session/session.service.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/session/transport-session.manager.ts
🧬 Code graph analysis (3)
libs/sdk/src/auth/session/utils/session-id.utils.ts (3)
libs/sdk/src/auth/session/encrypted-authorization-vault.ts (1)
getKey(143-150)libs/sdk/src/auth/authorization/authorization.class.ts (1)
getMachineId(19-19)libs/ui/src/bundler/index.ts (1)
sha256(147-147)
libs/sdk/src/auth/session/session.service.ts (2)
libs/sdk/src/auth/session/session.types.ts (1)
CreateSessionArgs(38-55)libs/sdk/src/auth/session/record/session.mcp.ts (1)
McpSession(7-16)
libs/sdk/src/auth/session/transport-session.manager.ts (3)
libs/utils/src/crypto/index.ts (2)
hkdfSha256(96-104)decryptValue(271-271)libs/utils/src/index.ts (2)
hkdfSha256(77-77)decryptValue(95-95)libs/sdk/src/auth/session/transport-session.types.ts (2)
TransportSession(21-45)SessionJwtPayload(124-137)
⏰ 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). (4)
- GitHub Check: Lint & Format Checks
- GitHub Check: Build Libraries
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
libs/sdk/src/auth/session/transport-session.manager.ts (2)
3-3: Good: crypto migrated to@frontmcp/utils(matches repo guidelines).
This aligns with the “don’t usenode:cryptodirectly” guidance. As per coding guidelines.
319-341: Good: non-prod-only debug logging on decrypt failures (avoid leaking in prod).
Looks reasonable; just ensureerrcan’t include sensitive plaintext (it shouldn’t if decrypt fails before parse).libs/sdk/src/auth/session/utils/session-id.utils.ts (2)
2-2: Good: crypto moved to@frontmcp/utilsinstead ofnode:crypto. Based on learnings, this is the right direction for cross-platform crypto support.
25-30: No issues found with encryption/decryption contract usage.
encryptValue()returns{ alg, iv, tag, data }with all fields as base64url-encoded strings, anddecryptValue()is synchronous and returns the decrypted payload directly—both assumptions in lines 25–30 and 36–45 are correct. The code properly constructs the dot-separated transport format inencryptJson()and correctly reconstructs the blob object indecryptSessionJson().
There was a problem hiding this 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
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/auth/session/session.service.ts (1)
31-37: Silent catch block may hide errors.The
catchblock silently swallows all errors when retrieving tool names. This could mask genuine issues like configuration problems or type errors. Consider at minimum logging a warning, or narrowing the catch to expected error types.Suggested improvement
try { const toolNames = app.tools.getTools().map((t) => String(t.metadata.name)); authorizedApps[app.id] = { id: app.id, toolIds: toolNames }; } catch { + // Log for debugging in case of unexpected issues during app registration + console.warn(`[SessionService] Failed to retrieve tools for app ${app.id}, defaulting to empty toolIds`); authorizedApps[app.id] = { id: app.id, toolIds: [] }; }
🧹 Nitpick comments (4)
libs/sdk/src/auth/session/utils/session-id.utils.ts (3)
165-179: Remove dead commented-out code.This commented block references undefined symbols (
MACHINE_ID,decoded,headerSse) and appears to be leftover from a previous implementation. Dead code clutters the codebase and can cause confusion.🧹 Remove dead code
return undefined; - // // Create fresh - - // const decodedSse: SessionIdPayload = { - // nodeId: MACHINE_ID, - // authSig: currentAuthSig, - // uuid: randomUUID(), - // iat: nowSec(), - // }; - // const header = encryptJson(decoded); - // const headerSse = encryptJson(decodedSse); - // cache.set(header, decoded); - // cache.set(headerSse, decodedSse); - // return { header, decoded, headerSse, isNew: true }; }
246-254: Self-referential validation is confusing.Line 247 passes the payload's own
authSigas the expected signature toisValidSessionPayload. This means the signature check (d['authSig'] === sig) will trivially pass if the structure is correct.If the intent is structural validation only, consider extracting a separate
hasValidSessionStructure()function for clarity. If signature verification is intended, this needs an external reference.♻️ Consider clarifying the intent
+function hasValidSessionStructure(dec: unknown): dec is SessionIdPayload { + if (typeof dec !== 'object' || dec === null) return false; + const d = dec as Record<string, unknown>; + return ( + typeof d['nodeId'] === 'string' && + typeof d['authSig'] === 'string' && + typeof d['uuid'] === 'string' && + typeof d['iat'] === 'number' + ); +} // In updateSessionPayload: const decrypted = safeDecrypt(sessionId); - if ( - isValidSessionPayload(decrypted, (decrypted as SessionIdPayload)?.authSig || '') || - isValidPublicSessionPayload(decrypted) - ) { + if (hasValidSessionStructure(decrypted) || isValidPublicSessionPayload(decrypted)) {
214-217: Consider addingSecureflag for production.The cookie lacks the
Secureflag, meaning it will be transmitted over HTTP. For production HTTPS deployments, this should beSecureto prevent session hijacking over unencrypted connections.🔒 Add secure flag option
-export function generateSessionCookie(sessionId: string, ttlInMinutes = 60 * 24): string { +export function generateSessionCookie( + sessionId: string, + ttlInMinutes = 60 * 24, + secure = process.env['NODE_ENV'] === 'production', +): string { const expires = new Date(Date.now() + ttlInMinutes * 60 * 1000).toUTCString(); - return `mcp_session_id=${sessionId}; Path=/; Expires=${expires}; HttpOnly; SameSite=Lax`; + const secureFlag = secure ? '; Secure' : ''; + return `mcp_session_id=${sessionId}; Path=/; Expires=${expires}; HttpOnly; SameSite=Lax${secureFlag}`; }libs/sdk/src/auth/session/session.service.ts (1)
78-81: Clarify the distinction betweenidandsessionId.Using
args.tokenas theidwhile also passing a separatesessionIdmay cause confusion. Consider adding a brief comment explaining the intent, or using a more descriptive identifier construction ifidserves a different purpose thansessionId.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
libs/sdk/src/auth/session/session.service.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/session/utils/session-id.utils.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
libs/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/session/session.service.ts
libs/{sdk,adapters}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
UsegetCapabilities()for dynamic capability exposure instead of hardcoding capabilities
Files:
libs/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/session/session.service.ts
libs/sdk/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/sdk/**/*.{ts,tsx}: Preferinterfacefor defining object shapes in TypeScript, avoidanytypes
Use type parameters with constraints instead of unconstrained generics withanydefaults
Validate URIs per RFC 3986 at metadata level usingisValidMcpUrirefinement
UsechangeScopeinstead ofscopein change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g.,Promise<GetPromptResult>) instead ofPromise<unknown>for MCP entry methods
Useunknowninstead ofanyfor generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows
Files:
libs/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/session/session.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/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/transport-session.manager.tslibs/sdk/src/auth/session/session.service.ts
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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`
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modules
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Applied to files:
libs/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Applied to files:
libs/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management
Applied to files:
libs/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/auth/session/utils/session-id.utils.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/session/utils/session-id.utils.tslibs/sdk/src/auth/session/transport-session.manager.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Return strictly typed MCP protocol responses (e.g., `Promise<GetPromptResult>`) instead of `Promise<unknown>` for MCP entry methods
Applied to files:
libs/sdk/src/auth/session/session.service.ts
🧬 Code graph analysis (2)
libs/sdk/src/auth/session/utils/session-id.utils.ts (3)
libs/sdk/src/auth/session/encrypted-authorization-vault.ts (1)
getKey(143-150)libs/sdk/src/auth/authorization/authorization.class.ts (1)
getMachineId(19-19)libs/ui/src/bundler/index.ts (1)
sha256(147-147)
libs/sdk/src/auth/session/session.service.ts (3)
libs/sdk/src/scope/scope.instance.ts (2)
Scope(37-392)apps(329-331)libs/sdk/src/auth/session/session.types.ts (1)
CreateSessionArgs(38-55)libs/sdk/src/auth/session/record/session.mcp.ts (1)
McpSession(7-16)
⏰ 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). (4)
- GitHub Check: Build Libraries
- GitHub Check: Lint & Format Checks
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (16)
libs/sdk/src/auth/session/utils/session-id.utils.ts (6)
1-8: LGTM! Crypto imports correctly use@frontmcp/utils.The migration from direct
node:cryptousage to@frontmcp/utilsaligns with the coding guidelines and learnings for cross-platform cryptographic support.
13-49: Good security posture with production enforcement.The key derivation logic correctly:
- Enforces
MCP_SESSION_SECRETin production (fail-fast)- Provides appropriate warnings for non-production fallback
- Uses
sha256for 32-byte key derivation (AES-256 compatible)Consider exposing a
clearCachedKey()function for test isolation if not already available elsewhere.
51-56: LGTM!Clean usage of
encryptValuewith consistent output format matching the decryption expectations.
62-71: LGTM!The validation correctly handles malformed input (e.g.,
"iv..data"where segments could be empty strings). ThedecryptValuecall properly specifies the algorithm.
188-212: LGTM!Session creation correctly uses
getMachineId()andrandomUUID()from the appropriate modules. Platform detection handling is sensible.
73-133: LGTM!The validation type guards and decryption helpers are well-structured with appropriate error handling via
safeDecrypt.libs/sdk/src/auth/session/transport-session.manager.ts (6)
3-3: Good alignment with project crypto standards.Using
@frontmcp/utilsfor cryptographic operations (decryptValue,encryptValue,hkdfSha256) follows the project's cross-platform cryptographic conventions. Based on learnings, this is the required approach instead ofnode:cryptodirectly.
120-120: LGTM!The
encryptionKeytype change fromBuffertoUint8Arrayaligns with thehkdfSha256return type from@frontmcp/utils. UsingTextEncoderfor encoding inputs is the modern, cross-platform approach.Also applies to: 152-158
202-205: Stateless session ID design is consistent.The pattern where
session.idbecomes the encrypted JWT in stateless mode is correctly implemented. The original UUID stored inpayload.sidserves as a unique identifier within the encrypted payload, while the JWT itself becomes the externally-visible session ID. This ensures round-trip consistency betweencreateSessionanddecryptSessionJwt.
315-349: LGTM - Decryption implementation is correct.The
decryptSessionJwtmethod correctly:
- Parses the
iv.tag.dataformat matchingencodeSessionJwtoutput- Returns
jwtassession.idmaintaining stateless mode consistency (well documented in comments)- Handles errors gracefully by returning
nullwithout exposing sensitive details- Only logs debug info in non-production environments (good security practice)
136-151: Good secret management with appropriate production safeguards.The secret handling correctly:
- Requires
MCP_SESSION_SECRETor explicitencryptionSecretin production (fails fast)- Warns clearly in development when falling back to machine ID
- Avoids hardcoded secrets (per coding guidelines)
This is a sensible approach for balancing development convenience with production security.
291-304: No action needed. The encryption algorithm is consistent.The
encryptValuefunction from@frontmcp/utilsalways uses AES-256-GCM by default (hardcoded asalg: 'A256GCM'at line 66 ofencrypted-blob.ts); it is not configurable. TheencodeSessionJwtmethod omits the algorithm field from the JWT string because it is redundant, anddecryptSessionJwtcorrectly reconstructs theEncryptedBlobwithalg: 'A256GCM'. Decryption will not fail—the algorithms match.libs/sdk/src/auth/session/session.service.ts (4)
1-4: LGTM!Imports are clean and appropriate for the refactored MCP-centric session creation flow.
6-13: Clean API simplification.The consolidation to a single
McpSessionreturn type is a good architectural decision. The delegation pattern keeps the public API clean while allowing internal flexibility.
67-76: LGTM!Good defensive handling of scope claims with support for both
scopeandscpclaim names, and proper handling of array vs. space/comma-delimited string formats.
40-65: LGTM!The provider snapshot derivation logic correctly handles all cases: using caller-provided values when available, deriving
authorizedProviderIdsfromauthorizedProviderskeys when needed, and building a default snapshot from the primary auth provider as a fallback.
There was a problem hiding this 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 (3)
libs/sdk/src/auth/session/session.service.ts (2)
32-37: Potential issue withString(t.metadata.name)if name is undefined.If
t.metadatais present butnameisundefined,String(t.metadata.name)will produce the literal string"undefined"which could lead to phantom tool IDs. Consider adding a guard or filtering:- const toolNames = app.tools.getTools().map((t) => String(t.metadata.name)); + const toolNames = app.tools.getTools() + .filter((t) => t.metadata?.name != null) + .map((t) => String(t.metadata.name));Additionally,
console.warnin library code can pollute consumer logs. Consider using an injectable logger or suppressing this in production builds.
43-44: Consider importingProviderSnapshotat the top of the file.The inline
import('./session.types').ProviderSnapshotsyntax works but reduces readability. Standard practice is to import types at the top of the file.import { Scope } from '../../scope'; -import { CreateSessionArgs } from './session.types'; +import { CreateSessionArgs, ProviderSnapshot } from './session.types'; import { McpSession } from './record/session.mcp';Then use
ProviderSnapshotdirectly in the code.Also applies to: 55-56
libs/sdk/src/auth/session/utils/session-id.utils.ts (1)
125-133: Redundant type casts after type guard.After
isValidPublicSessionPayload(dec)returns true, TypeScript narrowsdectoSessionIdPayload. The explicit casts are unnecessary.Suggested simplification
const dec = safeDecrypt(sessionId); if (isValidPublicSessionPayload(dec)) { // Cache the decrypted payload for future requests - cache.set(sessionId, dec as SessionIdPayload); - return dec as SessionIdPayload; + cache.set(sessionId, dec); + return dec; }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
libs/sdk/src/auth/session/session.service.tslibs/sdk/src/auth/session/utils/session-id.utils.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
libs/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/session.service.ts
libs/{sdk,adapters}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
UsegetCapabilities()for dynamic capability exposure instead of hardcoding capabilities
Files:
libs/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/session.service.ts
libs/sdk/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/sdk/**/*.{ts,tsx}: Preferinterfacefor defining object shapes in TypeScript, avoidanytypes
Use type parameters with constraints instead of unconstrained generics withanydefaults
Validate URIs per RFC 3986 at metadata level usingisValidMcpUrirefinement
UsechangeScopeinstead ofscopein change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g.,Promise<GetPromptResult>) instead ofPromise<unknown>for MCP entry methods
Useunknowninstead ofanyfor generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows
Files:
libs/sdk/src/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/session.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/auth/session/utils/session-id.utils.tslibs/sdk/src/auth/session/session.service.ts
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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`
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Applied to files:
libs/sdk/src/auth/session/utils/session-id.utils.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/sdk/src/auth/session/utils/session-id.utils.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Applied to files:
libs/sdk/src/auth/session/utils/session-id.utils.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Never hardcode encryption keys; use environment variables for key management
Applied to files:
libs/sdk/src/auth/session/utils/session-id.utils.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/auth/session/utils/session-id.utils.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/session/utils/session-id.utils.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Return strictly typed MCP protocol responses (e.g., `Promise<GetPromptResult>`) instead of `Promise<unknown>` for MCP entry methods
Applied to files:
libs/sdk/src/auth/session/session.service.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). (4)
- GitHub Check: Build Libraries
- GitHub Check: Lint & Format Checks
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (12)
libs/sdk/src/auth/session/session.service.ts (4)
6-13: LGTM!The public
createSessionmethod has a properly typed return ofMcpSession, and the clean delegation to the private method is appropriate for the refactored single-path architecture.
60-60: Verify hardcodedembedMode: 'plain'is intentional.When building the default
providerSnapshot,embedModeis hardcoded to'plain'. Ensure this is the correct default for all MCP session scenarios, or consider deriving it from context/args if it can vary.
69-78: LGTM!The scope resolution logic correctly handles the common JWT conventions where scopes can appear as either a space-delimited string (
scopeclaim), comma-delimited string, or array (scpclaim).
80-86: Verify usingargs.tokenas the sessionidis intentional.The session
idis set toargs.token(Line 81), whilesessionIdis a separate field (Line 82). Iftokenis a JWT, using the full token as an ID could:
- Result in very long IDs (JWTs can be 1KB+)
- Include sensitive claims if logged or stored
If this is intentional for lookup purposes, consider documenting the reasoning. Otherwise, consider using
sessionIdor a hash of the token as the ID.libs/sdk/src/auth/session/utils/session-id.utils.ts (8)
1-8: LGTM!Crypto operations correctly use
@frontmcp/utilsinstead ofnode:cryptodirectly, as per coding guidelines.
25-49: Approve with minor verification note: NODE_ENV case-sensitivity.The key derivation logic and production guardrails are well-implemented. One minor note: the
NODE_ENV === 'production'check is case-sensitive. If NODE_ENV is inadvertently set to'Production'or'PRODUCTION', the machine ID fallback would be used. This follows standard conventions, but consider a case-insensitive check for defense-in-depth.Optional: Case-insensitive NODE_ENV check
- if (nodeEnv === 'production') { + if (nodeEnv?.toLowerCase() === 'production') {
51-71: LGTM!Encryption and decryption correctly use
@frontmcp/utilsprimitives with proper format handling. The caller (safeDecrypt) appropriately handles exceptions.
73-106: LGTM!Good separation of concerns between structural validation (
hasValidSessionStructure) and signature verification (isValidSessionPayload). The type guard pattern is correctly implemented.
155-177: LGTM!The validation flow correctly checks cache first before decryption, and properly returns
undefinedon mismatch or failure, allowing callers to decide whether to create a new session.
186-210: LGTM!Session creation correctly uses
@frontmcp/utilsfor UUID generation and properly handles platform detection with sensible fallback behavior.
232-252: LGTM!The update logic correctly handles both regular and public sessions. The
Object.assignmutation and re-caching to refresh TTL is appropriate for this use case.
212-215: This function is not exported from the SDK public API and is not used anywhere in the codebase.The
generateSessionCookiefunction appears to be unused internal code. The SDK provides a properHttpCookieinterface (withsecureboolean support) andserializeCookiefunction inserver.validation.tsfor handling cookies through the standard response mechanism. If session cookies need to be set, developers should use theHttpCookieobject approach instead of raw cookie strings.Consider removing this dead code or, if it serves a documented purpose, exporting it with proper security controls (including the
Secureflag for production environments).Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🤖 Fix all issues with AI agents
In @apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts:
- Around line 38-39: The comment above the import line is misleading: the
symbols FrontMcp, Tool, ToolContext, Resource, ResourceContext, Prompt,
PromptContext, and App are SDK decorators and context types, not auth type
re-exports; update the comment to accurately describe these as "SDK decorators
and context/context types" (or similar) so it reflects the actual imports, e.g.,
replace "should re-export auth types" with a short note that these are SDK
decorators and context types.
In @apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts:
- Around line 125-141: The test inside the "JWKS Endpoint Compatibility" suite
currently allows non-200 responses to pass silently and asserts jwks.keys.length
>= 0 which is meaningless; update the test (the test titled 'should serve JWKS
endpoint for token verification') to explicitly assert that response.status ===
200 (or call test.skip conditionally if some configurations legitimately don't
expose JWKS) and then, after parsing JSON, assert that jwks.keys is an array and
that jwks.keys.length >= 1 to ensure at least one signing key is present; keep
the failing assertions explicit so regressions are caught.
- Around line 143-160: The test "should serve OAuth authorization server
metadata" inside the "Authorization Metadata Compatibility" describe block
currently treats 404 as an acceptable status in the expect([...]) check; remove
404 from that array so the test fails when the endpoint is missing, or instead
split into two explicit tests: one that asserts the endpoint exists (expect
status in [200,301,302,307,308]) for configurations that must expose
/.well-known/oauth-authorization-server, and a second conditional test (or one
guarded by a config/env flag) that documents and asserts 404 for configurations
that intentionally do not expose it; update the expect call(s) and test names
accordingly (referencing the existing test function and its use of response and
metadata parsing).
In @apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts:
- Around line 126-134: The test name "should provide helpful error messages for
missing required providers" is misleading because the body only calls
mcp.tools.list() and asserts tools contain 'get-app-info' and
'get-request-info'; either rename the test to reflect its behavior (e.g.,
"should list all expected tools when providers are configured") or modify the
test to actually trigger and assert error messages for missing providers by
simulating/omitting a required provider and verifying the error text; update the
test title and/or add assertions around the error scenario involving
mcp.tools.list() or the specific provider methods to match the chosen approach.
In @apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts:
- Around line 88-106: The test 'should delete a vault entry' fails to assert
success on the create call before parsing JSON; add
expect(createResult).toBeSuccessful() immediately after obtaining createResult
(before calling createResult.json<{ entryId: string }>()) so you only parse when
the response succeeded; locate the createResult variable in the test and insert
the assertion there (and apply the same pattern for any other tests that call
.json() like this).
- Around line 63-86: Before calling createResult.json() after
mcp.tools.call('create-vault-entry'), assert the createResult indicates success
(e.g., call the test helper like expect(createResult).toBeSuccessful() or
equivalent) so you only parse JSON on a successful response; update the test
around the create step (the createResult and createResult.json calls) to include
this success assertion and fail early with a clear message if creation did not
succeed.
In @apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-credentials.tool.ts:
- Around line 56-63: In the credentials mapping inside the mapped =
credentials.map(...) expression, the output field createdAt is incorrectly
sourced from c.createdAt; update the mapping to use c.acquiredAt instead (i.e.,
set createdAt to c.acquiredAt) so the produced objects match the AppCredential
schema from @frontmcp/auth and pass validation.
🧹 Nitpick comments (24)
apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts (2)
30-36: Assertion doesn't verify actual singleton behavior.The comment states "Both calls should return the same instance data" but the assertions only verify that both have
"providerScope":"GLOBAL". This doesn't prove they're the same instance. Consider extracting and comparinginstanceIdsimilar to the Provider Lifecycle test.♻️ Suggested improvement
// Both calls should return the same instance data const content1 = JSON.stringify(result1); const content2 = JSON.stringify(result2); - // Verify GLOBAL scope behavior + // Verify GLOBAL scope and singleton behavior expect(content1).toContain('"providerScope":"GLOBAL"'); expect(content2).toContain('"providerScope":"GLOBAL"'); + + // Verify same instance is returned (singleton) + const match1 = content1.match(/"instanceId":"([^"]+)"/); + const match2 = content2.match(/"instanceId":"([^"]+)"/); + expect(match1?.[1]).toBe(match2?.[1]);
145-151: Silent pass when content type is not 'text'.The conditional at line 146 means if
message.content.typeis not'text', the test passes without verifying the multi-provider content. Consider adding an assertion to ensure the expected content type is present, or fail explicitly if it's not.♻️ Suggested fix
const message = result.messages[0]; - if (message.content.type === 'text') { - // Prompt accesses both GLOBAL and CONTEXT providers - expect(message.content.text).toContain('GLOBAL Scope Provider'); - expect(message.content.text).toContain('CONTEXT Scope Provider'); - } + expect(message.content.type).toBe('text'); + if (message.content.type === 'text') { + // Prompt accesses both GLOBAL and CONTEXT providers + expect(message.content.text).toContain('GLOBAL Scope Provider'); + expect(message.content.text).toContain('CONTEXT Scope Provider'); + }apps/e2e/demo-e2e-transparent/e2e/backward-compat.e2e.test.ts (4)
27-39: Consider simplifying the OAuth server initialization pattern.The current approach starts the server, immediately stops it to capture the port, then recreates everything with the actual values. This adds unnecessary latency to test setup.
If
MockOAuthServersupports agetNextAvailablePort()static method or allows port 0 with subsequent resolution, the setup could be consolidated into a single start cycle.
170-189: Strengthen assertions to verify actual claim values.The test is named "should extract standard claims" but only asserts that
client.authis defined. To properly verify backward compatibility of claims extraction, consider asserting the actual claim values:expect(client.isConnected()).toBe(true); // Auth info should be available (client stores it from connection) expect(client.auth).toBeDefined(); + expect(client.auth.sub).toBe('user-claims-test'); + expect(client.auth.email).toBe('claims@example.com'); + expect(client.auth.name).toBe('Claims Test User');
473-516: Consider more specific status code assertions.Using
toBeGreaterThanOrEqual(400)is overly permissive—it would accept 500 (Internal Server Error) when you expect 400/401 (client auth errors). This could mask server bugs.For auth-related failures (empty Bearer, wrong scheme), the expected response should be 401:
- // Server should reject with 4xx or 5xx error for invalid format - expect(response.status).toBeGreaterThanOrEqual(400); + // Server should reject with 401 for invalid/missing token + expect(response.status).toBe(401);If the server implementation genuinely varies, use
expect([400, 401]).toContain(response.status)as done in line 361.
582-622: Test doesn't fully verify session isolation.The test is named "should isolate sessions between different users" but only verifies both users can create tasks independently. It doesn't confirm that user1's tasks are invisible to user2 (and vice versa).
Consider adding isolation verification:
expect(result2).toBeSuccessful(); + // Verify isolation: each user should only see their own tasks + const user1Tasks = await client1.tools.call('list-tasks', {}); + const user2Tasks = await client2.tools.call('list-tasks', {}); + + expect(user1Tasks.text()).toContain('User 1 Task'); + expect(user1Tasks.text()).not.toContain('User 2 Task'); + expect(user2Tasks.text()).toContain('User 2 Task'); + expect(user2Tasks.text()).not.toContain('User 1 Task'); await client1.disconnect();apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts (1)
242-252: Consider adding a negative test case forauthUserSchema.The
credentialTypeSchematest at lines 254-264 includes both valid and invalid input verification, butauthUserSchemaonly tests the valid case. Adding a negative case would improve test consistency and coverage.Suggested enhancement
test('should validate authUser schema', () => { const validUser = { sub: 'user-123', email: 'user@example.com', name: 'Test User', }; const result = authUserSchema.safeParse(validUser); expect(result.success).toBe(true); + + // Test invalid input (missing required field) + const invalidUser = { email: 'user@example.com' }; + const invalidResult = authUserSchema.safeParse(invalidUser); + expect(invalidResult.success).toBe(false); });apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts (5)
201-232: Verify intermediate operations succeed.Lines 211-225 add multiple credentials but don't verify each
add-credentialcall succeeded before checking the aggregate count. If one silently fails, the final assertion'"count":2'will fail without indicating which operation caused the issue.Proposed fix
// Add multiple credentials - await mcp.tools.call('add-credential', { + const addResult1 = await mcp.tools.call('add-credential', { entryId, appId: 'app1', providerId: 'provider1', credentialType: 'api_key', apiKey: 'key1', }); + expect(addResult1).toBeSuccessful(); - await mcp.tools.call('add-credential', { + const addResult2 = await mcp.tools.call('add-credential', { entryId, appId: 'app2', providerId: 'provider2', credentialType: 'bearer', bearerToken: 'token2', }); + expect(addResult2).toBeSuccessful();
257-291: Verify intermediate operations in consent filtering test.Lines 267-281 perform
update-consentandadd-credentialwithout verifying success. This makes debugging harder if the test fails at line 290.Proposed fix
// Set consent for specific app - await mcp.tools.call('update-consent', { + const consentResult = await mcp.tools.call('update-consent', { entryId, enabled: true, selectedToolIds: ['consented-app:tool1'], availableToolIds: ['consented-app:tool1', 'other-app:tool1'], }); + expect(consentResult).toBeSuccessful(); // Add credential for consented app - await mcp.tools.call('add-credential', { + const credResult = await mcp.tools.call('add-credential', { entryId, appId: 'consented-app', providerId: 'provider', credentialType: 'api_key', apiKey: 'allowed-key', }); + expect(credResult).toBeSuccessful();
466-478: Clarify expected behavior for operations on non-existent entries.The comment "may fail or succeed depending on implementation" indicates uncertainty about the expected behavior. E2E tests should document definitive contracts. Either:
- The API should return a specific error response (update assertions accordingly), or
- The API intentionally succeeds silently (document this as intentional idempotent behavior)
Consider adding assertions that verify the specific expected behavior rather than only checking for no crash.
// This may fail or succeed depending on implementation - // The important thing is it doesn't crash - expect(result).toBeSuccessful(); + // API handles non-existent entries gracefully (idempotent design) + expect(result).toBeSuccessful(); + expect(result).toHaveTextContent('not found'); // or whatever the expected message is
522-558: Consider adding expiration validation test.The test stores both expired and non-expired credentials and verifies the count is 2. Consider adding assertions or a separate test that verifies the system correctly identifies/filters expired credentials when retrieving them for actual use, rather than just confirming storage.
605-641: Add success assertions for intermediate operations in persistence test.Lines 616-633 perform multiple operations (
add-credential,update-consent,authorize-app) without verifying each succeeds. If any intermediate operation fails, the final assertions will fail without clear indication of the root cause.Proposed fix
// Add credential - await mcp.tools.call('add-credential', { + const credResult = await mcp.tools.call('add-credential', { entryId, appId: 'persist-app', providerId: 'persist-provider', credentialType: 'bearer', bearerToken: 'persist-token', }); + expect(credResult).toBeSuccessful(); // Update consent - await mcp.tools.call('update-consent', { + const consentResult = await mcp.tools.call('update-consent', { entryId, enabled: true, selectedToolIds: ['persist-app:tool'], availableToolIds: ['persist-app:tool'], }); + expect(consentResult).toBeSuccessful(); // Authorize app - await mcp.tools.call('authorize-app', { entryId, appId: 'persist-app' }); + const authResult = await mcp.tools.call('authorize-app', { entryId, appId: 'persist-app' }); + expect(authResult).toBeSuccessful();apps/e2e/demo-e2e-redis/src/apps/vault/tools/authorize-app.tool.ts (1)
27-39: Missing error handling for vault operations.The
vault.authorizeAppandvault.isAppAuthorizedcalls can throw errors, but there's no try/catch block. This is inconsistent withcreate-pending-auth.tool.tswhich properly handles errors and returns{ success: false, message: ... }.Suggested fix: Add error handling
async execute(input: z.infer<typeof inputSchema>): Promise<z.infer<typeof outputSchema>> { const sessionId = this.getAuthInfo().sessionId ?? 'mock-session-default'; const vault = await getVault(sessionId); + try { await vault.authorizeApp(input.entryId, input.appId); const isAuthorized = await vault.isAppAuthorized(input.entryId, input.appId); return { success: true, isAuthorized, message: `App ${input.appId} is ${isAuthorized ? '' : 'not '}authorized`, }; + } catch (error) { + return { + success: false, + isAuthorized: false, + message: `Failed to authorize app: ${error instanceof Error ? error.message : String(error)}`, + }; + } }apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (2)
31-39: Consider validating that at least one field is provided for update.The current implementation allows calling
vault.updatewith an emptyupdatesobject if neitheruserEmailnoruserNameis provided. This is a no-op that may confuse callers.Optional: Add validation for non-empty updates
const updates: Record<string, unknown> = {}; if (input.userEmail !== undefined) { updates.userEmail = input.userEmail; } if (input.userName !== undefined) { updates.userName = input.userName; } + if (Object.keys(updates).length === 0) { + return { + success: false, + message: 'No fields provided to update', + }; + } + await vault.update(input.entryId, updates);
26-45: Missing error handling for vault.update operation.Similar to other vault tools,
vault.updatecan throw if the entry doesn't exist or another error occurs. Adding try/catch would provide better error feedback to callers.apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts (1)
30-49: Missing error handling for vault.create operation.The
vault.createcall can throw errors (e.g., duplicate entry, storage failure), but unlikeCreatePendingAuthTool, this tool doesn't wrap the operation in a try/catch block.Suggested fix: Add error handling
async execute(input: z.infer<typeof inputSchema>): Promise<z.infer<typeof outputSchema>> { const sessionId = this.getAuthInfo().sessionId ?? 'mock-session-default'; const vault = await getVault(sessionId); + try { const entry = await vault.create({ userSub: input.userSub, userEmail: input.userEmail, userName: input.userName, clientId: input.clientId, }); return { success: true, entryId: entry.id, userSub: entry.userSub, clientId: entry.clientId, message: `Created vault entry ${entry.id} for user ${input.userSub}`, }; + } catch (error) { + return { + success: false, + entryId: '', + userSub: input.userSub, + clientId: input.clientId, + message: `Failed to create vault entry: ${error instanceof Error ? error.message : String(error)}`, + }; + } }apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts (1)
24-35: Missing error handling for vault.delete operation.The
vault.deleteoperation may throw if the entry doesn't exist or a storage error occurs. Consider adding try/catch for consistency withCreatePendingAuthTool.apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
58-64: Consider disconnecting storage adapters inclearAllVaults.The
clearAllVaultsfunction clears the Maps but doesn't calldisconnect()on theMemoryStorageAdapterinstances. While this may be fine for in-memory adapters in E2E tests, it could cause resource leaks if the adapter implementation holds connections or timers.♻️ Suggested improvement
export function clearAllVaults(): void { + // Disconnect all storage adapters before clearing + for (const storage of storageInstances.values()) { + storage.disconnect?.(); + } vaultInstances.clear(); storageInstances.clear(); + initPromises.clear(); }apps/e2e/demo-e2e-redis/e2e/transport-session.e2e.test.ts (1)
13-17: Consider adding test isolation withbeforeEachcleanup.The tests share the same MCP session, so state set in one test may affect subsequent tests. For example,
test('should list all state keys')at Line 73 may pick up keys from earlier tests.For more deterministic tests, consider adding a cleanup hook or using unique key prefixes per test:
test.describe('Transport Session E2E', () => { test.use({ server: 'apps/e2e/demo-e2e-redis/src/main.ts', publicMode: true, }); // If the testing framework supports it: // test.beforeEach(async ({ mcp }) => { // await mcp.tools.call('clear-session-state', {}); // });apps/e2e/demo-e2e-redis/src/apps/vault/tools/add-credential.tool.ts (2)
49-85: Consider validating required fields per credential type.The input schema makes all credential-specific fields optional, but certain fields should be required for each type (e.g.,
accessTokenforoauth,apiKeyforapi_key). While mock fallbacks work for E2E testing, explicit validation would catch misconfigured test inputs earlier.♻️ Optional: Use discriminated union for stricter validation
const inputSchema = z.discriminatedUnion('credentialType', [ z.object({ credentialType: z.literal('oauth'), entryId: z.string(), appId: z.string(), providerId: z.string(), accessToken: z.string(), refreshToken: z.string().optional(), scopes: z.array(z.string()).optional(), expiresAt: z.number().optional(), }), z.object({ credentialType: z.literal('api_key'), entryId: z.string(), appId: z.string(), providerId: z.string(), apiKey: z.string(), headerName: z.string().optional(), }), // ... other types ]);
80-85: Unreachable default case (defensive but acceptable).The
z.enum(['oauth', 'api_key', 'bearer', 'basic'])ensurescredentialTypeis one of the four values, making this default case unreachable at runtime. This is acceptable as defensive coding, but you could add a TypeScript exhaustiveness check.♻️ Optional: Add exhaustiveness check
default: + const _exhaustive: never = input.credentialType; return { success: false, - message: `Unsupported credential type: ${input.credentialType}`, + message: `Unsupported credential type: ${_exhaustive}`, };apps/e2e/demo-e2e-redis/src/apps/transport/tools/check-session.tool.ts (1)
11-20: Output schemasessionIdis declared nullable but never returns null.The
sessionIdfield is typed asz.string().nullable(), but line 30 ensures it's always a non-null string via the?? 'mock-session-default'fallback. Consider updating the schema toz.string()for accuracy.♻️ Proposed fix
const outputSchema = z .object({ success: z.boolean(), - sessionId: z.string().nullable(), + sessionId: z.string(), stateFound: z.boolean(), stateValue: z.string().nullable(), allStateKeys: z.array(z.string()), message: z.string(), }) .strict();apps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.ts (1)
31-43: Missing error handling for vault operations.Unlike
CreatePendingAuthTool(which wraps vault operations in try-catch and returns a structured error response), this tool lets exceptions propagate. For consistency and better E2E test diagnostics, consider adding error handling.♻️ Proposed fix
async execute(input: z.infer<typeof inputSchema>): Promise<z.infer<typeof outputSchema>> { const sessionId = this.getAuthInfo().sessionId ?? 'mock-session-default'; const vault = await getVault(sessionId); + try { if (input.action === 'complete') { await vault.completePendingAuth(input.entryId, input.pendingAuthId); return { success: true, message: `Completed pending auth ${input.pendingAuthId}`, }; } else { await vault.cancelPendingAuth(input.entryId, input.pendingAuthId); return { success: true, message: `Cancelled pending auth ${input.pendingAuthId}`, }; } + } catch (error) { + return { + success: false, + message: `Failed to ${input.action} pending auth: ${error instanceof Error ? error.message : String(error)}`, + }; + } }apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-credentials.tool.ts (1)
44-54:providerIdis silently ignored whenappIdis not provided.The schema description states "Filter by provider ID (requires appId)", but the code doesn't enforce this. If a caller provides only
providerId, it falls through togetAllCredentials, ignoring the filter.♻️ Option 1: Enforce via schema refinement
const inputSchema = z .object({ entryId: z.string().describe('Vault entry ID'), appId: z.string().optional().describe('Filter by application ID'), providerId: z.string().optional().describe('Filter by provider ID (requires appId)'), }) - .strict(); + .strict() + .refine( + (data) => !data.providerId || data.appId, + { message: 'providerId requires appId to be specified' } + );♻️ Option 2: Return error in execute
async execute(input: z.infer<typeof inputSchema>): Promise<z.infer<typeof outputSchema>> { const sessionId = this.getAuthInfo().sessionId ?? 'mock-session-default'; const vault = await getVault(sessionId); + if (input.providerId && !input.appId) { + return { + success: false, + credentials: [], + count: 0, + message: 'providerId requires appId to be specified', + }; + } + let credentials;Note: Option 2 requires adding
success: falsehandling in the output schema or changing it to allow failures.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (52)
apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.tsapps/e2e/demo-e2e-config/jest.e2e.config.tsapps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.tsapps/e2e/demo-e2e-orchestrated/jest.e2e.config.tsapps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.tsapps/e2e/demo-e2e-providers/jest.e2e.config.tsapps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.tsapps/e2e/demo-e2e-redis/e2e/transport-session.e2e.test.tsapps/e2e/demo-e2e-redis/jest.e2e.config.tsapps/e2e/demo-e2e-redis/src/apps/sessions/resources/session-current.resource.tsapps/e2e/demo-e2e-redis/src/apps/sessions/tools/get-session-data.tool.tsapps/e2e/demo-e2e-redis/src/apps/sessions/tools/set-session-data.tool.tsapps/e2e/demo-e2e-redis/src/apps/transport/index.tsapps/e2e/demo-e2e-redis/src/apps/transport/tools/check-session.tool.tsapps/e2e/demo-e2e-redis/src/apps/transport/tools/session-info.tool.tsapps/e2e/demo-e2e-redis/src/apps/transport/tools/session-isolation.tool.tsapps/e2e/demo-e2e-redis/src/apps/transport/tools/update-session-state.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.tsapps/e2e/demo-e2e-redis/src/apps/vault/index.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/add-credential.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/authorize-app.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/create-pending-auth.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/get-credentials.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/update-consent.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.tsapps/e2e/demo-e2e-redis/src/main.tsapps/e2e/demo-e2e-transparent/e2e/backward-compat.e2e.test.tsapps/e2e/demo-e2e-transparent/jest.e2e.config.tsapps/e2e/demo-e2e-transparent/src/apps/tasks/tools/create-task.tool.tsapps/e2e/demo-e2e-transparent/src/apps/tasks/tools/list-tasks.tool.tslibs/sdk/src/common/interfaces/execution-context.interface.tslibs/sdk/src/context/__tests__/frontmcp-context-storage.test.tslibs/sdk/src/context/__tests__/frontmcp-context.test.tslibs/sdk/src/context/__tests__/metadata.utils.test.tslibs/sdk/src/context/__tests__/session-key.test.tslibs/sdk/src/context/__tests__/trace-context.test.tslibs/sdk/src/context/frontmcp-context-storage.tslibs/sdk/src/context/frontmcp-context.provider.tslibs/sdk/src/context/frontmcp-context.tslibs/sdk/src/context/index.tslibs/sdk/src/context/metadata.utils.tslibs/sdk/src/context/request-context-storage.tslibs/sdk/src/context/request-context.provider.tslibs/sdk/src/context/request-context.tslibs/sdk/src/context/session-key.provider.tslibs/sdk/src/provider/__tests__/provider-session-scope.test.tslibs/sdk/src/provider/provider.registry.tslibs/sdk/src/scope/scope.instance.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/e2e/demo-e2e-transparent/jest.e2e.config.ts
- apps/e2e/demo-e2e-redis/jest.e2e.config.ts
- apps/e2e/demo-e2e-providers/jest.e2e.config.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
apps/e2e/demo-e2e-redis/e2e/transport-session.e2e.test.tsapps/e2e/demo-e2e-transparent/src/apps/tasks/tools/create-task.tool.tsapps/e2e/demo-e2e-redis/src/apps/sessions/tools/set-session-data.tool.tsapps/e2e/demo-e2e-redis/src/apps/transport/index.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.tsapps/e2e/demo-e2e-redis/src/apps/transport/tools/check-session.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/get-credentials.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/index.tsapps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.tsapps/e2e/demo-e2e-redis/src/apps/sessions/tools/get-session-data.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/create-pending-auth.tool.tsapps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.tsapps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.tsapps/e2e/demo-e2e-redis/src/apps/transport/tools/update-session-state.tool.tsapps/e2e/demo-e2e-redis/src/apps/transport/tools/session-info.tool.tsapps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.tsapps/e2e/demo-e2e-transparent/e2e/backward-compat.e2e.test.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/update-consent.tool.tsapps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/add-credential.tool.tsapps/e2e/demo-e2e-redis/src/apps/sessions/resources/session-current.resource.tsapps/e2e/demo-e2e-transparent/src/apps/tasks/tools/list-tasks.tool.tsapps/e2e/demo-e2e-config/jest.e2e.config.tsapps/e2e/demo-e2e-redis/src/main.tsapps/e2e/demo-e2e-redis/src/apps/transport/tools/session-isolation.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/authorize-app.tool.tsapps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Do not use prefixes like 'PT-001' in test names
Do not skip constructor validation tests for error classes and types
Includeinstanceofchecks in tests for error classes to verify proper error hierarchy
Files:
apps/e2e/demo-e2e-redis/e2e/transport-session.e2e.test.tsapps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.tsapps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.tsapps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.tsapps/e2e/demo-e2e-transparent/e2e/backward-compat.e2e.test.tsapps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts
🧠 Learnings (24)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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-redis/e2e/transport-session.e2e.test.tsapps/e2e/demo-e2e-redis/src/apps/transport/index.tsapps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.tsapps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.tsapps/e2e/demo-e2e-transparent/e2e/backward-compat.e2e.test.tsapps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.tsapps/e2e/demo-e2e-config/jest.e2e.config.tsapps/e2e/demo-e2e-orchestrated/jest.e2e.config.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:
apps/e2e/demo-e2e-redis/src/apps/transport/index.tsapps/e2e/demo-e2e-redis/src/apps/vault/index.tsapps/e2e/demo-e2e-redis/src/main.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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:
apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.tsapps/e2e/demo-e2e-config/jest.e2e.config.tsapps/e2e/demo-e2e-redis/src/main.tsapps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Applied to files:
apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.tsapps/e2e/demo-e2e-redis/src/main.tsapps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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:
apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.tsapps/e2e/demo-e2e-config/jest.e2e.config.tsapps/e2e/demo-e2e-redis/src/main.tsapps/e2e/demo-e2e-orchestrated/jest.e2e.config.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:
apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.tsapps/e2e/demo-e2e-config/jest.e2e.config.tsapps/e2e/demo-e2e-orchestrated/jest.e2e.config.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:
apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Applied to files:
apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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:
apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.tsapps/e2e/demo-e2e-config/jest.e2e.config.tsapps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{renderers,handlebars,utils}/**/*.{test,spec}.{ts,tsx,js,jsx} : Test HTML escaping for user-provided content to prevent XSS vulnerabilities
Applied to files:
apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directly
Applied to files:
apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.tsapps/e2e/demo-e2e-config/jest.e2e.config.tsapps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/*.{test,spec}.{ts,tsx,js,jsx} : Every component and utility must test invalid inputs and edge cases
Applied to files:
apps/e2e/demo-e2e-config/e2e/cross-package-imports.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/**/*.test.{ts,tsx} : SSR components must be tested for correct HTML output and client-side hydration must be tested separately
Applied to files:
apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Applied to files:
apps/e2e/demo-e2e-redis/src/apps/transport/tools/session-info.tool.tsapps/e2e/demo-e2e-redis/src/apps/transport/tools/session-isolation.tool.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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:
apps/e2e/demo-e2e-redis/src/apps/transport/tools/session-info.tool.tsapps/e2e/demo-e2e-redis/src/apps/transport/tools/session-isolation.tool.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Applied to files:
apps/e2e/demo-e2e-config/jest.e2e.config.tsapps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.test.{ts,tsx} : Do not use prefixes like 'PT-001' in test names
Applied to files:
apps/e2e/demo-e2e-config/jest.e2e.config.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/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:
apps/e2e/demo-e2e-config/jest.e2e.config.tsapps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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:
apps/e2e/demo-e2e-config/jest.e2e.config.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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:
apps/e2e/demo-e2e-config/jest.e2e.config.tsapps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Applies to libs/uipack/**/{src,tests}/**/*.{ts,tsx,js,jsx} : Maintain 95%+ code coverage across statements, branches, functions, and lines
Applied to files:
apps/e2e/demo-e2e-config/jest.e2e.config.tsapps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Configure approval requirements on tools using the `approval` decorator property with fields: `required`, `defaultScope`, `category`, `riskLevel`, and `approvalMessage`
Applied to files:
apps/e2e/demo-e2e-redis/src/apps/vault/tools/authorize-app.tool.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/**/*.test.{ts,tsx} : Test coverage must be 95%+ across statements, branches, functions, and lines
Applied to files:
apps/e2e/demo-e2e-orchestrated/jest.e2e.config.ts
🧬 Code graph analysis (17)
apps/e2e/demo-e2e-transparent/src/apps/tasks/tools/create-task.tool.ts (2)
libs/sdk/src/common/interfaces/execution-context.interface.ts (1)
authInfo(89-91)libs/sdk/src/context/frontmcp-context.ts (1)
authInfo(244-246)
apps/e2e/demo-e2e-redis/src/apps/transport/index.ts (1)
apps/e2e/demo-e2e-redis/src/apps/vault/index.ts (1)
App(13-29)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.ts (3)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts (1)
Tool(24-50)apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (1)
Tool(20-46)apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
getVault(17-49)
apps/e2e/demo-e2e-redis/src/apps/transport/tools/check-session.tool.ts (3)
apps/e2e/demo-e2e-redis/src/apps/sessions/tools/get-session-data.tool.ts (1)
Tool(17-37)apps/e2e/demo-e2e-redis/src/apps/sessions/tools/set-session-data.tool.ts (1)
Tool(21-41)apps/e2e/demo-e2e-redis/src/apps/transport/tools/update-session-state.tool.ts (1)
Tool(21-43)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-credentials.tool.ts (3)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/add-credential.tool.ts (1)
Tool(36-103)apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.ts (1)
Tool(34-74)apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
getVault(17-49)
apps/e2e/demo-e2e-redis/src/apps/vault/index.ts (1)
apps/e2e/demo-e2e-redis/src/apps/transport/index.ts (1)
App(7-12)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (5)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/add-credential.tool.ts (1)
Tool(36-103)apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts (1)
Tool(24-50)apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts (1)
Tool(18-36)apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.ts (1)
Tool(34-74)apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-consent.tool.ts (1)
Tool(22-48)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.ts (2)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-pending-auth.tool.ts (1)
Tool(25-58)apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
getVault(17-49)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts (1)
apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
getVault(17-49)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-pending-auth.tool.ts (4)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts (1)
Tool(18-36)apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.ts (1)
Tool(34-74)apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (1)
Tool(20-46)apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
getVault(17-49)
apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts (1)
libs/sdk/src/scope/scope.instance.ts (1)
tools(333-335)
apps/e2e/demo-e2e-redis/src/apps/transport/tools/update-session-state.tool.ts (3)
apps/e2e/demo-e2e-redis/src/apps/sessions/tools/set-session-data.tool.ts (1)
Tool(21-41)apps/e2e/demo-e2e-redis/src/apps/transport/tools/check-session.tool.ts (1)
Tool(22-55)apps/e2e/demo-e2e-redis/src/apps/transport/tools/session-info.tool.ts (1)
Tool(20-44)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts (4)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts (1)
Tool(24-50)apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.ts (1)
Tool(34-74)apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (1)
Tool(20-46)apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
getVault(17-49)
apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts (2)
libs/sdk/src/scope/scope.instance.ts (4)
auth(313-315)tools(333-335)resources(341-343)prompts(345-347)libs/sdk/src/context/frontmcp-context.ts (1)
fetch(467-502)
apps/e2e/demo-e2e-transparent/src/apps/tasks/tools/list-tasks.tool.ts (2)
libs/sdk/src/common/interfaces/execution-context.interface.ts (1)
authInfo(89-91)libs/sdk/src/context/frontmcp-context.ts (1)
authInfo(244-246)
apps/e2e/demo-e2e-redis/src/main.ts (12)
apps/e2e/demo-e2e-public/src/main.ts (1)
FrontMcp(6-15)apps/e2e/demo-e2e-agents/src/main.ts (1)
FrontMcp(6-25)apps/e2e/demo-e2e-cache/src/main.ts (1)
FrontMcp(6-25)apps/e2e/demo-e2e-codecall/src/main.ts (1)
FrontMcp(6-25)apps/e2e/demo-e2e-errors/src/main.ts (1)
FrontMcp(6-25)apps/e2e/demo-e2e-notifications/src/main.ts (1)
FrontMcp(6-25)apps/e2e/demo-e2e-openapi/src/main.ts (1)
FrontMcp(6-25)apps/e2e/demo-e2e-providers/src/main.ts (1)
FrontMcp(6-25)apps/e2e/demo-e2e-remember/src/main.ts (1)
FrontMcp(6-25)apps/e2e/demo-e2e-remote/src/main.ts (1)
FrontMcp(8-55)apps/e2e/demo-e2e-serverless/src/main.ts (1)
FrontMcp(6-25)apps/e2e/demo-e2e-ui/src/main.ts (1)
FrontMcp(6-25)
apps/e2e/demo-e2e-redis/src/apps/transport/tools/session-isolation.tool.ts (2)
apps/e2e/demo-e2e-redis/src/apps/sessions/tools/set-session-data.tool.ts (1)
Tool(21-41)apps/e2e/demo-e2e-redis/src/apps/transport/tools/check-session.tool.ts (1)
Tool(22-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). (4)
- GitHub Check: Build Libraries
- GitHub Check: Lint & Format Checks
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
There was a problem hiding this 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
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/auth/session/__tests__/redis-session.store.test.ts (1)
580-597: Add test coverage for rate limiting and signature verification features.Verification confirms that the
redis-session.store.tsimplementation includes two critical features that lack test coverage inredis-session.store.test.ts:
Rate Limiting: Initialized in the constructor (lines 61-64) using
SessionRateLimiterwhensecurity.enableRateLimitingis true, but no tests cover this functionality.Signature Verification: Used in both the
get()method (lines 173-177) andset()method (lines 265-266) viaverifyOrParseSession()andsignSession()whensecurity.enableSigningis true, but no tests exercise these paths.These are core security features that must be tested to meet the 95%+ code coverage requirement and ensure proper behavior when signing and rate limiting are enabled.
🧹 Nitpick comments (2)
libs/sdk/src/auth/session/__tests__/redis-session.store.test.ts (2)
116-123: Consider using a properly typed mock instead of{} as never.Using
as neveris a type escape hatch that circumvents TypeScript's type checking. While it avoidsany, it still bypasses type safety. Consider creating a minimal typed mock object or using a library likejest-mock-extendedfor proper Redis mock typing.♻️ Suggested improvement
- const mockRedis = {} as never; + // Create a minimal mock satisfying the Redis interface requirements + const mockRedis = { + status: 'ready', + disconnect: jest.fn(), + } as unknown as Redis;This provides slightly better documentation of the mock's intended shape while still allowing the test to compile.
203-212: Avoid non-null assertion - use proper type guard instead.Line 211 uses
result!.lastAccessedAtwhich violates the coding guidelines that state to avoid non-null assertions (!) and use proper error handling and type guards instead.♻️ Proposed fix
const result = await store.get('test-session-id'); expect(result).not.toBeNull(); - expect(result!.lastAccessedAt).toBeGreaterThanOrEqual(originalLastAccessed); + expect(result?.lastAccessedAt).toBeGreaterThanOrEqual(originalLastAccessed);Using optional chaining (
?.) is safer here. Since the assertionexpect(result).not.toBeNull()runs first, the test will fail appropriately ifresultis null, and the optional chaining prevents potential runtime issues while satisfying TypeScript.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
libs/sdk/src/auth/session/__tests__/redis-session.store.test.tslibs/sdk/src/auth/session/index.tslibs/sdk/src/auth/session/redis-session.store.tslibs/sdk/src/auth/session/session-store.factory.tslibs/sdk/src/auth/session/vercel-kv-session.store.tslibs/sdk/src/store/adapters/store.base.adapter.tslibs/sdk/src/store/adapters/store.memory.adapter.tslibs/sdk/src/store/adapters/store.redis.adapter.tslibs/sdk/src/store/adapters/store.vercel-kv.adapter.tslibs/sdk/src/store/index.tslibs/sdk/src/store/store.helpers.tslibs/sdk/src/store/store.registry.tslibs/sdk/src/store/store.tokens.tslibs/sdk/src/store/store.types.tslibs/sdk/src/store/store.utils.tslibs/sdk/src/transport/transport.registry.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
libs/sdk/src/auth/session/index.tslibs/sdk/src/auth/session/__tests__/redis-session.store.test.ts
libs/{sdk,adapters}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
UsegetCapabilities()for dynamic capability exposure instead of hardcoding capabilities
Files:
libs/sdk/src/auth/session/index.tslibs/sdk/src/auth/session/__tests__/redis-session.store.test.ts
libs/sdk/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/sdk/**/*.{ts,tsx}: Preferinterfacefor defining object shapes in TypeScript, avoidanytypes
Use type parameters with constraints instead of unconstrained generics withanydefaults
Validate URIs per RFC 3986 at metadata level usingisValidMcpUrirefinement
UsechangeScopeinstead ofscopein change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g.,Promise<GetPromptResult>) instead ofPromise<unknown>for MCP entry methods
Useunknowninstead ofanyfor generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows
Files:
libs/sdk/src/auth/session/index.tslibs/sdk/src/auth/session/__tests__/redis-session.store.test.ts
libs/**
⚙️ CodeRabbit configuration file
libs/**: Contains publishable SDK libraries. Review for API correctness, breaking changes, and consistency with docs. When public APIs change, ensure there is a matching docs/draft/docs/** update (not direct edits under docs/docs/**).
Files:
libs/sdk/src/auth/session/index.tslibs/sdk/src/auth/session/__tests__/redis-session.store.test.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Do not use prefixes like 'PT-001' in test names
Do not skip constructor validation tests for error classes and types
Includeinstanceofchecks in tests for error classes to verify proper error hierarchy
Files:
libs/sdk/src/auth/session/__tests__/redis-session.store.test.ts
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/auth/session/index.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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/auth/session/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files
Applied to files:
libs/sdk/src/auth/session/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/*/src/index.ts : Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliases
Applied to files:
libs/sdk/src/auth/session/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/{sdk,adapters}/**/*.{ts,tsx} : Use `getCapabilities()` for dynamic capability exposure instead of hardcoding capabilities
Applied to files:
libs/sdk/src/auth/session/index.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Applied to files:
libs/sdk/src/auth/session/index.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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:
libs/sdk/src/auth/session/__tests__/redis-session.store.test.ts
🧬 Code graph analysis (1)
libs/sdk/src/auth/session/__tests__/redis-session.store.test.ts (1)
libs/sdk/src/auth/session/redis-session.store.ts (1)
RedisSessionStore(35-345)
⏰ 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). (4)
- GitHub Check: Build Libraries
- GitHub Check: Lint & Format Checks
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (8)
libs/sdk/src/auth/session/__tests__/redis-session.store.test.ts (6)
10-31: LGTM!The mock setup is well-structured. Using
jest.requireActualto preserve other exports while mocking onlyRedisStorageAdapteris the correct approach for partial mocking. The separation ofmockStorageAdapterfor adapter methods andmockRedisClientfor direct Redis operations (GETEX) aligns with the implementation architecture.
241-292: LGTM!The set() tests properly verify:
- TTL conversion from milliseconds (60000) to seconds (60)
- Fallback to session's
expiresAtwhen no TTL is explicitly provided- Storage without TTL when neither explicit TTL nor
expiresAtis present- Proper JSON serialization
The assertions correctly reflect the adapter-based architecture where TTL is specified in seconds via
{ ttlSeconds }options.
305-315: LGTM!The test correctly verifies that the session ID is passed to the adapter without manual prefix application. The comment at lines 313-314 appropriately clarifies that key prefix handling is delegated to the adapter, which aligns with the architectural shift from direct Redis calls to adapter-based storage.
418-430: LGTM!The
getRedisClient()tests cover both the success case (returning the client from the adapter) and the edge case (returning undefined when the adapter has no client). This provides good coverage for the delegation pattern.
446-453: Good error handling coverage.The tests for empty sessionId rejection properly verify that all CRUD operations throw with the expected error message
'[RedisSessionStore] sessionId cannot be empty'. This ensures consistent validation across the API surface.
401-411: No action required—mock isolation is properly handled.The
beforeEachhook callsjest.clearAllMocks()before each test runs, ensuring the external connection test starts with a clean mock state. WhenexternalStore.disconnect()is called on line 407, the implementation correctly checksif (!this.externalInstance)(line 319 of the implementation) and skips the adapter call since the instance was created with an external Redis client. The assertion at line 410 is correct and the test isolation works as intended.libs/sdk/src/auth/session/index.ts (2)
27-47: Good consolidation - re-exporting authorization APIs from @frontmcp/auth.This properly maintains backward compatibility for SDK consumers by re-exporting the authorization store functionality from the new
@frontmcp/authpackage. The clean separation between value exports and type exports usingexport typeis correct.The organized grouping (Classes, Functions, Schemas, Types) with comments aids readability. All exported symbols are correctly defined in
@frontmcp/auth.
7-8: Replace genericErrorwith MCP-specific error class at line 114.The
createSessionStoreSyncfunction throws a genericErrorwhen Vercel KV provider is used, but should use a specific MCP error class per the libs/sdk guidelines. Replace withPublicMcpError(or appropriate public/internal error type) with an MCP error code:throw new PublicMcpError('Vercel KV session store requires async initialization. Use createSessionStore() instead.', 'INVALID_SESSION_STORE_CONFIGURATION', 400);The factory functions themselves are well-implemented with proper type safety and lazy loading optimization. The exports in index.ts lines 7-8 are clean and correctly structured.
⛔ Skipped due to learnings
Learnt from: CR Repo: agentfront/frontmcp PR: 0 File: libs/plugins/CLAUDE.md:0-0 Timestamp: 2026-01-06T02:34:55.689Z Learning: Applies to libs/plugins/**/*.ts : Use proper ES module imports instead of `require()` for SDK imports; avoid dynamic require of `frontmcp/sdk` modulesLearnt from: CR Repo: agentfront/frontmcp PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-01-06T17:16:04.304Z Learning: Applies to libs/{sdk,adapters}/**/*.{ts,tsx} : Use specific error classes with MCP error codes instead of generic errorsLearnt from: CR Repo: agentfront/frontmcp PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-01-06T17:16:04.304Z Learning: Applies to libs/*/src/index.ts : Export everything users need through barrel exports (index.ts), do not add backwards compatibility aliasesLearnt from: CR Repo: agentfront/frontmcp PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-01-06T17:16:04.304Z Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directlyLearnt from: CR Repo: agentfront/frontmcp PR: 0 File: libs/plugins/CLAUDE.md:0-0 Timestamp: 2026-01-06T02:34:55.689Z Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic supportLearnt from: CR Repo: agentfront/frontmcp PR: 0 File: libs/uipack/CLAUDE.md:0-0 Timestamp: 2026-01-04T14:35:18.366Z Learning: Applies to libs/uipack/**/{src}/**/*.{ts,tsx,js,jsx} : Do not expose internal error details in public APIs - use sanitized error messagesLearnt from: CR Repo: agentfront/frontmcp PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-01-06T17:16:04.304Z Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for file system operations instead of `fs/promises` or `node:fs` directlyLearnt from: CR Repo: agentfront/frontmcp PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-01-06T17:16:04.304Z Learning: Applies to libs/sdk/**/*.{ts,tsx} : Return strictly typed MCP protocol responses (e.g., `Promise<GetPromptResult>`) instead of `Promise<unknown>` for MCP entry methodsLearnt from: CR Repo: agentfront/frontmcp PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-01-06T17:16:04.304Z Learning: Applies to libs/sdk/**/*.{ts,tsx} : Centralize record types in common/records and import from there, not from module-specific files
…mprove test coverage
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In @apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts:
- Around line 121-129: The test named 'should handle missing optional providers
gracefully' is misleading because ConfigApp always registers AppConfigProvider
and RequestLoggerProvider under publicMode: true and the test only calls
mcp.tools.call('get-app-info'); either rename the test to reflect it is a basic
smoke test (e.g., 'should work with all providers available') or add a new test
that constructs ConfigApp without AppConfigProvider/RequestLoggerProvider (or
toggles their registration) and then calls mcp.tools.call('get-app-info') to
assert graceful degradation; update the test title or add the new test
accordingly and keep the original smoke-check if desired.
In @apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts:
- Around line 261-296: The test omits validating the result of the
update-consent RPC call; capture the response from
mcp.tools.call('update-consent') and assert it succeeded before continuing
(e.g., assert success and expected payload fields like
selectedToolIds/availableToolIds or an updated consent flag) so that adding
credentials and later assertions only run when consent was actually updated;
update the test "should filter credentials based on consent" to check the
update-consent call result (the call named 'update-consent' and its response)
and fail early if it indicates an error or unexpected state.
- Around line 112-146: Add an assertion that the vault entry creation succeeded
before extracting entryId: after each call that assigns createResult (the
mcp.tools.call('create-vault-entry') calls used in the tests that extract
entryId), assert createResult.toBeSuccessful() (or the equivalent test assertion
helper used elsewhere) immediately before calling createResult.json() and
reading entryId; apply this change in the current test (where createResult is
extracted) and the other credential tests that extract entryId (the same pattern
at the other createResult occurrences referenced in the review).
- Around line 610-646: The test "should persist vault entries across multiple
tool calls" currently performs several mcp.tools.call operations without
verifying each call succeeded; add immediate assertions after createResult and
after the calls to 'add-credential', 'update-consent', and 'authorize-app' to
validate their responses (e.g., check status/success flags, returned IDs, or
expected JSON fields) so failures surface at the exact operation (use the
existing createResult, the add/credential/update-consent/authorize-app call
results, and get-vault-entry to assert expected values).
In @apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts:
- Around line 61-71: The clearAllVaults function fails to clear in-flight
initialization trackers (initPromises) so resolved inits can repopulate
vaultInstances/storageInstances after clearing; fix by snapshotting current
initPromises (Array.from(initPromises.values())), immediately clearing
initPromises (initPromises.clear()) to mark them cancelled, then await
Promise.all on the snapshot to let any in-flight work finish before
disconnecting storage and finally clear vaultInstances and storageInstances;
update clearAllVaults to reference initPromises, vaultInstances,
storageInstances and the existing disconnect logic accordingly.
🧹 Nitpick comments (8)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (1)
31-40: Consider validating that at least one update field is provided.If both
userEmailanduserNameareundefined, theupdatesobject will be empty, resulting in a potentially unnecessary or confusing vault update call. Consider returning an early error or success message indicating no changes were requested.Suggested validation
try { const updates: Record<string, unknown> = {}; if (input.userEmail !== undefined) { updates.userEmail = input.userEmail; } if (input.userName !== undefined) { updates.userName = input.userName; } + if (Object.keys(updates).length === 0) { + return { + success: false, + message: 'No fields to update. Provide at least userEmail or userName.', + }; + } + await vault.update(input.entryId, updates);apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts (3)
33-52: Consider adding error handling forjson()parsing.At line 43, if
createResultdoesn't contain valid JSON or the expected structure,json<{ entryId: string }>()could throw or return undefined. While line 40 asserts success, consider defensive handling or type guards for robustness.💡 Suggested improvement
expect(createResult).toBeSuccessful(); // Extract entry ID from response using json() method const parsed = createResult.json<{ entryId: string }>(); + expect(parsed.entryId).toBeDefined(); const entryId = parsed.entryId;
442-525: Consider making error handling assertions more deterministic.Several tests in this section have comments like "depends on implementation" (line 449, 480) or "may fail or succeed" (line 523). While testing that operations don't crash is valuable, these tests could be more useful if they verified the specific expected behavior. If the behavior is undefined, consider documenting the expected contract first.
Example improvements:
- Line 449-450: If update on non-existent entry should be a no-op, assert the response indicates this
- Line 480-482: If adding credential to non-existent vault should fail, assert the failure message
- Line 523: Define whether completing non-existent pending auth should return an error
527-563: Good edge case coverage for credential expiration states.Testing credentials with past expiration (line 543) and far-future expiration (line 555) is valuable. However, consider adding a test that verifies the system's behavior when retrieving expired credentials - does it filter them out or return them with an expiration flag?
apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
31-45: Init failure leaves stale rejected promise in cache.If
storage.connect()or vault construction throws,initPromises.delete(sessionId)at line 43 won't execute. Subsequent calls togetVault(sessionId)will return the cached rejected promise instead of retrying initialization.Proposed fix with try/finally
const initPromise = (async () => { + try { const storage = new MemoryStorageAdapter(); await storage.connect(); // Connect the adapter storageInstances.set(sessionId, storage); const vault = new StorageAuthorizationVault(storage, { namespace: 'vault', pendingAuthTtlMs: 60000, // 1 minute for testing validateOnRead: false, // Disable for simpler testing }); vaultInstances.set(sessionId, vault); - initPromises.delete(sessionId); return vault; + } finally { + initPromises.delete(sessionId); + } })();apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts (3)
52-69: Credential scope pattern tests have nearly identical assertions.Both
credential scope pattern - machine/globalandcredential scope pattern - sessiontests assert only that the response contains'instanceId'. The tests don't actually verify the distinguishing behavior between machine/global and session scopes.Consider adding more specific assertions:
- For machine/global: Verify consistent
instanceIdacross multiple calls (like the GLOBAL scope test does)- For session: Verify
providerScopeisCONTEXTor that instances differ across sessions♻️ Suggested enhancement
test('should demonstrate credential scope pattern - machine/global', async ({ mcp }) => { // Machine/global scope credentials would be shared across all users // This tests the pattern without actual credential storage - const result = await mcp.tools.call('get-app-info', {}); + const result1 = await mcp.tools.call('get-app-info', {}); + const result2 = await mcp.tools.call('get-app-info', {}); - expect(result).toBeSuccessful(); + expect(result1).toBeSuccessful(); + expect(result2).toBeSuccessful(); // Instance ID being consistent across calls demonstrates global/machine scope - expect(result).toHaveTextContent('instanceId'); + expect(result1).toHaveTextContent('"providerScope":"GLOBAL"'); + expect(result2).toHaveTextContent('"providerScope":"GLOBAL"'); }); test('should demonstrate credential scope pattern - session', async ({ mcp }) => { // Session scope credentials are tied to the current session const result = await mcp.tools.call('get-request-info', {}); expect(result).toBeSuccessful(); // Request ID being session-scoped demonstrates session credential pattern - expect(result).toHaveTextContent('instanceId'); + expect(result).toHaveTextContent('"providerScope":"CONTEXT"'); });
91-97: Consider guarding against undefinedmessagesproperty.Line 96 accesses
result.messages.lengthdirectly afterexpect(result).toBeSuccessful(). If the result structure is unexpected ormessagesis undefined, this will throw a runtime error before the assertion can fail gracefully.♻️ Optional defensive check
test('should access provider in prompt context', async ({ mcp }) => { // Prompts can access providers via DI const result = await mcp.prompts.get('debug-context', {}); expect(result).toBeSuccessful(); + expect(result.messages).toBeDefined(); expect(result.messages.length).toBeGreaterThan(0); });
149-156: Type guard may cause silent test pass if content type is unexpected.The type guard
if (message.content.type === 'text')correctly narrows the type, but if the content type is not'text', the test will pass without verifying the expected content. Consider adding an explicit expectation or failure path.♻️ Ensure assertions are always executed
const message = result.messages[0]; - expect(message.content.type).toBe('text'); - if (message.content.type === 'text') { - // Prompt accesses both GLOBAL and CONTEXT providers - expect(message.content.text).toContain('GLOBAL Scope Provider'); - expect(message.content.text).toContain('CONTEXT Scope Provider'); - } + expect(message.content.type).toBe('text'); + // Type assertion is safe here since we asserted the type above + const textContent = (message.content as { type: 'text'; text: string }).text; + // Prompt accesses both GLOBAL and CONTEXT providers + expect(textContent).toContain('GLOBAL Scope Provider'); + expect(textContent).toContain('CONTEXT Scope Provider');
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (23)
apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.tsapps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.tsapps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.tsapps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.tsapps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/authorize-app.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/get-credentials.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.tsapps/e2e/demo-e2e-transparent/e2e/backward-compat.e2e.test.tslibs/sdk/src/auth/session/redis-session.store.tslibs/sdk/src/auth/session/session-store.factory.tslibs/sdk/src/auth/session/session.schema.tslibs/ui/src/render/react-dom-static.d.tslibs/utils/src/storage/adapters/redis.tsplugins/plugin-approval/src/approval/errors.d.tsplugins/plugin-approval/src/approval/factories.d.tsplugins/plugin-approval/src/approval/guards.d.tsplugins/plugin-approval/src/approval/index.d.tsplugins/plugin-approval/src/approval/schemas.d.tsplugins/plugin-approval/src/approval/types.d.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- apps/e2e/demo-e2e-config/e2e/cross-package-imports.e2e.test.ts
- apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-credentials.tool.ts
- apps/e2e/demo-e2e-redis/src/apps/vault/tools/authorize-app.tool.ts
- apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts
- apps/e2e/demo-e2e-transparent/e2e/backward-compat.e2e.test.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.tsapps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.tsapps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.tsapps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Do not use prefixes like 'PT-001' in test names
Do not skip constructor validation tests for error classes and types
Includeinstanceofchecks in tests for error classes to verify proper error hierarchy
Files:
apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.tsapps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.tsapps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to libs/sdk/**/*.{ts,tsx} : Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Applied to files:
apps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.ts
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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-orchestrated/e2e/backward-compat.e2e.test.tsapps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.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:
apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts
🧬 Code graph analysis (5)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts (1)
apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
getVault(17-49)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.ts (3)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts (1)
Tool(18-43)apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-pending-auth.tool.ts (1)
Tool(25-58)apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
getVault(17-49)
apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts (2)
libs/sdk/src/scope/scope.instance.ts (4)
auth(313-315)tools(333-335)resources(341-343)prompts(345-347)libs/sdk/src/context/frontmcp-context.ts (1)
fetch(467-502)
apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts (1)
libs/sdk/src/scope/scope.instance.ts (1)
tools(333-335)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (3)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts (1)
Tool(18-43)apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.ts (1)
Tool(34-74)apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
getVault(17-49)
🪛 Gitleaks (8.30.0)
apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts
[high] 196-196: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
⏰ 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). (4)
- GitHub Check: Lint & Format Checks
- GitHub Check: Build Libraries
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (19)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (2)
26-52: Implementation is consistent with existing vault tools.The tool follows the established patterns from
DeleteVaultEntryToolandGetVaultEntryTool(session fallback, error handling, structured responses). The error handling properly catches exceptions and returns descriptive failure messages.
5-11: Input schema is well-defined with proper Zod v4 syntax.The schema uses appropriate validators including
.email()for the email field and.strict()to reject unknown properties. This aligns with Zod v4 patterns.apps/e2e/demo-e2e-orchestrated/e2e/backward-compat.e2e.test.ts (8)
1-19: LGTM!The file header clearly documents the test purpose and scope. The import from
@frontmcp/testingand server configuration viatest.usefollow the established e2e testing patterns in the codebase.
21-88: LGTM!The stateful session compatibility tests are well-structured with:
- Proper client cleanup via
disconnect()in all tests- Clear assertions for session creation, persistence, and ID stability
- Helpful inline comment explaining mock data behavior on lines 63-64
90-123: LGTM!Token signing compatibility tests properly verify:
- Locally signed tokens are accepted
- User claims are correctly extracted from signed tokens
- Proper use of optional chaining for user property access
125-140: LGTM!JWKS endpoint test properly validates the well-known endpoint structure. The assertions verify the essential JWKS format requirements (keys array with at least one signing key).
142-159: LGTM!The OAuth authorization server metadata test appropriately handles federation scenarios by accepting redirects. The permissive validation approach is suitable for backward compatibility testing where the goal is to ensure the endpoint exists and responds, not to enforce specific metadata fields.
161-228: LGTM!Comprehensive MCP capability tests covering tools, resources, and prompts operations. The tests properly:
- Verify listing operations return results
- Validate tool call success with custom matchers
- Clean up client connections consistently
230-268: LGTM!Error response tests properly validate:
- 401 status for unauthenticated requests with WWW-Authenticate header verification
- 401 status for invalid token scenarios
The tests intentionally use raw
fetchto bypass the test framework's auth handling, which is the correct approach for testing error responses.
270-352: LGTM!Multi-user and session data tests are well-designed:
- Session isolation test verifies different users have separate sessions and identities
- Concurrent users test efficiently uses
Promise.allfor both client creation and cleanup- Unique session ID verification using
Set(line 321-322) is a clean approach- Session data persistence test validates multiple operations within a single session
apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts (3)
1-17: LGTM! Well-structured test setup.The test file header clearly documents the scope of tests, and the import/configuration setup is clean. Using
publicMode: truefor e2e vault testing is appropriate.
181-201: Static analysis false positive - test token is placeholder data.The Gitleaks warning at line 196 flagging
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...as a "Generic API Key" is a false positive. This is clearly a truncated placeholder JWT used for testing purposes, not a real secret. The token is incomplete and used only within the test context.
386-397: Verify the expected behavior for non-existent vault pending auth.The test expects
toBeSuccessful()on line 393 but then checks for'Failed'in content on line 394. This suggests the API returns HTTP 200 with an error message in the body. While this may be intentional, it's worth verifying this is the expected behavior, as it could make error handling more difficult for consumers who rely on success/failure status.apps/e2e/demo-e2e-redis/src/apps/vault/tools/complete-pending-auth.tool.ts (1)
1-52: LGTM!The implementation correctly follows the established pattern for vault tools. The input/output schemas use proper Zod 4 syntax, error handling returns structured responses, and the action branching logic is clean.
apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
1-12: Good use of@frontmcp/utilsfor storage adapter.The import from
@frontmcp/utilsfollows the coding guidelines for using shared utilities rather than direct implementations.apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts (1)
1-60: LGTM!The tool implementation is consistent with other vault tools in the PR. Input schema correctly uses Zod 4's
.email()validator for optional email, and error handling follows the established pattern of returning structured responses.apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts (3)
1-19: Well-documented test file header and setup.The documentation clearly outlines the test scope and appropriately notes that full OAuth/API key credential testing is handled in a separate app. The test configuration using
@frontmcp/testingwith the correct server path is appropriate for e2e testing.
100-119: Factory pattern tests look good.The tests appropriately verify that factory-created providers include expected timestamp properties (
createdAt,startedAt), demonstrating both synchronous and async initialization patterns.
175-219: Well-structured lifecycle tests with proper singleton verification.The parallel call pattern using
Promise.alleffectively tests concurrent access, and the regex-based instance ID extraction correctly verifies singleton behavior for GLOBAL providers and session consistency for CONTEXT providers. The null checks before comparisons are appropriate.
…ons with enhanced assertions
There was a problem hiding this 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 (7)
apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts (3)
31-41: Consider consistent assertion patterns.Lines 35-36 use manual
JSON.stringify+ string matching, while lines 61-62 use the cleaner.toHaveTextContent()matcher. The manual approach is more fragile if the serialization format changes.For instanceId extraction (lines 39-41), consider extracting a helper function since this pattern is repeated in lines 193-197 and 215-219.
♻️ Suggested helper extraction
// Helper at top of file or in a shared test utils function extractInstanceId(result: unknown, pattern: RegExp): string | null { const content = JSON.stringify(result); const match = content.match(pattern); return match ? match[1] : null; }
125-143: Consider follow-up for graceful degradation coverage.The smoke tests are appropriate, but the comment correctly notes that full graceful degradation testing (when providers are missing or fail) requires a dedicated test app. Consider tracking this as a follow-up task if not already planned.
Would you like me to open an issue to track adding a dedicated test app for graceful degradation scenarios?
155-162: Type assertion could be improved with a type guard.The Jest assertion on line 156 doesn't narrow TypeScript's type, so the manual type assertion on line 158 bypasses type safety. Per coding guidelines about avoiding unsafe assertions, consider using a proper type guard or structured access.
♻️ Safer alternative using type guard
const message = result.messages[0]; - expect(message.content.type).toBe('text'); - // Type assertion is safe since we asserted the type above - const textContent = (message.content as { type: 'text'; text: string }).text; + expect(message.content).toHaveProperty('type', 'text'); + expect(message.content).toHaveProperty('text'); + const textContent = 'text' in message.content && typeof message.content.text === 'string' + ? message.content.text + : ''; + expect(textContent).not.toBe(''); // Ensure we got valid content // Prompt accesses both GLOBAL and CONTEXT providers expect(textContent).toContain('GLOBAL Scope Provider'); expect(textContent).toContain('CONTEXT Scope Provider');apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (1)
32-47: Consider using a more specific type for the updates object.Using
Record<string, unknown>weakens type safety. Thevault.update()method acceptsPartial<AuthorizationVaultEntry>, so a more specific type would improve type safety while remaining compatible.🔧 Suggested improvement
- const updates: Record<string, unknown> = {}; + const updates: { userEmail?: string; userName?: string } = {}; if (input.userEmail !== undefined) { updates.userEmail = input.userEmail; } if (input.userName !== undefined) { updates.userName = input.userName; }apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts (3)
182-184: Prefer structured assertions over string matching for JSON content.Using
toHaveTextContent('"count":1')is brittle and could break if the JSON formatting changes. Consider using thejson()method for more robust assertions.Suggested improvement
- expect(getResult).toBeSuccessful(); - expect(getResult).toHaveTextContent('"count":1'); + expect(getResult).toBeSuccessful(); + const credResult = getResult.json<{ count: number }>(); + expect(credResult.count).toBe(1);This pattern applies to similar assertions at lines 244, 308, 481, 575, and 665.
484-496: Clarify test expectations for error scenarios.The comment "This may fail or succeed depending on implementation" suggests the test isn't validating specific behavior. If the expected behavior is undefined, this test may pass regardless of whether the implementation is correct.
Consider either:
- Defining the expected behavior and asserting accordingly
- Checking for a specific error message or status if failure is expected
- Documenting why success is acceptable for non-existent entries
// This may fail or succeed depending on implementation // The important thing is it doesn't crash expect(result).toBeSuccessful(); + // Verify specific expected behavior - e.g., error message or empty result + expect(result).toHaveTextContent('not found');Similar concerns apply to tests at lines 498-506 and 508-518.
540-576: Test should verify expiration handling behavior, not just storage.This test stores both expired and valid credentials and verifies they're both stored (
"count":2), but doesn't validate how the system handles expired credentials on retrieval. Should expired credentials be filtered out, flagged, or returned as-is?Consider adding assertions that verify the actual expiration handling behavior:
// Get all credentials - both should be stored const getResult = await mcp.tools.call('get-credentials', { entryId }); expect(getResult).toHaveTextContent('"count":2'); + + // Verify individual credential expiration states are accessible + const creds = getResult.json<{ credentials: Array<{ appId: string; expiresAt?: number }> }>(); + const expiredCred = creds.credentials.find(c => c.appId === 'expired-app'); + const futureCred = creds.credentials.find(c => c.appId === 'future-app'); + + // Assert expected expiration handling behavior + expect(expiredCred).toBeDefined(); + expect(futureCred).toBeDefined();
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.tsapps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.tsapps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.tsapps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.tslibs/sdk/package.jsonlibs/utils/package.json
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts
- libs/sdk/package.json
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.tsapps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.tsapps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx}: Achieve 95%+ test coverage across all metrics (statements, branches, functions, lines)
Use Jest for testing with 95%+ coverage requirement and 100% test pass rate
Do not use prefixes like 'PT-001' in test names
Do not skip constructor validation tests for error classes and types
Includeinstanceofchecks in tests for error classes to verify proper error hierarchy
Files:
apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.tsapps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.
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
📚 Learning: 2026-01-04T14:35:18.366Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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-providers/e2e/auth-providers.e2e.test.ts
🧬 Code graph analysis (2)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (4)
apps/e2e/demo-e2e-redis/src/apps/vault/tools/create-vault-entry.tool.ts (1)
Tool(24-60)apps/e2e/demo-e2e-redis/src/apps/vault/tools/delete-vault-entry.tool.ts (1)
Tool(18-43)apps/e2e/demo-e2e-redis/src/apps/vault/tools/get-vault-entry.tool.ts (1)
Tool(34-74)apps/e2e/demo-e2e-redis/src/apps/vault/data/vault.store.ts (1)
getVault(17-52)
apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts (2)
scripts/bump-synchronized-versions.mjs (1)
result(130-130)libs/sdk/src/scope/scope.instance.ts (1)
tools(333-335)
⏰ 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). (4)
- GitHub Check: Lint & Format Checks
- GitHub Check: Build Libraries
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (11)
apps/e2e/demo-e2e-providers/e2e/auth-providers.e2e.test.ts (4)
1-20: Well-documented test setup.The header documentation clearly explains the test scope and intent, and the configuration is appropriate for E2E testing of auth providers integration patterns.
75-102: LGTM!The context extension tests properly validate DI accessibility across tools, resources, and prompts.
104-123: LGTM!The factory pattern tests appropriately validate instantiation behavior without coupling to implementation details.
181-225: Good lifecycle validation tests.The concurrent call pattern with
Promise.alleffectively validates singleton behavior for GLOBAL providers and session persistence for CONTEXT providers.Note: The regex patterns on lines 195 and 217 have hardcoded prefixes (
instance-,req-). This is acceptable for E2E tests but creates coupling to implementation details. If ID formats change, these tests will need updates.apps/e2e/demo-e2e-redis/src/apps/vault/tools/update-vault-entry.tool.ts (3)
1-3: LGTM!Imports are appropriate and consistent with other vault tools in the same directory.
5-18: LGTM!Input and output schemas are well-defined with proper validation. Using
.strict()ensures no unexpected properties are passed, and the email validation viaz.string().email()is a good practice.
26-59: LGTM!The execute method follows established patterns from other vault tools:
- Consistent session ID handling with fallback
- Proper error handling with informative messages
- Correct use of
!== undefinedchecks to allow empty strings- Early return for validation failure (no fields to update)
The error handling pattern aligns with
CreateVaultEntryToolandDeleteVaultEntryTool.apps/e2e/demo-e2e-redis/e2e/storage-vault.e2e.test.ts (4)
1-17: LGTM! Well-structured test file with clear documentation.The file header clearly documents the test coverage areas, and the test configuration properly sets up the server and public mode for E2E testing.
19-110: LGTM! Comprehensive CRUD test coverage.The CRUD tests properly validate create, read, update, and delete operations with appropriate assertions. Good use of the
json()method for extracting structured response data.
623-668: Good comprehensive integration test for persistence validation.This test effectively validates that data persists across multiple tool calls in a realistic workflow. The test properly chains operations and verifies the final state.
13-17: Review comment is based on a valid pattern but overstates the concern.Tests use unique identifiers to prevent interference, which is sufficient for this in-memory mocked Redis store. While cleanup functions exist (
clearAllVaults(),clearAllSessions()in the data layer), they aren't explicitly called in the tests. This is acceptable since:
- The store is mocked in-memory (simulates Redis for E2E testing), not persistent
- Data doesn't accumulate across test runs
- Unique identifiers prevent conflicts within a single run
If you prefer explicit cleanup for hygiene, consider adding:
test.afterEach(async ({ mcp }) => { // Call cleanup if needed });However, the current approach is sufficient given the test isolation strategy.
…ons with enhanced assertions
There was a problem hiding this 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/auth/flows/session.verify.flow.ts (1)
114-142: Consider using@frontmcp/utilsfor UUID generation.Direct usage of
crypto.randomUUID()appears in multiple places (lines 116, 137, 142). Per coding guidelines, cryptographic operations should use@frontmcp/utilsinstead of direct crypto APIs for consistency and cross-platform support.♻️ Suggested refactor
+import { randomUUID } from '@frontmcp/utils'; ... - : { sub: `anon:${crypto.randomUUID()}`, iss: issuer, name: 'Anonymous', scope: scopes.join(' ') }; + : { sub: `anon:${randomUUID()}`, iss: issuer, name: 'Anonymous', scope: scopes.join(' ') }; ... - sub: `anon:${crypto.randomUUID()}`, + sub: `anon:${randomUUID()}`, ... - const uuid = crypto.randomUUID(); + const uuid = randomUUID();Based on coding guidelines: "Use
@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly."
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (2)
libs/sdk/src/auth/flows/session.verify.flow.tspackage.json
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use strict TypeScript mode with noanytypes without strong justification
Avoid non-null assertions (!) - use proper error handling and type guards instead
Use@frontmcp/utilsfor cryptographic operations instead ofnode:cryptodirectly
Use@frontmcp/utilsfor file system operations instead offs/promisesornode:fsdirectly
Files:
libs/sdk/src/auth/flows/session.verify.flow.ts
libs/{sdk,adapters}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/{sdk,adapters}/**/*.{ts,tsx}: Use specific error classes with MCP error codes instead of generic errors
UsegetCapabilities()for dynamic capability exposure instead of hardcoding capabilities
Files:
libs/sdk/src/auth/flows/session.verify.flow.ts
libs/sdk/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
libs/sdk/**/*.{ts,tsx}: Preferinterfacefor defining object shapes in TypeScript, avoidanytypes
Use type parameters with constraints instead of unconstrained generics withanydefaults
Validate URIs per RFC 3986 at metadata level usingisValidMcpUrirefinement
UsechangeScopeinstead ofscopein change event properties to avoid confusion with Scope class
Fail fast on invalid hook flows by validating hooks match their entry type
Centralize record types in common/records and import from there, not from module-specific files
Return strictly typed MCP protocol responses (e.g.,Promise<GetPromptResult>) instead ofPromise<unknown>for MCP entry methods
Useunknowninstead ofanyfor generic type parameter defaults (not for MCP protocol types)
Create shared base classes for common functionality like ExecutionContextBase for ToolContext and ResourceContext
Do not mutate rawInput in flows - use state.set() for managing flow state instead
Validate inputs and outputs through parseOutput/safeParseOutput methods in validation flows
Files:
libs/sdk/src/auth/flows/session.verify.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/auth/flows/session.verify.flow.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
Learning: Organize code following the frontmcp/uipack directory structure (adapters/, bundler/, theme/, renderers/, validation/, etc.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.)
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/uipack/CLAUDE.md:0-0
Timestamp: 2026-01-04T14:35:18.366Z
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.
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
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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`
📚 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/session.verify.flow.ts
📚 Learning: 2026-01-06T17:16:04.304Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-06T17:16:04.304Z
Learning: Applies to **/*.{ts,tsx} : Use `frontmcp/utils` for cryptographic operations instead of `node:crypto` directly
Applied to files:
libs/sdk/src/auth/flows/session.verify.flow.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
Learning: Applies to libs/plugins/**/*.ts : Avoid using `node:crypto` directly; always use `frontmcp/utils` for cross-platform cryptographic support
Applied to files:
libs/sdk/src/auth/flows/session.verify.flow.ts
📚 Learning: 2026-01-06T02:34:55.689Z
Learnt from: CR
Repo: agentfront/frontmcp PR: 0
File: libs/plugins/CLAUDE.md:0-0
Timestamp: 2026-01-06T02:34:55.689Z
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/sdk/src/auth/flows/session.verify.flow.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). (4)
- GitHub Check: Lint & Format Checks
- GitHub Check: Build Libraries
- GitHub Check: Package E2E (Verdaccio)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
libs/sdk/src/auth/flows/session.verify.flow.ts (5)
23-23: LGTM - Import migration to@frontmcp/auth.The import source change aligns with the PR objective to centralize auth utilities in the published
@frontmcp/authpackage.
188-194: LGTM - Formatting improvement.The reformatted ternary chain improves readability while maintaining the same protocol selection logic.
300-334: Good defensive improvements to token validation.The changes properly handle edge cases:
- Missing/empty token when Authorization header exists but
extractBearerTokenreturns undefined (e.g., "Bearer " or "Basic xxx")- Defensive null check for
this.scope.authbefore accessing its propertiesThis prevents potential runtime errors and provides consistent 401 responses for invalid authentication scenarios.
336-367: LGTM - Verification logic.The mode-based verification correctly routes:
- Transparent mode → remote provider's JWKS via
verifyTransparentToken- Public/orchestrated modes → local gateway keys via
verifyGatewayTokenProper error handling with 401 response on verification failure.
369-408: LGTM - Execute stages.The flow plan ensures
verifyIfJwtcompletes before these execute stages run. SinceverifyIfJwtresponds early for missing/invalid tokens, therequired.tokenaccess inparseSessionHeaderandbuildAuthorizedOutputis safe.
Summary by CodeRabbit
New Features
Refactor
Tests
Docs
✏️ Tip: You can customize this high-level summary in your review settings.