/**
* Tool Configuration Manager
*
* Manages tool enablement configuration stored in JSON format.
* Handles loading, saving, and querying tool configuration.
*/
import fs from 'fs';
import path from 'path';
import os from 'os';
import { logger } from './logger.js';
import { TOOL_GROUPS, findToolGroup, getAllTools } from './tool-registry.js';
/**
* Configuration file location
* User-global only: ~/.ssh-manager/tools-config.json
*/
const CONFIG_DIR = path.join(os.homedir(), '.ssh-manager');
const CONFIG_FILE = path.join(CONFIG_DIR, 'tools-config.json');
/**
* Tool Configuration Manager Class
*/
export class ToolConfigManager {
constructor() {
this.config = null;
this.configPath = CONFIG_FILE;
}
/**
* Load tool configuration from file
* @returns {Promise<Object>} Configuration object
*/
async load() {
try {
if (fs.existsSync(this.configPath)) {
const content = fs.readFileSync(this.configPath, 'utf8');
this.config = JSON.parse(content);
// Validate config structure
if (!this.validateConfig(this.config)) {
logger.warn('Invalid tool configuration, using defaults');
this.config = this.getDefaultConfig();
} else {
logger.info(`Tool configuration loaded from ${this.configPath}`);
logger.info(`Mode: ${this.config.mode}, Enabled tools: ${this.getEnabledTools().length}/37`);
}
} else {
// No config file - default to all tools enabled
logger.info('No tool configuration found, enabling all tools (default)');
logger.info('Run "ssh-manager tools configure" to optimize and reduce context usage');
this.config = this.getDefaultConfig();
}
} catch (error) {
logger.error(`Failed to load tool configuration: ${error.message}`);
logger.info('Using default configuration (all tools enabled)');
this.config = this.getDefaultConfig();
}
return this.config;
}
/**
* Get default configuration (all tools enabled)
* @returns {Object} Default configuration
*/
getDefaultConfig() {
return {
version: '1.0',
mode: 'all',
groups: {
core: { enabled: true },
sessions: { enabled: true },
monitoring: { enabled: true },
backup: { enabled: true },
database: { enabled: true },
advanced: { enabled: true }
},
tools: {},
_comment: 'Tool configuration for MCP SSH Manager. Run "ssh-manager tools configure" to customize.'
};
}
/**
* Validate configuration structure
* @param {Object} config - Configuration to validate
* @returns {boolean} True if valid
*/
validateConfig(config) {
if (!config || typeof config !== 'object') {
return false;
}
// Check required fields
if (!config.version || !config.mode) {
return false;
}
// Validate mode
if (!['all', 'minimal', 'custom'].includes(config.mode)) {
return false;
}
// Check groups structure
if (config.mode === 'custom' && !config.groups) {
return false;
}
return true;
}
/**
* Check if a specific tool is enabled
* @param {string} toolName - Name of the tool
* @returns {boolean} True if enabled
*/
isToolEnabled(toolName) {
if (!this.config) {
return true; // Default to enabled if no config loaded
}
// Mode: all - everything enabled
if (this.config.mode === 'all') {
return true;
}
// Check individual tool override first
if (this.config.tools && toolName in this.config.tools) {
return this.config.tools[toolName];
}
// Mode: minimal - only core tools
if (this.config.mode === 'minimal') {
const group = findToolGroup(toolName);
return group === 'core';
}
// Mode: custom - check group setting
if (this.config.mode === 'custom') {
const group = findToolGroup(toolName);
if (group && this.config.groups && group in this.config.groups) {
return this.config.groups[group].enabled;
}
}
// Default to enabled if group not found (for new tools in updates)
return true;
}
/**
* Get array of all enabled tool names
* @returns {string[]} Array of enabled tool names
*/
getEnabledTools() {
const allTools = getAllTools();
return allTools.filter(tool => this.isToolEnabled(tool));
}
/**
* Get array of all disabled tool names
* @returns {string[]} Array of disabled tool names
*/
getDisabledTools() {
const allTools = getAllTools();
return allTools.filter(tool => !this.isToolEnabled(tool));
}
/**
* Check if a group is enabled
* @param {string} groupName - Name of the group
* @returns {boolean} True if enabled
*/
isGroupEnabled(groupName) {
if (!this.config) {
return true;
}
if (this.config.mode === 'all') {
return true;
}
if (this.config.mode === 'minimal') {
return groupName === 'core';
}
if (this.config.mode === 'custom' && this.config.groups) {
return this.config.groups[groupName]?.enabled ?? true;
}
return true;
}
/**
* Save configuration to file
* @returns {Promise<boolean>} True if saved successfully
*/
async save() {
try {
// Ensure config directory exists
if (!fs.existsSync(CONFIG_DIR)) {
fs.mkdirSync(CONFIG_DIR, { recursive: true });
}
// Write config file
const content = JSON.stringify(this.config, null, 2);
fs.writeFileSync(this.configPath, content, 'utf8');
logger.info(`Tool configuration saved to ${this.configPath}`);
return true;
} catch (error) {
logger.error(`Failed to save tool configuration: ${error.message}`);
return false;
}
}
/**
* Enable a tool group
* @param {string} groupName - Name of the group to enable
* @returns {Promise<boolean>} True if successful
*/
async enableGroup(groupName) {
if (!TOOL_GROUPS[groupName]) {
logger.error(`Unknown tool group: ${groupName}`);
return false;
}
// Ensure we're in custom mode
if (this.config.mode !== 'custom') {
this.config.mode = 'custom';
}
// Initialize groups if needed
if (!this.config.groups) {
this.config.groups = {};
}
// Enable the group
this.config.groups[groupName] = { enabled: true };
logger.info(`Enabled tool group: ${groupName}`);
return await this.save();
}
/**
* Disable a tool group
* @param {string} groupName - Name of the group to disable
* @returns {Promise<boolean>} True if successful
*/
async disableGroup(groupName) {
if (!TOOL_GROUPS[groupName]) {
logger.error(`Unknown tool group: ${groupName}`);
return false;
}
if (groupName === 'core') {
logger.error('Cannot disable core group (required for basic functionality)');
return false;
}
// Ensure we're in custom mode
if (this.config.mode !== 'custom') {
this.config.mode = 'custom';
}
// Initialize groups if needed
if (!this.config.groups) {
this.config.groups = {};
}
// Disable the group
this.config.groups[groupName] = { enabled: false };
logger.info(`Disabled tool group: ${groupName}`);
return await this.save();
}
/**
* Enable a specific tool (individual override)
* @param {string} toolName - Name of the tool to enable
* @returns {Promise<boolean>} True if successful
*/
async enableTool(toolName) {
const allTools = getAllTools();
if (!allTools.includes(toolName)) {
logger.error(`Unknown tool: ${toolName}`);
return false;
}
// Initialize tools object if needed
if (!this.config.tools) {
this.config.tools = {};
}
// Enable the tool
this.config.tools[toolName] = true;
logger.info(`Enabled tool: ${toolName}`);
return await this.save();
}
/**
* Disable a specific tool (individual override)
* @param {string} toolName - Name of the tool to disable
* @returns {Promise<boolean>} True if successful
*/
async disableTool(toolName) {
const allTools = getAllTools();
if (!allTools.includes(toolName)) {
logger.error(`Unknown tool: ${toolName}`);
return false;
}
// Check if it's a core tool
if (TOOL_GROUPS.core.includes(toolName)) {
logger.warn(`Disabling core tool: ${toolName} (may limit functionality)`);
}
// Initialize tools object if needed
if (!this.config.tools) {
this.config.tools = {};
}
// Disable the tool
this.config.tools[toolName] = false;
logger.info(`Disabled tool: ${toolName}`);
return await this.save();
}
/**
* Set configuration mode
* @param {string} mode - Mode to set ('all', 'minimal', 'custom')
* @returns {Promise<boolean>} True if successful
*/
async setMode(mode) {
if (!['all', 'minimal', 'custom'].includes(mode)) {
logger.error(`Invalid mode: ${mode}`);
return false;
}
this.config.mode = mode;
logger.info(`Set tool configuration mode to: ${mode}`);
return await this.save();
}
/**
* Reset configuration to defaults
* @returns {Promise<boolean>} True if successful
*/
async reset() {
this.config = this.getDefaultConfig();
logger.info('Reset tool configuration to defaults (all tools enabled)');
return await this.save();
}
/**
* Get configuration summary
* @returns {Object} Summary object
*/
getSummary() {
const enabledTools = this.getEnabledTools();
const disabledTools = this.getDisabledTools();
return {
mode: this.config.mode,
configPath: this.configPath,
totalTools: 37,
enabledCount: enabledTools.length,
disabledCount: disabledTools.length,
groups: Object.keys(TOOL_GROUPS).map(groupName => ({
name: groupName,
enabled: this.isGroupEnabled(groupName),
toolCount: TOOL_GROUPS[groupName].length
}))
};
}
/**
* Export Claude Code auto-approval configuration
* @returns {Object} Auto-approval config snippet
*/
exportClaudeCodeConfig() {
const enabledTools = this.getEnabledTools();
const autoApprovalPatterns = enabledTools.map(tool => `mcp__ssh-manager__${tool}`);
return {
comment: 'Add these patterns to autoApprove.tools in claude_code_config.json',
patterns: autoApprovalPatterns,
exampleConfig: {
autoApprove: {
tools: autoApprovalPatterns
}
}
};
}
}
/**
* Singleton instance
*/
let toolConfigInstance = null;
/**
* Load tool configuration (singleton)
* @returns {Promise<ToolConfigManager>} Configuration manager instance
*/
export async function loadToolConfig() {
if (!toolConfigInstance) {
toolConfigInstance = new ToolConfigManager();
await toolConfigInstance.load();
}
return toolConfigInstance;
}
/**
* Check if a tool is enabled (convenience function)
* @param {string} toolName - Name of the tool
* @returns {boolean} True if enabled
*/
export function isToolEnabled(toolName) {
if (!toolConfigInstance) {
return true; // Default to enabled before config is loaded
}
return toolConfigInstance.isToolEnabled(toolName);
}
/**
* Get the tool configuration manager instance
* @returns {ToolConfigManager|null} Configuration manager or null if not loaded
*/
export function getToolConfigManager() {
return toolConfigInstance;
}