@nqminds/mcp-client 1.0.7 → 1.0.9
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/MCPChat.d.ts +1 -1
- package/dist/MCPChat.d.ts.map +1 -1
- package/dist/MCPChat.js +191 -85
- package/dist/api-helpers.d.ts.map +1 -1
- package/dist/api-helpers.js +4 -4
- package/dist/openai-client.d.ts +12 -1
- package/dist/openai-client.d.ts.map +1 -1
- package/dist/openai-client.js +141 -40
- package/dist/styles/MCPChat.css +583 -72
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -2
package/dist/MCPChat.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { MCPChatProps } from "./types";
|
|
3
|
-
export declare function MCPChat({ companyNumber, apiEndpoint, customStyles, className }: MCPChatProps): React.JSX.Element;
|
|
3
|
+
export declare function MCPChat({ companyNumber, apiEndpoint, customStyles, className, }: MCPChatProps): React.JSX.Element;
|
|
4
4
|
//# sourceMappingURL=MCPChat.d.ts.map
|
package/dist/MCPChat.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MCPChat.d.ts","sourceRoot":"","sources":["../src/MCPChat.tsx"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"MCPChat.d.ts","sourceRoot":"","sources":["../src/MCPChat.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAGxE,OAAO,KAAK,EAAyB,YAAY,EAAe,MAAM,SAAS,CAAC;AA+ChF,wBAAgB,OAAO,CAAC,EACtB,aAAa,EACb,WAA6B,EAC7B,YAAiB,EACjB,SAAc,GACf,EAAE,YAAY,qBAifd"}
|
package/dist/MCPChat.js
CHANGED
|
@@ -1,84 +1,123 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import React, { useState, useRef, useEffect } from "react";
|
|
2
|
+
import React, { useState, useRef, useEffect, useCallback } from "react";
|
|
3
3
|
import ReactMarkdown from "react-markdown";
|
|
4
|
-
|
|
4
|
+
import remarkGfm from "remark-gfm";
|
|
5
|
+
const DEFAULT_ACTIONS = [
|
|
6
|
+
{
|
|
7
|
+
label: "Company overview",
|
|
8
|
+
icon: "🏢",
|
|
9
|
+
prompt: "Please provide a comprehensive company overview. Include key business information, company structure, size, registered details, and any notable financial highlights.",
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
label: "Competitor analysis",
|
|
13
|
+
icon: "⚔️",
|
|
14
|
+
prompt: "Please perform a competitor analysis. Identify the main competitors in this sector, assess market positioning, and highlight competitive advantages or disadvantages.",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
label: "Risk analysis",
|
|
18
|
+
icon: "⚠️",
|
|
19
|
+
prompt: "Please perform a thorough risk analysis. Identify key business risks, financial risks, operational risks, and any concerning patterns in the data.",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
label: "Online sentiment",
|
|
23
|
+
icon: "🌐",
|
|
24
|
+
prompt: "Please assess the online sentiment and public perception. Look for relevant news coverage, reviews, reputational issues, and overall public standing.",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
label: "SWOT analysis",
|
|
28
|
+
icon: "📊",
|
|
29
|
+
prompt: "Please perform a full SWOT analysis. Cover Strengths, Weaknesses, Opportunities, and Threats in detail.",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
label: "Research key people",
|
|
33
|
+
icon: "👤",
|
|
34
|
+
prompt: "Please research the key people associated with this company. Include directors, persons of significant control, major shareholders, and relevant background information.",
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
export function MCPChat({ companyNumber, apiEndpoint = "/api/mcp/chat", customStyles = {}, className = "", }) {
|
|
5
38
|
const [messages, setMessages] = useState([]);
|
|
6
39
|
const [input, setInput] = useState("");
|
|
7
40
|
const [isLoading, setIsLoading] = useState(false);
|
|
8
41
|
const [thinkingSteps, setThinkingSteps] = useState([]);
|
|
9
|
-
const [
|
|
42
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
43
|
+
const [preparingAction, setPreparingAction] = useState(null);
|
|
44
|
+
const [directPromptOpen, setDirectPromptOpen] = useState(false);
|
|
45
|
+
const [directPromptText, setDirectPromptText] = useState("");
|
|
46
|
+
const [theme, setTheme] = useState(() => {
|
|
47
|
+
if (typeof window !== "undefined") {
|
|
48
|
+
return localStorage.getItem("mcp-chat-theme") ?? "light";
|
|
49
|
+
}
|
|
50
|
+
return "light";
|
|
51
|
+
});
|
|
10
52
|
const messagesEndRef = useRef(null);
|
|
11
53
|
const thinkingEndRef = useRef(null);
|
|
54
|
+
const inputRef = useRef(null);
|
|
55
|
+
const directPromptRef = useRef(null);
|
|
12
56
|
const abortControllerRef = useRef(null);
|
|
13
|
-
// Merge custom styles with default CSS variables
|
|
14
|
-
const containerStyle = {
|
|
15
|
-
...customStyles,
|
|
16
|
-
};
|
|
17
57
|
const scrollToBottom = () => {
|
|
18
58
|
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
19
59
|
};
|
|
20
60
|
useEffect(() => {
|
|
21
61
|
scrollToBottom();
|
|
22
62
|
}, [messages]);
|
|
63
|
+
// Focus input when panel opens in "chat now" mode
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (isOpen) {
|
|
66
|
+
setTimeout(() => inputRef.current?.focus(), 150);
|
|
67
|
+
}
|
|
68
|
+
}, [isOpen]);
|
|
23
69
|
const cancelRequest = () => {
|
|
24
70
|
if (abortControllerRef.current) {
|
|
25
71
|
abortControllerRef.current.abort();
|
|
26
72
|
abortControllerRef.current = null;
|
|
27
73
|
setIsLoading(false);
|
|
28
74
|
setThinkingSteps([]);
|
|
29
|
-
|
|
75
|
+
setPreparingAction(null);
|
|
30
76
|
setMessages((prev) => prev.filter((m) => !m.isStreaming));
|
|
31
77
|
}
|
|
32
78
|
};
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
if (!input.trim() || isLoading)
|
|
79
|
+
const sendMessage = useCallback(async (text, hidden = false, bypass = false) => {
|
|
80
|
+
if (!text.trim() || isLoading)
|
|
36
81
|
return;
|
|
37
|
-
// Set loading state immediately to show cancel button
|
|
38
82
|
setIsLoading(true);
|
|
39
83
|
const userMessage = {
|
|
40
84
|
role: "user",
|
|
41
|
-
content:
|
|
85
|
+
content: text.trim(),
|
|
42
86
|
timestamp: new Date(),
|
|
87
|
+
hidden,
|
|
43
88
|
};
|
|
44
89
|
setMessages((prev) => [...prev, userMessage]);
|
|
45
90
|
setInput("");
|
|
46
91
|
setThinkingSteps([]);
|
|
47
|
-
// Add initial thinking step
|
|
48
92
|
let thinkingStepCounter = 0;
|
|
49
93
|
const addThinkingStep = (message) => {
|
|
50
94
|
setThinkingSteps((prev) => [
|
|
51
95
|
...prev,
|
|
52
96
|
{ id: `${Date.now()}-${thinkingStepCounter++}`, message, timestamp: new Date() },
|
|
53
97
|
]);
|
|
54
|
-
// Auto-scroll thinking window
|
|
55
98
|
setTimeout(() => {
|
|
56
99
|
thinkingEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
57
100
|
}, 50);
|
|
58
101
|
};
|
|
59
|
-
// Create abort controller for this request
|
|
60
102
|
const abortController = new AbortController();
|
|
61
103
|
abortControllerRef.current = abortController;
|
|
62
104
|
try {
|
|
63
|
-
// Call your API route that communicates with MCP
|
|
64
105
|
const response = await fetch(apiEndpoint, {
|
|
65
106
|
method: "POST",
|
|
66
107
|
headers: { "Content-Type": "application/json" },
|
|
67
108
|
body: JSON.stringify({
|
|
68
109
|
message: userMessage.content,
|
|
69
110
|
context: companyNumber ? { company_number: companyNumber } : undefined,
|
|
111
|
+
bypassSystemPrompt: bypass || undefined,
|
|
70
112
|
}),
|
|
71
113
|
signal: abortController.signal,
|
|
72
114
|
});
|
|
73
|
-
if (!response.ok)
|
|
115
|
+
if (!response.ok)
|
|
74
116
|
throw new Error("Failed to get response");
|
|
75
|
-
}
|
|
76
117
|
const reader = response.body?.getReader();
|
|
77
118
|
const decoder = new TextDecoder();
|
|
78
|
-
if (!reader)
|
|
119
|
+
if (!reader)
|
|
79
120
|
throw new Error("No response body");
|
|
80
|
-
}
|
|
81
|
-
// Create a streaming message
|
|
82
121
|
const streamingMessage = {
|
|
83
122
|
role: "assistant",
|
|
84
123
|
content: "",
|
|
@@ -98,21 +137,18 @@ export function MCPChat({ companyNumber, apiEndpoint = "/api/mcp/chat", customSt
|
|
|
98
137
|
for (const line of lines) {
|
|
99
138
|
if (line.startsWith("data: ")) {
|
|
100
139
|
const data = line.slice(6);
|
|
101
|
-
if (data === "[DONE]")
|
|
140
|
+
if (data === "[DONE]")
|
|
102
141
|
continue;
|
|
103
|
-
}
|
|
104
142
|
try {
|
|
105
143
|
const parsed = JSON.parse(data);
|
|
106
144
|
if (parsed.type === "thinking") {
|
|
107
145
|
addThinkingStep(parsed.message || "Processing...");
|
|
108
146
|
}
|
|
109
147
|
else if (parsed.type === "content") {
|
|
110
|
-
// Update the streaming message with new content
|
|
111
148
|
setMessages((prev) => {
|
|
112
149
|
const updated = [...prev];
|
|
113
150
|
const lastIndex = updated.length - 1;
|
|
114
151
|
if (lastIndex >= 0 && updated[lastIndex].isStreaming) {
|
|
115
|
-
// Create a new message object instead of mutating
|
|
116
152
|
updated[lastIndex] = {
|
|
117
153
|
...updated[lastIndex],
|
|
118
154
|
content: updated[lastIndex].content + (parsed.chunk || ""),
|
|
@@ -122,22 +158,16 @@ export function MCPChat({ companyNumber, apiEndpoint = "/api/mcp/chat", customSt
|
|
|
122
158
|
});
|
|
123
159
|
}
|
|
124
160
|
else if (parsed.type === "done") {
|
|
125
|
-
// Mark streaming as complete
|
|
126
161
|
setMessages((prev) => {
|
|
127
162
|
const updated = [...prev];
|
|
128
163
|
const lastIndex = updated.length - 1;
|
|
129
164
|
if (lastIndex >= 0 && updated[lastIndex].isStreaming) {
|
|
130
|
-
|
|
131
|
-
updated[lastIndex] = {
|
|
132
|
-
...updated[lastIndex],
|
|
133
|
-
isStreaming: false,
|
|
134
|
-
};
|
|
165
|
+
updated[lastIndex] = { ...updated[lastIndex], isStreaming: false };
|
|
135
166
|
}
|
|
136
167
|
return updated;
|
|
137
168
|
});
|
|
138
169
|
}
|
|
139
170
|
else if (parsed.type === "error") {
|
|
140
|
-
// Handle error from stream
|
|
141
171
|
throw new Error(parsed.message || "Stream error");
|
|
142
172
|
}
|
|
143
173
|
}
|
|
@@ -150,34 +180,70 @@ export function MCPChat({ companyNumber, apiEndpoint = "/api/mcp/chat", customSt
|
|
|
150
180
|
}
|
|
151
181
|
}
|
|
152
182
|
catch (error) {
|
|
153
|
-
|
|
154
|
-
if (error instanceof Error && error.name === 'AbortError') {
|
|
183
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
155
184
|
console.log("Request was cancelled");
|
|
156
185
|
return;
|
|
157
186
|
}
|
|
158
187
|
console.error("Error:", error);
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return [...filtered, errorMessage];
|
|
168
|
-
});
|
|
188
|
+
setMessages((prev) => [
|
|
189
|
+
...prev.filter((m) => !m.isStreaming),
|
|
190
|
+
{
|
|
191
|
+
role: "assistant",
|
|
192
|
+
content: "Sorry, I encountered an error. Please try again.",
|
|
193
|
+
timestamp: new Date(),
|
|
194
|
+
},
|
|
195
|
+
]);
|
|
169
196
|
}
|
|
170
197
|
finally {
|
|
171
|
-
// Clean up abort controller
|
|
172
198
|
abortControllerRef.current = null;
|
|
173
199
|
setIsLoading(false);
|
|
174
|
-
|
|
200
|
+
setPreparingAction(null);
|
|
175
201
|
setTimeout(() => setThinkingSteps([]), 2000);
|
|
176
202
|
}
|
|
203
|
+
}, [apiEndpoint, companyNumber, isLoading]);
|
|
204
|
+
const handleSubmit = async (e) => {
|
|
205
|
+
e.preventDefault();
|
|
206
|
+
await sendMessage(input, false);
|
|
207
|
+
};
|
|
208
|
+
const handleActionButton = async (action) => {
|
|
209
|
+
const prompt = companyNumber
|
|
210
|
+
? `${action.prompt}\n\nCompany number: ${companyNumber}`
|
|
211
|
+
: action.prompt;
|
|
212
|
+
setPreparingAction(action.label);
|
|
213
|
+
// messages will be added by sendMessage → switches view automatically
|
|
214
|
+
setTimeout(() => sendMessage(prompt, true), 50);
|
|
215
|
+
};
|
|
216
|
+
const handleChatNow = () => {
|
|
217
|
+
// Just focus the input — no prompt sent
|
|
218
|
+
setTimeout(() => inputRef.current?.focus(), 50);
|
|
219
|
+
};
|
|
220
|
+
const openDirectPrompt = (prefill = "") => {
|
|
221
|
+
setDirectPromptText(prefill);
|
|
222
|
+
setDirectPromptOpen(true);
|
|
223
|
+
setTimeout(() => {
|
|
224
|
+
if (directPromptRef.current) {
|
|
225
|
+
directPromptRef.current.focus();
|
|
226
|
+
directPromptRef.current.selectionStart = prefill.length;
|
|
227
|
+
directPromptRef.current.selectionEnd = prefill.length;
|
|
228
|
+
}
|
|
229
|
+
}, 80);
|
|
230
|
+
};
|
|
231
|
+
const sendDirectPrompt = async () => {
|
|
232
|
+
if (!directPromptText.trim())
|
|
233
|
+
return;
|
|
234
|
+
setDirectPromptOpen(false);
|
|
235
|
+
// bypass=true: skips system prompt, sends raw to the model
|
|
236
|
+
await sendMessage(directPromptText, false, true);
|
|
237
|
+
setDirectPromptText("");
|
|
238
|
+
};
|
|
239
|
+
const toggleTheme = () => {
|
|
240
|
+
const next = theme === "dark" ? "light" : "dark";
|
|
241
|
+
setTheme(next);
|
|
242
|
+
if (typeof window !== "undefined")
|
|
243
|
+
localStorage.setItem("mcp-chat-theme", next);
|
|
177
244
|
};
|
|
178
245
|
const clearChat = async () => {
|
|
179
246
|
setMessages([]);
|
|
180
|
-
// Also clear server-side conversation history
|
|
181
247
|
try {
|
|
182
248
|
await fetch(apiEndpoint, {
|
|
183
249
|
method: "DELETE",
|
|
@@ -185,43 +251,83 @@ export function MCPChat({ companyNumber, apiEndpoint = "/api/mcp/chat", customSt
|
|
|
185
251
|
body: JSON.stringify({ sessionId: "default" }),
|
|
186
252
|
});
|
|
187
253
|
}
|
|
188
|
-
catch (
|
|
189
|
-
console.error("Failed to clear server conversation:",
|
|
254
|
+
catch (err) {
|
|
255
|
+
console.error("Failed to clear server conversation:", err);
|
|
190
256
|
}
|
|
191
257
|
};
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
React.createElement("
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
258
|
+
const visibleMessages = messages.filter((m) => !m.hidden);
|
|
259
|
+
const showHome = visibleMessages.length === 0 && !isLoading;
|
|
260
|
+
return (React.createElement("div", { className: `mcp-root ${className}`, style: customStyles, "data-theme": theme },
|
|
261
|
+
!isOpen && (React.createElement("button", { className: "mcp-float-icon", onClick: () => setIsOpen(true), "aria-label": "Open AI assistant", title: "Open AI assistant" }, "\uD83E\uDD16")),
|
|
262
|
+
isOpen && (React.createElement("div", { className: "mcp-overlay" },
|
|
263
|
+
React.createElement("div", { className: "mcp-panel" },
|
|
264
|
+
React.createElement("div", { className: "mcp-chat-header" },
|
|
265
|
+
React.createElement("div", { className: "mcp-chat-header-left" },
|
|
266
|
+
React.createElement("span", { className: "mcp-chat-header-icon" }, "\uD83E\uDD16"),
|
|
267
|
+
React.createElement("h3", { className: "mcp-chat-title" }, "AI Assistant"),
|
|
268
|
+
companyNumber && (React.createElement("span", { className: "mcp-chat-header-company" }, companyNumber))),
|
|
269
|
+
React.createElement("div", { className: "mcp-chat-header-actions" },
|
|
270
|
+
React.createElement("button", { onClick: () => openDirectPrompt(), className: "mcp-chat-button mcp-btn-dev", title: "Open direct-prompt editor (dev tool)" }, "\uD83D\uDD27 Direct"),
|
|
271
|
+
React.createElement("button", { onClick: toggleTheme, className: "mcp-btn-theme", "aria-label": theme === "dark" ? "Switch to light mode" : "Switch to dark mode", title: theme === "dark" ? "Light mode" : "Dark mode" }, theme === "dark" ? "☀️" : "🌙"),
|
|
272
|
+
visibleMessages.length > 0 && (React.createElement("button", { onClick: clearChat, className: "mcp-chat-button mcp-chat-button-secondary", title: "Clear conversation" }, "Clear")),
|
|
273
|
+
React.createElement("button", { onClick: () => setIsOpen(false), className: "mcp-chat-button mcp-chat-button-secondary mcp-btn-icon", "aria-label": "Minimise", title: "Minimise" }, "\u25BC"))),
|
|
274
|
+
directPromptOpen && (React.createElement("div", { className: "mcp-direct-overlay" },
|
|
275
|
+
React.createElement("div", { className: "mcp-direct-panel" },
|
|
276
|
+
React.createElement("div", { className: "mcp-direct-header" },
|
|
277
|
+
React.createElement("span", { className: "mcp-direct-title" }, "\uD83D\uDD27 Direct Prompt"),
|
|
278
|
+
React.createElement("button", { className: "mcp-chat-button mcp-chat-button-secondary mcp-btn-icon", onClick: () => setDirectPromptOpen(false), title: "Close" }, "\u2715")),
|
|
279
|
+
React.createElement("div", { className: "mcp-direct-presets" },
|
|
280
|
+
React.createElement("span", { className: "mcp-direct-presets-label" }, "Load preset:"),
|
|
281
|
+
DEFAULT_ACTIONS.map((a) => (React.createElement("button", { key: a.label, className: "mcp-direct-preset-chip", onClick: () => openDirectPrompt(companyNumber
|
|
282
|
+
? `${a.prompt}\n\nCompany number: ${companyNumber}`
|
|
283
|
+
: a.prompt), title: a.label },
|
|
284
|
+
a.icon,
|
|
285
|
+
" ",
|
|
286
|
+
a.label)))),
|
|
287
|
+
React.createElement("textarea", { ref: directPromptRef, className: "mcp-direct-textarea", value: directPromptText, onChange: (e) => setDirectPromptText(e.target.value), placeholder: "Type or paste any prompt here \u2014 it will be sent verbatim to the agent and shown in the chat\u2026", rows: 12, onKeyDown: (e) => {
|
|
288
|
+
// Ctrl/Cmd+Enter to send
|
|
289
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
|
|
290
|
+
e.preventDefault();
|
|
291
|
+
sendDirectPrompt();
|
|
292
|
+
}
|
|
293
|
+
} }),
|
|
294
|
+
React.createElement("div", { className: "mcp-direct-footer" },
|
|
295
|
+
React.createElement("span", { className: "mcp-direct-hint" }, "Ctrl+Enter to send"),
|
|
296
|
+
React.createElement("div", { className: "mcp-direct-footer-actions" },
|
|
297
|
+
React.createElement("button", { className: "mcp-chat-button mcp-chat-button-secondary", onClick: () => setDirectPromptText("") }, "Clear"),
|
|
298
|
+
React.createElement("button", { className: "mcp-chat-button mcp-btn-dev", onClick: sendDirectPrompt, disabled: !directPromptText.trim() || isLoading }, "Send to agent \u2192")))))),
|
|
299
|
+
preparingAction && (React.createElement("div", { className: "mcp-preparing-banner" },
|
|
300
|
+
React.createElement("span", { className: "mcp-preparing-spinner" }),
|
|
301
|
+
"Preparing ",
|
|
302
|
+
React.createElement("strong", null, preparingAction),
|
|
303
|
+
" \u2014 this may take a moment\u2026")),
|
|
304
|
+
React.createElement("div", { className: "mcp-chat-messages" },
|
|
305
|
+
showHome && (React.createElement("div", { className: "mcp-home" },
|
|
306
|
+
React.createElement("p", { className: "mcp-home-title" }, "\uD83D\uDC4B What would you like to know?"),
|
|
307
|
+
companyNumber && (React.createElement("p", { className: "mcp-home-subtitle" },
|
|
308
|
+
"Company: ",
|
|
309
|
+
React.createElement("strong", null, companyNumber))),
|
|
310
|
+
React.createElement("div", { className: "mcp-action-grid" }, DEFAULT_ACTIONS.map((action) => (React.createElement("button", { key: action.label, className: "mcp-action-card", onClick: () => handleActionButton(action) },
|
|
311
|
+
React.createElement("span", { className: "mcp-action-card-icon" }, action.icon),
|
|
312
|
+
React.createElement("span", { className: "mcp-action-card-label" }, action.label))))),
|
|
313
|
+
React.createElement("button", { className: "mcp-chat-now-button", onClick: handleChatNow }, "\uD83D\uDCAC Or just chat\u2026"))),
|
|
314
|
+
visibleMessages.map((msg, idx) => (React.createElement("div", { key: idx, className: `mcp-chat-message ${msg.role === "user" ? "mcp-chat-message-user" : "mcp-chat-message-assistant"}` },
|
|
315
|
+
React.createElement("div", { className: "mcp-chat-message-bubble" },
|
|
316
|
+
msg.role === "assistant" ? (React.createElement("div", { className: "mcp-chat-message-content markdown-content" },
|
|
317
|
+
React.createElement(ReactMarkdown, { remarkPlugins: [remarkGfm] }, msg.content))) : (React.createElement("div", { className: "mcp-chat-message-content" }, msg.content)),
|
|
318
|
+
React.createElement("div", { className: "mcp-chat-message-timestamp" }, msg.timestamp.toLocaleTimeString()))))),
|
|
319
|
+
isLoading && (React.createElement("div", { className: "mcp-chat-message mcp-chat-message-assistant" },
|
|
320
|
+
React.createElement("div", { className: "mcp-chat-thinking" },
|
|
321
|
+
React.createElement("div", { className: "mcp-chat-thinking-title" },
|
|
322
|
+
React.createElement("span", { className: "mcp-preparing-spinner" }),
|
|
323
|
+
thinkingSteps.length === 0
|
|
324
|
+
? " Thinking…"
|
|
325
|
+
: " Processing…"),
|
|
326
|
+
thinkingSteps.length > 0 && (React.createElement("div", { className: "mcp-chat-thinking-steps" },
|
|
327
|
+
thinkingSteps.map((step) => (React.createElement("div", { key: step.id, className: "mcp-chat-thinking-step" }, step.message))),
|
|
328
|
+
React.createElement("div", { ref: thinkingEndRef })))))),
|
|
329
|
+
React.createElement("div", { ref: messagesEndRef })),
|
|
330
|
+
React.createElement("form", { onSubmit: handleSubmit, className: "mcp-chat-input-form" },
|
|
331
|
+
React.createElement("input", { ref: inputRef, type: "text", value: input, onChange: (e) => setInput(e.target.value), placeholder: "Ask a question\u2026", className: "mcp-chat-input", disabled: isLoading }),
|
|
332
|
+
isLoading ? (React.createElement("button", { type: "button", onClick: cancelRequest, className: "mcp-chat-button mcp-chat-button-secondary" }, "Cancel")) : (React.createElement("button", { type: "submit", className: "mcp-chat-button mcp-chat-button-primary", disabled: !input.trim() }, "Send"))))))));
|
|
227
333
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-helpers.d.ts","sourceRoot":"","sources":["../src/api-helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,sBAAsB,IACnD,SAAS,OAAO,
|
|
1
|
+
{"version":3,"file":"api-helpers.d.ts","sourceRoot":"","sources":["../src/api-helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,sBAAsB,IACnD,SAAS,OAAO,uBAiG/B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,KACrB,SAAS,OAAO,uBAU/B;AAED;;GAEG;AACH,wBAAsB,iBAAiB,kBAKtC"}
|
package/dist/api-helpers.js
CHANGED
|
@@ -9,7 +9,7 @@ const clients = new Map();
|
|
|
9
9
|
*/
|
|
10
10
|
export function createMCPChatHandler(config) {
|
|
11
11
|
return async (request) => {
|
|
12
|
-
const { message, context, sessionId = "default" } = await request.json();
|
|
12
|
+
const { message, context, sessionId = "default", bypassSystemPrompt = false } = await request.json();
|
|
13
13
|
// Get or create client for this session
|
|
14
14
|
let client = clients.get(sessionId);
|
|
15
15
|
if (!client) {
|
|
@@ -41,12 +41,12 @@ export function createMCPChatHandler(config) {
|
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
43
|
try {
|
|
44
|
-
sendEvent("thinking", { message: "🤔 Analyzing your question..." });
|
|
44
|
+
sendEvent("thinking", { message: bypassSystemPrompt ? "🔧 Sending direct prompt (no system context)…" : "🤔 Analyzing your question..." });
|
|
45
45
|
// Process the query with thinking callback and abort signal
|
|
46
46
|
const response = await client.processQuery(context ? `${message}\nContext: ${JSON.stringify(context)}` : message, (thinkingMessage) => {
|
|
47
47
|
sendEvent("thinking", { message: thinkingMessage });
|
|
48
|
-
}, abortController.signal // Pass abort signal to enable cancellation
|
|
49
|
-
);
|
|
48
|
+
}, abortController.signal, // Pass abort signal to enable cancellation
|
|
49
|
+
bypassSystemPrompt);
|
|
50
50
|
// Check if aborted before streaming response
|
|
51
51
|
if (abortController.signal.aborted) {
|
|
52
52
|
return;
|
package/dist/openai-client.d.ts
CHANGED
|
@@ -18,8 +18,19 @@ export declare class MCPClientOpenAI {
|
|
|
18
18
|
private config;
|
|
19
19
|
constructor(config: MCPClientConfig);
|
|
20
20
|
private compactConversation;
|
|
21
|
+
/**
|
|
22
|
+
* Fetches the system prompt from the MCP server's registered "system-prompt" prompt
|
|
23
|
+
* and prepends it to conversationHistory. Cached — only runs once per session.
|
|
24
|
+
* Direct Prompt (bypass mode) skips this entirely.
|
|
25
|
+
*/
|
|
26
|
+
private ensureSystemPrompt;
|
|
21
27
|
connect(): Promise<void>;
|
|
22
|
-
processQuery(query: string, onThinking?: (message: string) => void, abortSignal?: AbortSignal): Promise<string>;
|
|
28
|
+
processQuery(query: string, onThinking?: (message: string) => void, abortSignal?: AbortSignal, bypassSystemPrompt?: boolean): Promise<string>;
|
|
29
|
+
/**
|
|
30
|
+
* Sends a raw query directly to the model — no system prompt, no conversation history.
|
|
31
|
+
* Used by the Direct Prompt dev tool to test prompts verbatim.
|
|
32
|
+
*/
|
|
33
|
+
private processRawQuery;
|
|
23
34
|
clearHistory(): void;
|
|
24
35
|
cleanup(): Promise<void>;
|
|
25
36
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-client.d.ts","sourceRoot":"","sources":["../src/openai-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,MAAM,CAA4B;gBAE9B,MAAM,EAAE,eAAe;
|
|
1
|
+
{"version":3,"file":"openai-client.d.ts","sourceRoot":"","sources":["../src/openai-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,MAAM,CAA4B;gBAE9B,MAAM,EAAE,eAAe;YAsCrB,mBAAmB;IAoBjC;;;;OAIG;YACW,kBAAkB;IA4B1B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EAAE,WAAW,CAAC,EAAE,WAAW,EAAE,kBAAkB,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAuNjJ;;;OAGG;YACW,eAAe;IA4E7B,YAAY,IAAI,IAAI;IAOd,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B"}
|