Skip to content

Conversation

@guitavano
Copy link
Contributor

@guitavano guitavano commented Oct 28, 2025

reindex tools tool

Summary by CodeRabbit

  • New Features
    • Added a new tool for reindexing application tools, which validates access, fetches the latest tools from the app's connection, and updates the tool registry. Returns summary counts of created, updated, and deleted tools for the operation.

- Introduced the `reindexAppTools` function in the registry API to fetch and update tool definitions for a specified app.
- Implemented input and output schemas for the function, ensuring proper validation and response structure.
- Enhanced error handling to manage cases where the app ID is not found and to log errors during tool fetching.
- Streamlined the process of deleting old tools and inserting fresh definitions, improving the overall efficiency of tool management.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

Walkthrough

A new reindexAppTools method is added to the registry API and exposed in PROJECT_TOOLS. This method refreshes an app's tools by fetching fresh tools from its MCP connection, deleting existing records, and reinserting them with current timestamps, returning operation counts.

Changes

Cohort / File(s) Summary
Tool Registration
packages/sdk/src/mcp/index.ts
Adds registryAPI.reindexAppTools to the PROJECT_TOOLS tool set, making the method available for project-scoped execution.
Registry API Implementation
packages/sdk/src/mcp/registry/api.ts
Implements new reindexAppTools method that accepts appId as input, validates workspace and resource access, fetches fresh tools from the app's MCP connection, deletes all existing app tools, reinserts them with updated timestamps, and returns operation summary (appId, toolsCount, deletedCount, upsertedCount).

Sequence Diagram

sequenceDiagram
    participant Client
    participant API as reindexAppTools
    participant DB as Database
    participant MCP as MCP Connection
    
    Client->>API: reindexAppTools(appId)
    API->>API: Validate workspace/resource access
    API->>DB: Verify app exists
    rect rgb(200, 220, 240)
    note over API,MCP: Fetch fresh tools
    API->>MCP: Get tools for app
    MCP-->>API: Tool definitions
    end
    
    rect rgb(255, 240, 200)
    note over API,DB: Delete & reinsert
    API->>DB: Delete existing tools for app
    API->>DB: Insert fetched tools with updated_at
    DB-->>API: Operation results
    end
    
    API-->>Client: {appId, toolsCount, deletedCount, upsertedCount}
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

  • Focus on the reindexAppTools method logic in api.ts to ensure delete/insert transactional integrity and proper count tracking
  • Verify that access validation and app existence checks are correctly ordered before database operations
  • Confirm that timestamps are properly set during tool reinsertion

Poem

🐰 Tools were scattered, now they're bright,
Reindexing through the moonlit night,
Delete the old, bring in the new,
Fresh timestamps for a better view!
With counts returned, our work is done,
App tools shining bright as sun.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The provided pull request description "reindex tools tool" is extremely minimal and does not follow the required template structure. The template specifies required sections including "What is this contribution about?" (explaining changes and why they're needed), "Screenshots/Demonstration", and a "Review Checklist". The submitted description lacks all these sections entirely and provides no meaningful explanation of the changes, their purpose, testing status, or impact on the codebase. This represents a largely incomplete pull request description that fails to meet the repository's documentation standards.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "feat(api): add reindexAppTools function to refresh app tools from MCP" is clear, specific, and directly related to the main change introduced in this pull request. It accurately describes that a new function named reindexAppTools has been added to the registry API to refresh app tools from MCP connections. The title is concise, uses proper semantic commit format, and provides enough context for someone reviewing the git history to understand the primary purpose of the changeset.
✨ 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 tavano/reindex-tool

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

@cloudflare-workers-and-pages
Copy link

Deploying decocms-admin-frontend with  Cloudflare Pages  Cloudflare Pages

Latest commit: 21893b3
Status: ✅  Deploy successful!
Preview URL: https://2c4f596c.admin-decocms-frontend.pages.dev
Branch Preview URL: https://tavano-reindex-tool.admin-decocms-frontend.pages.dev

View logs

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/sdk/src/mcp/registry/api.ts (1)

624-629: Consider renaming upsertedCount for accuracy.

Since the current implementation uses insert (not upsert) and upsertedCount equals toolsCount, the name is misleading. If you adopt the upsert approach, this naming would be accurate; otherwise, consider insertedCount.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a366241 and 21893b3.

📒 Files selected for processing (2)
  • packages/sdk/src/mcp/index.ts (1 hunks)
  • packages/sdk/src/mcp/registry/api.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
packages/sdk/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/structure.mdc)

Place shared SDK code for data access and MCP integrations under packages/sdk/src

Files:

  • packages/sdk/src/mcp/index.ts
  • packages/sdk/src/mcp/registry/api.ts
packages/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep shared logic (UI kit, SDK, runtime, CLI tooling) under packages/

Files:

  • packages/sdk/src/mcp/index.ts
  • packages/sdk/src/mcp/registry/api.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Biome formatting: use two-space indentation and double quotes
Keep imports sorted
Name hooks and utility functions using camelCase

Files:

  • packages/sdk/src/mcp/index.ts
  • packages/sdk/src/mcp/registry/api.ts
packages/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Enforce kebab-case filenames in shared packages

Files:

  • packages/sdk/src/mcp/index.ts
  • packages/sdk/src/mcp/registry/api.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer specific TypeScript types over any

Files:

  • packages/sdk/src/mcp/index.ts
  • packages/sdk/src/mcp/registry/api.ts
🧬 Code graph analysis (1)
packages/sdk/src/mcp/registry/api.ts (3)
packages/sdk/src/mcp/context.ts (1)
  • createTool (360-362)
packages/sdk/src/mcp/assertions.ts (1)
  • assertWorkspaceResourceAccess (125-314)
packages/ai/src/mcp.ts (1)
  • listToolsByConnectionType (261-320)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (4)
packages/sdk/src/mcp/index.ts (1)

155-155: LGTM!

The new reindexAppTools tool is correctly added to the PROJECT_TOOLS array, following the same pattern as other registry tools.

packages/sdk/src/mcp/registry/api.ts (3)

543-559: LGTM!

The function definition and schemas are well-structured, following the established patterns in this file. The input/output schemas provide clear validation and documentation.


560-575: LGTM!

Authorization and app validation are properly implemented. The error message clearly indicates when an app is not found.


588-595: LGTM!

Current tool counting logic is correct and includes proper error handling.

Comment on lines +577 to +585
// Fetch fresh tools from the MCP connection
const { tools = [] } = await listToolsByConnectionType(
app.connection,
c,
true,
).catch((e) => {
console.error("Error fetching tools for reindex:", e);
return { tools: [] };
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Potential data loss if tool fetch fails.

If listToolsByConnectionType fails and returns an empty tools array, the function proceeds to delete all existing tools (lines 598-605) and insert nothing, leaving the app with zero tools. This silent failure could cause significant data loss for apps that previously had tools.

Consider failing fast when tool fetch returns empty:

-    const { tools = [] } = await listToolsByConnectionType(
-      app.connection,
-      c,
-      true,
-    ).catch((e) => {
-      console.error("Error fetching tools for reindex:", e);
-      return { tools: [] };
-    });
+    const result = await listToolsByConnectionType(
+      app.connection,
+      c,
+      true,
+    ).catch((e) => {
+      console.error("Error fetching tools for reindex:", e);
+      throw new Error(`Failed to fetch tools for app "${appId}": ${e.message}`);
+    });
+
+    const { tools = [] } = result;
+
+    // Optionally: warn if legitimately no tools
+    if (tools.length === 0) {
+      console.warn(`App "${appId}" has no tools after reindex`);
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Fetch fresh tools from the MCP connection
const { tools = [] } = await listToolsByConnectionType(
app.connection,
c,
true,
).catch((e) => {
console.error("Error fetching tools for reindex:", e);
return { tools: [] };
});
// Fetch fresh tools from the MCP connection
const result = await listToolsByConnectionType(
app.connection,
c,
true,
).catch((e) => {
console.error("Error fetching tools for reindex:", e);
throw new Error(`Failed to fetch tools for app "${appId}": ${e.message}`);
});
const { tools = [] } = result;
// Optionally: warn if legitimately no tools
if (tools.length === 0) {
console.warn(`App "${appId}" has no tools after reindex`);
}
🤖 Prompt for AI Agents
In packages/sdk/src/mcp/registry/api.ts around lines 577 to 585, the current
catch for listToolsByConnectionType swallows errors and returns an empty tools
array which can lead to deleting all existing tools later; change the flow to
fail fast by rethrowing the caught error (or return a rejected promise) instead
of returning { tools: [] }, and add an explicit check after the fetch: if the
fetched tools array is empty but the app currently has existing tools, abort
(throw) and log a clear error so we do not proceed to delete existing tools;
alternatively, only proceed with deletion when the fetch succeeded and returned
a non-empty array (or when the app had no tools to begin with).

Comment on lines +597 to +605
// Delete all existing tools for this app
if (currentToolCount > 0) {
const { error: deleteError } = await c.db
.from(DECO_CHAT_REGISTRY_APPS_TOOLS_TABLE)
.delete()
.eq("app_id", appId);

if (deleteError) throw deleteError;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Missing transaction boundary risks data loss.

The delete and insert operations (lines 598-605 and 607-622) are not wrapped in a transaction. If the insert operation fails after deletion succeeds, all tools will be lost with no rollback mechanism.

Additionally, this "delete-all then insert-all" approach differs from publishApp (lines 503-516), which calculates a diff and only deletes tools that are no longer present. The diff approach is safer and more efficient.

Consider using a transaction and the diff approach:

+    const currentToolNames = new Set(currentTools?.map((t) => t.name) || []);
+    const newToolNames = new Set(tools.map((t) => t.name));
+
+    // Find tools to delete (exist in DB but not in new tools)
+    const toolsToDelete = Array.from(currentToolNames).filter(
+      (name) => !newToolNames.has(name),
+    );
+
-    // Delete all existing tools for this app
-    if (currentToolCount > 0) {
+    // Delete old tools that are no longer present
+    if (toolsToDelete.length > 0) {
       const { error: deleteError } = await c.db
         .from(DECO_CHAT_REGISTRY_APPS_TOOLS_TABLE)
         .delete()
-        .eq("app_id", appId);
+        .eq("app_id", appId)
+        .in("name", toolsToDelete);

       if (deleteError) throw deleteError;
     }

Then use upsert instead of insert (see next comment).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Delete all existing tools for this app
if (currentToolCount > 0) {
const { error: deleteError } = await c.db
.from(DECO_CHAT_REGISTRY_APPS_TOOLS_TABLE)
.delete()
.eq("app_id", appId);
if (deleteError) throw deleteError;
}
const currentToolNames = new Set(currentTools?.map((t) => t.name) || []);
const newToolNames = new Set(tools.map((t) => t.name));
// Find tools to delete (exist in DB but not in new tools)
const toolsToDelete = Array.from(currentToolNames).filter(
(name) => !newToolNames.has(name),
);
// Delete old tools that are no longer present
if (toolsToDelete.length > 0) {
const { error: deleteError } = await c.db
.from(DECO_CHAT_REGISTRY_APPS_TOOLS_TABLE)
.delete()
.eq("app_id", appId)
.in("name", toolsToDelete);
if (deleteError) throw deleteError;
}
🤖 Prompt for AI Agents
In packages/sdk/src/mcp/registry/api.ts around lines 597 to 605 (and following
insert block 607-622), the code deletes all existing tools then inserts new ones
without a transaction and without computing a diff; wrap the delete+insert
sequence in a single database transaction so deletes roll back on insert
failure, and replace the blind delete-all approach with the same diff logic used
in publishApp: compute which tools to delete, update, or add (delete only those
absent in the new list), then perform deletes/inserts/updates inside the
transaction and finally use upsert instead of raw insert for inserts/updates to
avoid conflicts and partial failures.

Comment on lines +607 to +622
// Insert all fresh tools
const now = new Date().toISOString();
await Promise.all(
tools.map((tool) =>
c.db.from(DECO_CHAT_REGISTRY_APPS_TOOLS_TABLE).insert({
// @ts-expect-error - this is a valid field
app_id: appId,
name: tool.name,
description: tool.description || null,
input_schema: tool.inputSchema || null,
output_schema: tool.outputSchema || null,
metadata: null,
updated_at: now,
}),
),
);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Prefer upsert for consistency with publishApp pattern.

The function uses insert (line 611) whereas publishApp uses upsert (line 521). If you adopt the diff approach (per previous comment), using upsert would be more appropriate and consistent with the established pattern.

Apply this diff (assumes diff approach from previous comment):

-    // Insert all fresh tools
+    // Upsert new/updated tools
     const now = new Date().toISOString();
     await Promise.all(
       tools.map((tool) =>
-        c.db.from(DECO_CHAT_REGISTRY_APPS_TOOLS_TABLE).insert({
+        c.db.from(DECO_CHAT_REGISTRY_APPS_TOOLS_TABLE).upsert(
+          {
             // @ts-expect-error - this is a valid field
             app_id: appId,
             name: tool.name,
             description: tool.description || null,
             input_schema: tool.inputSchema || null,
             output_schema: tool.outputSchema || null,
             metadata: null,
             updated_at: now,
-        }),
+          },
+          {
+            onConflict: "app_id,name",
+          },
+        ),
       ),
     );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Insert all fresh tools
const now = new Date().toISOString();
await Promise.all(
tools.map((tool) =>
c.db.from(DECO_CHAT_REGISTRY_APPS_TOOLS_TABLE).insert({
// @ts-expect-error - this is a valid field
app_id: appId,
name: tool.name,
description: tool.description || null,
input_schema: tool.inputSchema || null,
output_schema: tool.outputSchema || null,
metadata: null,
updated_at: now,
}),
),
);
// Upsert new/updated tools
const now = new Date().toISOString();
await Promise.all(
tools.map((tool) =>
c.db.from(DECO_CHAT_REGISTRY_APPS_TOOLS_TABLE).upsert(
{
// @ts-expect-error - this is a valid field
app_id: appId,
name: tool.name,
description: tool.description || null,
input_schema: tool.inputSchema || null,
output_schema: tool.outputSchema || null,
metadata: null,
updated_at: now,
},
{
onConflict: "app_id,name",
},
),
),
);

});

export const reindexAppTools = createTool({
name: "REGISTRY_REINDEX_APP_TOOLS",
Copy link
Collaborator

Choose a reason for hiding this comment

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

use REGISTRY_INDEX_APP_TOOLS

}),
),
handler: async ({ appId }, c) => {
await assertWorkspaceResourceAccess(c);
Copy link
Collaborator

Choose a reason for hiding this comment

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

you're duplicating the most part of the PUBLISH code. so you just need to query app and invoke PUBLISH tool. also you may want to check access by PUBLISH instead of creating a new policy.

assertWorkspaceResourceAccess("REGISTRY_PUBLISH_APP")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants