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.
@@ -0,0 +1,240 @@
1
+ /**
2
+ * MCP (Model Context Protocol) Manager
3
+ * Handles connections to MCP servers and tool execution
4
+ */
5
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
6
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
7
+ import { DEFAULT_MCP_CONFIG } from './mcp-types.js';
8
+ import path from 'path';
9
+ import fs from 'fs/promises';
10
+ import { getConfigDirectory } from './manager.js';
11
+ export class MCPManager {
12
+ constructor(config) {
13
+ this.connections = new Map();
14
+ this.processes = new Map();
15
+ this.config = config || DEFAULT_MCP_CONFIG;
16
+ }
17
+ /**
18
+ * Load MCP configuration from file
19
+ */
20
+ async loadConfig() {
21
+ try {
22
+ const configPath = path.join(getConfigDirectory(), 'mcp-config.json');
23
+ const configData = await fs.readFile(configPath, 'utf-8');
24
+ this.config = { ...DEFAULT_MCP_CONFIG, ...JSON.parse(configData) };
25
+ }
26
+ catch (error) {
27
+ // If no config file exists, use defaults
28
+ console.log('šŸ“” Using default MCP configuration');
29
+ this.config = DEFAULT_MCP_CONFIG;
30
+ }
31
+ }
32
+ /**
33
+ * Save MCP configuration to file
34
+ */
35
+ async saveConfig() {
36
+ try {
37
+ const configPath = path.join(getConfigDirectory(), 'mcp-config.json');
38
+ await fs.writeFile(configPath, JSON.stringify(this.config, null, 2));
39
+ }
40
+ catch (error) {
41
+ console.error('āŒ Failed to save MCP configuration:', error);
42
+ }
43
+ }
44
+ /**
45
+ * Connect to a specific MCP server
46
+ */
47
+ async connectToServer(serverName) {
48
+ const serverConfig = this.config.servers[serverName];
49
+ if (!serverConfig || !serverConfig.enabled) {
50
+ console.log(`āš ļø Server ${serverName} is not configured or disabled`);
51
+ return null;
52
+ }
53
+ try {
54
+ console.log(`šŸ”Œ Connecting to MCP server: ${serverName}`);
55
+ // Create transport for stdio communication
56
+ const transport = new StdioClientTransport({
57
+ command: serverConfig.command,
58
+ args: serverConfig.args || [],
59
+ env: {
60
+ ...Object.fromEntries(Object.entries(process.env).filter(([_, v]) => v !== undefined)),
61
+ ...serverConfig.env
62
+ }
63
+ });
64
+ // Create MCP client
65
+ const client = new Client({
66
+ name: 'protoagent',
67
+ version: '1.0.0'
68
+ }, {
69
+ capabilities: {
70
+ tools: {}
71
+ }
72
+ });
73
+ // Connect to the server
74
+ await client.connect(transport);
75
+ // Get server capabilities
76
+ const serverCapabilities = await client.listTools();
77
+ const capabilities = serverCapabilities.tools.map(tool => tool.name);
78
+ const connection = {
79
+ name: serverName,
80
+ client,
81
+ capabilities,
82
+ connected: true
83
+ };
84
+ this.connections.set(serverName, connection);
85
+ console.log(`āœ… Connected to ${serverName} with ${capabilities.length} tools`);
86
+ return connection;
87
+ }
88
+ catch (error) {
89
+ console.error(`āŒ Failed to connect to MCP server ${serverName}:`, error);
90
+ return null;
91
+ }
92
+ }
93
+ /**
94
+ * Connect to all enabled MCP servers
95
+ */
96
+ async connectToAllServers() {
97
+ console.log('šŸš€ Initializing MCP connections...');
98
+ const enabledServers = Object.keys(this.config.servers).filter(name => this.config.servers[name].enabled);
99
+ if (enabledServers.length === 0) {
100
+ console.log('ā„¹ļø No MCP servers enabled');
101
+ return;
102
+ }
103
+ const connectionPromises = enabledServers.map(serverName => this.connectToServer(serverName).catch(error => {
104
+ console.error(`Failed to connect to ${serverName}:`, error);
105
+ return null;
106
+ }));
107
+ const results = await Promise.all(connectionPromises);
108
+ const successfulConnections = results.filter(conn => conn !== null);
109
+ console.log(`šŸ“” MCP Status: ${successfulConnections.length}/${enabledServers.length} servers connected`);
110
+ }
111
+ /**
112
+ * Get all available tools from connected servers
113
+ */
114
+ async getAllTools() {
115
+ const allTools = [];
116
+ for (const [serverName, connection] of this.connections) {
117
+ if (!connection.connected)
118
+ continue;
119
+ try {
120
+ const tools = await connection.client.listTools();
121
+ for (const tool of tools.tools) {
122
+ allTools.push({
123
+ server: serverName,
124
+ name: tool.name,
125
+ description: tool.description,
126
+ schema: tool.inputSchema
127
+ });
128
+ }
129
+ }
130
+ catch (error) {
131
+ console.error(`āŒ Failed to get tools from ${serverName}:`, error);
132
+ }
133
+ }
134
+ return allTools;
135
+ }
136
+ /**
137
+ * Execute a tool on a specific MCP server
138
+ */
139
+ async callTool(serverName, toolName, args) {
140
+ const connection = this.connections.get(serverName);
141
+ if (!connection || !connection.connected) {
142
+ throw new Error(`Server ${serverName} is not connected`);
143
+ }
144
+ try {
145
+ console.log(`šŸ› ļø Calling ${serverName}:${toolName}`);
146
+ const result = await connection.client.callTool({
147
+ name: toolName,
148
+ arguments: args
149
+ });
150
+ return result;
151
+ }
152
+ catch (error) {
153
+ console.error(`āŒ Tool execution failed ${serverName}:${toolName}:`, error);
154
+ throw error;
155
+ }
156
+ }
157
+ /**
158
+ * Find which server has a specific tool
159
+ */
160
+ findToolServer(toolName) {
161
+ for (const [serverName, connection] of this.connections) {
162
+ if (connection.capabilities.includes(toolName)) {
163
+ return serverName;
164
+ }
165
+ }
166
+ return null;
167
+ }
168
+ /**
169
+ * Get connection status
170
+ */
171
+ getConnectionStatus() {
172
+ const status = {};
173
+ for (const [serverName, connection] of this.connections) {
174
+ status[serverName] = connection.connected;
175
+ }
176
+ return status;
177
+ }
178
+ /**
179
+ * Add a custom MCP server configuration
180
+ */
181
+ async addCustomServer(config) {
182
+ this.config.servers[config.name] = config;
183
+ await this.saveConfig();
184
+ console.log(`āœ… Added custom MCP server: ${config.name}`);
185
+ }
186
+ /**
187
+ * Remove a custom MCP server
188
+ */
189
+ async removeCustomServer(serverName) {
190
+ const serverConfig = this.config.servers[serverName];
191
+ if (serverConfig?.isBuiltIn) {
192
+ throw new Error('Cannot remove built-in MCP servers');
193
+ }
194
+ // Disconnect if connected
195
+ const connection = this.connections.get(serverName);
196
+ if (connection) {
197
+ await this.disconnectServer(serverName);
198
+ }
199
+ delete this.config.servers[serverName];
200
+ await this.saveConfig();
201
+ console.log(`āœ… Removed custom MCP server: ${serverName}`);
202
+ }
203
+ /**
204
+ * Disconnect from a specific server
205
+ */
206
+ async disconnectServer(serverName) {
207
+ const connection = this.connections.get(serverName);
208
+ if (connection) {
209
+ try {
210
+ await connection.client.close();
211
+ connection.connected = false;
212
+ this.connections.delete(serverName);
213
+ console.log(`šŸ”Œ Disconnected from ${serverName}`);
214
+ }
215
+ catch (error) {
216
+ console.error(`āŒ Error disconnecting from ${serverName}:`, error);
217
+ }
218
+ }
219
+ }
220
+ /**
221
+ * Disconnect from all servers
222
+ */
223
+ async disconnectAll() {
224
+ console.log('šŸ”Œ Disconnecting from all MCP servers...');
225
+ const disconnectPromises = Array.from(this.connections.keys()).map(serverName => this.disconnectServer(serverName));
226
+ await Promise.all(disconnectPromises);
227
+ }
228
+ /**
229
+ * Get server configuration
230
+ */
231
+ getServerConfig(serverName) {
232
+ return this.config.servers[serverName] || null;
233
+ }
234
+ /**
235
+ * List all configured servers
236
+ */
237
+ listServers() {
238
+ return { ...this.config.servers };
239
+ }
240
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * MCP (Model Context Protocol) configuration types
3
+ */
4
+ // Built-in server configurations
5
+ export const BUILTIN_MCP_SERVERS = {
6
+ filesystem: {
7
+ name: 'filesystem',
8
+ description: 'File system operations via MCP',
9
+ command: 'npx',
10
+ args: ['@modelcontextprotocol/server-filesystem'],
11
+ enabled: true,
12
+ isBuiltIn: true
13
+ },
14
+ 'sequential-thinking': {
15
+ name: 'sequential-thinking',
16
+ description: 'Sequential thinking and reasoning tools',
17
+ command: 'npx',
18
+ args: ['@modelcontextprotocol/server-sequential-thinking'],
19
+ enabled: true,
20
+ isBuiltIn: true
21
+ }
22
+ };
23
+ export const DEFAULT_MCP_CONFIG = {
24
+ servers: { ...BUILTIN_MCP_SERVERS },
25
+ globalTimeout: 30000, // 30 seconds
26
+ maxConcurrentConnections: 5,
27
+ retryAttempts: 3
28
+ };
@@ -0,0 +1,170 @@
1
+ /**
2
+ * OpenAI provider configuration for ProtoAgent
3
+ */
4
+ export const openaiModels = {
5
+ models: [
6
+ 'gpt-5-nano-2025-08-07',
7
+ 'gpt-5-mini-2025-08-07',
8
+ 'gpt-5-2025-08-07',
9
+ 'gpt-4o-mini-2024-07-18',
10
+ 'gpt-4o',
11
+ 'gpt-4o-2024-11-20',
12
+ 'gpt-4-turbo',
13
+ 'gpt-3.5-turbo'
14
+ ],
15
+ defaultModel: 'gpt-5-mini-2025-08-07'
16
+ };
17
+ export const openaiModelConfigs = {
18
+ 'gpt-5-nano-2025-08-07': {
19
+ name: 'GPT-5 Nano',
20
+ contextWindow: 128000,
21
+ pricing: { inputTokens: 0.00000005, outputTokens: 0.0000004 } // $0.05/$0.40 per 1M tokens
22
+ },
23
+ 'gpt-5-mini-2025-08-07': {
24
+ name: 'GPT-5 Mini',
25
+ contextWindow: 128000,
26
+ pricing: { inputTokens: 0.00000025, outputTokens: 0.000002 } // $0.25/$2.00 per 1M tokens
27
+ },
28
+ 'gpt-5-2025-08-07': {
29
+ name: 'GPT-5',
30
+ contextWindow: 128000,
31
+ pricing: { inputTokens: 0.00000125, outputTokens: 0.00001 } // $1.25/$10.00 per 1M tokens
32
+ },
33
+ 'gpt-4o-mini-2024-07-18': {
34
+ name: 'GPT-4o Mini',
35
+ contextWindow: 128000,
36
+ pricing: { inputTokens: 0.00000015, outputTokens: 0.0000006 } // $0.15/$0.60 per 1M tokens
37
+ },
38
+ 'gpt-4o': {
39
+ name: 'GPT-4o',
40
+ contextWindow: 128000,
41
+ pricing: { inputTokens: 0.0000025, outputTokens: 0.00001 } // $2.50/$10.00 per 1M tokens
42
+ },
43
+ 'gpt-4o-2024-11-20': {
44
+ name: 'GPT-4o (2024-11-20)',
45
+ contextWindow: 128000,
46
+ pricing: { inputTokens: 0.0000025, outputTokens: 0.00001 } // $2.50/$10.00 per 1M tokens
47
+ },
48
+ 'gpt-4-turbo': {
49
+ name: 'GPT-4 Turbo',
50
+ contextWindow: 128000,
51
+ pricing: { inputTokens: 0.00001, outputTokens: 0.00003 } // $10.00/$30.00 per 1M tokens
52
+ },
53
+ 'gpt-3.5-turbo': {
54
+ name: 'GPT-3.5 Turbo',
55
+ contextWindow: 16385,
56
+ pricing: { inputTokens: 0.0000005, outputTokens: 0.0000015 } // $0.50/$1.50 per 1M tokens
57
+ }
58
+ };
59
+ export const openaiProvider = {
60
+ id: 'openai',
61
+ name: 'OpenAI',
62
+ models: openaiModels.models,
63
+ defaultModel: 'gpt-5-mini-2025-08-07',
64
+ modelConfigs: openaiModelConfigs,
65
+ auth: {
66
+ type: 'api_key',
67
+ envVar: 'OPENAI_API_KEY',
68
+ label: 'OpenAI API Key',
69
+ helpUrl: 'https://platform.openai.com/api-keys'
70
+ }
71
+ };
72
+ export const geminiModels = {
73
+ models: [
74
+ 'models/gemini-2.5-pro',
75
+ 'models/gemini-2.5-flash',
76
+ 'models/gemini-2.5-flash-lite',
77
+ 'models/gemini-2.0-flash',
78
+ 'models/gemini-2.0-flash-lite'
79
+ ],
80
+ defaultModel: 'models/gemini-2.5-flash'
81
+ };
82
+ export const geminiModelConfigs = {
83
+ 'models/gemini-2.5-pro': {
84
+ name: 'Gemini 2.5 Pro',
85
+ contextWindow: 2000000,
86
+ pricing: { inputTokens: 0.00000125, outputTokens: 0.00001 } // $1.25/$10.00 per 1M tokens (<=200k prompts)
87
+ },
88
+ 'models/gemini-2.5-flash': {
89
+ name: 'Gemini 2.5 Flash',
90
+ contextWindow: 1000000,
91
+ pricing: { inputTokens: 0.0000003, outputTokens: 0.0000025 } // $0.30/$2.50 per 1M tokens
92
+ },
93
+ 'models/gemini-2.5-flash-lite': {
94
+ name: 'Gemini 2.5 Flash Lite',
95
+ contextWindow: 1000000,
96
+ pricing: { inputTokens: 0.0000001, outputTokens: 0.0000004 } // $0.10/$0.40 per 1M tokens
97
+ },
98
+ 'models/gemini-2.0-flash': {
99
+ name: 'Gemini 2.0 Flash',
100
+ contextWindow: 1000000,
101
+ pricing: { inputTokens: 0.0000001, outputTokens: 0.0000004 } // $0.10/$0.40 per 1M tokens (text/image/video)
102
+ },
103
+ 'models/gemini-2.0-flash-lite': {
104
+ name: 'Gemini 2.0 Flash Lite',
105
+ contextWindow: 1000000,
106
+ pricing: { inputTokens: 0.000000075, outputTokens: 0.0000003 } // $0.075/$0.30 per 1M tokens
107
+ }
108
+ };
109
+ export const geminiProvider = {
110
+ id: 'gemini',
111
+ name: 'Gemini',
112
+ models: geminiModels.models,
113
+ defaultModel: geminiModels.defaultModel,
114
+ modelConfigs: geminiModelConfigs,
115
+ auth: {
116
+ type: 'api_key',
117
+ envVar: 'GEMINI_API_KEY',
118
+ label: 'Gemini API Key',
119
+ helpUrl: 'https://aistudio.google.com/app/apikey'
120
+ },
121
+ baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai/'
122
+ };
123
+ export const cerebrasModels = {
124
+ models: [
125
+ 'gpt-oss-120b',
126
+ 'qwen-3-coder-480b'
127
+ ],
128
+ defaultModel: 'gpt-oss-120b'
129
+ };
130
+ export const cerebrasModelConfigs = {
131
+ 'gpt-oss-120b': {
132
+ name: 'GPT OSS 120B',
133
+ contextWindow: 128000,
134
+ pricing: { inputTokens: 0.00000035, outputTokens: 0.00000075 } // $0.35/$0.75 per 1M tokens
135
+ },
136
+ 'qwen-3-coder-480b': {
137
+ name: 'Qwen 3 Coder 480B',
138
+ contextWindow: 128000,
139
+ pricing: { inputTokens: 0.000002, outputTokens: 0.000002 } // $2.00/$2.00 per 1M tokens
140
+ }
141
+ };
142
+ export const cerebrasProvider = {
143
+ id: 'cerebras',
144
+ name: 'Cerebras',
145
+ models: cerebrasModels.models,
146
+ defaultModel: cerebrasModels.defaultModel,
147
+ modelConfigs: cerebrasModelConfigs,
148
+ auth: {
149
+ type: 'api_key',
150
+ envVar: 'CEREBRAS_API_KEY',
151
+ label: 'Cerebras API Key',
152
+ helpUrl: 'https://cloud.cerebras.ai/platform'
153
+ },
154
+ baseURL: 'https://api.cerebras.ai/v1'
155
+ };
156
+ /**
157
+ * Get model configuration for any provider
158
+ */
159
+ export function getModelConfig(provider, model) {
160
+ switch (provider) {
161
+ case 'openai':
162
+ return openaiModelConfigs[model] || null;
163
+ case 'gemini':
164
+ return geminiModelConfigs[model] || null;
165
+ case 'cerebras':
166
+ return cerebrasModelConfigs[model] || null;
167
+ default:
168
+ return null;
169
+ }
170
+ }
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Interactive configuration setup for ProtoAgent
3
+ */
4
+ import inquirer from 'inquirer';
5
+ import { openaiProvider, geminiProvider, cerebrasProvider } from './providers.js';
6
+ import { saveConfig, getConfigDirectory } from './manager.js';
7
+ /**
8
+ * Run interactive configuration setup
9
+ */
10
+ export async function setupConfig() {
11
+ console.log('\nšŸ¤– Welcome to ProtoAgent!');
12
+ console.log("Let's set up your model configuration.\n");
13
+ // Step 1: Select provider and model
14
+ const modelChoices = [
15
+ new inquirer.Separator('--- OpenAI Models ---'),
16
+ ...openaiProvider.models.map(model => ({
17
+ name: model === openaiProvider.defaultModel ? `${model} (recommended)` : model,
18
+ value: { provider: 'openai', model }
19
+ })),
20
+ new inquirer.Separator('--- Gemini Models ---'),
21
+ ...geminiProvider.models.map(model => ({
22
+ name: model === geminiProvider.defaultModel ? `${model} (recommended)` : model,
23
+ value: { provider: 'gemini', model }
24
+ })),
25
+ new inquirer.Separator('--- Cerebras Models ---'),
26
+ ...cerebrasProvider.models.map(model => ({
27
+ name: model === cerebrasProvider.defaultModel ? `${model} (recommended)` : model,
28
+ value: { provider: 'cerebras', model }
29
+ }))
30
+ ];
31
+ const { selection } = await inquirer.prompt([
32
+ {
33
+ type: 'list',
34
+ name: 'selection',
35
+ message: 'Select a model:',
36
+ choices: modelChoices,
37
+ default: { provider: 'openai', model: openaiProvider.defaultModel }
38
+ }
39
+ ]);
40
+ const provider = selection.provider;
41
+ const model = selection.model;
42
+ // Step 2: Get API credentials
43
+ let apiKey = '';
44
+ let credentials = {
45
+ OPENAI_API_KEY: '',
46
+ GEMINI_API_KEY: '',
47
+ CEREBRAS_API_KEY: ''
48
+ };
49
+ if (provider === 'openai') {
50
+ console.log(`\nšŸ“ ${openaiProvider.auth.label} Setup`);
51
+ console.log("You'll need an API key from OpenAI.");
52
+ console.log(`Get your API key from: ${openaiProvider.auth.helpUrl}`);
53
+ const response = await inquirer.prompt([
54
+ {
55
+ type: 'password',
56
+ name: 'apiKey',
57
+ message: `Enter your ${openaiProvider.auth.label}:`,
58
+ mask: '*',
59
+ validate: (input) => {
60
+ if (!input || input.trim().length === 0) {
61
+ return 'API key is required';
62
+ }
63
+ if (!input.trim().startsWith('sk-')) {
64
+ return 'OpenAI API keys should start with "sk-"';
65
+ }
66
+ return true;
67
+ }
68
+ }
69
+ ]);
70
+ apiKey = response.apiKey;
71
+ credentials.OPENAI_API_KEY = apiKey;
72
+ }
73
+ else if (provider === 'gemini') {
74
+ console.log(`\nšŸ“ ${geminiProvider.auth.label} Setup`);
75
+ console.log("You'll need an API key from Gemini.");
76
+ console.log(`Get your API key from: ${geminiProvider.auth.helpUrl}`);
77
+ const response = await inquirer.prompt([
78
+ {
79
+ type: 'password',
80
+ name: 'apiKey',
81
+ message: `Enter your ${geminiProvider.auth.label}:`,
82
+ mask: '*',
83
+ validate: (input) => {
84
+ if (!input || input.trim().length === 0) {
85
+ return 'API key is required';
86
+ }
87
+ // Gemini keys may have different format, so just check non-empty
88
+ return true;
89
+ }
90
+ }
91
+ ]);
92
+ apiKey = response.apiKey;
93
+ credentials.GEMINI_API_KEY = apiKey;
94
+ }
95
+ else if (provider === 'cerebras') {
96
+ console.log(`\nšŸ“ ${cerebrasProvider.auth.label} Setup`);
97
+ console.log("You'll need an API key from Cerebras.");
98
+ console.log(`Get your API key from: ${cerebrasProvider.auth.helpUrl}`);
99
+ const response = await inquirer.prompt([
100
+ {
101
+ type: 'password',
102
+ name: 'apiKey',
103
+ message: `Enter your ${cerebrasProvider.auth.label}:`,
104
+ mask: '*',
105
+ validate: (input) => {
106
+ if (!input || input.trim().length === 0) {
107
+ return 'API key is required';
108
+ }
109
+ // Cerebras keys may have different format, so just check non-empty
110
+ return true;
111
+ }
112
+ }
113
+ ]);
114
+ apiKey = response.apiKey;
115
+ credentials.CEREBRAS_API_KEY = apiKey;
116
+ }
117
+ // Step 3: Confirm configuration
118
+ console.log('\nšŸ“‹ Configuration Summary:');
119
+ let providerName = '';
120
+ if (provider === 'openai') {
121
+ providerName = openaiProvider.name;
122
+ }
123
+ else if (provider === 'gemini') {
124
+ providerName = geminiProvider.name;
125
+ }
126
+ else if (provider === 'cerebras') {
127
+ providerName = cerebrasProvider.name;
128
+ }
129
+ console.log(`Provider: ${providerName}`);
130
+ console.log(`Model: ${model}`);
131
+ console.log(`API Key: ${'*'.repeat(Math.min(apiKey.length, 20))}...`);
132
+ const { confirm } = await inquirer.prompt([
133
+ {
134
+ type: 'confirm',
135
+ name: 'confirm',
136
+ message: 'Save this configuration?',
137
+ default: true
138
+ }
139
+ ]);
140
+ if (!confirm) {
141
+ console.log('Configuration cancelled.');
142
+ process.exit(0);
143
+ }
144
+ // Step 4: Save configuration
145
+ const config = {
146
+ provider,
147
+ model,
148
+ credentials
149
+ };
150
+ try {
151
+ await saveConfig(config);
152
+ console.log(`\nāœ… Configuration saved successfully!`);
153
+ console.log(`Config location: ${getConfigDirectory()}/config.json`);
154
+ console.log("\nYou're all set! ProtoAgent is ready to use.\n");
155
+ return config;
156
+ }
157
+ catch (error) {
158
+ console.error(`\nāŒ Failed to save configuration: ${error.message}`);
159
+ process.exit(1);
160
+ }
161
+ }
162
+ /**
163
+ * Prompt to reconfigure
164
+ */
165
+ export async function promptReconfigure() {
166
+ const { reconfigure } = await inquirer.prompt([
167
+ {
168
+ type: 'confirm',
169
+ name: 'reconfigure',
170
+ message: 'Would you like to reconfigure ProtoAgent?',
171
+ default: false
172
+ }
173
+ ]);
174
+ return reconfigure;
175
+ }