@hef2024/llmasaservice-ui 0.24.2 → 0.24.4
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/index.css +256 -62
- package/dist/index.d.mts +158 -115
- package/dist/index.d.ts +158 -115
- package/dist/index.js +3099 -568
- package/dist/index.mjs +3099 -568
- package/index.ts +6 -1
- package/package.json +1 -1
- package/src/AIAgentPanel.tsx +647 -207
- package/src/AIChatPanel.css +286 -37
- package/src/AIChatPanel.tsx +2871 -358
- package/src/AgentPanel.tsx +4 -0
- package/src/ChatPanel.tsx +254 -104
- package/src/hooks/useAgentRegistry.ts +2 -1
- package/src/mcpAuth.ts +36 -0
- package/src/toolArgsParser.ts +346 -0
package/src/AgentPanel.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import materialLight from "react-syntax-highlighter/dist/esm/styles/prism/materi
|
|
|
4
4
|
import ChatPanel from "./ChatPanel";
|
|
5
5
|
import { LLMAsAServiceCustomer } from "llmasaservice-client";
|
|
6
6
|
import PrismStyle from "react-syntax-highlighter";
|
|
7
|
+
import { MCPAuthHeaderResolver } from "./mcpAuth";
|
|
7
8
|
|
|
8
9
|
export interface AgentPanelProps {
|
|
9
10
|
//project_id: string;
|
|
@@ -61,6 +62,7 @@ export interface AgentPanelProps {
|
|
|
61
62
|
createConversationOnFirstChat?: boolean;
|
|
62
63
|
customerEmailCaptureMode?: "HIDE" | "OPTIONAL" | "REQUIRED";
|
|
63
64
|
customerEmailCapturePlaceholder?: string;
|
|
65
|
+
resolveMcpAuthHeaders?: MCPAuthHeaderResolver;
|
|
64
66
|
}
|
|
65
67
|
interface ExtraProps extends React.HTMLAttributes<HTMLElement> {
|
|
66
68
|
inline?: boolean;
|
|
@@ -107,6 +109,7 @@ const AgentPanel: React.FC<AgentPanelProps & ExtraProps> = ({
|
|
|
107
109
|
//ragRankLimit = 5,
|
|
108
110
|
initialHistory = {},
|
|
109
111
|
hideRagContextInPrompt = true,
|
|
112
|
+
resolveMcpAuthHeaders,
|
|
110
113
|
}) => {
|
|
111
114
|
const searchParams = new URLSearchParams(location.search);
|
|
112
115
|
const customer_id = searchParams.get("customer_id") || null;
|
|
@@ -314,6 +317,7 @@ const AgentPanel: React.FC<AgentPanelProps & ExtraProps> = ({
|
|
|
314
317
|
"Please enter your email..."
|
|
315
318
|
}
|
|
316
319
|
mcpServers={mcpData}
|
|
320
|
+
resolveMcpAuthHeaders={resolveMcpAuthHeaders}
|
|
317
321
|
/>
|
|
318
322
|
)}
|
|
319
323
|
</>
|
package/src/ChatPanel.tsx
CHANGED
|
@@ -19,6 +19,12 @@ import materialLight from "react-syntax-highlighter/dist/esm/styles/prism/materi
|
|
|
19
19
|
import EmailModal from "./EmailModal";
|
|
20
20
|
import ToolInfoModal from "./ToolInfoModal";
|
|
21
21
|
import { ThinkingBlock as ThinkingBlockComponent } from './components/ui';
|
|
22
|
+
import {
|
|
23
|
+
MCPAuthHeaderResolver,
|
|
24
|
+
MCPAuthPhase,
|
|
25
|
+
normalizeMcpHeaders,
|
|
26
|
+
} from "./mcpAuth";
|
|
27
|
+
import { parseToolArguments } from "./toolArgsParser";
|
|
22
28
|
|
|
23
29
|
export interface ChatPanelProps {
|
|
24
30
|
project_id: string;
|
|
@@ -80,6 +86,7 @@ export interface ChatPanelProps {
|
|
|
80
86
|
customerEmailCapturePlaceholder?: string;
|
|
81
87
|
mcpServers?: [];
|
|
82
88
|
progressiveActions?: boolean;
|
|
89
|
+
resolveMcpAuthHeaders?: MCPAuthHeaderResolver;
|
|
83
90
|
}
|
|
84
91
|
|
|
85
92
|
interface HistoryEntry {
|
|
@@ -138,6 +145,7 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
138
145
|
customerEmailCapturePlaceholder = "Please enter your email...",
|
|
139
146
|
mcpServers,
|
|
140
147
|
progressiveActions = true,
|
|
148
|
+
resolveMcpAuthHeaders,
|
|
141
149
|
}) => {
|
|
142
150
|
const isEmailAddress = (email: string): boolean => {
|
|
143
151
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
@@ -624,6 +632,59 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
624
632
|
const [toolsLoading, setToolsLoading] = useState(false);
|
|
625
633
|
const [toolsFetchError, setToolsFetchError] = useState(false);
|
|
626
634
|
|
|
635
|
+
const buildMcpRequestHeaders = useCallback(
|
|
636
|
+
async ({
|
|
637
|
+
phase,
|
|
638
|
+
mcpServer,
|
|
639
|
+
toolName,
|
|
640
|
+
toolArgs,
|
|
641
|
+
}: {
|
|
642
|
+
phase: MCPAuthPhase;
|
|
643
|
+
mcpServer: Record<string, unknown>;
|
|
644
|
+
toolName?: string;
|
|
645
|
+
toolArgs?: unknown;
|
|
646
|
+
}): Promise<Record<string, string>> => {
|
|
647
|
+
const merged: Record<string, string> = {};
|
|
648
|
+
|
|
649
|
+
const baseAccessToken =
|
|
650
|
+
typeof mcpServer.accessToken === "string"
|
|
651
|
+
? mcpServer.accessToken.trim()
|
|
652
|
+
: "";
|
|
653
|
+
if (baseAccessToken) {
|
|
654
|
+
merged["x-mcp-access-token"] = baseAccessToken;
|
|
655
|
+
}
|
|
656
|
+
if (project_id) {
|
|
657
|
+
merged["x-project-id"] = project_id;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
if (!resolveMcpAuthHeaders) return merged;
|
|
661
|
+
|
|
662
|
+
try {
|
|
663
|
+
const resolved = await resolveMcpAuthHeaders({
|
|
664
|
+
phase,
|
|
665
|
+
mcpServer,
|
|
666
|
+
projectId: project_id,
|
|
667
|
+
customer: currentCustomer,
|
|
668
|
+
toolName,
|
|
669
|
+
toolArgs,
|
|
670
|
+
});
|
|
671
|
+
return {
|
|
672
|
+
...merged,
|
|
673
|
+
...normalizeMcpHeaders(
|
|
674
|
+
resolved as Record<string, unknown> | null | undefined
|
|
675
|
+
),
|
|
676
|
+
};
|
|
677
|
+
} catch (error) {
|
|
678
|
+
console.error(
|
|
679
|
+
`Failed to resolve MCP auth headers for ${phase} request:`,
|
|
680
|
+
error
|
|
681
|
+
);
|
|
682
|
+
return merged;
|
|
683
|
+
}
|
|
684
|
+
},
|
|
685
|
+
[project_id, currentCustomer, resolveMcpAuthHeaders]
|
|
686
|
+
);
|
|
687
|
+
|
|
627
688
|
// mcp servers are passed in in the mcpServers prop. Fetch tools for each one.
|
|
628
689
|
useEffect(() => {
|
|
629
690
|
//console.log("MCP servers", mcpServers);
|
|
@@ -647,7 +708,13 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
647
708
|
)}`;
|
|
648
709
|
|
|
649
710
|
try {
|
|
650
|
-
const
|
|
711
|
+
const requestHeaders = await buildMcpRequestHeaders({
|
|
712
|
+
phase: "list",
|
|
713
|
+
mcpServer: m,
|
|
714
|
+
});
|
|
715
|
+
const response = await fetch(urlToFetch, {
|
|
716
|
+
headers: requestHeaders,
|
|
717
|
+
});
|
|
651
718
|
if (!response.ok) {
|
|
652
719
|
console.error(
|
|
653
720
|
`Error fetching tools from ${m.url}: ${response.status} ${response.statusText}`
|
|
@@ -664,7 +731,7 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
664
731
|
...tool,
|
|
665
732
|
url: m.url,
|
|
666
733
|
accessToken: m.accessToken || "",
|
|
667
|
-
headers:
|
|
734
|
+
headers: requestHeaders,
|
|
668
735
|
}));
|
|
669
736
|
} else {
|
|
670
737
|
return [];
|
|
@@ -699,7 +766,7 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
699
766
|
};
|
|
700
767
|
|
|
701
768
|
fetchAndSetTools();
|
|
702
|
-
}, [mcpServers, publicAPIUrl]);
|
|
769
|
+
}, [mcpServers, publicAPIUrl, buildMcpRequestHeaders]);
|
|
703
770
|
|
|
704
771
|
const llmResult = useLLM({
|
|
705
772
|
project_id: project_id,
|
|
@@ -1203,7 +1270,7 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
1203
1270
|
|
|
1204
1271
|
const anthropic_toolAction = {
|
|
1205
1272
|
pattern:
|
|
1206
|
-
'\\{"type":"tool_use","id":"([^"]+)","name":"([^"]+)","input":(\\{[\\s\\S]
|
|
1273
|
+
'\\{"type":"tool_use","id":"([^"]+)","name":"([^"]+)","input":(\\{[\\s\\S]*?\\}),"service":"([^"]+)"\\}',
|
|
1207
1274
|
type: "markdown",
|
|
1208
1275
|
markdown: "<br />*Tool use requested: $2*",
|
|
1209
1276
|
actionType: "tool",
|
|
@@ -1211,7 +1278,7 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
1211
1278
|
|
|
1212
1279
|
const openAI_toolAction = {
|
|
1213
1280
|
pattern:
|
|
1214
|
-
'\\{"id":"([^"]+)","type":"function","function":\\{"name":"([^"]+)","arguments":"(
|
|
1281
|
+
'\\{"id":"([^"]+)","type":"function","function":\\{"name":"([^"]+)","arguments":"([\\s\\S]*?)"\\},"service":"([^"]+)"\\}',
|
|
1215
1282
|
type: "markdown",
|
|
1216
1283
|
markdown: "<br />*Tool use requested: $2*",
|
|
1217
1284
|
actionType: "tool",
|
|
@@ -1220,7 +1287,7 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
1220
1287
|
// google doesn't return an id, so we just grab functioCall
|
|
1221
1288
|
const google_toolAction = {
|
|
1222
1289
|
pattern:
|
|
1223
|
-
'^\\{\\s*"(functionCall)"\\s*:\\s*\\{\\s*"name"\\s*:\\s*"([^"]+)"\\s*,\\s*"args"\\s*:\\s*(\\{[\\s\\S]
|
|
1290
|
+
'^\\{\\s*"(functionCall)"\\s*:\\s*\\{\\s*"name"\\s*:\\s*"([^"]+)"\\s*,\\s*"args"\\s*:\\s*(\\{[\\s\\S]*?\\})\\s*\\}(?:\\s*,\\s*"thoughtSignature"\\s*:\\s*"[^"]*")?\\s*,\\s*"service"\\s*:\\s*"([^"]+)"\\s*\\}$',
|
|
1224
1291
|
type: "markdown",
|
|
1225
1292
|
markdown: "<br />*Tool use requested: $2*",
|
|
1226
1293
|
actionType: "tool",
|
|
@@ -1400,7 +1467,7 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
1400
1467
|
setPendingToolRequests([]);
|
|
1401
1468
|
try {
|
|
1402
1469
|
// Start with base messages including the user's original question
|
|
1403
|
-
const newMessages = [
|
|
1470
|
+
const newMessages: any[] = [
|
|
1404
1471
|
{
|
|
1405
1472
|
role: "user",
|
|
1406
1473
|
content: [
|
|
@@ -1412,72 +1479,108 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
1412
1479
|
},
|
|
1413
1480
|
];
|
|
1414
1481
|
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
content: [],
|
|
1419
|
-
tool_calls: [],
|
|
1420
|
-
};
|
|
1482
|
+
const parsedToolCalls = await Promise.all(
|
|
1483
|
+
toolsToProcess.map(async (req, index) => {
|
|
1484
|
+
if (!req) return null;
|
|
1421
1485
|
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1486
|
+
let parsedToolCall: any = null;
|
|
1487
|
+
try {
|
|
1488
|
+
parsedToolCall = JSON.parse(req.match);
|
|
1489
|
+
} catch (error) {
|
|
1490
|
+
console.error("Failed to parse tool call:", error);
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
const toolName =
|
|
1494
|
+
req.groups[1] ||
|
|
1495
|
+
req.toolName ||
|
|
1496
|
+
(typeof parsedToolCall?.name === "string" ? parsedToolCall.name : "") ||
|
|
1497
|
+
(typeof parsedToolCall?.function?.name === "string"
|
|
1498
|
+
? parsedToolCall.function.name
|
|
1499
|
+
: "");
|
|
1500
|
+
if (!toolName) return null;
|
|
1501
|
+
|
|
1502
|
+
const rawCallId =
|
|
1503
|
+
req.groups[0] ||
|
|
1504
|
+
parsedToolCall?.id ||
|
|
1505
|
+
parsedToolCall?.tool_call_id ||
|
|
1506
|
+
`${toolName}-${index + 1}`;
|
|
1507
|
+
const callId =
|
|
1508
|
+
typeof rawCallId === "string" &&
|
|
1509
|
+
rawCallId.trim().length > 0 &&
|
|
1510
|
+
rawCallId !== "functionCall"
|
|
1511
|
+
? rawCallId
|
|
1512
|
+
: `${toolName}-${index + 1}`;
|
|
1513
|
+
|
|
1514
|
+
let args: Record<string, unknown> = {};
|
|
1515
|
+
const rawArgs =
|
|
1516
|
+
req.groups[2] ??
|
|
1517
|
+
parsedToolCall?.input ??
|
|
1518
|
+
parsedToolCall?.args ??
|
|
1519
|
+
parsedToolCall?.function?.arguments ??
|
|
1520
|
+
"{}";
|
|
1521
|
+
|
|
1522
|
+
const parsedArgs = parseToolArguments(rawArgs);
|
|
1523
|
+
if (!parsedArgs) {
|
|
1524
|
+
console.error("Failed to parse tool arguments", {
|
|
1525
|
+
toolName,
|
|
1526
|
+
callId,
|
|
1527
|
+
rawArgsPreview:
|
|
1528
|
+
typeof rawArgs === "string"
|
|
1529
|
+
? rawArgs.slice(0, 500)
|
|
1530
|
+
: JSON.stringify(rawArgs).slice(0, 500),
|
|
1531
|
+
});
|
|
1532
|
+
return null;
|
|
1533
|
+
}
|
|
1534
|
+
args = parsedArgs;
|
|
1535
|
+
|
|
1536
|
+
const serviceTag =
|
|
1537
|
+
(typeof req.groups[3] === "string" && req.groups[3]) ||
|
|
1538
|
+
(typeof parsedToolCall?.service === "string" && parsedToolCall.service) ||
|
|
1539
|
+
"";
|
|
1425
1540
|
|
|
1426
|
-
try {
|
|
1427
1541
|
return {
|
|
1428
1542
|
req,
|
|
1429
|
-
|
|
1543
|
+
toolName,
|
|
1544
|
+
callId,
|
|
1545
|
+
args,
|
|
1546
|
+
serviceTag,
|
|
1430
1547
|
};
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
|
-
return null;
|
|
1434
|
-
}
|
|
1435
|
-
});
|
|
1436
|
-
|
|
1437
|
-
// Wait for all tool calls to be parsed
|
|
1438
|
-
const parsedToolCalls = await Promise.all(toolCallsPromises);
|
|
1439
|
-
|
|
1440
|
-
// Add all tool calls to the assistant message
|
|
1441
|
-
parsedToolCalls.forEach((item) => {
|
|
1442
|
-
if (item && item.parsedToolCall) {
|
|
1443
|
-
(toolCallsMessage.tool_calls as any[]).push(item.parsedToolCall);
|
|
1444
|
-
}
|
|
1445
|
-
});
|
|
1446
|
-
|
|
1447
|
-
// Add the assistant message with all tool calls
|
|
1448
|
-
newMessages.push(toolCallsMessage);
|
|
1449
|
-
|
|
1450
|
-
const finalToolCalls = toolCallsMessage.tool_calls;
|
|
1548
|
+
})
|
|
1549
|
+
);
|
|
1451
1550
|
|
|
1452
|
-
const
|
|
1453
|
-
|
|
1551
|
+
const toolCallBatch = parsedToolCalls.filter(Boolean) as Array<{
|
|
1552
|
+
req: any;
|
|
1553
|
+
toolName: string;
|
|
1554
|
+
callId: string;
|
|
1555
|
+
args: Record<string, unknown>;
|
|
1556
|
+
serviceTag: string;
|
|
1557
|
+
}>;
|
|
1454
1558
|
|
|
1455
|
-
|
|
1456
|
-
|
|
1559
|
+
const finalToolCalls = toolCallBatch.map((toolCall) => ({
|
|
1560
|
+
id: toolCall.callId,
|
|
1561
|
+
type: "tool_use",
|
|
1562
|
+
name: toolCall.toolName,
|
|
1563
|
+
input: toolCall.args,
|
|
1564
|
+
service: toolCall.serviceTag,
|
|
1565
|
+
}));
|
|
1457
1566
|
|
|
1458
|
-
|
|
1567
|
+
const toolResponsePromises = toolCallBatch.map(async (toolCall) => {
|
|
1568
|
+
const mcpTool = toolList.find((tool) => tool.name === toolCall.toolName);
|
|
1459
1569
|
|
|
1460
1570
|
if (!mcpTool) {
|
|
1461
|
-
console.error(`Tool ${
|
|
1462
|
-
return
|
|
1571
|
+
console.error(`Tool ${toolCall.toolName} not found in tool list`);
|
|
1572
|
+
return {
|
|
1573
|
+
tool_call_id: toolCall.callId,
|
|
1574
|
+
tool_name: toolCall.toolName,
|
|
1575
|
+
result: `Tool ${toolCall.toolName} not found in current tool list.`,
|
|
1576
|
+
isError: true,
|
|
1577
|
+
};
|
|
1463
1578
|
}
|
|
1464
1579
|
|
|
1465
1580
|
try {
|
|
1466
|
-
let args;
|
|
1467
|
-
try {
|
|
1468
|
-
args = JSON.parse(req.groups[2]);
|
|
1469
|
-
} catch (e) {
|
|
1470
|
-
try {
|
|
1471
|
-
args = JSON.parse(req.groups[2].replace(/\\"/g, '"'));
|
|
1472
|
-
} catch (err) {
|
|
1473
|
-
console.error("Failed to parse tool arguments:", err);
|
|
1474
|
-
return null;
|
|
1475
|
-
}
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
1581
|
const body = {
|
|
1479
|
-
tool:
|
|
1480
|
-
args: args,
|
|
1582
|
+
tool: toolCall.toolName,
|
|
1583
|
+
args: toolCall.args,
|
|
1481
1584
|
};
|
|
1482
1585
|
|
|
1483
1586
|
const result = await fetch(
|
|
@@ -1486,11 +1589,12 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
1486
1589
|
method: "POST",
|
|
1487
1590
|
headers: {
|
|
1488
1591
|
"Content-Type": "application/json",
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1592
|
+
...(await buildMcpRequestHeaders({
|
|
1593
|
+
phase: "call",
|
|
1594
|
+
mcpServer: mcpTool as Record<string, unknown>,
|
|
1595
|
+
toolName: toolCall.toolName,
|
|
1596
|
+
toolArgs: toolCall.args,
|
|
1597
|
+
})),
|
|
1494
1598
|
},
|
|
1495
1599
|
body: JSON.stringify(body),
|
|
1496
1600
|
}
|
|
@@ -1498,11 +1602,18 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
1498
1602
|
|
|
1499
1603
|
if (!result.ok) {
|
|
1500
1604
|
console.error(
|
|
1501
|
-
`Error calling tool ${
|
|
1605
|
+
`Error calling tool ${toolCall.toolName}: ${result.status} ${result.statusText}`
|
|
1502
1606
|
);
|
|
1503
1607
|
const errorBody = await result.text();
|
|
1504
1608
|
console.error(`Error body: ${errorBody}`);
|
|
1505
|
-
return
|
|
1609
|
+
return {
|
|
1610
|
+
tool_call_id: toolCall.callId,
|
|
1611
|
+
tool_name: toolCall.toolName,
|
|
1612
|
+
result: `HTTP ${result.status} ${result.statusText}: ${
|
|
1613
|
+
errorBody || "Tool call failed"
|
|
1614
|
+
}`,
|
|
1615
|
+
isError: true,
|
|
1616
|
+
};
|
|
1506
1617
|
}
|
|
1507
1618
|
|
|
1508
1619
|
let resultData;
|
|
@@ -1510,45 +1621,40 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
1510
1621
|
resultData = await result.json();
|
|
1511
1622
|
} catch (jsonError) {
|
|
1512
1623
|
console.error(
|
|
1513
|
-
`Error parsing JSON response for tool ${
|
|
1624
|
+
`Error parsing JSON response for tool ${toolCall.toolName}:`,
|
|
1514
1625
|
jsonError
|
|
1515
1626
|
);
|
|
1516
|
-
// Attempt to read as text for debugging if JSON fails
|
|
1517
|
-
try {
|
|
1518
|
-
const textBody = await result.text(); // Note: This consumes the body if json() failed early
|
|
1519
|
-
console.error("Response body (text):", textBody);
|
|
1520
|
-
} catch (textError) {
|
|
1521
|
-
console.error(
|
|
1522
|
-
"Failed to read response body as text either:",
|
|
1523
|
-
textError
|
|
1524
|
-
);
|
|
1525
|
-
}
|
|
1526
|
-
return null; // Exit if JSON parsing failed
|
|
1527
|
-
}
|
|
1528
|
-
|
|
1529
|
-
if (
|
|
1530
|
-
resultData &&
|
|
1531
|
-
resultData.content &&
|
|
1532
|
-
resultData.content.length > 0
|
|
1533
|
-
) {
|
|
1534
|
-
const textResult = resultData.content[0]?.text;
|
|
1535
1627
|
return {
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
text: textResult,
|
|
1541
|
-
},
|
|
1542
|
-
],
|
|
1543
|
-
tool_call_id: req.groups[0],
|
|
1628
|
+
tool_call_id: toolCall.callId,
|
|
1629
|
+
tool_name: toolCall.toolName,
|
|
1630
|
+
result: "Tool returned a non-JSON response.",
|
|
1631
|
+
isError: true,
|
|
1544
1632
|
};
|
|
1545
|
-
} else {
|
|
1546
|
-
console.error(`No content returned from tool ${req.toolName}`);
|
|
1547
|
-
return null;
|
|
1548
1633
|
}
|
|
1634
|
+
|
|
1635
|
+
const textResult =
|
|
1636
|
+
resultData?.content?.[0]?.text ??
|
|
1637
|
+
(resultData?.result
|
|
1638
|
+
? JSON.stringify(resultData.result)
|
|
1639
|
+
: JSON.stringify(resultData));
|
|
1640
|
+
|
|
1641
|
+
return {
|
|
1642
|
+
tool_call_id: toolCall.callId,
|
|
1643
|
+
tool_name: toolCall.toolName,
|
|
1644
|
+
result: textResult || "",
|
|
1645
|
+
isError: resultData?.isError === true,
|
|
1646
|
+
};
|
|
1549
1647
|
} catch (error) {
|
|
1550
|
-
console.error(`Error processing tool ${
|
|
1551
|
-
return
|
|
1648
|
+
console.error(`Error processing tool ${toolCall.toolName}:`, error);
|
|
1649
|
+
return {
|
|
1650
|
+
tool_call_id: toolCall.callId,
|
|
1651
|
+
tool_name: toolCall.toolName,
|
|
1652
|
+
result:
|
|
1653
|
+
error instanceof Error
|
|
1654
|
+
? error.message
|
|
1655
|
+
: `Unhandled error calling ${toolCall.toolName}`,
|
|
1656
|
+
isError: true,
|
|
1657
|
+
};
|
|
1552
1658
|
}
|
|
1553
1659
|
});
|
|
1554
1660
|
|
|
@@ -1577,11 +1683,55 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
1577
1683
|
});
|
|
1578
1684
|
}
|
|
1579
1685
|
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1686
|
+
const toReplayText = (value: unknown, maxLength = 2000): string => {
|
|
1687
|
+
const raw =
|
|
1688
|
+
typeof value === "string"
|
|
1689
|
+
? value
|
|
1690
|
+
: (() => {
|
|
1691
|
+
try {
|
|
1692
|
+
return JSON.stringify(value);
|
|
1693
|
+
} catch (_error) {
|
|
1694
|
+
return String(value ?? "");
|
|
1695
|
+
}
|
|
1696
|
+
})();
|
|
1697
|
+
const normalized = String(raw ?? "").replace(/\s+/g, " ").trim();
|
|
1698
|
+
if (normalized.length <= maxLength) return normalized;
|
|
1699
|
+
return `${normalized.slice(0, maxLength - 3)}...`;
|
|
1700
|
+
};
|
|
1701
|
+
|
|
1702
|
+
if (toolCallBatch.length > 0) {
|
|
1703
|
+
const replayLines = toolCallBatch.map((toolCall, index) => {
|
|
1704
|
+
const matchedResponse =
|
|
1705
|
+
finalToolResponses.find(
|
|
1706
|
+
(response: any) => response?.tool_call_id === toolCall.callId
|
|
1707
|
+
) || finalToolResponses[index];
|
|
1708
|
+
const status = matchedResponse?.isError ? "error" : "ok";
|
|
1709
|
+
const resultText = toReplayText(matchedResponse?.result ?? "No result returned");
|
|
1710
|
+
|
|
1711
|
+
return [
|
|
1712
|
+
`Tool: ${toolCall.toolName}`,
|
|
1713
|
+
`Call ID: ${toolCall.callId}`,
|
|
1714
|
+
`Status: ${status}`,
|
|
1715
|
+
`Args: ${toReplayText(toolCall.args, 600)}`,
|
|
1716
|
+
`Result: ${resultText}`,
|
|
1717
|
+
].join("\n");
|
|
1718
|
+
});
|
|
1719
|
+
|
|
1720
|
+
newMessages.push({
|
|
1721
|
+
role: "user",
|
|
1722
|
+
content: [
|
|
1723
|
+
{
|
|
1724
|
+
type: "text",
|
|
1725
|
+
text: [
|
|
1726
|
+
"Tool execution summary for the previous request:",
|
|
1727
|
+
...replayLines,
|
|
1728
|
+
"Continue the same assistant response from exactly where you paused using these tool results.",
|
|
1729
|
+
"If this response is using meta tags, keep the same format (<thinking>, <reasoning>, <searching>) in the continuation.",
|
|
1730
|
+
].join("\n\n"),
|
|
1731
|
+
},
|
|
1732
|
+
],
|
|
1733
|
+
});
|
|
1734
|
+
}
|
|
1585
1735
|
|
|
1586
1736
|
send(
|
|
1587
1737
|
"",
|
|
@@ -39,6 +39,8 @@ export interface MCPServer {
|
|
|
39
39
|
status: 'active' | 'inactive';
|
|
40
40
|
executionMode: 'CLIENT' | 'SERVER';
|
|
41
41
|
url?: string;
|
|
42
|
+
accessToken?: string;
|
|
43
|
+
headers?: Record<string, string>;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
export interface AgentProfile {
|
|
@@ -343,4 +345,3 @@ function extractAgentNameFromMessage(message: string): string | null {
|
|
|
343
345
|
}
|
|
344
346
|
|
|
345
347
|
export default useAgentRegistry;
|
|
346
|
-
|
package/src/mcpAuth.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { LLMAsAServiceCustomer } from "llmasaservice-client";
|
|
2
|
+
|
|
3
|
+
export type MCPAuthPhase = "list" | "call";
|
|
4
|
+
|
|
5
|
+
export interface MCPAuthHeaderResolverInput {
|
|
6
|
+
phase: MCPAuthPhase;
|
|
7
|
+
mcpServer: Record<string, unknown>;
|
|
8
|
+
projectId?: string;
|
|
9
|
+
customer?: LLMAsAServiceCustomer;
|
|
10
|
+
toolName?: string;
|
|
11
|
+
toolArgs?: unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type MCPAuthHeaderResolver = (
|
|
15
|
+
input: MCPAuthHeaderResolverInput
|
|
16
|
+
) =>
|
|
17
|
+
| Record<string, string>
|
|
18
|
+
| null
|
|
19
|
+
| undefined
|
|
20
|
+
| Promise<Record<string, string> | null | undefined>;
|
|
21
|
+
|
|
22
|
+
export function normalizeMcpHeaders(
|
|
23
|
+
value: Record<string, unknown> | null | undefined
|
|
24
|
+
): Record<string, string> {
|
|
25
|
+
const normalized: Record<string, string> = {};
|
|
26
|
+
if (!value) return normalized;
|
|
27
|
+
|
|
28
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
29
|
+
if (typeof raw !== "string") continue;
|
|
30
|
+
const trimmedValue = raw.trim();
|
|
31
|
+
if (!trimmedValue) continue;
|
|
32
|
+
normalized[key] = trimmedValue;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return normalized;
|
|
36
|
+
}
|