@mcp-use/cli 1.0.0 → 1.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/dist/InputPrompt.d.ts +13 -0
- package/dist/InputPrompt.js +188 -0
- package/dist/MultilineInput.d.ts +13 -0
- package/dist/MultilineInput.js +154 -0
- package/dist/MultilineTextInput.d.ts +11 -0
- package/dist/MultilineTextInput.js +97 -0
- package/dist/PasteAwareInput.d.ts +13 -0
- package/dist/PasteAwareInput.js +183 -0
- package/dist/SimpleMultilineInput.d.ts +11 -0
- package/dist/SimpleMultilineInput.js +125 -0
- package/dist/app.d.ts +1 -5
- package/dist/app.js +291 -186
- package/dist/cli.js +2 -5
- package/dist/commands.d.ts +15 -30
- package/dist/commands.js +308 -568
- package/dist/components/AsciiLogo.d.ts +2 -0
- package/dist/components/AsciiLogo.js +7 -0
- package/dist/components/Footer.d.ts +5 -0
- package/dist/components/Footer.js +19 -0
- package/dist/components/InputPrompt.d.ts +13 -0
- package/dist/components/InputPrompt.js +188 -0
- package/dist/components/Messages.d.ts +21 -0
- package/dist/components/Messages.js +80 -0
- package/dist/components/ServerStatus.d.ts +7 -0
- package/dist/components/ServerStatus.js +36 -0
- package/dist/components/Spinner.d.ts +16 -0
- package/dist/components/Spinner.js +63 -0
- package/dist/components/ToolStatus.d.ts +8 -0
- package/dist/components/ToolStatus.js +33 -0
- package/dist/components/textInput.d.ts +1 -0
- package/dist/components/textInput.js +1 -0
- package/dist/logger.d.ts +10 -0
- package/dist/logger.js +48 -0
- package/dist/mcp-service.d.ts +5 -4
- package/dist/mcp-service.js +98 -207
- package/dist/services/agent-service.d.ts +56 -0
- package/dist/services/agent-service.js +203 -0
- package/dist/services/cli-service.d.ts +132 -0
- package/dist/services/cli-service.js +591 -0
- package/dist/services/index.d.ts +4 -0
- package/dist/services/index.js +4 -0
- package/dist/services/llm-service.d.ts +174 -0
- package/dist/services/llm-service.js +567 -0
- package/dist/services/mcp-config-service.d.ts +69 -0
- package/dist/services/mcp-config-service.js +426 -0
- package/dist/services/mcp-service.d.ts +1 -0
- package/dist/services/mcp-service.js +1 -0
- package/dist/services/utility-service.d.ts +47 -0
- package/dist/services/utility-service.js +208 -0
- package/dist/storage.js +4 -4
- package/dist/types.d.ts +30 -0
- package/dist/types.js +1 -0
- package/package.json +22 -8
- package/readme.md +68 -39
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Box, Text } from 'ink';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export const Footer = ({ servers, modelSlug }) => {
|
|
4
|
+
return (React.createElement(Box, { flexDirection: "row", justifyContent: "space-between", paddingX: 1, minWidth: 25 },
|
|
5
|
+
React.createElement(Box, { justifyContent: 'flex-start', paddingX: 1, minWidth: 25 }, servers.length > 0 ? (React.createElement(React.Fragment, null,
|
|
6
|
+
React.createElement(Text, { color: "green" },
|
|
7
|
+
"\u25CF ",
|
|
8
|
+
' '),
|
|
9
|
+
React.createElement(Text, null,
|
|
10
|
+
servers.length,
|
|
11
|
+
" Server",
|
|
12
|
+
servers.length > 1 ? 's' : '',
|
|
13
|
+
" Connected"))) : (React.createElement(Text, null, "No servers connected"))),
|
|
14
|
+
React.createElement(Box, { justifyContent: 'flex-end', paddingX: 1, minWidth: 25 },
|
|
15
|
+
React.createElement(Text, { color: "blue", bold: true }, "Model:"),
|
|
16
|
+
React.createElement(Text, { color: "white" },
|
|
17
|
+
' ',
|
|
18
|
+
modelSlug.replace('/', ' ')))));
|
|
19
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface InputPromptProps {
|
|
3
|
+
value: string;
|
|
4
|
+
onChange: (value: string) => void;
|
|
5
|
+
onSubmit: (value: string) => void;
|
|
6
|
+
onHistoryUp?: () => void;
|
|
7
|
+
onHistoryDown?: () => void;
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
mask?: string;
|
|
10
|
+
focus?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare const InputPrompt: React.FC<InputPromptProps>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
|
2
|
+
import { Box, Text, useInput } from 'ink';
|
|
3
|
+
export const InputPrompt = ({ value, onChange, onSubmit, onHistoryUp, onHistoryDown, placeholder = 'Type your message...', mask, focus = true, }) => {
|
|
4
|
+
const [cursorPosition, setCursorPosition] = useState(value.length);
|
|
5
|
+
const [isMultiline, setIsMultiline] = useState(false);
|
|
6
|
+
const pasteBufferRef = useRef('');
|
|
7
|
+
const pasteTimeoutRef = useRef(null);
|
|
8
|
+
const lastInputTimeRef = useRef(Date.now());
|
|
9
|
+
// Track if we should allow multiline
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
setIsMultiline(value.includes('\n'));
|
|
12
|
+
}, [value]);
|
|
13
|
+
// Update cursor position when value changes externally
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
setCursorPosition(value.length);
|
|
16
|
+
}, [value]);
|
|
17
|
+
const handleSubmit = useCallback(() => {
|
|
18
|
+
const trimmedValue = value.trim();
|
|
19
|
+
if (trimmedValue) {
|
|
20
|
+
onSubmit(trimmedValue);
|
|
21
|
+
onChange('');
|
|
22
|
+
setCursorPosition(0);
|
|
23
|
+
setIsMultiline(false);
|
|
24
|
+
}
|
|
25
|
+
}, [value, onSubmit, onChange]);
|
|
26
|
+
const processPasteBuffer = useCallback(() => {
|
|
27
|
+
if (pasteBufferRef.current) {
|
|
28
|
+
const beforeCursor = value.slice(0, cursorPosition);
|
|
29
|
+
const afterCursor = value.slice(cursorPosition);
|
|
30
|
+
// Process the paste buffer to handle carriage returns
|
|
31
|
+
let processedPaste = pasteBufferRef.current
|
|
32
|
+
.replace(/\r\n/g, '\n')
|
|
33
|
+
.replace(/\r/g, '\n');
|
|
34
|
+
const newValue = beforeCursor + processedPaste + afterCursor;
|
|
35
|
+
onChange(newValue);
|
|
36
|
+
setCursorPosition(cursorPosition + processedPaste.length);
|
|
37
|
+
if (processedPaste.includes('\n')) {
|
|
38
|
+
setIsMultiline(true);
|
|
39
|
+
}
|
|
40
|
+
pasteBufferRef.current = '';
|
|
41
|
+
}
|
|
42
|
+
}, [value, cursorPosition, onChange]);
|
|
43
|
+
useInput((input, key) => {
|
|
44
|
+
if (!focus)
|
|
45
|
+
return;
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
const timeSinceLastInput = now - lastInputTimeRef.current;
|
|
48
|
+
lastInputTimeRef.current = now;
|
|
49
|
+
// Detect paste by checking if we're getting rapid inputs or multi-character input
|
|
50
|
+
const isProbablyPaste = input &&
|
|
51
|
+
(timeSinceLastInput < 50 || input.length > 1) &&
|
|
52
|
+
!key.ctrl &&
|
|
53
|
+
!key.meta;
|
|
54
|
+
if (isProbablyPaste) {
|
|
55
|
+
// Accumulate paste buffer
|
|
56
|
+
pasteBufferRef.current += input;
|
|
57
|
+
// Clear existing timeout
|
|
58
|
+
if (pasteTimeoutRef.current) {
|
|
59
|
+
clearTimeout(pasteTimeoutRef.current);
|
|
60
|
+
}
|
|
61
|
+
// Set new timeout to process paste
|
|
62
|
+
pasteTimeoutRef.current = setTimeout(() => {
|
|
63
|
+
processPasteBuffer();
|
|
64
|
+
pasteTimeoutRef.current = null;
|
|
65
|
+
}, 100);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// Process any pending paste buffer first
|
|
69
|
+
if (pasteBufferRef.current) {
|
|
70
|
+
processPasteBuffer();
|
|
71
|
+
}
|
|
72
|
+
// Submit on Enter (without modifiers)
|
|
73
|
+
if (key.return && !key.ctrl && !key.meta && !key.shift) {
|
|
74
|
+
handleSubmit();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// New line on Ctrl+Enter, Meta+Enter, or Shift+Enter
|
|
78
|
+
if (key.return && (key.ctrl || key.meta || key.shift)) {
|
|
79
|
+
const beforeCursor = value.slice(0, cursorPosition);
|
|
80
|
+
const afterCursor = value.slice(cursorPosition);
|
|
81
|
+
onChange(beforeCursor + '\n' + afterCursor);
|
|
82
|
+
setCursorPosition(cursorPosition + 1);
|
|
83
|
+
setIsMultiline(true);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
// History navigation with up/down arrows (only in single-line mode)
|
|
87
|
+
if (!isMultiline) {
|
|
88
|
+
if (key.upArrow && onHistoryUp) {
|
|
89
|
+
onHistoryUp();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (key.downArrow && onHistoryDown) {
|
|
93
|
+
onHistoryDown();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Backspace
|
|
98
|
+
if (key.backspace || key.delete) {
|
|
99
|
+
if (cursorPosition > 0) {
|
|
100
|
+
const beforeCursor = value.slice(0, cursorPosition - 1);
|
|
101
|
+
const afterCursor = value.slice(cursorPosition);
|
|
102
|
+
onChange(beforeCursor + afterCursor);
|
|
103
|
+
setCursorPosition(cursorPosition - 1);
|
|
104
|
+
}
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// Navigation keys
|
|
108
|
+
if (key.leftArrow) {
|
|
109
|
+
setCursorPosition(Math.max(0, cursorPosition - 1));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (key.rightArrow) {
|
|
113
|
+
setCursorPosition(Math.min(value.length, cursorPosition + 1));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Home/End keys
|
|
117
|
+
if (key.ctrl && input === 'a') {
|
|
118
|
+
setCursorPosition(0);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (key.ctrl && input === 'e') {
|
|
122
|
+
setCursorPosition(value.length);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// Regular character input (not paste)
|
|
126
|
+
if (input && !key.ctrl && !key.meta) {
|
|
127
|
+
const beforeCursor = value.slice(0, cursorPosition);
|
|
128
|
+
const afterCursor = value.slice(cursorPosition);
|
|
129
|
+
// Process input to handle carriage returns
|
|
130
|
+
const processedInput = input.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
131
|
+
const newValue = beforeCursor + processedInput + afterCursor;
|
|
132
|
+
onChange(newValue);
|
|
133
|
+
setCursorPosition(cursorPosition + processedInput.length);
|
|
134
|
+
if (processedInput.includes('\n')) {
|
|
135
|
+
setIsMultiline(true);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
// Render the input
|
|
140
|
+
const renderContent = () => {
|
|
141
|
+
if (!value && placeholder && !focus) {
|
|
142
|
+
return React.createElement(Text, { dimColor: true }, placeholder);
|
|
143
|
+
}
|
|
144
|
+
let displayValue = mask ? value.replace(/./g, mask) : value;
|
|
145
|
+
if (!isMultiline) {
|
|
146
|
+
// Single line rendering with cursor
|
|
147
|
+
const beforeCursor = displayValue.slice(0, cursorPosition);
|
|
148
|
+
const atCursor = displayValue[cursorPosition] || ' ';
|
|
149
|
+
const afterCursor = displayValue.slice(cursorPosition + 1);
|
|
150
|
+
return (React.createElement(Text, null,
|
|
151
|
+
beforeCursor,
|
|
152
|
+
focus && React.createElement(Text, { inverse: true }, atCursor),
|
|
153
|
+
afterCursor));
|
|
154
|
+
}
|
|
155
|
+
// Multiline rendering
|
|
156
|
+
const lines = displayValue.split('\n');
|
|
157
|
+
let pos = 0;
|
|
158
|
+
let cursorRow = 0;
|
|
159
|
+
let cursorCol = 0;
|
|
160
|
+
for (let row = 0; row < lines.length; row++) {
|
|
161
|
+
const lineLength = lines[row]?.length || 0;
|
|
162
|
+
if (pos + lineLength >= cursorPosition) {
|
|
163
|
+
cursorRow = row;
|
|
164
|
+
cursorCol = cursorPosition - pos;
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
pos += lineLength + 1; // +1 for newline
|
|
168
|
+
}
|
|
169
|
+
return lines.map((line, row) => {
|
|
170
|
+
if (row === cursorRow && focus) {
|
|
171
|
+
const before = line.slice(0, cursorCol);
|
|
172
|
+
const at = line[cursorCol] || ' ';
|
|
173
|
+
const after = line.slice(cursorCol + 1);
|
|
174
|
+
return (React.createElement(Box, { key: row },
|
|
175
|
+
React.createElement(Text, null,
|
|
176
|
+
before,
|
|
177
|
+
React.createElement(Text, { inverse: true }, at),
|
|
178
|
+
after)));
|
|
179
|
+
}
|
|
180
|
+
return (React.createElement(Box, { key: row },
|
|
181
|
+
React.createElement(Text, null, line || ' ')));
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
return (React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
|
|
185
|
+
renderContent(),
|
|
186
|
+
isMultiline && focus && (React.createElement(Box, null,
|
|
187
|
+
React.createElement(Text, { dimColor: true, italic: true }, "Enter to submit \u2022 Ctrl/Shift+Enter for new line")))));
|
|
188
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CommandMessage, ToolCall } from '../types.js';
|
|
2
|
+
import { Message } from '../types.js';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
export declare const UserMessageRenderer: ({ message }: {
|
|
5
|
+
message: Message;
|
|
6
|
+
}) => React.JSX.Element;
|
|
7
|
+
export declare const AssistantMessageRenerer: ({ message }: {
|
|
8
|
+
message: Message;
|
|
9
|
+
}) => React.JSX.Element;
|
|
10
|
+
export declare const ThoughtMessageRenderer: ({ message }: {
|
|
11
|
+
message: Message;
|
|
12
|
+
}) => React.JSX.Element;
|
|
13
|
+
export declare const ToolCallRenderer: ({ message }: {
|
|
14
|
+
message: ToolCall;
|
|
15
|
+
}) => React.JSX.Element;
|
|
16
|
+
export declare const CommandMessageRenderer: ({ message, }: {
|
|
17
|
+
message: CommandMessage;
|
|
18
|
+
}) => React.JSX.Element;
|
|
19
|
+
export declare const MessageRenderer: ({ message, }: {
|
|
20
|
+
message: Message | ToolCall | CommandMessage;
|
|
21
|
+
}) => React.JSX.Element | null;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { ServerStatus } from './ServerStatus.js';
|
|
4
|
+
import { ToolStatus } from './ToolStatus.js';
|
|
5
|
+
export const UserMessageRenderer = ({ message }) => {
|
|
6
|
+
return (React.createElement(Box, { key: message.id, marginBottom: 1, flexDirection: "row" },
|
|
7
|
+
React.createElement(Box, { marginRight: 1 },
|
|
8
|
+
React.createElement(Text, { color: "white", bold: true }, "\u25CF")),
|
|
9
|
+
React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
|
|
10
|
+
React.createElement(Text, { wrap: "wrap" }, message.content))));
|
|
11
|
+
};
|
|
12
|
+
export const AssistantMessageRenerer = ({ message }) => {
|
|
13
|
+
return (React.createElement(Box, { key: message.id, marginBottom: 1, flexDirection: "row" },
|
|
14
|
+
React.createElement(Box, { marginRight: 1 },
|
|
15
|
+
React.createElement(Text, { color: "blue", bold: true }, "\u25CF")),
|
|
16
|
+
React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
|
|
17
|
+
React.createElement(Text, { wrap: "wrap" }, message.content))));
|
|
18
|
+
};
|
|
19
|
+
export const ThoughtMessageRenderer = ({ message }) => {
|
|
20
|
+
return (React.createElement(Box, { key: message.id, flexDirection: "row" },
|
|
21
|
+
React.createElement(Box, { flexDirection: "column", marginRight: 1, flexGrow: 1 },
|
|
22
|
+
React.createElement(Text, { color: "gray", italic: true }, message.content))));
|
|
23
|
+
};
|
|
24
|
+
export const ToolCallRenderer = ({ message }) => {
|
|
25
|
+
const input = JSON.stringify(message.tool_input);
|
|
26
|
+
const output = JSON.stringify(message.tool_output);
|
|
27
|
+
return (React.createElement(Box, { key: message.id, flexDirection: "row" },
|
|
28
|
+
React.createElement(Box, { marginRight: 1 },
|
|
29
|
+
React.createElement(Text, { color: "white", bold: true }, "\u25CF")),
|
|
30
|
+
React.createElement(Box, { flexGrow: 1, paddingX: 1, borderStyle: "round", borderColor: "white", flexDirection: "column" },
|
|
31
|
+
React.createElement(Text, { color: "white", bold: true },
|
|
32
|
+
"Tool: ",
|
|
33
|
+
message.tool_name),
|
|
34
|
+
React.createElement(Text, null,
|
|
35
|
+
"Input: ",
|
|
36
|
+
input.length > 100 ? `${input.slice(0, 97)}...` : input),
|
|
37
|
+
React.createElement(Text, null,
|
|
38
|
+
"Output: ",
|
|
39
|
+
output.length > 100 ? `${output.slice(0, 97)}...` : output))));
|
|
40
|
+
};
|
|
41
|
+
export const CommandMessageRenderer = ({ message, }) => {
|
|
42
|
+
const { commandResult } = message;
|
|
43
|
+
let color = 'magenta'; // Default for info/system
|
|
44
|
+
if (commandResult.type === 'error')
|
|
45
|
+
color = 'red';
|
|
46
|
+
if (commandResult.type === 'success')
|
|
47
|
+
color = 'green';
|
|
48
|
+
if (commandResult.type === 'prompt_key' ||
|
|
49
|
+
commandResult.type === 'prompt_server_config') {
|
|
50
|
+
color = 'yellow';
|
|
51
|
+
}
|
|
52
|
+
if (commandResult.type === 'list_servers')
|
|
53
|
+
color = 'magenta';
|
|
54
|
+
if (commandResult.type === 'list_tools')
|
|
55
|
+
color = 'cyan';
|
|
56
|
+
return (React.createElement(Box, { key: message.id, marginBottom: 1, flexDirection: "row" },
|
|
57
|
+
React.createElement(Box, { marginRight: 1 },
|
|
58
|
+
React.createElement(Text, { color: color, bold: true }, "\u25CF")),
|
|
59
|
+
React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
|
|
60
|
+
React.createElement(Text, null, message.content),
|
|
61
|
+
commandResult.type === 'list_servers' &&
|
|
62
|
+
commandResult.data?.servers && (React.createElement(ServerStatus, { servers: commandResult.data.servers })),
|
|
63
|
+
commandResult.type === 'list_tools' && commandResult.data && (React.createElement(ToolStatus, { tools: commandResult.data.tools, error: commandResult.data.error })))));
|
|
64
|
+
};
|
|
65
|
+
export const MessageRenderer = ({ message, }) => {
|
|
66
|
+
switch (message.role) {
|
|
67
|
+
case 'tool':
|
|
68
|
+
return React.createElement(ToolCallRenderer, { message: message });
|
|
69
|
+
case 'user':
|
|
70
|
+
return React.createElement(UserMessageRenderer, { message: message });
|
|
71
|
+
case 'assistant':
|
|
72
|
+
return React.createElement(AssistantMessageRenerer, { message: message });
|
|
73
|
+
case 'thought':
|
|
74
|
+
return React.createElement(ThoughtMessageRenderer, { message: message });
|
|
75
|
+
case 'command':
|
|
76
|
+
return React.createElement(CommandMessageRenderer, { message: message });
|
|
77
|
+
default:
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ServerStatus as ServerStatusData } from '../types.js';
|
|
3
|
+
interface ServerStatusProps {
|
|
4
|
+
servers: ServerStatusData[];
|
|
5
|
+
}
|
|
6
|
+
export declare function ServerStatus({ servers }: ServerStatusProps): React.JSX.Element;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Box, Text } from 'ink';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export function ServerStatus({ servers }) {
|
|
4
|
+
return (React.createElement(Box, { flexDirection: "column" }, servers.map(server => (React.createElement(Box, { key: server.name, flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1 },
|
|
5
|
+
React.createElement(Box, null, server.isConnected ? (React.createElement(Text, { bold: true },
|
|
6
|
+
" \uD83D\uDD39",
|
|
7
|
+
server.name)) : (React.createElement(Text, { bold: true },
|
|
8
|
+
" \uD83D\uDD38 ",
|
|
9
|
+
server.name))),
|
|
10
|
+
React.createElement(Box, null,
|
|
11
|
+
React.createElement(Text, null,
|
|
12
|
+
"Status:",
|
|
13
|
+
' ',
|
|
14
|
+
server.isConnected ? (React.createElement(Text, { color: "green" }, "Connected")) : (React.createElement(Text, { color: "red" }, "Disconnected")))),
|
|
15
|
+
React.createElement(Box, null,
|
|
16
|
+
React.createElement(Text, null,
|
|
17
|
+
"Command: ",
|
|
18
|
+
server.config.command)),
|
|
19
|
+
server.config.args && server.config.args.length > 0 && (React.createElement(Box, null,
|
|
20
|
+
React.createElement(Text, null,
|
|
21
|
+
"Args: ",
|
|
22
|
+
server.config.args.join(' ')))),
|
|
23
|
+
server.config.env && Object.keys(server.config.env).length > 0 && (React.createElement(Box, null,
|
|
24
|
+
React.createElement(Text, null,
|
|
25
|
+
"Env:",
|
|
26
|
+
' ',
|
|
27
|
+
Object.entries(server.config.env)
|
|
28
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
29
|
+
.join(', ')))),
|
|
30
|
+
React.createElement(Box, null,
|
|
31
|
+
React.createElement(Text, null,
|
|
32
|
+
"Action: /server ",
|
|
33
|
+
server.isConnected ? 'disconnect' : 'connect',
|
|
34
|
+
' ',
|
|
35
|
+
server.name)))))));
|
|
36
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { SpinnerName } from 'cli-spinners';
|
|
3
|
+
type SpinnerProps = {
|
|
4
|
+
/**
|
|
5
|
+
* Type of a spinner.
|
|
6
|
+
* See [cli-spinners](https://github.com/sindresorhus/cli-spinners) for available spinners.
|
|
7
|
+
*
|
|
8
|
+
* @default dots
|
|
9
|
+
*/
|
|
10
|
+
type?: SpinnerName | 'mcpuse';
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Spinner.
|
|
14
|
+
*/
|
|
15
|
+
declare function Spinner({ type }: SpinnerProps): React.JSX.Element;
|
|
16
|
+
export default Spinner;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
import spinners from 'cli-spinners';
|
|
4
|
+
const mcpuseSpinner = {
|
|
5
|
+
interval: 80,
|
|
6
|
+
frames: [
|
|
7
|
+
"⠋",
|
|
8
|
+
"⠙",
|
|
9
|
+
"⠹",
|
|
10
|
+
"⠸",
|
|
11
|
+
"m", // 5
|
|
12
|
+
"⠼",
|
|
13
|
+
"⠴",
|
|
14
|
+
"⠦",
|
|
15
|
+
"c", // 9
|
|
16
|
+
"⠧",
|
|
17
|
+
"⠇",
|
|
18
|
+
"⠏",
|
|
19
|
+
"p", // 13
|
|
20
|
+
"⠋",
|
|
21
|
+
"⠙",
|
|
22
|
+
"⠹",
|
|
23
|
+
"-", // 17
|
|
24
|
+
"⠸",
|
|
25
|
+
"⠼",
|
|
26
|
+
"⠴",
|
|
27
|
+
"u", // 21
|
|
28
|
+
"⠦",
|
|
29
|
+
"⠧",
|
|
30
|
+
"⠇",
|
|
31
|
+
"s", // 25
|
|
32
|
+
"⠏",
|
|
33
|
+
"⠋",
|
|
34
|
+
"⠙",
|
|
35
|
+
"e" // 29
|
|
36
|
+
]
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Spinner.
|
|
40
|
+
*/
|
|
41
|
+
function Spinner({ type = 'mcpuse' }) {
|
|
42
|
+
const [frame, setFrame] = useState(0);
|
|
43
|
+
let spinner;
|
|
44
|
+
if (type === 'mcpuse') {
|
|
45
|
+
spinner = mcpuseSpinner;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
spinner = spinners[type];
|
|
49
|
+
}
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
const timer = setInterval(() => {
|
|
52
|
+
setFrame(previousFrame => {
|
|
53
|
+
const isLastFrame = previousFrame === spinner.frames.length - 1;
|
|
54
|
+
return isLastFrame ? 0 : previousFrame + 1;
|
|
55
|
+
});
|
|
56
|
+
}, spinner.interval);
|
|
57
|
+
return () => {
|
|
58
|
+
clearInterval(timer);
|
|
59
|
+
};
|
|
60
|
+
}, [spinner]);
|
|
61
|
+
return React.createElement(Text, null, spinner.frames[frame]);
|
|
62
|
+
}
|
|
63
|
+
export default Spinner;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
interface ToolStatusProps {
|
|
4
|
+
tools: Tool[];
|
|
5
|
+
error?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function ToolStatus({ tools, error }: ToolStatusProps): React.JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Box, Text } from 'ink';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export function ToolStatus({ tools, error }) {
|
|
4
|
+
if (error) {
|
|
5
|
+
return (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
|
|
6
|
+
React.createElement(Text, { color: "red" },
|
|
7
|
+
"\u274C Error: ",
|
|
8
|
+
error),
|
|
9
|
+
React.createElement(Text, null, "\uD83D\uDCA1 This might indicate:"),
|
|
10
|
+
React.createElement(Box, { marginLeft: 2, flexDirection: "column" },
|
|
11
|
+
React.createElement(Text, null, "\u2022 MCP servers failed to start"),
|
|
12
|
+
React.createElement(Text, null, "\u2022 Connection issues with configured servers")),
|
|
13
|
+
React.createElement(Text, null, "Check console logs for more details.")));
|
|
14
|
+
}
|
|
15
|
+
if (tools.length === 0) {
|
|
16
|
+
return (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
|
|
17
|
+
React.createElement(Text, { color: "white" }, " No MCP tools found"),
|
|
18
|
+
React.createElement(Text, null,
|
|
19
|
+
' ',
|
|
20
|
+
"Add tools from a server by running /server connect <name>")));
|
|
21
|
+
}
|
|
22
|
+
return (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
|
|
23
|
+
React.createElement(Text, { color: "green" },
|
|
24
|
+
"Found ",
|
|
25
|
+
tools.length,
|
|
26
|
+
" MCP tools:"),
|
|
27
|
+
tools.map((tool, index) => (React.createElement(Box, { key: tool.name, flexDirection: "column", marginTop: 1 },
|
|
28
|
+
React.createElement(Text, null,
|
|
29
|
+
index + 1,
|
|
30
|
+
". ",
|
|
31
|
+
React.createElement(Text, { bold: true }, tool.name || 'Unknown'),
|
|
32
|
+
tool.description && `: ${tool.description}`))))));
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class Logger {
|
|
2
|
+
private static formatMessage;
|
|
3
|
+
private static writeToFile;
|
|
4
|
+
static debug(message: string, data?: any): void;
|
|
5
|
+
static info(message: string, data?: any): void;
|
|
6
|
+
static warn(message: string, data?: any): void;
|
|
7
|
+
static error(message: string, data?: any): void;
|
|
8
|
+
static getLogPath(): string;
|
|
9
|
+
static clearLogs(): void;
|
|
10
|
+
}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
const LOG_DIR = path.join(os.homedir(), '.mcp-use-cli');
|
|
5
|
+
const LOG_FILE = path.join(LOG_DIR, 'debug.log');
|
|
6
|
+
// Ensure log directory exists
|
|
7
|
+
if (!fs.existsSync(LOG_DIR)) {
|
|
8
|
+
fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
9
|
+
}
|
|
10
|
+
export class Logger {
|
|
11
|
+
static formatMessage(level, message, data) {
|
|
12
|
+
const timestamp = new Date().toISOString();
|
|
13
|
+
const dataStr = data ? ` | Data: ${JSON.stringify(data, null, 2)}` : '';
|
|
14
|
+
return `[${timestamp}] ${level.toUpperCase()}: ${message}${dataStr}\n`;
|
|
15
|
+
}
|
|
16
|
+
static writeToFile(content) {
|
|
17
|
+
try {
|
|
18
|
+
fs.appendFileSync(LOG_FILE, content);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
// Fallback to console if file write fails
|
|
22
|
+
console.error('Failed to write to log file:', error);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
static debug(message, data) {
|
|
26
|
+
this.writeToFile(this.formatMessage('debug', message, data));
|
|
27
|
+
}
|
|
28
|
+
static info(message, data) {
|
|
29
|
+
this.writeToFile(this.formatMessage('info', message, data));
|
|
30
|
+
}
|
|
31
|
+
static warn(message, data) {
|
|
32
|
+
this.writeToFile(this.formatMessage('warn', message, data));
|
|
33
|
+
}
|
|
34
|
+
static error(message, data) {
|
|
35
|
+
this.writeToFile(this.formatMessage('error', message, data));
|
|
36
|
+
}
|
|
37
|
+
static getLogPath() {
|
|
38
|
+
return LOG_FILE;
|
|
39
|
+
}
|
|
40
|
+
static clearLogs() {
|
|
41
|
+
try {
|
|
42
|
+
fs.writeFileSync(LOG_FILE, '');
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error('Failed to clear log file:', error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
package/dist/mcp-service.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CommandResult } from './commands.js';
|
|
2
|
+
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
2
3
|
export interface MCPMessage {
|
|
3
4
|
id: string;
|
|
4
5
|
role: 'user' | 'assistant';
|
|
@@ -17,9 +18,9 @@ export declare class MCPService {
|
|
|
17
18
|
private isInitialized;
|
|
18
19
|
private commandHandler;
|
|
19
20
|
private client;
|
|
20
|
-
initialize(
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
initialize(): Promise<void>;
|
|
22
|
+
initializeAgent(): Promise<void>;
|
|
23
|
+
refreshAgent(): Promise<void>;
|
|
23
24
|
sendMessage(message: string, isApiKeyInput?: boolean, pendingProvider?: string, pendingModel?: string, isServerConfigInput?: boolean, serverConfigStep?: string, serverConfig?: any): Promise<{
|
|
24
25
|
response: string;
|
|
25
26
|
toolCalls: MCPToolCall[];
|
|
@@ -37,7 +38,7 @@ export declare class MCPService {
|
|
|
37
38
|
getConfiguredServers(): string[];
|
|
38
39
|
getConnectedServers(): string[];
|
|
39
40
|
getAvailableTools(): Promise<{
|
|
40
|
-
tools:
|
|
41
|
+
tools: Tool[];
|
|
41
42
|
error?: string;
|
|
42
43
|
}>;
|
|
43
44
|
}
|