diff --git a/ccw/frontend/src/components/mcp/CcwToolsMcpCard.test.tsx b/ccw/frontend/src/components/mcp/CcwToolsMcpCard.test.tsx
index 9ead3b49..455b8d5e 100644
--- a/ccw/frontend/src/components/mcp/CcwToolsMcpCard.test.tsx
+++ b/ccw/frontend/src/components/mcp/CcwToolsMcpCard.test.tsx
@@ -41,6 +41,43 @@ describe('CcwToolsMcpCard', () => {
vi.clearAllMocks();
});
+ it('disables all tools when clicking "Disable All" button', async () => {
+ const { CcwToolsMcpCard } = await import('./CcwToolsMcpCard');
+ const onUpdateConfigMock = vi.fn();
+
+ render(
+ ,
+ { locale: 'en' }
+ );
+
+ const user = userEvent.setup();
+ // Expand the card
+ await act(async () => {
+ await user.click(screen.getByText(/CCW MCP Server|mcp\.ccw\.title/i));
+ });
+
+ // Find and click "Disable All" button
+ const disableAllButton = screen.getByRole('button', {
+ name: /Disable All|mcp\.ccw\.actions\.disableAll/i,
+ });
+ expect(disableAllButton).toBeEnabled();
+ await act(async () => {
+ await user.click(disableAllButton);
+ });
+
+ // Verify onUpdateConfig was called with empty enabledTools array
+ await waitFor(() => {
+ expect(onUpdateConfigMock).toHaveBeenCalledWith({ enabledTools: [] });
+ });
+ });
+
it('preserves enabledTools when saving config (Codex)', async () => {
const { CcwToolsMcpCard } = await import('./CcwToolsMcpCard');
const updateCodexMock = vi.mocked(apiMock.updateCcwConfigForCodex);
diff --git a/ccw/frontend/src/lib/api.ts b/ccw/frontend/src/lib/api.ts
index 58abdfbc..645d166c 100644
--- a/ccw/frontend/src/lib/api.ts
+++ b/ccw/frontend/src/lib/api.ts
@@ -4371,7 +4371,9 @@ function buildCcwMcpServerConfig(config: {
}): { command: string; args: string[]; env: Record } {
const env: Record = {};
- if (config.enabledTools && config.enabledTools.length > 0) {
+ // Only use default when enabledTools is undefined (not provided)
+ // When enabledTools is an empty array, set to empty string to disable all tools
+ if (config.enabledTools !== undefined) {
env.CCW_ENABLED_TOOLS = config.enabledTools.join(',');
} else {
env.CCW_ENABLED_TOOLS = 'write_file,edit_file,read_file,core_memory,ask_question,smart_search';
@@ -4455,11 +4457,20 @@ export async function fetchCcwMcpConfig(currentProjectPath?: string): Promise t.trim());
+ const enabledToolsStr = env.CCW_ENABLED_TOOLS;
+ let enabledTools: string[];
+ if (enabledToolsStr === undefined || enabledToolsStr === null) {
+ // No setting = use default tools
+ enabledTools = ['write_file', 'edit_file', 'read_file', 'core_memory', 'ask_question', 'smart_search'];
+ } else if (enabledToolsStr === '' || enabledToolsStr === 'all') {
+ // Empty string = all tools disabled, 'all' = default set (for backward compatibility)
+ enabledTools = enabledToolsStr === '' ? [] : ['write_file', 'edit_file', 'read_file', 'core_memory', 'ask_question', 'smart_search'];
+ } else {
+ // Comma-separated list
+ enabledTools = enabledToolsStr.split(',').map((t: string) => t.trim()).filter(Boolean);
+ }
return {
isInstalled: true,
@@ -4601,10 +4612,19 @@ export async function fetchCcwMcpConfigForCodex(): Promise {
}
const env = ccwServer.env || {};
- const enabledToolsStr = env.CCW_ENABLED_TOOLS || 'all';
- const enabledTools = enabledToolsStr === 'all'
- ? ['write_file', 'edit_file', 'read_file', 'core_memory', 'ask_question', 'smart_search']
- : enabledToolsStr.split(',').map((t: string) => t.trim());
+ // Note: CCW_ENABLED_TOOLS can be empty string (all tools disabled), 'all' (default set), or comma-separated list
+ const enabledToolsStr = env.CCW_ENABLED_TOOLS;
+ let enabledTools: string[];
+ if (enabledToolsStr === undefined || enabledToolsStr === null) {
+ // No setting = use default tools
+ enabledTools = ['write_file', 'edit_file', 'read_file', 'core_memory', 'ask_question', 'smart_search'];
+ } else if (enabledToolsStr === '' || enabledToolsStr === 'all') {
+ // Empty string = all tools disabled, 'all' = default set (for backward compatibility)
+ enabledTools = enabledToolsStr === '' ? [] : ['write_file', 'edit_file', 'read_file', 'core_memory', 'ask_question', 'smart_search'];
+ } else {
+ // Comma-separated list
+ enabledTools = enabledToolsStr.split(',').map((t: string) => t.trim()).filter(Boolean);
+ }
return {
isInstalled: true,
@@ -4630,7 +4650,9 @@ function buildCcwMcpServerConfigForCodex(config: {
}): { command: string; args: string[]; env: Record } {
const env: Record = {};
- if (config.enabledTools && config.enabledTools.length > 0) {
+ // Only use default when enabledTools is undefined (not provided)
+ // When enabledTools is an empty array, set to empty string to disable all tools
+ if (config.enabledTools !== undefined) {
env.CCW_ENABLED_TOOLS = config.enabledTools.join(',');
} else {
env.CCW_ENABLED_TOOLS = 'write_file,edit_file,read_file,core_memory,ask_question,smart_search';
diff --git a/ccw/frontend/src/stores/queueSchedulerStore.ts b/ccw/frontend/src/stores/queueSchedulerStore.ts
index 72317867..2c20b617 100644
--- a/ccw/frontend/src/stores/queueSchedulerStore.ts
+++ b/ccw/frontend/src/stores/queueSchedulerStore.ts
@@ -195,9 +195,21 @@ export const useQueueSchedulerStore = create()(
'loadInitialState'
);
} catch (error) {
+ // Silently ignore network errors (backend not connected)
+ // Only log non-network errors
const message = error instanceof Error ? error.message : 'Unknown error';
- console.error('[QueueScheduler] loadInitialState error:', message);
- set({ error: message }, false, 'loadInitialState/error');
+ const isNetworkError =
+ message.includes('Failed to fetch') ||
+ message.includes('NetworkError') ||
+ message.includes('Network request failed') ||
+ message.includes('ERR_CONNECTION_REFUSED') ||
+ message.includes('ERR_CONNECTION_RESET');
+
+ if (!isNetworkError) {
+ console.error('[QueueScheduler] loadInitialState error:', message);
+ set({ error: message }, false, 'loadInitialState/error');
+ }
+ // For network errors, keep state as-is without showing error
}
},
diff --git a/ccw/src/core/routes/auth-routes.ts b/ccw/src/core/routes/auth-routes.ts
index b5b1613b..b3d3559a 100644
--- a/ccw/src/core/routes/auth-routes.ts
+++ b/ccw/src/core/routes/auth-routes.ts
@@ -100,14 +100,16 @@ export async function handleAuthRoutes(ctx: RouteContext): Promise {
} else {
// Batch token response (pool pattern)
const tokens = tokenManager.generateTokens(sessionId, count);
- const firstToken = tokens[0];
+
+ // If no tokens generated (session at max capacity), force generate one
+ const firstToken = tokens.length > 0 ? tokens[0] : tokenManager.generateToken(sessionId);
// Set header and cookie with first token for compatibility
res.setHeader('X-CSRF-Token', firstToken);
setCsrfCookie(res, firstToken, 15 * 60);
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
res.end(JSON.stringify({
- tokens,
+ tokens: tokens.length > 0 ? tokens : [firstToken],
expiresIn: 15 * 60, // seconds
}));
}
diff --git a/codex-lens/src/codex_lens.egg-info/PKG-INFO b/codex-lens/src/codex_lens.egg-info/PKG-INFO
index 947ba20f..1cffe19e 100644
--- a/codex-lens/src/codex_lens.egg-info/PKG-INFO
+++ b/codex-lens/src/codex_lens.egg-info/PKG-INFO
@@ -8,53 +8,53 @@ Project-URL: Homepage, https://github.com/openai/codex-lens
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
-Requires-Dist: typer>=0.9
-Requires-Dist: rich>=13
-Requires-Dist: pydantic>=2.0
-Requires-Dist: tree-sitter>=0.20
-Requires-Dist: tree-sitter-python>=0.25
-Requires-Dist: tree-sitter-javascript>=0.25
-Requires-Dist: tree-sitter-typescript>=0.23
-Requires-Dist: pathspec>=0.11
-Requires-Dist: watchdog>=3.0
-Requires-Dist: ast-grep-py>=0.40.0
+Requires-Dist: typer~=0.9.0
+Requires-Dist: rich~=13.0.0
+Requires-Dist: pydantic~=2.0.0
+Requires-Dist: tree-sitter~=0.20.0
+Requires-Dist: tree-sitter-python~=0.25.0
+Requires-Dist: tree-sitter-javascript~=0.25.0
+Requires-Dist: tree-sitter-typescript~=0.23.0
+Requires-Dist: pathspec~=0.11.0
+Requires-Dist: watchdog~=3.0.0
+Requires-Dist: ast-grep-py~=0.40.0
Provides-Extra: semantic
-Requires-Dist: numpy>=1.24; extra == "semantic"
-Requires-Dist: fastembed>=0.2; extra == "semantic"
-Requires-Dist: hnswlib>=0.8.0; extra == "semantic"
+Requires-Dist: numpy~=1.24.0; extra == "semantic"
+Requires-Dist: fastembed~=0.2.0; extra == "semantic"
+Requires-Dist: hnswlib~=0.8.0; extra == "semantic"
Provides-Extra: semantic-gpu
-Requires-Dist: numpy>=1.24; extra == "semantic-gpu"
-Requires-Dist: fastembed>=0.2; extra == "semantic-gpu"
-Requires-Dist: hnswlib>=0.8.0; extra == "semantic-gpu"
-Requires-Dist: onnxruntime-gpu>=1.15.0; extra == "semantic-gpu"
+Requires-Dist: numpy~=1.24.0; extra == "semantic-gpu"
+Requires-Dist: fastembed~=0.2.0; extra == "semantic-gpu"
+Requires-Dist: hnswlib~=0.8.0; extra == "semantic-gpu"
+Requires-Dist: onnxruntime-gpu~=1.15.0; extra == "semantic-gpu"
Provides-Extra: semantic-directml
-Requires-Dist: numpy>=1.24; extra == "semantic-directml"
-Requires-Dist: fastembed>=0.2; extra == "semantic-directml"
-Requires-Dist: hnswlib>=0.8.0; extra == "semantic-directml"
-Requires-Dist: onnxruntime-directml>=1.15.0; extra == "semantic-directml"
+Requires-Dist: numpy~=1.24.0; extra == "semantic-directml"
+Requires-Dist: fastembed~=0.2.0; extra == "semantic-directml"
+Requires-Dist: hnswlib~=0.8.0; extra == "semantic-directml"
+Requires-Dist: onnxruntime-directml~=1.15.0; extra == "semantic-directml"
Provides-Extra: reranker-onnx
-Requires-Dist: optimum>=1.16; extra == "reranker-onnx"
-Requires-Dist: onnxruntime>=1.15; extra == "reranker-onnx"
-Requires-Dist: transformers>=4.36; extra == "reranker-onnx"
+Requires-Dist: optimum~=1.16.0; extra == "reranker-onnx"
+Requires-Dist: onnxruntime~=1.15.0; extra == "reranker-onnx"
+Requires-Dist: transformers~=4.36.0; extra == "reranker-onnx"
Provides-Extra: reranker-api
-Requires-Dist: httpx>=0.25; extra == "reranker-api"
+Requires-Dist: httpx~=0.25.0; extra == "reranker-api"
Provides-Extra: reranker-litellm
-Requires-Dist: ccw-litellm>=0.1; extra == "reranker-litellm"
+Requires-Dist: ccw-litellm~=0.1.0; extra == "reranker-litellm"
Provides-Extra: reranker-legacy
-Requires-Dist: sentence-transformers>=2.2; extra == "reranker-legacy"
+Requires-Dist: sentence-transformers~=2.2.0; extra == "reranker-legacy"
Provides-Extra: reranker
-Requires-Dist: optimum>=1.16; extra == "reranker"
-Requires-Dist: onnxruntime>=1.15; extra == "reranker"
-Requires-Dist: transformers>=4.36; extra == "reranker"
+Requires-Dist: optimum~=1.16.0; extra == "reranker"
+Requires-Dist: onnxruntime~=1.15.0; extra == "reranker"
+Requires-Dist: transformers~=4.36.0; extra == "reranker"
Provides-Extra: encoding
-Requires-Dist: chardet>=5.0; extra == "encoding"
+Requires-Dist: chardet~=5.0.0; extra == "encoding"
Provides-Extra: clustering
-Requires-Dist: hdbscan>=0.8.1; extra == "clustering"
-Requires-Dist: scikit-learn>=1.3.0; extra == "clustering"
+Requires-Dist: hdbscan~=0.8.1; extra == "clustering"
+Requires-Dist: scikit-learn~=1.3.0; extra == "clustering"
Provides-Extra: full
-Requires-Dist: tiktoken>=0.5.0; extra == "full"
+Requires-Dist: tiktoken~=0.5.0; extra == "full"
Provides-Extra: lsp
-Requires-Dist: pygls>=1.3.0; extra == "lsp"
+Requires-Dist: pygls~=1.3.0; extra == "lsp"
Dynamic: license-file
# CodexLens
diff --git a/codex-lens/src/codex_lens.egg-info/SOURCES.txt b/codex-lens/src/codex_lens.egg-info/SOURCES.txt
index dd7bf022..b84f547e 100644
--- a/codex-lens/src/codex_lens.egg-info/SOURCES.txt
+++ b/codex-lens/src/codex_lens.egg-info/SOURCES.txt
@@ -153,10 +153,12 @@ tests/test_hybrid_chunker.py
tests/test_hybrid_search_e2e.py
tests/test_hybrid_search_reranker_backend.py
tests/test_hybrid_search_unit.py
+tests/test_incremental_indexer.py
tests/test_incremental_indexing.py
tests/test_litellm_reranker.py
tests/test_lsp_graph_builder_depth.py
tests/test_merkle_detection.py
+tests/test_migrations.py
tests/test_parser_integration.py
tests/test_parsers.py
tests/test_path_mapper_windows_drive.py
diff --git a/codex-lens/src/codex_lens.egg-info/requires.txt b/codex-lens/src/codex_lens.egg-info/requires.txt
index d15ba892..a4f28711 100644
--- a/codex-lens/src/codex_lens.egg-info/requires.txt
+++ b/codex-lens/src/codex_lens.egg-info/requires.txt
@@ -1,59 +1,59 @@
-typer>=0.9
-rich>=13
-pydantic>=2.0
-tree-sitter>=0.20
-tree-sitter-python>=0.25
-tree-sitter-javascript>=0.25
-tree-sitter-typescript>=0.23
-pathspec>=0.11
-watchdog>=3.0
-ast-grep-py>=0.40.0
+typer~=0.9.0
+rich~=13.0.0
+pydantic~=2.0.0
+tree-sitter~=0.20.0
+tree-sitter-python~=0.25.0
+tree-sitter-javascript~=0.25.0
+tree-sitter-typescript~=0.23.0
+pathspec~=0.11.0
+watchdog~=3.0.0
+ast-grep-py~=0.40.0
[clustering]
-hdbscan>=0.8.1
-scikit-learn>=1.3.0
+hdbscan~=0.8.1
+scikit-learn~=1.3.0
[encoding]
-chardet>=5.0
+chardet~=5.0.0
[full]
-tiktoken>=0.5.0
+tiktoken~=0.5.0
[lsp]
-pygls>=1.3.0
+pygls~=1.3.0
[reranker]
-optimum>=1.16
-onnxruntime>=1.15
-transformers>=4.36
+optimum~=1.16.0
+onnxruntime~=1.15.0
+transformers~=4.36.0
[reranker-api]
-httpx>=0.25
+httpx~=0.25.0
[reranker-legacy]
-sentence-transformers>=2.2
+sentence-transformers~=2.2.0
[reranker-litellm]
-ccw-litellm>=0.1
+ccw-litellm~=0.1.0
[reranker-onnx]
-optimum>=1.16
-onnxruntime>=1.15
-transformers>=4.36
+optimum~=1.16.0
+onnxruntime~=1.15.0
+transformers~=4.36.0
[semantic]
-numpy>=1.24
-fastembed>=0.2
-hnswlib>=0.8.0
+numpy~=1.24.0
+fastembed~=0.2.0
+hnswlib~=0.8.0
[semantic-directml]
-numpy>=1.24
-fastembed>=0.2
-hnswlib>=0.8.0
-onnxruntime-directml>=1.15.0
+numpy~=1.24.0
+fastembed~=0.2.0
+hnswlib~=0.8.0
+onnxruntime-directml~=1.15.0
[semantic-gpu]
-numpy>=1.24
-fastembed>=0.2
-hnswlib>=0.8.0
-onnxruntime-gpu>=1.15.0
+numpy~=1.24.0
+fastembed~=0.2.0
+hnswlib~=0.8.0
+onnxruntime-gpu~=1.15.0