Add dynamic MCP tools: list_indexes, index_repo, delete_index#15
Add dynamic MCP tools: list_indexes, index_repo, delete_index#15richhankins wants to merge 24 commits intomainfrom
Conversation
- Remove readonly from indexNames and indexes fields - Remove empty-index checks at initialization (allow zero indexes) - Add refreshIndexList() method to reload indexes from store - Add invalidateClient() method to clear cached SearchClient - Update MCPServerConfig.store to accept both IndexStoreReader and IndexStore Agent-Id: agent-49436113-35bf-4532-983a-fb01da818555
- Add list_indexes tool to ListToolsRequestSchema handler - Add handler in CallToolRequestSchema that refreshes index list and returns formatted metadata (name, type, identifier, syncedAt) - Returns helpful message when no indexes exist - Add withListIndexesReference() helper in tool-descriptions.ts - Update MCP server to use withListIndexesReference instead of embedding static index list in tool descriptions - Keep withIndexList() for CLI agent backward compatibility Agent-Id: agent-b187383f-2e44-4592-9ec9-6f0f4e0eafb6
- Add delete_index tool definition (only when store supports delete) - Add handler to delete index from store, refresh runner state, and invalidate cached client - Validates index exists before attempting delete - Returns appropriate error messages for missing index or unsupported store Agent-Id: agent-8cf675a5-fcdf-402a-a3a1-f3fa2a8c3f1b
Agent-Id: agent-8cf675a5-fcdf-402a-a3a1-f3fa2a8c3f1b Linked-Note-Id: 18ee4796-4931-48c2-80e4-e85d51fe2ccd
This tool allows creating/updating indexes dynamically from: - GitHub repositories (requires owner and repo) - GitLab repositories (requires project_id) - BitBucket repositories (requires workspace and repo) - Websites (requires url) Features: - Validates required parameters for each source type - Checks if store supports write operations - Refreshes runner state after indexing - Invalidates cached client for updated indexes - Returns success message with stats (type, filesIndexed, duration) Agent-Id: agent-4dd9b0b4-3ecf-4a2a-850a-49fd55aba0b5
Remove the empty-index check from the CLI so the MCP server can start even when no indexes exist. This enables the dynamic indexing workflow where users can call list_indexes (empty) then index_repo to create indexes on-demand.
Agent-Id: agent-9c6981c4-45dd-4c45-bec4-386d78c4bd30
Agent-Id: agent-49ab3e98-e947-4ed1-8796-9454c317bd15
Agent-Id: agent-e9ca9165-d91d-4f53-a17c-da25a2a71927 Linked-Note-Id: 8e3113fe-0ce3-4374-81ed-e0536c4bbe35
Agent-Id: agent-bb8e0202-acb6-4479-940b-8cb958aa88f1 Linked-Note-Id: 5039c3be-a4e3-4fed-a279-827b12453884
The agentManaged flag was correctly calculated at the top of both stdio and http handlers, but then recalculated incorrectly when passing to the MCP server config. This caused the --agent-managed flag to be ignored when combined with -i specs. Changes: - Line 56 (stdio): Use calculated agentManaged variable instead of recalculating - Line 130 (http): Use calculated agentManaged variable instead of recalculating This ensures --agent-managed -i s3://... correctly sets agentManaged: true in the server config. Agent-Id: agent-1fdc5885-eb2c-4a33-9451-323cf14bb29a
- Replace --agent-managed flag with --discovery in cmd-mcp.ts - Remove index_repo and delete_index tool handlers from mcp-server.ts - Delete layered-store.ts and layered-store.test.ts files - Keep list_indexes in both modes - Use withListIndexesReference() in discovery mode, withIndexList() in fixed mode - Update MCPServerConfig to use discovery flag instead of agentManaged - All tests pass, build succeeds Agent-Id: agent-84f44748-891f-477f-b3f3-c078cb5bd2c5
Agent-Id: agent-84f44748-891f-477f-b3f3-c078cb5bd2c5 Linked-Note-Id: 75b599ae-9ec2-4535-b5d2-f83cc77a9d6b
- Create ReadOnlyLayeredStore to merge local (FilesystemStore) and remote (CompositeStoreReader) indexes - Update cmd-mcp.ts to use ReadOnlyLayeredStore when both --discovery and -i flags are present - Add comprehensive tests for ReadOnlyLayeredStore (12 tests) - Export ReadOnlyLayeredStore from stores/index.ts Behavior: - ctxc mcp stdio --discovery -i s3://bucket/shared-index: Merges local + remote indexes - ctxc mcp stdio -i pytorch -i react: Fixed mode (no discovery) - ctxc mcp stdio --discovery: Discovery mode with local indexes only All tests pass (175 passed, 24 skipped) Agent-Id: agent-5834efcf-335b-415a-b8a9-19ed489133ed
Merge commits: - ff69f62: Fix search/list/read operations to use indexed commit ref (#13) - ef6a46a: feat: add consistent User-Agent string (#14) Resolved conflicts: - src/clients/mcp-server.ts: Kept Discovery mode logic while integrating User-Agent changes - src/clients/multi-index-runner.ts: Added clientUserAgent parameter and updateClientUserAgent method Fixed type errors from main branch: - Removed invalid clientUserAgent parameter from DirectContext.create/import calls - Removed invalid clientUserAgent parameter from AugmentLanguageModel config All tests pass and build succeeds.
The main branch code attempted to pass clientUserAgent to DirectContext.create/import and AugmentLanguageModel, but these APIs don't accept that parameter. Changes: - Removed clientUserAgent from DirectContext.create() call in indexer.ts - Removed clientUserAgent from DirectContext.import() calls in indexer.ts and search-client.ts - Removed clientUserAgent from AugmentLanguageModel config in cli-agent.ts The clientUserAgent is still stored in SearchClient and Indexer for future use, but is not passed to APIs that don't support it.
|
augment review |
🤖 Augment PR SummarySummary: This PR adds a read-only “discovery mode” to the MCP server so agents can dynamically discover available indexes at runtime via a new Changes:
Technical Notes: Discovery mode is intentionally read-only; index creation/deletion remains a CLI responsibility ( 🤖 Was this summary useful? React with 👍 or 👎 |
| } else { | ||
| // No --index: use default store, list all indexes | ||
| // Discovery mode only: use FilesystemStore | ||
| store = new FilesystemStore(); |
There was a problem hiding this comment.
This branch sets up discovery mode without requiring any indexes, but MultiIndexRunner.create() still throws when the store has zero valid indexes, so ctxc mcp ... can still fail to start on a fresh install. If discovery mode is intended to allow “start empty and index later”, consider aligning runner/server startup behavior with that expectation.
Severity: high
Other Locations
src/bin/cmd-mcp.ts:106
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
src/clients/mcp-server.ts
Outdated
|
|
||
| // Handle list_indexes separately (no index_name required) | ||
| if (name === "list_indexes") { | ||
| await runner.refreshIndexList(); |
There was a problem hiding this comment.
list_indexes is handled before the surrounding try/catch, so failures in refreshIndexList() (e.g., store I/O errors) may escape and fail the MCP request differently than other tools. Consider applying the same error handling behavior as the search/list_files/read_file branches.
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
src/clients/mcp-server.ts
Outdated
| const { indexes } = runner; | ||
| if (indexes.length === 0) { | ||
| return { | ||
| content: [{ type: "text", text: "No indexes available. Use index_repo to create one." }], |
There was a problem hiding this comment.
This response says “Use index_repo to create one”, but this server doesn’t expose index_repo (and the PR description says index management is via CLI in discovery mode). Consider pointing users to the appropriate ctxc index ... CLI workflow instead to avoid confusion.
Severity: low
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
src/clients/mcp-server.ts
Outdated
| version?: string; | ||
| /** | ||
| * Discovery mode flag. | ||
| * When true: use withListIndexesReference (no enum in schemas), no write tools |
There was a problem hiding this comment.
The discovery docstring says “write tools available” when false, but this server doesn’t currently expose any write tools (e.g., index_repo/delete_index). Consider updating the comment to match actual tool availability to prevent misleading integrators.
Severity: low
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
…docs Agent-Id: agent-8f86dfa1-8342-4d24-9127-87412acf9740
|
augment review All 4 review comments from the previous review have been addressed:
|
| } | ||
| } | ||
|
|
||
| this.indexNames = newIndexNames; |
There was a problem hiding this comment.
refreshIndexList() overwrites this.indexNames from store.list(), which can override an initially configured indexNames allowlist (fixed mode) and unintentionally widen what getClient() will accept after a refresh. If fixed mode is meant to stay static, consider keeping the originally requested list separate from the refreshed/discovered list.
Severity: medium
Other Locations
src/clients/mcp-server.ts:300
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| server.setRequestHandler(ListToolsRequestSchema, async () => { | ||
| const tools: Tool[] = [ | ||
| { | ||
| name: "list_indexes", |
There was a problem hiding this comment.
The new list_indexes tool + the discovery-vs-fixed behavior (no enum vs enum for index_name) doesn’t appear to be covered by existing MCP server tests; a focused unit test here could help prevent regressions.
Severity: low
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
In fixed mode (when indexNames are specified via CLI), calling list_indexes would trigger refreshIndexList() which overwrote this.indexNames with ALL indexes from the store, bypassing the original allowlist. Changes: - Add readonly originalIndexNames field to MultiIndexRunner to store the original allowlist in fixed mode - In constructor, save the original indexNames if provided - In refreshIndexList(), filter results to only include indexes in the original allowlist when in fixed mode This ensures that in fixed mode (-i pytorch -i react), list_indexes only returns the originally specified indexes, even if other indexes exist in the store. In discovery mode (--discovery), all indexes are returned as before. Tests added to verify: - Discovery mode includes all indexes from store - Fixed mode respects the original allowlist - Fixed mode handles missing indexes gracefully Agent-Id: agent-cf0760a8-47e4-4742-a697-7de4bf2367e6
In fixed mode, refreshIndexList() is now a no-op. The index list is completely static and never changes, even if indexes are deleted from the store. This fixes the issue where: 1. Fixed mode starts with [-i pytorch -i react] 2. User deletes pytorch from the store 3. Agent calls list_indexes → triggers refreshIndexList() 4. Previously: indexNames would shrink to ["react"] 5. Now: indexNames remains ["pytorch", "react"] In discovery mode, refreshIndexList() still refreshes from the store to pick up new/deleted indexes. Changes: - src/clients/multi-index-runner.ts: Make refreshIndexList() a no-op in fixed mode - src/clients/multi-index-runner.test.ts: Update test to expect static list in fixed mode All tests pass, build succeeds. Agent-Id: agent-0ac871dd-5181-4ed9-84d1-446aa4b19c81
|
augment review Changes since last review:
|
src/clients/multi-index-runner.ts
Outdated
| } | ||
|
|
||
| return new MultiIndexRunner(store, validIndexNames, indexes, searchOnly, config.clientUserAgent); | ||
| // Allow empty - server can start with no indexes and user can add via CLI |
There was a problem hiding this comment.
MultiIndexRunner.create() now allows returning an empty validIndexNames even when config.indexNames was explicitly provided (fixed mode), which can make the server start “successfully” but reject all tool calls. Consider whether fixed mode should still fail fast when none of the requested indexes can be loaded (e.g., due to corruption/partial state).
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| } | ||
| } | ||
|
|
||
| this.indexNames = newIndexNames; |
There was a problem hiding this comment.
In discovery mode, refreshIndexList() updates indexNames/indexes but doesn’t prune or invalidate entries in clientCache, so a re-indexed name could keep serving stale data (and removed indexes can remain resident in memory). Consider whether refresh should reconcile the cache with the refreshed list/metadata.
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| } | ||
|
|
||
| // Import DirectContext from saved state | ||
| this.context = await DirectContext.import(this.state.contextState, { |
There was a problem hiding this comment.
SearchClientConfig.clientUserAgent is still accepted/stored, but it’s no longer passed into DirectContext.import(), so the option appears to be a no-op while docs say it’s “sent to the Augment API”. Consider either wiring it through again (if supported) or removing/updating the option/docs to avoid misleading callers.
Severity: low
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
Agent-Id: agent-42c9e35e-86c9-4eea-abf2-253ad24da923
Agent-Id: agent-81feb144-5dc6-4a1f-9cd1-7e74cd26e3d1
Agent-Id: agent-5dbe4a24-0e78-4075-b85c-d6ff632c88c7
Agent-Id: agent-9eaf775a-04cb-45c8-bc31-4705c63e8b54
|
augment review |
|
|
||
| if (indexSpecs && indexSpecs.length > 0) { | ||
| // Parse index specs and create composite store | ||
| if (discoveryFlag && indexSpecs && indexSpecs.length > 0) { |
There was a problem hiding this comment.
--discovery is currently only honored when --index is also provided; ctxc mcp stdio --discovery (without -i) falls through to the “No flags” branch and sets discovery = false, so discovery mode never activates.
Severity: high
Other Locations
src/bin/cmd-mcp.ts:98
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| it("handles errors gracefully and returns isError: true", async () => { | ||
| const store = createMockStore(createMockState()); | ||
| // Mock store.list() to throw an error | ||
| store.list = vi.fn().mockRejectedValue(new Error("Store error")); |
There was a problem hiding this comment.
This test mocks store.list() to reject before calling createMCPServer(), but createMCPServer()/MultiIndexRunner.create() calls store.list() during startup, so the server will likely fail to construct and the test won’t actually exercise the list_indexes error handling path.
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| server.setRequestHandler(ListToolsRequestSchema, async () => { | ||
| const tools: Tool[] = [ | ||
| { | ||
| name: "list_indexes", |
There was a problem hiding this comment.
PR title mentions index_repo/delete_index, but this server only exposes list_indexes (and the PR description says write tools are unavailable). Consider aligning the PR title (and any related docs) with the actual tool set to avoid confusion for integrators.
Severity: low
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
Overview
This PR adds dynamic index discovery to the context-connectors MCP server, enabling agents to see new indexes as they're added by users without restarting the MCP server.
Two Modes
ctxc mcp stdio -i pytorch -i reactctxc mcp stdio --discoveryTool Availability
searchenumenumlist_filesenumenumread_fileenumenumlist_indexesindex_repodelete_indexNote: In Discovery mode, users manage indexes via CLI (
ctxc index ...commands), not via MCP tools.Key Changes
1. Dynamic MultiIndexRunner
readonlyfromindexNamesandindexesrefreshIndexList()method to reload from storeinvalidateClient(name)to clear cached SearchClient2. list_indexes Tool
3. Mode-Based Tool Descriptions
enumin tool schemas for zero-tool-call discoveryenum, agent must calllist_indexes4. CLI Flag
--discoveryflag for explicit mode selection-iflags use CompositeStoreReader in fixed modeBenefits
Example Workflow
Testing
Future Work
Discovery mode is an incremental step. A future PR can add full agent-managed mode with:
index_repoanddelete_indextools