@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 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
@@ -1 +1 @@
1
- {"version":3,"file":"MCPChat.d.ts","sourceRoot":"","sources":["../src/MCPChat.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAE3D,OAAO,KAAK,EAAyB,YAAY,EAAe,MAAM,SAAS,CAAC;AAEhF,wBAAgB,OAAO,CAAC,EACtB,aAAa,EACb,WAA6B,EAC7B,YAAiB,EACjB,SAAc,EACf,EAAE,YAAY,qBAmVd"}
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
- export function MCPChat({ companyNumber, apiEndpoint = "/api/mcp/chat", customStyles = {}, className = "" }) {
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 [isExpanded, setIsExpanded] = useState(false);
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
- // Remove any streaming message that was in progress
75
+ setPreparingAction(null);
30
76
  setMessages((prev) => prev.filter((m) => !m.isStreaming));
31
77
  }
32
78
  };
33
- const handleSubmit = async (e) => {
34
- e.preventDefault();
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: input.trim(),
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
- // Create a new message object instead of mutating
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
- // Don't show error message if request was cancelled
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
- const errorMessage = {
160
- role: "assistant",
161
- content: "Sorry, I encountered an error. Please try again.",
162
- timestamp: new Date(),
163
- };
164
- setMessages((prev) => {
165
- // Remove streaming message if it exists
166
- const filtered = prev.filter((m) => !m.isStreaming);
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
- // Clear thinking steps after a brief delay
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 (error) {
189
- console.error("Failed to clear server conversation:", error);
254
+ catch (err) {
255
+ console.error("Failed to clear server conversation:", err);
190
256
  }
191
257
  };
192
- if (!isExpanded) {
193
- return (React.createElement("div", { className: `mcp-chat-trigger ${className}`, style: containerStyle },
194
- React.createElement("button", { onClick: () => setIsExpanded(true), className: "mcp-chat-trigger-button" }, "\uD83D\uDCAC Ask AI Assistant")));
195
- }
196
- return (React.createElement("div", { className: `mcp-chat-container ${className}`, style: containerStyle },
197
- React.createElement("div", { className: "mcp-chat-header" },
198
- React.createElement("h3", { className: "mcp-chat-title" }, "AI Assistant"),
199
- React.createElement("div", { className: "mcp-chat-header-actions" },
200
- messages.length > 0 && (React.createElement("button", { onClick: clearChat, className: "mcp-chat-button mcp-chat-button-secondary" }, "Clear")),
201
- React.createElement("button", { onClick: () => setIsExpanded(false), className: "mcp-chat-button mcp-chat-button-secondary" }, "\u2715"))),
202
- React.createElement("div", { className: "mcp-chat-messages" },
203
- messages.length === 0 && (React.createElement("div", { className: "mcp-chat-welcome" },
204
- React.createElement("p", { className: "mcp-chat-welcome-title" }, "\uD83D\uDC4B Hi! I can help you with company information."),
205
- companyNumber && (React.createElement("p", { className: "mcp-chat-welcome-subtitle" },
206
- "Currently viewing: ",
207
- React.createElement("strong", null, companyNumber))),
208
- React.createElement("p", { className: "mcp-chat-welcome-subtitle" }, "Try asking:"),
209
- React.createElement("ul", { className: "mcp-chat-suggestions" }, companyNumber && (React.createElement(React.Fragment, null,
210
- React.createElement("li", null, "\u201CGet company info\u201D"),
211
- React.createElement("li", null, "\u201CShow me financial data from 2020\u201D")))))),
212
- messages.map((msg, idx) => (React.createElement("div", { key: idx, className: `mcp-chat-message ${msg.role === "user" ? "mcp-chat-message-user" : "mcp-chat-message-assistant"}` },
213
- React.createElement("div", { className: "mcp-chat-message-bubble" },
214
- msg.role === "assistant" ? (React.createElement("div", { className: "mcp-chat-message-content markdown-content" },
215
- React.createElement(ReactMarkdown, null, msg.content))) : (React.createElement("div", { className: "mcp-chat-message-content" }, msg.content)),
216
- React.createElement("div", { className: "mcp-chat-message-timestamp" }, msg.timestamp.toLocaleTimeString()))))),
217
- isLoading && thinkingSteps.length > 0 && (React.createElement("div", { className: "mcp-chat-message mcp-chat-message-assistant" },
218
- React.createElement("div", { className: "mcp-chat-thinking" },
219
- React.createElement("div", { className: "mcp-chat-thinking-title" }, "\uD83D\uDCAD Processing..."),
220
- React.createElement("div", { className: "mcp-chat-thinking-steps" },
221
- thinkingSteps.map((step) => (React.createElement("div", { key: step.id, className: "mcp-chat-thinking-step" }, step.message))),
222
- React.createElement("div", { ref: thinkingEndRef }))))),
223
- React.createElement("div", { ref: messagesEndRef })),
224
- React.createElement("form", { onSubmit: handleSubmit, className: "mcp-chat-input-form" },
225
- React.createElement("input", { type: "text", value: input, onChange: (e) => setInput(e.target.value), placeholder: "Ask a question...", className: "mcp-chat-input", disabled: isLoading }),
226
- 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")))));
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,uBAgG/B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,KACrB,SAAS,OAAO,uBAU/B;AAED;;GAEG;AACH,wBAAsB,iBAAiB,kBAKtC"}
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"}
@@ -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;
@@ -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;YAwErB,mBAAmB;IAoB3B,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,GAAG,OAAO,CAAC,MAAM,CAAC;IAoMrH,YAAY,IAAI,IAAI;IAMd,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B"}
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"}