protoagent 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +39 -0
- package/dist/agentic-loop.js +277 -0
- package/dist/config/client.js +166 -0
- package/dist/config/commands.js +208 -0
- package/dist/config/manager.js +117 -0
- package/dist/config/mcp-commands.js +266 -0
- package/dist/config/mcp-manager.js +240 -0
- package/dist/config/mcp-types.js +28 -0
- package/dist/config/providers.js +170 -0
- package/dist/config/setup.js +175 -0
- package/dist/config/system-prompt.js +301 -0
- package/dist/config/types.js +4 -0
- package/dist/index.js +156 -0
- package/dist/tools/create-directory.js +76 -0
- package/dist/tools/directory-operations.js +195 -0
- package/dist/tools/edit-file.js +144 -0
- package/dist/tools/file-operations.js +211 -0
- package/dist/tools/index.js +95 -0
- package/dist/tools/list-directory.js +84 -0
- package/dist/tools/read-file.js +111 -0
- package/dist/tools/run-shell-command.js +340 -0
- package/dist/tools/search-files.js +177 -0
- package/dist/tools/search-operations.js +179 -0
- package/dist/tools/shell-operations.js +342 -0
- package/dist/tools/todo.js +177 -0
- package/dist/tools/view-directory-tree.js +125 -0
- package/dist/tools/write-file.js +136 -0
- package/dist/tools.js +2 -0
- package/dist/utils/conversation-compactor.js +139 -0
- package/dist/utils/cost-tracker.js +106 -0
- package/dist/utils/file-operations-approval.js +163 -0
- package/dist/utils/logger.js +149 -0
- package/package.json +61 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration management commands for ProtoAgent CLI
|
|
3
|
+
*/
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
import { hasConfig, loadConfig, saveConfig, getConfigDirectory } from './manager.js';
|
|
6
|
+
import { setupConfig } from './setup.js';
|
|
7
|
+
import { openaiProvider, geminiProvider, cerebrasProvider } from './providers.js';
|
|
8
|
+
import fs from 'fs/promises';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
/**
|
|
11
|
+
* Mask API key for display (show first 8 and last 4 characters)
|
|
12
|
+
*/
|
|
13
|
+
function maskApiKey(apiKey) {
|
|
14
|
+
if (apiKey.length <= 12) {
|
|
15
|
+
return '*'.repeat(apiKey.length);
|
|
16
|
+
}
|
|
17
|
+
const start = apiKey.substring(0, 8);
|
|
18
|
+
const end = apiKey.substring(apiKey.length - 4);
|
|
19
|
+
const middle = '*'.repeat(apiKey.length - 12);
|
|
20
|
+
return `${start}${middle}${end}`;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Show current configuration (without exposing sensitive data)
|
|
24
|
+
*/
|
|
25
|
+
export async function showCurrentConfig() {
|
|
26
|
+
try {
|
|
27
|
+
const configExists = await hasConfig();
|
|
28
|
+
if (!configExists) {
|
|
29
|
+
console.log('โ No configuration found. Run ProtoAgent to set up.');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const config = await loadConfig();
|
|
33
|
+
const configDir = getConfigDirectory();
|
|
34
|
+
console.log('\n๐ Current ProtoAgent Configuration:');
|
|
35
|
+
console.log('โ'.repeat(40));
|
|
36
|
+
console.log(`Provider: ${config.provider}`);
|
|
37
|
+
console.log(`Model: ${config.model}`);
|
|
38
|
+
// Show the appropriate API key based on provider
|
|
39
|
+
if (config.provider === 'openai' && config.credentials.OPENAI_API_KEY) {
|
|
40
|
+
console.log(`API Key: ${maskApiKey(config.credentials.OPENAI_API_KEY)}`);
|
|
41
|
+
}
|
|
42
|
+
else if (config.provider === 'gemini' && config.credentials.GEMINI_API_KEY) {
|
|
43
|
+
console.log(`API Key: ${maskApiKey(config.credentials.GEMINI_API_KEY)}`);
|
|
44
|
+
}
|
|
45
|
+
else if (config.provider === 'cerebras' && config.credentials.CEREBRAS_API_KEY) {
|
|
46
|
+
console.log(`API Key: ${maskApiKey(config.credentials.CEREBRAS_API_KEY)}`);
|
|
47
|
+
}
|
|
48
|
+
console.log(`Config Location: ${configDir}/config.json`);
|
|
49
|
+
console.log('โ'.repeat(40));
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.error(`โ Error reading configuration: ${error.message}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Update API key
|
|
57
|
+
*/
|
|
58
|
+
export async function updateApiKey() {
|
|
59
|
+
try {
|
|
60
|
+
const configExists = await hasConfig();
|
|
61
|
+
if (!configExists) {
|
|
62
|
+
console.log('โ No configuration found. Run ProtoAgent to set up first.');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const config = await loadConfig();
|
|
66
|
+
console.log(`\n๐ Update ${config.provider.toUpperCase()} API Key`);
|
|
67
|
+
// Show current API key based on provider
|
|
68
|
+
let currentKey = '';
|
|
69
|
+
let helpUrl = '';
|
|
70
|
+
let keyPrefix = '';
|
|
71
|
+
if (config.provider === 'openai') {
|
|
72
|
+
currentKey = config.credentials.OPENAI_API_KEY || '';
|
|
73
|
+
helpUrl = 'https://platform.openai.com/api-keys';
|
|
74
|
+
keyPrefix = 'sk-';
|
|
75
|
+
}
|
|
76
|
+
else if (config.provider === 'gemini') {
|
|
77
|
+
currentKey = config.credentials.GEMINI_API_KEY || '';
|
|
78
|
+
helpUrl = 'https://aistudio.google.com/app/apikey';
|
|
79
|
+
keyPrefix = '';
|
|
80
|
+
}
|
|
81
|
+
else if (config.provider === 'cerebras') {
|
|
82
|
+
currentKey = config.credentials.CEREBRAS_API_KEY || '';
|
|
83
|
+
helpUrl = 'https://cloud.cerebras.ai/platform';
|
|
84
|
+
keyPrefix = '';
|
|
85
|
+
}
|
|
86
|
+
console.log(`Current API Key: ${maskApiKey(currentKey)}`);
|
|
87
|
+
console.log(`Get your API key from: ${helpUrl}\n`);
|
|
88
|
+
const { newApiKey } = await inquirer.prompt([
|
|
89
|
+
{
|
|
90
|
+
type: 'password',
|
|
91
|
+
name: 'newApiKey',
|
|
92
|
+
message: `Enter your new ${config.provider.toUpperCase()} API key:`,
|
|
93
|
+
mask: '*',
|
|
94
|
+
validate: (input) => {
|
|
95
|
+
if (!input || input.trim().length === 0) {
|
|
96
|
+
return 'API key is required';
|
|
97
|
+
}
|
|
98
|
+
if (keyPrefix && !input.trim().startsWith(keyPrefix)) {
|
|
99
|
+
return `${config.provider.toUpperCase()} API keys should start with "${keyPrefix}"`;
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
]);
|
|
105
|
+
// Update configuration based on provider
|
|
106
|
+
if (config.provider === 'openai') {
|
|
107
|
+
config.credentials.OPENAI_API_KEY = newApiKey.trim();
|
|
108
|
+
}
|
|
109
|
+
else if (config.provider === 'gemini') {
|
|
110
|
+
config.credentials.GEMINI_API_KEY = newApiKey.trim();
|
|
111
|
+
}
|
|
112
|
+
else if (config.provider === 'cerebras') {
|
|
113
|
+
config.credentials.CEREBRAS_API_KEY = newApiKey.trim();
|
|
114
|
+
}
|
|
115
|
+
await saveConfig(config);
|
|
116
|
+
console.log('\nโ
API key updated successfully!');
|
|
117
|
+
console.log(`New API Key: ${maskApiKey(newApiKey)}`);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
console.error(`โ Error updating API key: ${error.message}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Update model
|
|
125
|
+
*/
|
|
126
|
+
export async function updateModel() {
|
|
127
|
+
try {
|
|
128
|
+
const configExists = await hasConfig();
|
|
129
|
+
if (!configExists) {
|
|
130
|
+
console.log('โ No configuration found. Run ProtoAgent to set up first.');
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const config = await loadConfig();
|
|
134
|
+
console.log(`\n๐ค Update ${config.provider.toUpperCase()} Model`);
|
|
135
|
+
console.log(`Current Model: ${config.model}\n`);
|
|
136
|
+
// Get available models based on provider
|
|
137
|
+
let availableModels = [];
|
|
138
|
+
if (config.provider === 'openai') {
|
|
139
|
+
availableModels = openaiProvider.models;
|
|
140
|
+
}
|
|
141
|
+
else if (config.provider === 'gemini') {
|
|
142
|
+
availableModels = geminiProvider.models;
|
|
143
|
+
}
|
|
144
|
+
else if (config.provider === 'cerebras') {
|
|
145
|
+
availableModels = cerebrasProvider.models;
|
|
146
|
+
}
|
|
147
|
+
const { newModel } = await inquirer.prompt([
|
|
148
|
+
{
|
|
149
|
+
type: 'list',
|
|
150
|
+
name: 'newModel',
|
|
151
|
+
message: `Select a new ${config.provider.toUpperCase()} model:`,
|
|
152
|
+
choices: availableModels.map(model => ({
|
|
153
|
+
name: model === config.model ? `${model} (current)` : model,
|
|
154
|
+
value: model
|
|
155
|
+
})),
|
|
156
|
+
default: config.model
|
|
157
|
+
}
|
|
158
|
+
]);
|
|
159
|
+
if (newModel === config.model) {
|
|
160
|
+
console.log('โจ No change needed - same model selected.');
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// Update configuration
|
|
164
|
+
const previousModel = config.model;
|
|
165
|
+
config.model = newModel;
|
|
166
|
+
await saveConfig(config);
|
|
167
|
+
console.log('\nโ
Model updated successfully!');
|
|
168
|
+
console.log(`Previous Model: ${previousModel}`);
|
|
169
|
+
console.log(`New Model: ${newModel}`);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
console.error(`โ Error updating model: ${error.message}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Reset configuration (full reconfiguration)
|
|
177
|
+
*/
|
|
178
|
+
export async function resetConfiguration() {
|
|
179
|
+
try {
|
|
180
|
+
const configExists = await hasConfig();
|
|
181
|
+
if (configExists) {
|
|
182
|
+
console.log('\nโ ๏ธ Reset Configuration');
|
|
183
|
+
console.log('This will delete your current configuration and set up ProtoAgent from scratch.');
|
|
184
|
+
const { confirm } = await inquirer.prompt([
|
|
185
|
+
{
|
|
186
|
+
type: 'confirm',
|
|
187
|
+
name: 'confirm',
|
|
188
|
+
message: 'Are you sure you want to reset your configuration?',
|
|
189
|
+
default: false
|
|
190
|
+
}
|
|
191
|
+
]);
|
|
192
|
+
if (!confirm) {
|
|
193
|
+
console.log('Configuration reset cancelled.');
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
// Delete existing configuration
|
|
197
|
+
const configPath = path.join(getConfigDirectory(), 'config.json');
|
|
198
|
+
await fs.unlink(configPath);
|
|
199
|
+
console.log('โ
Existing configuration deleted.');
|
|
200
|
+
}
|
|
201
|
+
// Run setup again
|
|
202
|
+
console.log('\n๐ Starting fresh configuration setup...');
|
|
203
|
+
await setupConfig();
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
console.error(`โ Error resetting configuration: ${error.message}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration manager for ProtoAgent
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'fs/promises';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import { geminiProvider, openaiProvider, cerebrasProvider } from './providers.js';
|
|
8
|
+
/**
|
|
9
|
+
* Get the config directory path
|
|
10
|
+
*/
|
|
11
|
+
function getConfigDir() {
|
|
12
|
+
const homeDir = os.homedir();
|
|
13
|
+
// Use XDG Base Directory specification on Unix-like systems
|
|
14
|
+
if (process.platform === 'win32') {
|
|
15
|
+
return path.join(homeDir, 'AppData', 'Local', 'protoagent');
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
return path.join(homeDir, '.local', 'share', 'protoagent');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get the config file path
|
|
23
|
+
*/
|
|
24
|
+
function getConfigPath() {
|
|
25
|
+
return path.join(getConfigDir(), 'config.json');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Ensure config directory exists
|
|
29
|
+
*/
|
|
30
|
+
async function ensureConfigDir() {
|
|
31
|
+
const configDir = getConfigDir();
|
|
32
|
+
try {
|
|
33
|
+
await fs.access(configDir);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Check if configuration exists
|
|
41
|
+
*/
|
|
42
|
+
export async function hasConfig() {
|
|
43
|
+
try {
|
|
44
|
+
await fs.access(getConfigPath());
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Load user configuration
|
|
53
|
+
*/
|
|
54
|
+
export async function loadConfig() {
|
|
55
|
+
try {
|
|
56
|
+
const configPath = getConfigPath();
|
|
57
|
+
const configData = await fs.readFile(configPath, 'utf-8');
|
|
58
|
+
return JSON.parse(configData);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
throw new Error(`Failed to load configuration: ${error.message}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Save user configuration
|
|
66
|
+
*/
|
|
67
|
+
export async function saveConfig(config) {
|
|
68
|
+
try {
|
|
69
|
+
await ensureConfigDir();
|
|
70
|
+
const configPath = getConfigPath();
|
|
71
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2));
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
throw new Error(`Failed to save configuration: ${error.message}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Validate configuration
|
|
79
|
+
*/
|
|
80
|
+
export function validateConfig(config) {
|
|
81
|
+
if (!config || typeof config !== 'object') {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
const { provider, model, credentials } = config;
|
|
85
|
+
if (!model || !credentials || !(credentials.OPENAI_API_KEY || credentials.GEMINI_API_KEY || credentials.CEREBRAS_API_KEY)) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
if (provider === 'gemini') {
|
|
89
|
+
// Check if model is available for Gemini
|
|
90
|
+
if (!geminiProvider.models.includes(model)) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
// Additional model validation for Gemini can be added here
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
if (provider === 'openai') {
|
|
97
|
+
// Check if model is available for OpenAI
|
|
98
|
+
if (!openaiProvider.models.includes(model)) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
if (provider === 'cerebras') {
|
|
104
|
+
// Check if model is available for Cerebras
|
|
105
|
+
if (!cerebrasProvider.models.includes(model)) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get configuration directory path for display
|
|
114
|
+
*/
|
|
115
|
+
export function getConfigDirectory() {
|
|
116
|
+
return getConfigDir();
|
|
117
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP management commands for ProtoAgent CLI
|
|
3
|
+
*/
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
import { MCPManager } from './mcp-manager.js';
|
|
6
|
+
const mcpManager = new MCPManager();
|
|
7
|
+
/**
|
|
8
|
+
* Show current MCP configuration and status
|
|
9
|
+
*/
|
|
10
|
+
export async function showMCPStatus() {
|
|
11
|
+
try {
|
|
12
|
+
await mcpManager.loadConfig();
|
|
13
|
+
console.log('\n๐ MCP Configuration & Status:');
|
|
14
|
+
console.log('โ'.repeat(50));
|
|
15
|
+
const servers = mcpManager.listServers();
|
|
16
|
+
const connectionStatus = mcpManager.getConnectionStatus();
|
|
17
|
+
if (Object.keys(servers).length === 0) {
|
|
18
|
+
console.log('No MCP servers configured');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
for (const [name, config] of Object.entries(servers)) {
|
|
22
|
+
const status = connectionStatus[name];
|
|
23
|
+
const statusIcon = status === true ? '๐ข' : status === false ? '๐ด' : 'โช';
|
|
24
|
+
const typeLabel = config.isBuiltIn ? '(built-in)' : '(custom)';
|
|
25
|
+
console.log(`${statusIcon} ${name} ${typeLabel}`);
|
|
26
|
+
console.log(` Description: ${config.description}`);
|
|
27
|
+
console.log(` Command: ${config.command} ${(config.args || []).join(' ')}`);
|
|
28
|
+
console.log(` Enabled: ${config.enabled ? 'Yes' : 'No'}`);
|
|
29
|
+
console.log('');
|
|
30
|
+
}
|
|
31
|
+
// Show available tools if connected
|
|
32
|
+
const tools = await mcpManager.getAllTools();
|
|
33
|
+
if (tools.length > 0) {
|
|
34
|
+
console.log('๐ ๏ธ Available Tools:');
|
|
35
|
+
for (const tool of tools) {
|
|
36
|
+
console.log(` โข ${tool.server}:${tool.name} - ${tool.description || 'No description'}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
console.log('โ'.repeat(50));
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error(`โ Error showing MCP status: ${error.message}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Connect to MCP servers
|
|
47
|
+
*/
|
|
48
|
+
export async function connectMCPServers() {
|
|
49
|
+
try {
|
|
50
|
+
await mcpManager.loadConfig();
|
|
51
|
+
await mcpManager.connectToAllServers();
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error(`โ Error connecting to MCP servers: ${error.message}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Add a custom MCP server
|
|
59
|
+
*/
|
|
60
|
+
export async function addCustomMCPServer() {
|
|
61
|
+
try {
|
|
62
|
+
console.log('\n๐ง Add Custom MCP Server');
|
|
63
|
+
console.log('Configure a new MCP server for ProtoAgent\n');
|
|
64
|
+
const serverConfig = await inquirer.prompt([
|
|
65
|
+
{
|
|
66
|
+
type: 'input',
|
|
67
|
+
name: 'name',
|
|
68
|
+
message: 'Server name (unique identifier):',
|
|
69
|
+
validate: (input) => {
|
|
70
|
+
if (!input || input.trim().length === 0) {
|
|
71
|
+
return 'Server name is required';
|
|
72
|
+
}
|
|
73
|
+
if (!/^[a-zA-Z0-9\-_]+$/.test(input.trim())) {
|
|
74
|
+
return 'Server name can only contain letters, numbers, hyphens, and underscores';
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
type: 'input',
|
|
81
|
+
name: 'description',
|
|
82
|
+
message: 'Description:',
|
|
83
|
+
validate: (input) => input.trim().length > 0 ? true : 'Description is required'
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
type: 'input',
|
|
87
|
+
name: 'command',
|
|
88
|
+
message: 'Command to run the server:',
|
|
89
|
+
validate: (input) => input.trim().length > 0 ? true : 'Command is required'
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
type: 'input',
|
|
93
|
+
name: 'args',
|
|
94
|
+
message: 'Arguments (space-separated, optional):',
|
|
95
|
+
default: ''
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
type: 'confirm',
|
|
99
|
+
name: 'enabled',
|
|
100
|
+
message: 'Enable this server?',
|
|
101
|
+
default: true
|
|
102
|
+
}
|
|
103
|
+
]);
|
|
104
|
+
const newServer = {
|
|
105
|
+
name: serverConfig.name.trim(),
|
|
106
|
+
description: serverConfig.description.trim(),
|
|
107
|
+
command: serverConfig.command.trim(),
|
|
108
|
+
args: serverConfig.args.trim() ? serverConfig.args.trim().split(' ') : [],
|
|
109
|
+
enabled: serverConfig.enabled,
|
|
110
|
+
isBuiltIn: false
|
|
111
|
+
};
|
|
112
|
+
await mcpManager.loadConfig();
|
|
113
|
+
await mcpManager.addCustomServer(newServer);
|
|
114
|
+
console.log(`\nโ
Custom MCP server '${newServer.name}' added successfully!`);
|
|
115
|
+
if (newServer.enabled) {
|
|
116
|
+
const { connectNow } = await inquirer.prompt([
|
|
117
|
+
{
|
|
118
|
+
type: 'confirm',
|
|
119
|
+
name: 'connectNow',
|
|
120
|
+
message: 'Connect to this server now?',
|
|
121
|
+
default: true
|
|
122
|
+
}
|
|
123
|
+
]);
|
|
124
|
+
if (connectNow) {
|
|
125
|
+
const connection = await mcpManager.connectToServer(newServer.name);
|
|
126
|
+
if (connection) {
|
|
127
|
+
console.log(`๐ Successfully connected to ${newServer.name}!`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
console.error(`โ Error adding custom MCP server: ${error.message}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Remove a custom MCP server
|
|
138
|
+
*/
|
|
139
|
+
export async function removeCustomMCPServer() {
|
|
140
|
+
try {
|
|
141
|
+
await mcpManager.loadConfig();
|
|
142
|
+
const servers = mcpManager.listServers();
|
|
143
|
+
const customServers = Object.entries(servers).filter(([_, config]) => !config.isBuiltIn);
|
|
144
|
+
if (customServers.length === 0) {
|
|
145
|
+
console.log('๐ญ No custom MCP servers configured');
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const { serverToRemove } = await inquirer.prompt([
|
|
149
|
+
{
|
|
150
|
+
type: 'list',
|
|
151
|
+
name: 'serverToRemove',
|
|
152
|
+
message: 'Select server to remove:',
|
|
153
|
+
choices: customServers.map(([name, config]) => ({
|
|
154
|
+
name: `${name} - ${config.description}`,
|
|
155
|
+
value: name
|
|
156
|
+
}))
|
|
157
|
+
}
|
|
158
|
+
]);
|
|
159
|
+
const { confirm } = await inquirer.prompt([
|
|
160
|
+
{
|
|
161
|
+
type: 'confirm',
|
|
162
|
+
name: 'confirm',
|
|
163
|
+
message: `Are you sure you want to remove '${serverToRemove}'?`,
|
|
164
|
+
default: false
|
|
165
|
+
}
|
|
166
|
+
]);
|
|
167
|
+
if (confirm) {
|
|
168
|
+
await mcpManager.removeCustomServer(serverToRemove);
|
|
169
|
+
console.log(`โ
Custom MCP server '${serverToRemove}' removed successfully!`);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
console.log('Operation cancelled.');
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
console.error(`โ Error removing custom MCP server: ${error.message}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Toggle MCP server enabled/disabled status
|
|
181
|
+
*/
|
|
182
|
+
export async function toggleMCPServer() {
|
|
183
|
+
try {
|
|
184
|
+
await mcpManager.loadConfig();
|
|
185
|
+
const servers = mcpManager.listServers();
|
|
186
|
+
if (Object.keys(servers).length === 0) {
|
|
187
|
+
console.log('๐ญ No MCP servers configured');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const { serverToToggle } = await inquirer.prompt([
|
|
191
|
+
{
|
|
192
|
+
type: 'list',
|
|
193
|
+
name: 'serverToToggle',
|
|
194
|
+
message: 'Select server to enable/disable:',
|
|
195
|
+
choices: Object.entries(servers).map(([name, config]) => ({
|
|
196
|
+
name: `${name} - ${config.enabled ? '๐ข Enabled' : '๐ด Disabled'} - ${config.description}`,
|
|
197
|
+
value: name
|
|
198
|
+
}))
|
|
199
|
+
}
|
|
200
|
+
]);
|
|
201
|
+
const currentConfig = servers[serverToToggle];
|
|
202
|
+
const newStatus = !currentConfig.enabled;
|
|
203
|
+
// Update the configuration
|
|
204
|
+
await mcpManager.loadConfig();
|
|
205
|
+
const updatedServers = mcpManager.listServers();
|
|
206
|
+
updatedServers[serverToToggle].enabled = newStatus;
|
|
207
|
+
// Save the updated configuration
|
|
208
|
+
const updatedConfig = {
|
|
209
|
+
servers: updatedServers,
|
|
210
|
+
globalTimeout: 30000,
|
|
211
|
+
maxConcurrentConnections: 5,
|
|
212
|
+
retryAttempts: 3
|
|
213
|
+
};
|
|
214
|
+
const mcpManagerForSave = new MCPManager(updatedConfig);
|
|
215
|
+
await mcpManagerForSave.saveConfig();
|
|
216
|
+
console.log(`โ
Server '${serverToToggle}' ${newStatus ? 'enabled' : 'disabled'}`);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
console.error(`โ Error toggling MCP server: ${error.message}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Test connection to a specific MCP server
|
|
224
|
+
*/
|
|
225
|
+
export async function testMCPServer() {
|
|
226
|
+
try {
|
|
227
|
+
await mcpManager.loadConfig();
|
|
228
|
+
const servers = mcpManager.listServers();
|
|
229
|
+
const enabledServers = Object.entries(servers).filter(([_, config]) => config.enabled);
|
|
230
|
+
if (enabledServers.length === 0) {
|
|
231
|
+
console.log('๐ญ No enabled MCP servers to test');
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const { serverToTest } = await inquirer.prompt([
|
|
235
|
+
{
|
|
236
|
+
type: 'list',
|
|
237
|
+
name: 'serverToTest',
|
|
238
|
+
message: 'Select server to test:',
|
|
239
|
+
choices: enabledServers.map(([name, config]) => ({
|
|
240
|
+
name: `${name} - ${config.description}`,
|
|
241
|
+
value: name
|
|
242
|
+
}))
|
|
243
|
+
}
|
|
244
|
+
]);
|
|
245
|
+
console.log(`๐งช Testing connection to ${serverToTest}...`);
|
|
246
|
+
const connection = await mcpManager.connectToServer(serverToTest);
|
|
247
|
+
if (connection) {
|
|
248
|
+
console.log(`โ
Successfully connected to ${serverToTest}!`);
|
|
249
|
+
console.log(`๐ ๏ธ Available tools: ${connection.capabilities.join(', ')}`);
|
|
250
|
+
// Clean up test connection
|
|
251
|
+
await mcpManager.disconnectServer(serverToTest);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
console.log(`โ Failed to connect to ${serverToTest}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
console.error(`โ Error testing MCP server: ${error.message}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Get the MCP manager instance (for use in main application)
|
|
263
|
+
*/
|
|
264
|
+
export function getMCPManager() {
|
|
265
|
+
return mcpManager;
|
|
266
|
+
}
|