From 6d17e6db8103fa9d3c98d0d3d2092fa23e4f75bc Mon Sep 17 00:00:00 2001 From: simos Date: Mon, 11 Aug 2025 14:20:54 +0300 Subject: [PATCH] feat: update version to 1.6.0 and enhance ToolsSettings component with loading from json and adding project MCP servers --- package.json | 2 +- server/routes/mcp.js | 38 +++++--- src/App.jsx | 1 + src/components/ToolsSettings.jsx | 161 +++++++++++++++++++++++-------- 4 files changed, 149 insertions(+), 53 deletions(-) diff --git a/package.json b/package.json index 0aa1810..b665cb7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "claude-code-ui", - "version": "1.5.0", + "version": "1.6.0", "description": "A web-based UI for Claude Code CLI", "type": "module", "main": "server/index.js", diff --git a/server/routes/mcp.js b/server/routes/mcp.js index 1b27539..080be6a 100644 --- a/server/routes/mcp.js +++ b/server/routes/mcp.js @@ -58,16 +58,16 @@ router.get('/cli/list', async (req, res) => { // POST /api/mcp/cli/add - Add MCP server using Claude CLI router.post('/cli/add', async (req, res) => { try { - const { name, type = 'stdio', command, args = [], url, headers = {}, env = {} } = req.body; + const { name, type = 'stdio', command, args = [], url, headers = {}, env = {}, scope = 'user', projectPath } = req.body; - console.log('➕ Adding MCP server using Claude CLI (user scope):', name); + console.log(`➕ Adding MCP server using Claude CLI (${scope} scope):`, name); const { spawn } = await import('child_process'); let cliArgs = ['mcp', 'add']; - // Always add with user scope (global availability) - cliArgs.push('--scope', 'user'); + // Add scope flag + cliArgs.push('--scope', scope); if (type === 'http') { cliArgs.push('--transport', 'http', name, url); @@ -96,9 +96,17 @@ router.post('/cli/add', async (req, res) => { console.log('🔧 Running Claude CLI command:', 'claude', cliArgs.join(' ')); - const process = spawn('claude', cliArgs, { + // For local scope, we need to run the command in the project directory + const spawnOptions = { stdio: ['pipe', 'pipe', 'pipe'] - }); + }; + + if (scope === 'local' && projectPath) { + spawnOptions.cwd = projectPath; + console.log('📁 Running in project directory:', projectPath); + } + + const process = spawn('claude', cliArgs, spawnOptions); let stdout = ''; let stderr = ''; @@ -133,7 +141,7 @@ router.post('/cli/add', async (req, res) => { // POST /api/mcp/cli/add-json - Add MCP server using JSON format router.post('/cli/add-json', async (req, res) => { try { - const { name, jsonConfig } = req.body; + const { name, jsonConfig, scope = 'user', projectPath } = req.body; console.log('➕ Adding MCP server using JSON format:', name); @@ -172,8 +180,8 @@ router.post('/cli/add-json', async (req, res) => { const { spawn } = await import('child_process'); - // Build the command: claude mcp add-json --scope user '' - const cliArgs = ['mcp', 'add-json', '--scope', 'user', name]; + // Build the command: claude mcp add-json --scope '' + const cliArgs = ['mcp', 'add-json', '--scope', scope, name]; // Add the JSON config as a properly formatted string const jsonString = JSON.stringify(parsedConfig); @@ -181,9 +189,17 @@ router.post('/cli/add-json', async (req, res) => { console.log('🔧 Running Claude CLI command:', 'claude', cliArgs[0], cliArgs[1], cliArgs[2], cliArgs[3], cliArgs[4], jsonString); - const process = spawn('claude', cliArgs, { + // For local scope, we need to run the command in the project directory + const spawnOptions = { stdio: ['pipe', 'pipe', 'pipe'] - }); + }; + + if (scope === 'local' && projectPath) { + spawnOptions.cwd = projectPath; + console.log('📁 Running in project directory:', projectPath); + } + + const process = spawn('claude', cliArgs, spawnOptions); let stdout = ''; let stderr = ''; diff --git a/src/App.jsx b/src/App.jsx index b9a9d8c..14231f6 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -635,6 +635,7 @@ function AppContent() { setShowToolsSettings(false)} + projects={projects} /> {/* Version Upgrade Modal */} diff --git a/src/components/ToolsSettings.jsx b/src/components/ToolsSettings.jsx index c5e621d..7a378b2 100644 --- a/src/components/ToolsSettings.jsx +++ b/src/components/ToolsSettings.jsx @@ -1,12 +1,11 @@ -import React, { useState, useEffect } from 'react'; +import { useState, useEffect } from 'react'; import { Button } from './ui/button'; import { Input } from './ui/input'; -import { ScrollArea } from './ui/scroll-area'; import { Badge } from './ui/badge'; -import { X, Plus, Settings, Shield, AlertTriangle, Moon, Sun, Server, Edit3, Trash2, Play, Globe, Terminal, Zap } from 'lucide-react'; +import { X, Plus, Settings, Shield, AlertTriangle, Moon, Sun, Server, Edit3, Trash2, Globe, Terminal, Zap, FolderOpen } from 'lucide-react'; import { useTheme } from '../contexts/ThemeContext'; -function ToolsSettings({ isOpen, onClose }) { +function ToolsSettings({ isOpen, onClose, projects = [] }) { const { isDarkMode, toggleDarkMode } = useTheme(); const [allowedTools, setAllowedTools] = useState([]); const [disallowedTools, setDisallowedTools] = useState([]); @@ -17,14 +16,14 @@ function ToolsSettings({ isOpen, onClose }) { const [saveStatus, setSaveStatus] = useState(null); const [projectSortOrder, setProjectSortOrder] = useState('name'); - // MCP server management state const [mcpServers, setMcpServers] = useState([]); const [showMcpForm, setShowMcpForm] = useState(false); const [editingMcpServer, setEditingMcpServer] = useState(null); const [mcpFormData, setMcpFormData] = useState({ name: '', type: 'stdio', - scope: 'user', // Always use user scope + scope: 'user', + projectPath: '', // For local scope config: { command: '', args: [], @@ -42,7 +41,6 @@ function ToolsSettings({ isOpen, onClose }) { const [mcpToolsLoading, setMcpToolsLoading] = useState({}); const [activeTab, setActiveTab] = useState('tools'); const [jsonValidationError, setJsonValidationError] = useState(''); - // Common tool patterns const commonTools = [ 'Bash(git log:*)', @@ -153,6 +151,8 @@ function ToolsSettings({ isOpen, onClose }) { body: JSON.stringify({ name: serverData.name, type: serverData.type, + scope: serverData.scope, + projectPath: serverData.projectPath, command: serverData.config?.command, args: serverData.config?.args || [], url: serverData.config?.url, @@ -285,8 +285,9 @@ function ToolsSettings({ isOpen, onClose }) { setProjectSortOrder('name'); } - // Load MCP servers from API + // Load MCP servers and projects from API await fetchMcpServers(); + await fetchAvailableProjects(); } catch (error) { console.error('Error loading tool settings:', error); // Set defaults on error @@ -354,7 +355,8 @@ function ToolsSettings({ isOpen, onClose }) { setMcpFormData({ name: '', type: 'stdio', - scope: 'user', // Always use user scope for global availability + scope: 'user', // Default to user scope + projectPath: '', config: { command: '', args: [], @@ -378,8 +380,11 @@ function ToolsSettings({ isOpen, onClose }) { name: server.name, type: server.type, scope: server.scope, + projectPath: server.projectPath || '', config: { ...server.config }, - raw: server.raw // Store raw config for display + raw: server.raw, // Store raw config for display + importMode: 'form', // Always use form mode when editing + jsonInput: '' }); } else { resetMcpForm(); @@ -404,7 +409,9 @@ function ToolsSettings({ isOpen, onClose }) { }, body: JSON.stringify({ name: mcpFormData.name, - jsonConfig: mcpFormData.jsonInput + jsonConfig: mcpFormData.jsonInput, + scope: mcpFormData.scope, + projectPath: mcpFormData.projectPath }) }); @@ -972,39 +979,12 @@ function ToolsSettings({ isOpen, onClose }) {
- - @@ -1013,6 +993,7 @@ function ToolsSettings({ isOpen, onClose }) { variant="ghost" size="sm" className="text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300" + title="Delete server" > @@ -1042,7 +1023,8 @@ function ToolsSettings({ isOpen, onClose }) {
- {/* Import Mode Toggle */} + + {!editingMcpServer && (
+ )} + + {/* Show current scope when editing */} + {mcpFormData.importMode === 'form' && editingMcpServer && ( +
+ +
+ {mcpFormData.scope === 'user' ? : } + + {mcpFormData.scope === 'user' ? 'User (Global)' : 'Project (Local)'} + + {mcpFormData.scope === 'local' && mcpFormData.projectPath && ( + + - {mcpFormData.projectPath} + + )} +
+

+ Scope cannot be changed when editing an existing server +

+
+ )} + + {/* Scope Selection - Moved to top, disabled when editing */} + {mcpFormData.importMode === 'form' && !editingMcpServer && ( +
+
+ +
+ + +
+

+ {mcpFormData.scope === 'user' + ? 'User scope: Available across all projects on your machine' + : 'Local scope: Only available in the selected project' + } +

+
+ + {/* Project Selection for Local Scope */} + {mcpFormData.scope === 'local' && !editingMcpServer && ( +
+ + + {mcpFormData.projectPath && ( +

+ Path: {mcpFormData.projectPath} +

+ )} +
+ )} +
+ )} {/* Basic Info */}
@@ -1104,7 +1184,6 @@ function ToolsSettings({ isOpen, onClose }) { )}
- {/* Scope is fixed to user - no selection needed */} {/* Show raw configuration details when editing */} {editingMcpServer && mcpFormData.raw && mcpFormData.importMode === 'form' && (