@mcp-use/cli 1.0.0

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/dist/app.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ type Props = {
3
+ name?: string;
4
+ };
5
+ export default function App({ name }: Props): React.JSX.Element;
6
+ export {};
package/dist/app.js ADDED
@@ -0,0 +1,343 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Box, Text, useInput, useStdout } from 'ink';
3
+ import TextInput from 'ink-text-input';
4
+ import BigText from 'ink-big-text';
5
+ import { mcpService } from './mcp-service.js';
6
+ const MessageRenderer = ({ message }) => {
7
+ switch (message.role) {
8
+ case "tool":
9
+ return React.createElement(ToolCallRenderer, { message: message });
10
+ case "user":
11
+ return React.createElement(UserMessageRenderer, { message: message });
12
+ case "assistant":
13
+ return React.createElement(AssistantMessageRenerer, { message: message });
14
+ case "command":
15
+ return React.createElement(CommandMessageRenderer, { message: message });
16
+ default:
17
+ return null;
18
+ }
19
+ };
20
+ const UserMessageRenderer = ({ message }) => {
21
+ return React.createElement(React.Fragment, null,
22
+ React.createElement(Box, { key: message.id, marginBottom: 1 },
23
+ React.createElement(Box, { marginRight: 1 },
24
+ React.createElement(Text, { color: 'green', bold: true }, "'\u276F'")),
25
+ React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
26
+ React.createElement(Text, { wrap: "wrap" }, message.content))));
27
+ };
28
+ const AssistantMessageRenerer = ({ message }) => {
29
+ return (React.createElement(React.Fragment, null,
30
+ React.createElement(Box, { key: message.id, marginBottom: 1 },
31
+ React.createElement(Box, { marginRight: 1 },
32
+ React.createElement(Text, { color: 'blue', bold: true }, "'\u25E6'")),
33
+ React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
34
+ React.createElement(Text, { wrap: "wrap" }, message.content)))));
35
+ };
36
+ const ToolCallRenderer = ({ message }) => {
37
+ return React.createElement(React.Fragment, null,
38
+ React.createElement(Box, { key: message.id, marginBottom: 1, borderStyle: "round", flexDirection: 'column', gap: 1 },
39
+ React.createElement(Box, { marginRight: 1 },
40
+ React.createElement(Text, { color: 'white', bold: true },
41
+ "\uD83D\uDD28 Tool: ",
42
+ message.tool_name)),
43
+ React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
44
+ React.createElement(Text, { wrap: "wrap" },
45
+ " Input: ",
46
+ message.tool_input.toString(),
47
+ " "),
48
+ React.createElement(Text, { wrap: "wrap" },
49
+ " Output:",
50
+ message.tool_output.toString(),
51
+ " "))));
52
+ };
53
+ const CommandMessageRenderer = ({ message }) => {
54
+ const { commandResult } = message;
55
+ let icon = '💻';
56
+ let color = 'cyan';
57
+ if (commandResult.type === 'error') {
58
+ icon = '❌';
59
+ color = 'red';
60
+ }
61
+ else if (commandResult.type === 'success') {
62
+ icon = '✅';
63
+ color = 'green';
64
+ }
65
+ else if (commandResult.type === 'prompt_key') {
66
+ icon = '🔑';
67
+ color = 'yellow';
68
+ }
69
+ else if (commandResult.type === 'prompt_server_config') {
70
+ icon = '🔧';
71
+ color = 'blue';
72
+ }
73
+ return React.createElement(React.Fragment, null,
74
+ React.createElement(Box, { key: message.id, marginBottom: 1 },
75
+ React.createElement(Box, { marginRight: 1 },
76
+ React.createElement(Text, { color: color, bold: true }, icon)),
77
+ React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
78
+ React.createElement(Text, { wrap: "wrap", color: color }, message.content))));
79
+ };
80
+ export default function App({ name }) {
81
+ const [messages, setMessages] = useState([]);
82
+ const [input, setInput] = useState('');
83
+ const [isLoading, setIsLoading] = useState(false);
84
+ const [showInput, setShowInput] = useState(false);
85
+ const [initializationError, setInitializationError] = useState('');
86
+ const [currentModel, setCurrentModel] = useState('openai/gpt-4o-mini');
87
+ const [connectedServers, setConnectedServers] = useState([]);
88
+ const [isWaitingForApiKey, setIsWaitingForApiKey] = useState(false);
89
+ const [pendingProvider, setPendingProvider] = useState('');
90
+ const [pendingModel, setPendingModel] = useState('');
91
+ const [isWaitingForServerConfig, setIsWaitingForServerConfig] = useState(false);
92
+ const [serverConfigStep, setServerConfigStep] = useState('');
93
+ const [currentServerConfig, setCurrentServerConfig] = useState(null);
94
+ const { stdout } = useStdout();
95
+ // Initialize MCP service on component mount
96
+ useEffect(() => {
97
+ const initializeMCP = async () => {
98
+ try {
99
+ setIsLoading(true);
100
+ await mcpService.initialize();
101
+ setCurrentModel(mcpService.getCurrentModel());
102
+ setConnectedServers(mcpService.getConnectedServers());
103
+ setShowInput(true);
104
+ }
105
+ catch (error) {
106
+ setInitializationError(error instanceof Error ? error.message : 'Failed to initialize MCP service');
107
+ }
108
+ finally {
109
+ setIsLoading(false);
110
+ }
111
+ };
112
+ initializeMCP();
113
+ }, []);
114
+ useInput((input, key) => {
115
+ if (key.ctrl && input === 'c') {
116
+ process.exit(0);
117
+ }
118
+ if (key.ctrl && input === 'd') {
119
+ process.exit(0);
120
+ }
121
+ });
122
+ const handleSubmit = async (userInput) => {
123
+ if (!userInput.trim())
124
+ return;
125
+ // Check if we're waiting for server configuration input
126
+ if (isWaitingForServerConfig) {
127
+ const userMessage = {
128
+ id: Date.now().toString(),
129
+ role: 'user',
130
+ content: userInput.trim(),
131
+ timestamp: new Date(),
132
+ };
133
+ setMessages(prev => [...prev, userMessage]);
134
+ setInput('');
135
+ setIsLoading(true);
136
+ try {
137
+ // Import CommandHandler to access handleServerConfigInput
138
+ const result = await mcpService.sendMessage(userInput.trim(), false, '', '', true, serverConfigStep, currentServerConfig);
139
+ if (result.commandResult) {
140
+ const commandMessage = {
141
+ id: (Date.now() + 1).toString(),
142
+ role: 'command',
143
+ content: result.response,
144
+ commandResult: result.commandResult,
145
+ timestamp: new Date(),
146
+ };
147
+ setMessages(prev => [...prev, commandMessage]);
148
+ // Check if we're continuing server config or done
149
+ if (result.commandResult.type === 'prompt_server_config' && result.commandResult.data) {
150
+ setServerConfigStep(result.commandResult.data.step);
151
+ setCurrentServerConfig(result.commandResult.data.config);
152
+ }
153
+ else {
154
+ // Server config is done
155
+ setIsWaitingForServerConfig(false);
156
+ setServerConfigStep('');
157
+ setCurrentServerConfig(null);
158
+ // Update connected servers if servers were connected or disconnected
159
+ if (result.commandResult.data?.serverConnected || result.commandResult.data?.serverDisconnected) {
160
+ setConnectedServers(mcpService.getConnectedServers());
161
+ }
162
+ }
163
+ }
164
+ setIsLoading(false);
165
+ }
166
+ catch (error) {
167
+ const errorMessage = {
168
+ id: (Date.now() + 1).toString(),
169
+ role: 'assistant',
170
+ content: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
171
+ timestamp: new Date(),
172
+ };
173
+ setMessages(prev => [...prev, errorMessage]);
174
+ setIsLoading(false);
175
+ // Reset server config state on error
176
+ setIsWaitingForServerConfig(false);
177
+ setServerConfigStep('');
178
+ setCurrentServerConfig(null);
179
+ }
180
+ return;
181
+ }
182
+ // Check if we're waiting for an API key
183
+ if (isWaitingForApiKey) {
184
+ const maskedInput = userInput.replace(/./g, '*');
185
+ const userMessage = {
186
+ id: Date.now().toString(),
187
+ role: 'user',
188
+ content: maskedInput,
189
+ timestamp: new Date(),
190
+ };
191
+ setMessages(prev => [...prev, userMessage]);
192
+ setInput('');
193
+ setIsLoading(true);
194
+ try {
195
+ const result = await mcpService.sendMessage(userInput.trim(), true, pendingProvider, pendingModel);
196
+ if (result.commandResult) {
197
+ const commandMessage = {
198
+ id: (Date.now() + 1).toString(),
199
+ role: 'command',
200
+ content: result.response,
201
+ commandResult: result.commandResult,
202
+ timestamp: new Date(),
203
+ };
204
+ setMessages(prev => [...prev, commandMessage]);
205
+ // Update current model if successful
206
+ if (result.commandResult.data?.llmConfig) {
207
+ setCurrentModel(mcpService.getCurrentModel());
208
+ }
209
+ // Reset API key input state
210
+ setIsWaitingForApiKey(false);
211
+ setPendingProvider('');
212
+ setPendingModel('');
213
+ }
214
+ setIsLoading(false);
215
+ }
216
+ catch (error) {
217
+ const errorMessage = {
218
+ id: (Date.now() + 1).toString(),
219
+ role: 'assistant',
220
+ content: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
221
+ timestamp: new Date(),
222
+ };
223
+ setMessages(prev => [...prev, errorMessage]);
224
+ setIsLoading(false);
225
+ // Reset API key input state on error
226
+ setIsWaitingForApiKey(false);
227
+ setPendingProvider('');
228
+ setPendingModel('');
229
+ }
230
+ return;
231
+ }
232
+ const userMessage = {
233
+ id: Date.now().toString(),
234
+ role: 'user',
235
+ content: userInput.trim(),
236
+ timestamp: new Date(),
237
+ };
238
+ setMessages(prev => [...prev, userMessage]);
239
+ setInput('');
240
+ setIsLoading(true);
241
+ try {
242
+ const result = await mcpService.sendMessage(userInput.trim());
243
+ if (result.isCommand && result.commandResult) {
244
+ // Handle command response
245
+ const commandMessage = {
246
+ id: (Date.now() + 1).toString(),
247
+ role: 'command',
248
+ content: result.response,
249
+ commandResult: result.commandResult,
250
+ timestamp: new Date(),
251
+ };
252
+ setMessages(prev => [...prev, commandMessage]);
253
+ // Check if we need to prompt for API key
254
+ if (result.commandResult.type === 'prompt_key' && result.commandResult.data) {
255
+ setIsWaitingForApiKey(true);
256
+ setPendingProvider(result.commandResult.data.provider);
257
+ setPendingModel(result.commandResult.data.model);
258
+ }
259
+ else if (result.commandResult.type === 'prompt_server_config' && result.commandResult.data) {
260
+ setIsWaitingForServerConfig(true);
261
+ setServerConfigStep(result.commandResult.data.step);
262
+ setCurrentServerConfig(result.commandResult.data.config || null);
263
+ }
264
+ else if (result.commandResult.data?.serverConnected || result.commandResult.data?.serverDisconnected) {
265
+ // Update connected servers if servers were connected or disconnected
266
+ setConnectedServers(mcpService.getConnectedServers());
267
+ }
268
+ else if (result.commandResult.data?.hasOwnProperty('llmConfig')) {
269
+ // Update current model if it changed (including null for clearkeys)
270
+ setCurrentModel(mcpService.getCurrentModel());
271
+ }
272
+ }
273
+ else {
274
+ // Handle regular assistant response
275
+ const assistantMessage = {
276
+ id: (Date.now() + 1).toString(),
277
+ role: 'assistant',
278
+ content: result.response,
279
+ timestamp: new Date(),
280
+ };
281
+ // Add assistant message and any tool calls
282
+ setMessages(prev => [...prev, assistantMessage, ...result.toolCalls]);
283
+ }
284
+ setIsLoading(false);
285
+ }
286
+ catch (error) {
287
+ const errorMessage = {
288
+ id: (Date.now() + 1).toString(),
289
+ role: 'assistant',
290
+ content: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
291
+ timestamp: new Date(),
292
+ };
293
+ setMessages(prev => [...prev, errorMessage]);
294
+ setIsLoading(false);
295
+ }
296
+ };
297
+ return (React.createElement(Box, { flexDirection: "column", minHeight: stdout.rows || 24 },
298
+ React.createElement(Box, { borderStyle: "round", borderColor: "blue", paddingX: 1, marginBottom: 1 },
299
+ React.createElement(Box, { flexDirection: "column", width: "100%" },
300
+ React.createElement(Box, { flexDirection: "row", justifyContent: "space-between", width: "100%" },
301
+ React.createElement(Text, { color: "blue", bold: true },
302
+ "MCP-Use CLI ",
303
+ name ? `- ${name}` : ' '),
304
+ React.createElement(Text, { color: "gray" },
305
+ "Model: ",
306
+ currentModel)),
307
+ React.createElement(Box, { flexDirection: "row", justifyContent: "flex-start", width: "100%" },
308
+ React.createElement(Text, { color: "gray" },
309
+ "Connected servers: ",
310
+ connectedServers.length > 0 ? connectedServers.join(', ') : 'none')))),
311
+ React.createElement(Box, { flexDirection: "column", flexGrow: 1, paddingX: 1 },
312
+ initializationError && (React.createElement(Box, { marginBottom: 1 },
313
+ React.createElement(Text, { color: "red" },
314
+ "\u274C ",
315
+ initializationError))),
316
+ !initializationError && messages.length === 0 && !isLoading && (React.createElement(Box, { marginBottom: 1, flexDirection: "column" },
317
+ React.createElement(BigText, { text: "MCP USE CLI", colors: ['white'] }),
318
+ React.createElement(Text, { color: "gray" }, "Welcome to MCP-Use CLI!"),
319
+ currentModel.includes('No') ? (React.createElement(Box, { flexDirection: "column" },
320
+ React.createElement(Text, { color: "yellow" },
321
+ "\u26A0\uFE0F ",
322
+ currentModel),
323
+ React.createElement(Text, { color: "gray" }, "Choose a model to get started - the CLI will help you set up the API key."),
324
+ React.createElement(Text, { color: "cyan" }, "\uD83D\uDCA1 Try: /model openai gpt-4o-mini"),
325
+ React.createElement(Text, { color: "gray" }, "Or use /models to see all options, /help for commands."))) : (React.createElement(Box, { flexDirection: "column" },
326
+ React.createElement(Text, { color: "gray" }, "Type your message and press Enter to start chatting."),
327
+ React.createElement(Text, { color: "gray" }, "Use slash commands like /help, /model, or /status for configuration."))))),
328
+ !initializationError && messages.length === 0 && isLoading && (React.createElement(Box, { marginBottom: 1 },
329
+ React.createElement(Text, { color: "blue" }, "\uD83D\uDD04 Initializing MCP service..."))),
330
+ messages.map(message => React.createElement(MessageRenderer, { message: message })),
331
+ isLoading && (React.createElement(Box, { marginBottom: 1 },
332
+ React.createElement(Box, { marginRight: 1 },
333
+ React.createElement(Text, { color: "blue", bold: true }, "\u25E6")),
334
+ React.createElement(Text, { color: "gray" }, "Thinking...")))),
335
+ showInput && !initializationError && (React.createElement(Box, { borderStyle: "round", borderColor: isWaitingForApiKey ? "yellow" : isWaitingForServerConfig ? "blue" : "gray", paddingX: 1 },
336
+ React.createElement(Box, { marginRight: 1 },
337
+ React.createElement(Text, { color: isWaitingForApiKey ? "yellow" : isWaitingForServerConfig ? "blue" : "green", bold: true }, isWaitingForApiKey ? "🔑" : isWaitingForServerConfig ? "🔧" : "❯")),
338
+ React.createElement(TextInput, { value: input, onChange: setInput, onSubmit: handleSubmit, placeholder: isWaitingForApiKey
339
+ ? `Enter ${pendingProvider.toUpperCase()} API key...`
340
+ : isWaitingForServerConfig
341
+ ? "Enter server configuration..."
342
+ : "Type your message...", mask: isWaitingForApiKey ? "*" : undefined })))));
343
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ import React from 'react';
3
+ import { render } from 'ink';
4
+ import meow from 'meow';
5
+ import App from './app.js';
6
+ const cli = meow(`
7
+ Usage
8
+ $ mcp-use-cli
9
+
10
+ Options
11
+ --name Your name (optional)
12
+ --config Path to MCP configuration file (optional)
13
+
14
+ Examples
15
+ $ mcp-use-cli
16
+ $ mcp-use-cli --name=Jane
17
+ $ mcp-use-cli --config=./mcp-config.json
18
+
19
+ Environment Variables
20
+ OPENAI_API_KEY Required - Your OpenAI API key
21
+
22
+ Setup
23
+ 1. Set your OpenAI API key: export OPENAI_API_KEY=your_key_here
24
+ 2. Run: mcp-use-cli
25
+ 3. Start chatting with MCP servers!
26
+ `, {
27
+ importMeta: import.meta,
28
+ flags: {
29
+ name: {
30
+ type: 'string',
31
+ },
32
+ config: {
33
+ type: 'string',
34
+ },
35
+ },
36
+ });
37
+ render(React.createElement(App, { name: cli.flags.name }));
@@ -0,0 +1,51 @@
1
+ import { StoredConfig } from './storage.js';
2
+ export interface LLMConfig {
3
+ provider: 'openai' | 'anthropic' | 'google' | 'mistral';
4
+ model: string;
5
+ temperature?: number;
6
+ maxTokens?: number;
7
+ }
8
+ export interface CommandResult {
9
+ type: 'success' | 'error' | 'info' | 'prompt_key' | 'prompt_server_config';
10
+ message: string;
11
+ data?: any;
12
+ }
13
+ export declare class CommandHandler {
14
+ private currentLLMConfig;
15
+ private sessionApiKeys;
16
+ private persistentConfig;
17
+ private sessionServers;
18
+ private availableModels;
19
+ constructor();
20
+ private initializeDefaultProvider;
21
+ getAvailableProviders(): string[];
22
+ private getApiKey;
23
+ isAnyProviderAvailable(): boolean;
24
+ handleCommand(input: string): Promise<CommandResult>;
25
+ private handleHelp;
26
+ private handleModel;
27
+ private handleListModels;
28
+ private handleStatus;
29
+ private handleConfig;
30
+ private handleSetKey;
31
+ private validateApiKey;
32
+ private maskApiKey;
33
+ createLLM(): any;
34
+ getCurrentConfig(): LLMConfig | null;
35
+ getCurrentStoredConfig(): StoredConfig;
36
+ getSessionServers(): Record<string, {
37
+ command: string;
38
+ args?: string[];
39
+ env?: Record<string, string>;
40
+ }>;
41
+ private handleListTools;
42
+ private handleTestServer;
43
+ isCommand(input: string): boolean;
44
+ private handleClearKeys;
45
+ handleServerConfigInput(input: string, step: string, serverConfig?: any): CommandResult;
46
+ private handleServer;
47
+ private handleListServers;
48
+ private handleConnectServer;
49
+ private handleDisconnectServer;
50
+ handleApiKeyInput(apiKey: string, provider: string, model: string): CommandResult;
51
+ }