Skip to content

Comments

012 visual builder update#25

Merged
MikePfunk28 merged 3 commits intomainfrom
012-visual-builder-update
Feb 13, 2026
Merged

012 visual builder update#25
MikePfunk28 merged 3 commits intomainfrom
012-visual-builder-update

Conversation

@MikePfunk28
Copy link
Owner

@MikePfunk28 MikePfunk28 commented Feb 12, 2026

feat: Enhance token usage tracking and error handling across workflow…

@MikePfunk28
MikePfunk28 committed 2 hours ago
feat: Enhance non-fatal billing error handling and token usage meteri…

@MikePfunk28
MikePfunk28 committed 38 minutes ago

Summary by CodeRabbit

  • Bug Fixes

    • Deployments, agent runs, and tools no longer fail when billing/reporting calls error — operations continue and log non-fatal billing issues.
  • Improvements

    • More accurate token-usage estimation for billing.
    • Improved dispute handling with better customer resolution and error logging.
    • Tighter user identity handling across workflows and gates for more reliable authentication.
  • UI

    • MCP server list: system servers are labeled and non-interactive; user servers remain manageable.

…s and billing processes

- Improved user ID handling for token billing in multiple workflows.
- Added non-fatal error handling for billing failures in token usage tracking.
- Enhanced customer resolution logic for Stripe disputes.
- Updated user ID types for Bedrock access control to ensure type safety.
@MikePfunk28 MikePfunk28 self-assigned this Feb 12, 2026
@MikePfunk28 MikePfunk28 added the bug Something isn't working label Feb 12, 2026
Copilot AI review requested due to automatic review settings February 12, 2026 23:00
@MikePfunk28 MikePfunk28 added the enhancement New feature or request label Feb 12, 2026
@vercel
Copy link

vercel bot commented Feb 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agent-builder-application Ready Ready Preview, Comment Feb 12, 2026 11:15pm

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR enhances token usage tracking and error handling across the workflow execution system. It makes billing operations non-fatal throughout the codebase, improves type safety by removing unsafe type casts, and enhances user identity resolution.

Changes:

  • Wrapped all billing/metering operations in try-catch blocks to treat them as non-fatal, preventing billing failures from breaking primary functionality
  • Removed unsafe as any type casts and replaced with proper Id<"users"> types from the generated dataModel
  • Improved user identity resolution by removing email as a fallback (emails can collide across providers) and enhanced error messages
  • Enhanced Stripe dispute webhook handling to properly resolve customer IDs from unexpanded charge objects
  • Added validation for missing user IDs in bedrockGate and improved token estimation accuracy in multiple execution paths

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated no comments.

Show a summary per file
File Description
convex/workflowExecutor.ts Updated user identity resolution logic (removed email fallback), added non-fatal billing error handling, fixed type safety for gateResult userId
convex/unifiedAgentExecution.ts Added user record validation before Bedrock gate check, wrapped billing in try-catch, removed as any cast
convex/tools.ts Added non-fatal billing error handling for all tool actions (selfConsistency, treeOfThoughts, reflexion, mapReduce, parallelPrompts), fixed userId types
convex/testExecution.ts Removed unsafe as any casts for userId parameters
convex/strandsAgentExecutionDynamic.ts Added non-fatal billing error handling, improved token estimation to use user-facing text instead of full JSON payload
convex/strandsAgentExecution.ts Added non-fatal billing error handling with try-catch wrapper
convex/promptChainExecutor.ts Added non-fatal billing error handling, fixed userId types, added error field to executeParallelPrompts return type
convex/mcpConfig.ts Added BuiltInMcpServer interface to properly type built-in servers with synthetic string IDs instead of database IDs
convex/mcpClient.ts Added non-fatal billing error handling, added logic to skip database operations for built-in servers, improved token estimation
convex/http.ts Enhanced dispute webhook handling to fetch charge details when charge is an unexpanded ID, added comprehensive error logging
convex/deployments.ts Removed unsafe as any casts for userId
convex/deploymentRouter.ts Added non-fatal billing error handling, fixed userId types, removed unsafe casts
convex/awsDiagramGenerator.ts Removed unsafe as any cast for userId
convex/awsDeployment.ts Added non-fatal billing error handling, cleaned up userId type handling in createDeploymentInternal
convex/automatedAgentBuilder.ts Added non-fatal billing error handling with try-catch
convex/agentBuilderWorkflow.ts Added non-fatal billing error handling with try-catch
convex/lib/bedrockGate.ts Added validation for missing user _id, fixed return type to use proper Id<"users">
convex/interleavedReasoning.ts Added non-fatal billing error handling with try-catch

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai
Copy link

coderabbitai bot commented Feb 12, 2026

📝 Walkthrough

Walkthrough

Converted many loose string/any userId usages to Id<"users">, removed unnecessary casts, and made billing/token-usage mutations non-fatal by wrapping them in try/catch with structured logging; plus enhanced Stripe dispute customer resolution and introduced typed MCP server entries and guards.

Changes

Cohort / File(s) Summary
Billing non-fatal / token metering
convex/agentBuilderWorkflow.ts, convex/automatedAgentBuilder.ts, convex/interleavedReasoning.ts, convex/strandsAgentExecution.ts, convex/strandsAgentExecutionDynamic.ts, convex/mcpClient.ts, convex/promptChainExecutor.ts, convex/tools.ts, convex/unifiedAgentExecution.ts, convex/workflowExecutor.ts
Wrapped token-usage / billing mutations (incrementUsageAndReportOverage / stripe increments) in try/catch across many execution paths; on failure log non-fatal billing errors with context (userId, model, token counts) and continue execution.
UserId typing & cast removal
convex/awsDeployment.ts, convex/deploymentRouter.ts, convex/deployments.ts, convex/awsDiagramGenerator.ts, convex/testExecution.ts, convex/lib/bedrockGate.ts, convex/promptChainExecutor.ts, convex/tools.ts, convex/workflowExecutor.ts
Replaced string/any userId usages with Id<"users">, removed as any casts, updated function signatures (e.g., createDeploymentInternal, deployTier1) and adjusted callsites to pass typed IDs.
Stripe webhook dispute resolution
convex/http.ts
Expanded charge.dispute.created handler to resolve customer via multiple paths: direct dispute.customer, retrieving the charge from Stripe when dispute.charge is an ID, or using expanded charge object; added error logging for charge retrieval and missing-customer scenarios before mutating.
MCP server typing & behavior
convex/mcpConfig.ts, convex/mcpClient.ts, src/components/.../MCPServerSelector.tsx, src/components/.../MCPManagementPanel.tsx, src/components/.../MCPServerForm.tsx, src/components/.../MCPToolTester.tsx
Introduced BuiltInMcpServer / DbMcpServer types and MCPServerEntry union plus isDbMcpServer guard; made built-in vs DB-backed semantics explicit (source field), adjusted list/get/add/update/delete/test handlers and UI to account for system vs user servers and typed IDs.
Token usage estimation fallbacks
convex/strandsAgentExecutionDynamic.ts, convex/mcpClient.ts, convex/automatedAgentBuilder.ts
Improved fallback token estimation by building input text from system prompt, conversation history, and user message when reported totals are zero (replacing JSON.stringify(payload) fallback).
Misc / CI & docs
.github/workflows/setup-github-projects_Version3.yml, package.json
Removed GH CLI auth step and updated default project name/description in workflow; minor package/manifest edits.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Stripe as Stripe (Webhook source)
participant Server as App Server (Webhook handler)
participant StripeAPI as Stripe API (charges.retrieve)
participant DB as Convex DB / Mutations
Stripe->>Server: POST charge.dispute.created (dispute)
alt dispute.customer is string
Server->>DB: mutate restrictAccountForDispute(customerId, disputeId)
else dispute.charge is ID
Server->>StripeAPI: charges.retrieve(chargeId)
alt retrieval succeeds
StripeAPI-->>Server: charge (may include customer or customer id)
Server->>DB: mutate restrictAccountForDispute(resolvedCustomerId, disputeId)
else retrieval fails
StripeAPI-->>Server: error
Server->>Server: log error (chargeId, err)
end
else dispute.charge is expanded object with customer
Server->>DB: mutate restrictAccountForDispute(resolvedCustomerId, disputeId)
else
Server->>Server: log error (unable to resolve customer for disputeId)
end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • 012 visual builder update #24: Overlapping changes touching Convex backend gating, Bedrock access, and non-fatal billing handling—strong code-level overlap.

Poem

🐰 Hops the rabbit, small and spry,

Wrapped the billing, gave it a try.
Types got tighter, casts undone,
Disputes resolved — fetch if one.
Non-fatal logs now keep things spry. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (24 files):

⚔️ .github/workflows/setup-github-projects_Version3.yml (content)
⚔️ convex/agentBuilderWorkflow.ts (content)
⚔️ convex/automatedAgentBuilder.ts (content)
⚔️ convex/awsDeployment.ts (content)
⚔️ convex/awsDiagramGenerator.ts (content)
⚔️ convex/deploymentRouter.ts (content)
⚔️ convex/deployments.ts (content)
⚔️ convex/http.ts (content)
⚔️ convex/interleavedReasoning.ts (content)
⚔️ convex/lib/bedrockGate.ts (content)
⚔️ convex/mcpClient.ts (content)
⚔️ convex/mcpConfig.ts (content)
⚔️ convex/promptChainExecutor.ts (content)
⚔️ convex/strandsAgentExecution.ts (content)
⚔️ convex/strandsAgentExecutionDynamic.ts (content)
⚔️ convex/testExecution.ts (content)
⚔️ convex/tools.ts (content)
⚔️ convex/unifiedAgentExecution.ts (content)
⚔️ convex/workflowExecutor.ts (content)
⚔️ src/App.tsx (content)
⚔️ src/components/MCPManagementPanel.tsx (content)
⚔️ src/components/MCPServerForm.tsx (content)
⚔️ src/components/MCPServerSelector.tsx (content)
⚔️ src/components/MCPToolTester.tsx (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
Title check ❓ Inconclusive The title '012 visual builder update' is vague and uses non-descriptive terms that don't convey meaningful information about the actual changes (improved billing error handling, non-fatal billing errors, type safety improvements, MCP server source distinction, enhanced token tracking). Revise the title to be more descriptive of the primary changes, such as 'Make billing errors non-fatal and improve type safety for user IDs' or 'Add source property to distinguish MCP servers and harden billing error handling'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 012-visual-builder-update
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch 012-visual-builder-update
  • Create stacked PR with resolved conflicts
  • Post resolved changes as copyable diffs in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (3)
convex/mcpConfig.ts (2)

238-248: ⚠️ Potential issue | 🟡 Minor

User can create a server whose name collides with a built-in, making it unreachable.

addMCPServer only checks the DB for duplicate names (line 239-244). A user could insert a server named "bedrock-agentcore-mcp-server", but getMCPServerByName (line 157) always returns the built-in first, permanently shadowing the user's record.

Add a check against BUILT_IN_MCP_SERVERS names before inserting:

Proposed fix
+    // Reject names that collide with built-in servers
+    const isBuiltInName = BUILT_IN_MCP_SERVERS.some(s => s.name === args.name);
+    if (isBuiltInName) {
+      throw new Error(`"${args.name}" is a reserved built-in server name`);
+    }
+
     // Check if server with this name already exists for this user
     const existingServer = await ctx.db

378-407: ⚠️ Potential issue | 🟠 Major

updateMCPServerStatus mutation lacks authentication and authorization checks.

Unlike addMCPServer, updateMCPServer, and deleteMCPServer in the same file, this mutation has no getAuthUserId() check or ownership verification. Any unauthenticated or unauthorized user can update status, lastError, lastConnected, and availableTools for any server by providing a valid serverId. This is called from both testMCPConnection and invokeMCPTool actions, which are also public.

Add authentication and ownership verification consistent with the other mutations:

  • Call getAuthUserId(ctx) to verify the user is authenticated
  • Verify that server.userId === userId before patching
convex/promptChainExecutor.ts (1)

246-253: ⚠️ Potential issue | 🟠 Major

Missing non-fatal try-catch around billing in executeParallelPrompts.

Unlike executePromptChain (line 124) and testPrompt (line 470), this billing call is not wrapped in a try-catch. A billing failure here will throw an unhandled exception and crash the entire parallel prompts execution, since there's no outer try-catch in this handler either. This is inconsistent with the non-fatal billing pattern applied everywhere else in this PR.

🐛 Proposed fix: wrap in try-catch like other billing calls
     // Meter: token-based billing for all parallel prompts
     if ( gateUserId && ( totalInputTokens > 0 || totalOutputTokens > 0 ) ) {
-      await ctx.runMutation( internal.stripeMutations.incrementUsageAndReportOverage, {
-        userId: gateUserId,
-        modelId: gateModelId,
-        inputTokens: totalInputTokens,
-        outputTokens: totalOutputTokens,
-      } );
+      try {
+        await ctx.runMutation( internal.stripeMutations.incrementUsageAndReportOverage, {
+          userId: gateUserId,
+          modelId: gateModelId,
+          inputTokens: totalInputTokens,
+          outputTokens: totalOutputTokens,
+        } );
+      } catch ( billingErr ) {
+        console.error( "promptChainExecutor: billing failed (non-fatal)", {
+          userId: gateUserId, modelId: gateModelId,
+          inputTokens: totalInputTokens, outputTokens: totalOutputTokens,
+          error: billingErr instanceof Error ? billingErr.message : billingErr,
+        } );
+      }
     }
🤖 Fix all issues with AI agents
In `@convex/awsDeployment.ts`:
- Line 707: The deployTier2 → createDeploymentInternal type mismatch: change
deployTier2's userId parameter (and any internal actions like
executeWebIdentityDeploymentInternal and executeCrossAccountDeploymentInternal)
from plain string/v.string() to the user-id type expected by
createDeploymentInternal (use v.id("users") / Id<"users"> as used in
createDeploymentInternal) so the passed userId is strongly typed; update the
function signatures and any internal action/mutation validators and callers to
accept and propagate v.id("users") instead of string to keep types consistent
end-to-end (search for deployTier2, createDeploymentInternal,
executeWebIdentityDeploymentInternal, executeCrossAccountDeploymentInternal to
locate all sites to change).

In `@convex/mcpConfig.ts`:
- Around line 22-36: BuiltInMcpServer currently defines _id as string while DB
records use Id<"mcpServers">, causing incompatible union return types from
listMCPServers and lookup helpers (getMCPServerByName,
getMCPServerByNameInternal); add a discriminant like isBuiltIn: true to
BuiltInMcpServer and update its type to reflect _id: string, then adjust
listMCPServers/getMCPServerByName/getMCPServerByNameInternal return types to a
discriminated union (BuiltInMcpServer | DbMcpServer) so callers can narrow on
isBuiltIn and avoid type assertions when handling differing _id types.
🧹 Nitpick comments (5)
convex/mcpConfig.ts (1)

41-41: Date.now() evaluated at module-load time, not per-request.

All four built-in server entries share the same _creationTime, createdAt, and updatedAt values, frozen at the moment the Convex module was first loaded. If these timestamps are displayed in the UI or used for sorting/ordering, they'll be misleading. Consider using fixed epoch values (e.g., 0) to make it explicit that these are synthetic, or compute them lazily inside each query handler.

Also applies to: 53-54

convex/deploymentRouter.ts (1)

68-68: deployTier1 correctly typed as Id<"users">, but deployTier2/deployTier3 are still string.

Lines 164 and 192 still declare _userId: string while the caller on lines 42/45 passes the same Id<"users"> value. This compiles because Id<"users"> extends string, but it's inconsistent with the type-tightening intent of this PR.

Suggested fix for consistency
-async function deployTier2(ctx: any, args: any, _userId: string): Promise<any> {
+async function deployTier2(ctx: any, args: any, _userId: Id<"users">): Promise<any> {
-async function deployTier3(_ctx: any, _args: any, _userId: string): Promise<any> {
+async function deployTier3(_ctx: any, _args: any, _userId: Id<"users">): Promise<any> {
convex/workflowExecutor.ts (1)

166-166: Consider using the exported BedrockAccessGranted type.

The inline type { allowed: true; userId: import("./_generated/dataModel").Id<"users">; tier: string } duplicates the shape of BedrockAccessGranted from bedrockGate.ts. Using the exported type would reduce duplication and stay in sync if the gate result shape changes.

♻️ Suggested refactor
+import type { BedrockAccessGranted } from "./lib/bedrockGate";
 // ...
-    let gateResult: { allowed: true; userId: import("./_generated/dataModel").Id<"users">; tier: string } | undefined;
+    let gateResult: BedrockAccessGranted | undefined;
convex/tools.ts (1)

364-364: Repeated inline type annotation — consider importing BedrockAccessGranted once.

The type { allowed: true; userId: import("./_generated/dataModel").Id<"users">; tier: string } is repeated 5 times in this file and additionally in workflowExecutor.ts and promptChainExecutor.ts. BedrockAccessGranted is already exported from ./lib/bedrockGate and carries the same shape.

♻️ Suggested refactor

Add a single import at the top of the file:

+import type { BedrockAccessGranted } from "./lib/bedrockGate";

Then replace each occurrence:

-    let gateResult: { allowed: true; userId: import("./_generated/dataModel").Id<"users">; tier: string } | undefined;
+    let gateResult: BedrockAccessGranted | undefined;

Also applies to: 453-453, 552-552, 658-658, 761-761

convex/promptChainExecutor.ts (1)

46-46: Same inline type duplication as other files.

import("./_generated/dataModel").Id<"users"> is used 3 times here. Same BedrockAccessGranted import refactor applies (see comment on convex/tools.ts).

Also applies to: 188-188, 444-444

args: {
agentId: v.id( "agents" ),
userId: v.union( v.id( "users" ), v.string() ),
userId: v.id( "users" ),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, find the file and check its existence
find . -name "awsDeployment.ts" -type f

Repository: MikePfunk28/agent_builder_application

Length of output: 106


🏁 Script executed:

# Read the area around line 707 to see createDeploymentInternal
sed -n '700,730p' convex/awsDeployment.ts | cat -n

Repository: MikePfunk28/agent_builder_application

Length of output: 1256


🏁 Script executed:

# Read the area around line 989 to see deployTier2
sed -n '980,1010p' convex/awsDeployment.ts | cat -n

Repository: MikePfunk28/agent_builder_application

Length of output: 1377


🏁 Script executed:

# Check line 10 for the import
sed -n '1,15p' convex/awsDeployment.ts | cat -n

Repository: MikePfunk28/agent_builder_application

Length of output: 783


🏁 Script executed:

# Search for all calls to createDeploymentInternal
rg -n "createDeploymentInternal" convex/awsDeployment.ts -A 2 -B 2

Repository: MikePfunk28/agent_builder_application

Length of output: 804


🏁 Script executed:

# Check the executeCrossAccountDeploymentInternal and executeWebIdentityDeploymentInternal mentioned in scratchpad
rg -n "executeCrossAccountDeploymentInternal\|executeWebIdentityDeploymentInternal" convex/awsDeployment.ts | head -20

Repository: MikePfunk28/agent_builder_application

Length of output: 63


🏁 Script executed:

# Find where deployTier2 is called
rg -n "deployTier2" convex/awsDeployment.ts -B 2 -A 2

Repository: MikePfunk28/agent_builder_application

Length of output: 588


🏁 Script executed:

# Check the context where deployTier2 is called (around line 88)
sed -n '75,100p' convex/awsDeployment.ts | cat -n

Repository: MikePfunk28/agent_builder_application

Length of output: 1411


🏁 Script executed:

# Check the function signature that contains this code (around line 75)
sed -n '40,80p' convex/awsDeployment.ts | cat -n

Repository: MikePfunk28/agent_builder_application

Length of output: 1914


🏁 Script executed:

# Check what getAuthUserId returns
rg -n "function getAuthUserId\|const getAuthUserId\|export.*getAuthUserId" convex/awsDeployment.ts -A 5

Repository: MikePfunk28/agent_builder_application

Length of output: 63


🏁 Script executed:

# Search for getAuthUserId definition more broadly
rg -n "getAuthUserId" convex/awsDeployment.ts

Repository: MikePfunk28/agent_builder_application

Length of output: 445


🏁 Script executed:

# Check imports at the top of the file more carefully
sed -n '1,50p' convex/awsDeployment.ts | cat -n

Repository: MikePfunk28/agent_builder_application

Length of output: 2237


🏁 Script executed:

# Verify the return type of getAuthUserId by checking how it's used throughout
# and looking for null checks
sed -n '44,90p' convex/awsDeployment.ts | cat -n

Repository: MikePfunk28/agent_builder_application

Length of output: 2189


🏁 Script executed:

# Check for all calls to deployTier2
rg -n "deployTier2\(" convex/awsDeployment.ts

Repository: MikePfunk28/agent_builder_application

Length of output: 222


🏁 Script executed:

# Verify how getAuthUserId is actually typed in the Convex auth library
# by checking if there are any .d.ts files or checking the node_modules
find . -path "*/node_modules/@convex-dev/auth*" -name "*.d.ts" 2>/dev/null | head -5

Repository: MikePfunk28/agent_builder_application

Length of output: 63


🏁 Script executed:

# Check how userId is used within deployTier2 - particularly when passed to createDeploymentInternal
sed -n '989,1010p' convex/awsDeployment.ts | cat -n

Repository: MikePfunk28/agent_builder_application

Length of output: 1064


🏁 Script executed:

# Also check if there are any TypeScript errors or types in comments
rg -n "@convex-dev/auth" . --type=ts -A 2 -B 2 | head -30

Repository: MikePfunk28/agent_builder_application

Length of output: 1652


🏁 Script executed:

# Check all the internal action/mutation signatures that accept userId
rg -n "userId: (string|any)" convex/awsDeployment.ts | head -20

Repository: MikePfunk28/agent_builder_application

Length of output: 166


🏁 Script executed:

# Check all internal action/mutation function signatures to see which ones accept userId parameter
rg -n "async function" convex/awsDeployment.ts | grep -E "deployTier|executeWebIdentity|executeCrossAccount"

Repository: MikePfunk28/agent_builder_application

Length of output: 257


🏁 Script executed:

# Get the full signatures of these functions
sed -n '950,955p' convex/awsDeployment.ts | cat -n

Repository: MikePfunk28/agent_builder_application

Length of output: 417


🏁 Script executed:

# Check executeWebIdentityDeploymentInternal
rg -n "executeWebIdentityDeploymentInternal" convex/awsDeployment.ts -A 5 | head -20

Repository: MikePfunk28/agent_builder_application

Length of output: 571


🏁 Script executed:

# Check executeCrossAccountDeploymentInternal as mentioned in the review scratchpad
rg -n "executeCrossAccountDeploymentInternal" convex/awsDeployment.ts -A 5 | head -30

Repository: MikePfunk28/agent_builder_application

Length of output: 312


deployTier2 still accepts userId: string, creating a type mismatch with createDeploymentInternal.

createDeploymentInternal requires userId: v.id("users") (line 708), but deployTier2 (line 989) declares userId: string and passes it to this mutation (line 1000). While this works at runtime because userId originates from getAuthUserId() which returns Id<"users">, it defeats the purpose of the tightened typing.

Additionally, executeWebIdentityDeploymentInternal (line 1138) and executeCrossAccountDeploymentInternal (line 1063) also declare userId: v.string(), creating further inconsistency in the internal deployment flow.

🔧 Proposed fixes
-async function deployTier2( ctx: any, args: any, userId: string ): Promise<any> {
+async function deployTier2( ctx: any, args: any, userId: Id<"users"> ): Promise<any> {

Also update the internal actions:

-    userId: v.string(),
+    userId: v.id( "users" ),
🤖 Prompt for AI Agents
In `@convex/awsDeployment.ts` at line 707, The deployTier2 →
createDeploymentInternal type mismatch: change deployTier2's userId parameter
(and any internal actions like executeWebIdentityDeploymentInternal and
executeCrossAccountDeploymentInternal) from plain string/v.string() to the
user-id type expected by createDeploymentInternal (use v.id("users") /
Id<"users"> as used in createDeploymentInternal) so the passed userId is
strongly typed; update the function signatures and any internal action/mutation
validators and callers to accept and propagate v.id("users") instead of string
to keep types consistent end-to-end (search for deployTier2,
createDeploymentInternal, executeWebIdentityDeploymentInternal,
executeCrossAccountDeploymentInternal to locate all sites to change).

- Introduced a `source` property to distinguish between built-in and user-defined MCP servers.
- Updated interfaces and types across the MCP server management codebase to accommodate the new `source` property.
- Modified the `listMCPServers` query to include the `source` attribute for user-defined servers.
- Adjusted the MCP management panel to conditionally render action buttons based on the server's source.
- Ensured compatibility with both string synthetic IDs for built-in servers and Convex IDs for user-defined servers.
- Updated forms and selectors to handle the new `source` property and maintain type safety.
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Feb 12, 2026

Deploying agent-builder-application with  Cloudflare Pages  Cloudflare Pages

Latest commit: 9a8e7cd
Status: ✅  Deploy successful!
Preview URL: https://d7f02845.agent-builder-application.pages.dev
Branch Preview URL: https://012-visual-builder-update.agent-builder-application.pages.dev

View logs

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
convex/mcpConfig.ts (1)

266-318: ⚠️ Potential issue | 🔴 Critical

Fix v.object({}) to accept environment variable objects.

v.object({}) in Convex validates that an object has exactly zero properties. This causes validation to fail when the UI form sends environment variables (e.g., { AWS_REGION: "us-east-1" }), preventing users from saving MCP servers with env vars. The same issue exists in updateMCPServer.

The database schema already expects arbitrary env maps (with comment: "Accepts arbitrary env maps (e.g. { "PATH": "/usr/bin" })"), and the codebase uses v.record(v.string(), v.string()) elsewhere for this exact pattern.

🐛 Proposed fix
  args: {
    name: v.string(),
    command: v.string(),
    args: v.array( v.string() ),
-   env: v.optional( v.object( {} ) ),
+   env: v.optional( v.record( v.string(), v.string() ) ),
    disabled: v.optional( v.boolean() ),
    timeout: v.optional( v.number() ),
  },

Apply the same fix to updateMCPServer:

    updates: v.object( {
      name: v.optional( v.string() ),
      command: v.optional( v.string() ),
      args: v.optional( v.array( v.string() ) ),
-     env: v.optional( v.object( {} ) ),
+     env: v.optional( v.record( v.string(), v.string() ) ),
      disabled: v.optional( v.boolean() ),
      timeout: v.optional( v.number() ),
    } ),
🤖 Fix all issues with AI agents
In `@convex/mcpClient.ts`:
- Around line 522-523: Remove the unused dynamic import that shadows the
module-level api inside invokeBedrockAgentCore: delete the line "const { api } =
await import(\"./_generated/api.js\")" and any leftover references to that local
binding so the function uses the module-level api import instead; after removal,
run build/tests to ensure there are no remaining references or linter errors.

In `@convex/mcpConfig.ts`:
- Around line 505-530: The docstring/comment for getMCPServerById says "internal
query" but the export uses query(), exposing it publicly; either change the
comment to reflect that getMCPServerById is a public query or convert the
exported symbol to an internal query by replacing query(...) with
internalQuery(...) (or the project's internal-query helper) so the handler
remains server-only; update any import/usage accordingly and keep the function
name getMCPServerById and its auth/authorization logic unchanged.
- Around line 228-261: The fallback branch in getMCPServerByNameInternal (inside
the handler when args.userId is falsy) currently queries the mcpServers table
and returns the first record matching name, which can leak another user's
server; change the logic so that when args.userId is not provided the handler
returns null (or otherwise restricts results to non-user/scoped entries) instead
of querying by name across all users — update the code paths that reference
args.userId and the mcpServers query to short-circuit to null (or add an
explicit public/source filter) and adjust any type/return expectations
accordingly.
🧹 Nitpick comments (3)
convex/mcpClient.ts (1)

254-263: as any cast weakens the type guard — consider narrowing the query return type instead.

isDbMcpServer(server as any) works at runtime but defeats compile-time safety. The server variable from the query is untyped because runQuery returns any-ish. A cleaner approach is to cast to MCPServerEntry (the union) rather than any, so TypeScript still checks the guard's input.

♻️ Suggested change
-      if ( isDbMcpServer( server as any ) ) {
+      if ( isDbMcpServer( server as MCPServerEntry ) ) {

(Apply in both locations — lines 257 and 300. You'll also need to import MCPServerEntry from ./mcpConfig.)

Also applies to: 299-306

src/components/MCPManagementPanel.tsx (1)

202-233: Good: source-based UI gating for action buttons.

System servers correctly show only a "System" badge, while user servers expose Test/Edit/Delete actions. The as Id<"mcpServers"> casts on lines 207 and 224 are safe here because they're only reachable when server.source === 'user'.

One nit: line 217 uses handleEdit(server as any) — consider casting to the MCPServer interface instead of any to preserve some type safety.

src/components/MCPServerForm.tsx (1)

8-20: Type widening accepted but _id is passed unchecked to a mutation that requires Id<"mcpServers">.

On line 124, server._id is passed as serverId to updateMCPServer, which validates with v.id("mcpServers"). If a system server (with a plain string _id like "system_ollama") were ever passed to this form, the mutation would throw at runtime.

Currently this is safe because MCPManagementPanel gates the edit button behind source === 'user', but nothing in the form itself prevents misuse. Consider adding a guard or narrowing the type for edit mode.

🛡️ Optional: add a runtime guard
         if (isEditMode) {
+          if (server.source === 'system') {
+            toast.error('System servers cannot be edited');
+            return;
+          }
           // Update existing server
           await updateServer({
-            serverId: server._id,
+            serverId: server._id as Id<"mcpServers">,

Comment on lines +522 to 523
const { api } = await import( "./_generated/api.js" );

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Unused dynamic import of api — dead code.

const { api } = await import("./_generated/api.js") is imported but never used within invokeBedrockAgentCore. It also shadows the module-level api import (line 5). Remove it.

🧹 Proposed fix
-    // Get Cognito JWT token
-    const { api } = await import( "./_generated/api.js" );
-
-    // Import action runner - this is a workaround since we're in a non-Convex context
-    // In production, you'd inject the ctx or use a proper service
+    // Get Cognito JWT token
🤖 Prompt for AI Agents
In `@convex/mcpClient.ts` around lines 522 - 523, Remove the unused dynamic import
that shadows the module-level api inside invokeBedrockAgentCore: delete the line
"const { api } = await import(\"./_generated/api.js\")" and any leftover
references to that local binding so the function uses the module-level api
import instead; after removal, run build/tests to ensure there are no remaining
references or linter errors.

Comment on lines +228 to +261
export const getMCPServerByNameInternal = internalQuery( {
args: {
serverName: v.string(),
userId: v.optional(v.id("users")),
userId: v.optional( v.id( "users" ) ),
},
handler: async (ctx, args) => {
handler: async ( ctx, args ) => {
// Check if it's a built-in server first
const builtInServer = BUILT_IN_MCP_SERVERS.find(s => s.name === args.serverName);
if (builtInServer) {
const builtInServer = BUILT_IN_MCP_SERVERS.find( s => s.name === args.serverName );
if ( builtInServer ) {
return builtInServer;
}

if (args.userId) {
if ( args.userId ) {
// Get server by name for specific user
const userId = args.userId; // Type narrowing
const server = await ctx.db
.query("mcpServers")
.withIndex("by_user_and_name", (q) =>
q.eq("userId", userId).eq("name", args.serverName)
const serverRaw = await ctx.db
.query( "mcpServers" )
.withIndex( "by_user_and_name", ( q ) =>
q.eq( "userId", userId ).eq( "name", args.serverName )
)
.first();
return server;
if ( !serverRaw ) return null;
return { ...serverRaw, source: "user" } as DbMcpServer;
} else {
// Get first server by name (for other MCP servers)
const server = await ctx.db
.query("mcpServers")
.filter((q) => q.eq(q.field("name"), args.serverName))
const serverRaw = await ctx.db
.query( "mcpServers" )
.filter( ( q ) => q.eq( q.field( "name" ), args.serverName ) )
.first();
return server;
if ( !serverRaw ) return null;
return { ...serverRaw, source: "user" } as DbMcpServer;
}
},
});
} );
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fallback path (no userId) returns the first matching server from any user.

Lines 253-258: when args.userId is not provided, the query fetches the first mcpServers record matching the name across all users. This could return another user's server configuration, which is a potential data leak for an internal query.

Consider whether this fallback is intentional. If so, document it clearly. If not, return null instead.

🤖 Prompt for AI Agents
In `@convex/mcpConfig.ts` around lines 228 - 261, The fallback branch in
getMCPServerByNameInternal (inside the handler when args.userId is falsy)
currently queries the mcpServers table and returns the first record matching
name, which can leak another user's server; change the logic so that when
args.userId is not provided the handler returns null (or otherwise restricts
results to non-user/scoped entries) instead of querying by name across all users
— update the code paths that reference args.userId and the mcpServers query to
short-circuit to null (or add an explicit public/source filter) and adjust any
type/return expectations accordingly.

Comment on lines +505 to +530
export const getMCPServerById = query( {
args: {
serverId: v.id("mcpServers"),
serverId: v.id( "mcpServers" ),
},
handler: async (ctx, args) => {
handler: async ( ctx, args ) => {
// Get Convex user document ID
const userId = await getAuthUserId(ctx);
const userId = await getAuthUserId( ctx );

if (!userId) {
throw new Error("Not authenticated");
if ( !userId ) {
throw new Error( "Not authenticated" );
}

const server = await ctx.db.get(args.serverId);
const server = await ctx.db.get( args.serverId );

if (!server) {
if ( !server ) {
return null;
}

// Verify ownership
if (server.userId !== userId) {
throw new Error("Not authorized to access this MCP server");
if ( server.userId !== userId ) {
throw new Error( "Not authorized to access this MCP server" );
}

return server;
},
});
} );
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Docstring says "internal query" but this is a public query.

Line 503: the comment says "Get MCP server by ID (internal query)" but line 505 uses query() (public). Either update the comment or convert to internalQuery if it's not meant to be client-accessible.

🤖 Prompt for AI Agents
In `@convex/mcpConfig.ts` around lines 505 - 530, The docstring/comment for
getMCPServerById says "internal query" but the export uses query(), exposing it
publicly; either change the comment to reflect that getMCPServerById is a public
query or convert the exported symbol to an internal query by replacing
query(...) with internalQuery(...) (or the project's internal-query helper) so
the handler remains server-only; update any import/usage accordingly and keep
the function name getMCPServerById and its auth/authorization logic unchanged.

@MikePfunk28 MikePfunk28 merged commit c359068 into main Feb 13, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

❌ Failed to execute test kd7db8d9jwtb9rb39ezzf3fecs7sv6be: t is not a constructor Add Branches

1 participant