@xalia/agent 0.6.4 → 0.6.5
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/agent/src/agent/agent.js +12 -11
- package/dist/agent/src/agent/dummyLLM.js +24 -15
- package/dist/agent/src/agent/llm.js +0 -22
- package/dist/agent/src/agent/mcpServerManager.js +73 -15
- package/dist/agent/src/agent/openAI.js +32 -0
- package/dist/agent/src/agent/openAILLM.js +25 -1
- package/dist/agent/src/agent/openAILLMStreaming.js +8 -3
- package/dist/agent/src/agent/sudoMcpServerManager.js +22 -9
- package/dist/agent/src/chat/client/chatClient.js +2 -1
- package/dist/agent/src/chat/client/sessionClient.js +28 -3
- package/dist/agent/src/chat/data/dbSessionFileModels.js +10 -0
- package/dist/agent/src/chat/protocol/messages.js +1 -0
- package/dist/agent/src/chat/server/chatContextManager.js +4 -4
- package/dist/agent/src/chat/server/conversation.js +1 -1
- package/dist/agent/src/chat/server/imageGeneratorTools.js +7 -4
- package/dist/agent/src/chat/server/openSession.js +85 -12
- package/dist/agent/src/chat/server/sessionFileManager.js +17 -6
- package/dist/agent/src/chat/server/sessionRegistry.js +1 -1
- package/dist/agent/src/chat/server/tools.js +58 -10
- package/dist/agent/src/test/agent.test.js +26 -2
- package/dist/agent/src/test/chatContextManager.test.js +5 -5
- package/dist/agent/src/test/mcpServerManager.test.js +5 -1
- package/dist/agent/src/test/testTools.js +5 -2
- package/dist/agent/src/tool/chatMain.js +23 -3
- package/dist/agent/src/tool/files.js +0 -27
- package/package.json +3 -3
- package/scripts/test_chat +3 -1
- package/src/agent/agent.ts +53 -47
- package/src/agent/agentUtils.ts +7 -7
- package/src/agent/compressingContextManager.ts +4 -9
- package/src/agent/context.ts +28 -37
- package/src/agent/dummyLLM.ts +38 -28
- package/src/agent/iAgentEventHandler.ts +6 -9
- package/src/agent/imageGenLLM.ts +11 -5
- package/src/agent/llm.ts +41 -106
- package/src/agent/mcpServerManager.ts +145 -29
- package/src/agent/openAI.ts +123 -0
- package/src/agent/openAILLM.ts +52 -5
- package/src/agent/openAILLMStreaming.ts +36 -32
- package/src/agent/repeatLLM.ts +5 -6
- package/src/agent/sudoMcpServerManager.ts +48 -16
- package/src/agent/tools.ts +3 -5
- package/src/chat/client/chatClient.ts +3 -1
- package/src/chat/client/sessionClient.ts +47 -7
- package/src/chat/data/dataModels.ts +3 -3
- package/src/chat/data/dbSessionFileModels.ts +22 -0
- package/src/chat/protocol/messages.ts +39 -13
- package/src/chat/server/chatContextManager.ts +20 -24
- package/src/chat/server/conversation.ts +10 -10
- package/src/chat/server/imageGeneratorTools.ts +18 -9
- package/src/chat/server/openSession.ts +111 -22
- package/src/chat/server/sessionFileManager.ts +33 -10
- package/src/chat/server/sessionRegistry.ts +1 -1
- package/src/chat/server/tools.ts +77 -18
- package/src/chat/utils/approvalManager.ts +2 -2
- package/src/test/agent.test.ts +56 -31
- package/src/test/approvalManager.test.ts +2 -2
- package/src/test/chatContextManager.test.ts +11 -14
- package/src/test/compressingContextManager.test.ts +3 -3
- package/src/test/context.test.ts +3 -3
- package/src/test/conversation.test.ts +7 -7
- package/src/test/dbSessionMessages.test.ts +3 -3
- package/src/test/mcpServerManager.test.ts +10 -1
- package/src/test/testTools.ts +44 -33
- package/src/tool/agentChat.ts +10 -8
- package/src/tool/agentMain.ts +2 -2
- package/src/tool/chatMain.ts +38 -6
- package/src/tool/commandPrompt.ts +2 -4
- package/src/tool/files.ts +0 -34
- package/test_data/dummyllm_script_image_gen.json +27 -17
- package/test_data/dummyllm_script_invoke_image_gen_tool.json +9 -2
- package/test_data/dummyllm_script_render_tool.json +29 -0
- package/test_data/dummyllm_script_test_auto_approve.json +81 -0
- package/test_data/dummyllm_script_test_simplecalc_addition.json +29 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { OpenAI } from "openai";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
AssistantMessageParam,
|
|
4
|
+
MessageToolCall,
|
|
5
|
+
ToolMessageParam,
|
|
6
6
|
} from "./llm";
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -17,7 +17,7 @@ export interface IAgentEventHandler {
|
|
|
17
17
|
* `onAgentMessage`. This call is for client code which wishes to track the
|
|
18
18
|
* LLM context.
|
|
19
19
|
*/
|
|
20
|
-
onCompletion(result:
|
|
20
|
+
onCompletion(result: AssistantMessageParam): void;
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Images do not appear as part of `ChatCompletionAssistantMessageParam`,
|
|
@@ -30,7 +30,7 @@ export interface IAgentEventHandler {
|
|
|
30
30
|
* Called when a tool call execution completes (success, error, or denial).
|
|
31
31
|
* These messages are insertes into the LLM context.
|
|
32
32
|
*/
|
|
33
|
-
onToolCallResult(result:
|
|
33
|
+
onToolCallResult(result: ToolMessageParam): void;
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Called when the agent produces a message chunk (streaming). Calls here
|
|
@@ -55,10 +55,7 @@ export interface IAgentEventHandler {
|
|
|
55
55
|
* @param internal - If true, this is an "internal" or "Agent" tool.
|
|
56
56
|
* @returns Promise<boolean> - true if approved, false if denied
|
|
57
57
|
*/
|
|
58
|
-
onToolCall(
|
|
59
|
-
toolCall: ChatCompletionMessageToolCall,
|
|
60
|
-
agentTool: boolean
|
|
61
|
-
): Promise<boolean>;
|
|
58
|
+
onToolCall(toolCall: MessageToolCall, agentTool: boolean): Promise<boolean>;
|
|
62
59
|
|
|
63
60
|
// Future extensibility examples:
|
|
64
61
|
// onError?(error: AgentError): void;
|
package/src/agent/imageGenLLM.ts
CHANGED
|
@@ -4,7 +4,13 @@ import { writeFileSync } from "fs";
|
|
|
4
4
|
|
|
5
5
|
import { getLogger } from "@xalia/xmcp/sdk";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
ILLM,
|
|
9
|
+
Completion,
|
|
10
|
+
XALIA_APP_HEADER,
|
|
11
|
+
ToolDescriptor,
|
|
12
|
+
MessageParam,
|
|
13
|
+
} from "./llm";
|
|
8
14
|
|
|
9
15
|
const logger = getLogger();
|
|
10
16
|
|
|
@@ -49,10 +55,10 @@ export class ImageGenLLM implements ILLM {
|
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
public async getConversationResponse(
|
|
52
|
-
messages:
|
|
53
|
-
tools?:
|
|
58
|
+
messages: MessageParam[],
|
|
59
|
+
tools?: ToolDescriptor[],
|
|
54
60
|
onMessage?: (msg: string, end: boolean) => Promise<void>
|
|
55
|
-
): Promise<
|
|
61
|
+
): Promise<Completion> {
|
|
56
62
|
assert(!tools || tools.length === 0, "tools not supported in ImageGenLLM");
|
|
57
63
|
|
|
58
64
|
// Designed for image generation using openrouter, which tweaks the Create
|
|
@@ -67,7 +73,7 @@ export class ImageGenLLM implements ILLM {
|
|
|
67
73
|
|
|
68
74
|
const completion = (await this.openai.chat.completions.create(
|
|
69
75
|
params as OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming
|
|
70
|
-
)) as
|
|
76
|
+
)) as Completion;
|
|
71
77
|
|
|
72
78
|
// const completion = {} as unknown as ChatCompletion;
|
|
73
79
|
|
package/src/agent/llm.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as openai from "./openAI";
|
|
1
2
|
import { OpenAI } from "openai";
|
|
2
3
|
|
|
3
4
|
export const XALIA_APP_HEADER = {
|
|
@@ -5,123 +6,57 @@ export const XALIA_APP_HEADER = {
|
|
|
5
6
|
"X-Title": "Xalia",
|
|
6
7
|
};
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
// with google/gemini-2.5-flash-image-preview.
|
|
9
|
+
export type ContentPartImage = openai.ChatCompletionContentPartImage;
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
};
|
|
11
|
+
// For now, internally we only support "function" tool call requests, not
|
|
12
|
+
// "custom" (impacts AssistantMessageParam).
|
|
13
|
+
export type MessageToolCall = OpenAI.ChatCompletionMessageFunctionToolCall;
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export type ChatCompletion = Omit<
|
|
22
|
-
OpenAI.Chat.Completions.ChatCompletion,
|
|
23
|
-
"choices"
|
|
24
|
-
> & { choices: Array<ChatCompletionChoice> };
|
|
25
|
-
|
|
26
|
-
// Shortcuts to other useful OpenAI types.
|
|
27
|
-
|
|
28
|
-
export type ChatCompletionMessageToolCall =
|
|
29
|
-
OpenAI.ChatCompletionMessageToolCall;
|
|
30
|
-
|
|
31
|
-
export type ChatCompletionAssistantMessageParam =
|
|
32
|
-
OpenAI.ChatCompletionAssistantMessageParam;
|
|
15
|
+
// ChatCompletionAssistantMessageParam, but with only "function" tool calls.
|
|
16
|
+
export interface AssistantMessageParam
|
|
17
|
+
extends openai.ChatCompletionAssistantMessageParam {
|
|
18
|
+
tool_calls?: Array<MessageToolCall>;
|
|
19
|
+
}
|
|
33
20
|
|
|
34
|
-
export type
|
|
35
|
-
OpenAI.ChatCompletionUserMessageParam;
|
|
21
|
+
export type UserMessageParam = openai.ChatCompletionUserMessageParam;
|
|
36
22
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
23
|
+
// Tool call results `ToolMessageParam` are only "function" results for now
|
|
24
|
+
export interface ToolMessageParam
|
|
25
|
+
extends OpenAI.ChatCompletionToolMessageParam {
|
|
26
|
+
_meta?: Record<string, string>;
|
|
27
|
+
structuredContent?: unknown;
|
|
28
|
+
}
|
|
41
29
|
|
|
42
|
-
export type
|
|
30
|
+
export type MessageParam =
|
|
43
31
|
| OpenAI.Chat.Completions.ChatCompletionSystemMessageParam
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
32
|
+
| AssistantMessageParam
|
|
33
|
+
| UserMessageParam
|
|
34
|
+
| ToolMessageParam;
|
|
47
35
|
|
|
48
36
|
// The tool description type
|
|
49
37
|
|
|
50
|
-
export type
|
|
51
|
-
|
|
52
|
-
// CompletionCreate params
|
|
53
|
-
|
|
54
|
-
// openrouter reasoning type
|
|
55
|
-
|
|
56
|
-
export type ReasoningEffort = {
|
|
57
|
-
effort?: OpenAI.ReasoningEffort;
|
|
58
|
-
max_tokens?: never;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export type ReasoningMaxTokens = { effort?: never; max_tokens?: number };
|
|
62
|
-
|
|
63
|
-
export type ReasoningExclude = { exclude?: boolean; enabled?: never };
|
|
64
|
-
|
|
65
|
-
export type ReasoningEnabled = {
|
|
66
|
-
exclude?: never;
|
|
67
|
-
enabled?: boolean;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
export type Reasoning = (ReasoningEffort | ReasoningMaxTokens) &
|
|
71
|
-
(ReasoningExclude | ReasoningEnabled);
|
|
72
|
-
|
|
73
|
-
export type ReasoningDetails = {
|
|
74
|
-
type: "reasoning.text" | "<unknown>";
|
|
75
|
-
text?: string;
|
|
76
|
-
signature?: string;
|
|
77
|
-
format?: string;
|
|
78
|
-
index?: number;
|
|
79
|
-
};
|
|
38
|
+
export type ToolDescriptor = OpenAI.Chat.Completions.ChatCompletionFunctionTool;
|
|
80
39
|
|
|
81
40
|
/**
|
|
82
|
-
*
|
|
41
|
+
*
|
|
83
42
|
*/
|
|
84
|
-
export
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
export function choiceDeltaExtractReasoning(
|
|
91
|
-
delta: ChatCompletionChunkChoiceDeltaWithReasoning
|
|
92
|
-
): string | undefined {
|
|
93
|
-
if (delta.reasoning) {
|
|
94
|
-
return delta.reasoning;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (delta.reasoning_details) {
|
|
98
|
-
let reasoning = "";
|
|
99
|
-
for (const details of delta.reasoning_details) {
|
|
100
|
-
if (details.type !== "reasoning.text") {
|
|
101
|
-
throw new Error(`unexpected details.type: ${details.type}`);
|
|
102
|
-
}
|
|
103
|
-
if (details.text) {
|
|
104
|
-
if (typeof details.text !== "string") {
|
|
105
|
-
throw new Error(
|
|
106
|
-
`unexpected typeof details.text: ${typeof details.text}`
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
reasoning += details.text;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return reasoning;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return undefined;
|
|
43
|
+
export interface Message /* WithReasoning */
|
|
44
|
+
extends openai.ChatCompletionMessage {
|
|
45
|
+
reasoning?: string;
|
|
46
|
+
tool_calls?: Array<MessageToolCall>;
|
|
116
47
|
}
|
|
117
48
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
export
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
49
|
+
// export interface Message extends MessageWithReasoning {}
|
|
50
|
+
|
|
51
|
+
// Extend ChatCompletionChoice to only hold function tool calls
|
|
52
|
+
export interface Choice extends openai.ChatCompletionChoice {
|
|
53
|
+
message: Message;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Extends ChatCompletion to only hold function tool calls.
|
|
57
|
+
export interface Completion extends openai.ChatCompletion {
|
|
58
|
+
choices: Array<Choice>;
|
|
59
|
+
}
|
|
125
60
|
|
|
126
61
|
export interface ILLM {
|
|
127
62
|
getModel(): string;
|
|
@@ -129,11 +64,11 @@ export interface ILLM {
|
|
|
129
64
|
getUrl(): string;
|
|
130
65
|
|
|
131
66
|
getConversationResponse(
|
|
132
|
-
messages:
|
|
133
|
-
tools?:
|
|
67
|
+
messages: MessageParam[],
|
|
68
|
+
tools?: ToolDescriptor[],
|
|
134
69
|
onMessage?: (msg: string, end: boolean) => Promise<void>,
|
|
135
70
|
onReasoning?: (reasoning: string) => Promise<void>
|
|
136
|
-
): Promise<
|
|
71
|
+
): Promise<Completion>;
|
|
137
72
|
|
|
138
73
|
setModel(model: string): void;
|
|
139
74
|
}
|
|
@@ -1,25 +1,61 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
2
2
|
import {
|
|
3
3
|
SSEClientTransport,
|
|
4
4
|
SSEClientTransportOptions,
|
|
5
5
|
} from "@modelcontextprotocol/sdk/client/sse.js";
|
|
6
|
+
import {
|
|
7
|
+
StreamableHTTPClientTransport,
|
|
8
|
+
StreamableHTTPClientTransportOptions,
|
|
9
|
+
} from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
6
10
|
import { Client as McpClient } from "@modelcontextprotocol/sdk/client/index.js";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
import { strict as assert } from "assert";
|
|
11
|
+
import { Tool, Resource } from "@modelcontextprotocol/sdk/types.js";
|
|
12
|
+
|
|
10
13
|
import { McpServerSettings, getLogger } from "@xalia/xmcp/sdk";
|
|
11
14
|
export type { McpServerSettings } from "@xalia/xmcp/sdk";
|
|
12
15
|
|
|
16
|
+
import { TokenAuth } from "./tokenAuth";
|
|
17
|
+
import { strict as assert } from "assert";
|
|
18
|
+
import { ToolDescriptor } from "./llm";
|
|
19
|
+
import { ToolCallResult } from "./agent";
|
|
20
|
+
|
|
13
21
|
const logger = getLogger();
|
|
14
22
|
|
|
23
|
+
export type McpServerToolCallMetaData = {
|
|
24
|
+
"xalia/mcpServerName": string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const mcpServerToolCallMetaKeys: (keyof McpServerToolCallMetaData)[] = [
|
|
28
|
+
"xalia/mcpServerName",
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export function isMcpServerToolCallMetaData(
|
|
32
|
+
meta: unknown
|
|
33
|
+
): meta is McpServerToolCallMetaData {
|
|
34
|
+
return (
|
|
35
|
+
!!meta &&
|
|
36
|
+
typeof meta === "object" &&
|
|
37
|
+
mcpServerToolCallMetaKeys.every((k) => k in meta)
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type McpServerToolCallResult = ToolCallResult<McpServerToolCallMetaData>;
|
|
42
|
+
|
|
15
43
|
export type VerifiedMcpToolCall = {
|
|
16
44
|
mcpServerName: string;
|
|
17
45
|
toolName: string;
|
|
18
46
|
args: unknown;
|
|
19
47
|
};
|
|
20
48
|
|
|
49
|
+
export type ResourceContent = {
|
|
50
|
+
_meta?: Record<string, unknown>;
|
|
51
|
+
uri: string;
|
|
52
|
+
mimeType?: string;
|
|
53
|
+
text?: string;
|
|
54
|
+
blob?: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
21
57
|
/// Callback into an Mcp server
|
|
22
|
-
export type McpCallback = { (args: string): Promise<
|
|
58
|
+
export type McpCallback = { (args: string): Promise<McpServerToolCallResult> };
|
|
23
59
|
|
|
24
60
|
/// Map of tool name to callback
|
|
25
61
|
export type McpCallbacks = Map<string, McpCallback>;
|
|
@@ -35,11 +71,13 @@ export type EnabledToolsMap = Map<string, boolean>;
|
|
|
35
71
|
* class.
|
|
36
72
|
*/
|
|
37
73
|
export class McpServerInfo {
|
|
74
|
+
private readonly name: string;
|
|
38
75
|
private readonly tools: Tool[]; // TODO: May not need both tools and toolsMap
|
|
76
|
+
private readonly resources: Resource[];
|
|
39
77
|
private readonly toolsMap: { [toolName: string]: Tool };
|
|
40
78
|
protected enabledToolsMap: EnabledToolsMap;
|
|
41
79
|
|
|
42
|
-
constructor(tools: Tool[]) {
|
|
80
|
+
constructor(name: string, tools: Tool[], resources: Resource[]) {
|
|
43
81
|
const toolsMap: { [toolName: string]: Tool } = {};
|
|
44
82
|
|
|
45
83
|
for (const mcpTool of tools) {
|
|
@@ -47,11 +85,17 @@ export class McpServerInfo {
|
|
|
47
85
|
toolsMap[toolName] = mcpTool;
|
|
48
86
|
}
|
|
49
87
|
|
|
88
|
+
this.name = name;
|
|
50
89
|
this.tools = tools;
|
|
90
|
+
this.resources = resources;
|
|
51
91
|
this.toolsMap = toolsMap;
|
|
52
92
|
this.enabledToolsMap = new Map();
|
|
53
93
|
}
|
|
54
94
|
|
|
95
|
+
public getName(): string {
|
|
96
|
+
return this.name;
|
|
97
|
+
}
|
|
98
|
+
|
|
55
99
|
public getEnabledTools(): EnabledToolsMap {
|
|
56
100
|
return this.enabledToolsMap;
|
|
57
101
|
}
|
|
@@ -60,6 +104,10 @@ export class McpServerInfo {
|
|
|
60
104
|
return this.tools;
|
|
61
105
|
}
|
|
62
106
|
|
|
107
|
+
public getResources(): Resource[] {
|
|
108
|
+
return this.resources;
|
|
109
|
+
}
|
|
110
|
+
|
|
63
111
|
public getTool(toolName: string): Tool | undefined {
|
|
64
112
|
return this.toolsMap[toolName];
|
|
65
113
|
}
|
|
@@ -88,8 +136,15 @@ class McpServerInfoInternal extends McpServerInfoRW {
|
|
|
88
136
|
private readonly client: McpClient;
|
|
89
137
|
private readonly callbacks: McpCallbacks;
|
|
90
138
|
|
|
91
|
-
constructor(
|
|
92
|
-
|
|
139
|
+
constructor(
|
|
140
|
+
name: string,
|
|
141
|
+
client: McpClient,
|
|
142
|
+
tools: Tool[],
|
|
143
|
+
resources: Resource[]
|
|
144
|
+
) {
|
|
145
|
+
super(name, tools, resources);
|
|
146
|
+
|
|
147
|
+
logger.debug(`[McpServerInfoInternal] tools: ${JSON.stringify(tools)}`);
|
|
93
148
|
|
|
94
149
|
const callbacks: McpCallbacks = new Map();
|
|
95
150
|
|
|
@@ -97,7 +152,9 @@ class McpServerInfoInternal extends McpServerInfoRW {
|
|
|
97
152
|
const toolName = mcpTool.name;
|
|
98
153
|
|
|
99
154
|
// Create callback
|
|
100
|
-
const callback = async (
|
|
155
|
+
const callback = async (
|
|
156
|
+
argStr: string
|
|
157
|
+
): Promise<McpServerToolCallResult> => {
|
|
101
158
|
logger.debug(
|
|
102
159
|
`cb for ${toolName} invoked with args (${typeof argStr}): ` +
|
|
103
160
|
JSON.stringify(argStr)
|
|
@@ -120,7 +177,19 @@ class McpServerInfoInternal extends McpServerInfoRW {
|
|
|
120
177
|
assert(typeof content0 === "object");
|
|
121
178
|
const content0Text = content0.text;
|
|
122
179
|
assert(typeof content0Text === "string");
|
|
123
|
-
|
|
180
|
+
|
|
181
|
+
const meta: Record<string, string> = (toolResult._meta ||
|
|
182
|
+
{}) as McpServerToolCallMetaData;
|
|
183
|
+
meta["xalia/mcpServerName"] = this.getName();
|
|
184
|
+
assert(isMcpServerToolCallMetaData(meta));
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
response: content0Text,
|
|
188
|
+
_meta: meta,
|
|
189
|
+
...(toolResult.structuredContent
|
|
190
|
+
? { structuredContent: toolResult.structuredContent }
|
|
191
|
+
: {}),
|
|
192
|
+
};
|
|
124
193
|
};
|
|
125
194
|
|
|
126
195
|
callbacks.set(toolName, callback);
|
|
@@ -134,9 +203,18 @@ class McpServerInfoInternal extends McpServerInfoRW {
|
|
|
134
203
|
await this.client.close();
|
|
135
204
|
}
|
|
136
205
|
|
|
137
|
-
public getCallback(toolName: string) {
|
|
206
|
+
public getCallback(toolName: string): McpCallback | undefined {
|
|
138
207
|
return this.callbacks.get(toolName);
|
|
139
208
|
}
|
|
209
|
+
|
|
210
|
+
public async readResource(uri: string): Promise<ResourceContent[]> {
|
|
211
|
+
const res = await this.client.readResource({
|
|
212
|
+
uri,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
logger.info(`readResource: got: ${JSON.stringify(res)}`);
|
|
216
|
+
return res.contents;
|
|
217
|
+
}
|
|
140
218
|
}
|
|
141
219
|
|
|
142
220
|
/**
|
|
@@ -169,7 +247,7 @@ export interface IMcpServerManager {
|
|
|
169
247
|
export class McpServerManager implements IMcpServerManager {
|
|
170
248
|
private mcpServers = new Map<string, McpServerInfoInternal>();
|
|
171
249
|
private enabledToolsDirty: boolean = true;
|
|
172
|
-
private enabledOpenAITools:
|
|
250
|
+
private enabledOpenAITools: ToolDescriptor[] = [];
|
|
173
251
|
|
|
174
252
|
public async shutdown() {
|
|
175
253
|
await Promise.all(
|
|
@@ -194,19 +272,11 @@ export class McpServerManager implements IMcpServerManager {
|
|
|
194
272
|
return this.getMcpServerInternal(mcpServerName);
|
|
195
273
|
}
|
|
196
274
|
|
|
197
|
-
public async
|
|
275
|
+
public async addMcpServerWithTransport(
|
|
198
276
|
mcpServerName: string,
|
|
199
|
-
|
|
200
|
-
apiKey?: string,
|
|
277
|
+
transport: Transport,
|
|
201
278
|
tools?: Tool[]
|
|
202
279
|
): Promise<void> {
|
|
203
|
-
logger.debug(`Adding mcp server ${mcpServerName}: ${url}`);
|
|
204
|
-
const sseTransportOptions: SSEClientTransportOptions = {};
|
|
205
|
-
if (apiKey) {
|
|
206
|
-
sseTransportOptions.authProvider = new TokenAuth(apiKey);
|
|
207
|
-
}
|
|
208
|
-
const urlO = new URL(url);
|
|
209
|
-
const transport = new SSEClientTransport(urlO, sseTransportOptions);
|
|
210
280
|
const client = new McpClient({
|
|
211
281
|
name: "@xalia/agent",
|
|
212
282
|
version: "1.0.0",
|
|
@@ -223,6 +293,37 @@ export class McpServerManager implements IMcpServerManager {
|
|
|
223
293
|
await this.addMcpServerWithClient(client, mcpServerName, tools);
|
|
224
294
|
}
|
|
225
295
|
|
|
296
|
+
public async addMcpServerWithSSEUrl(
|
|
297
|
+
mcpServerName: string,
|
|
298
|
+
url: string,
|
|
299
|
+
apiKey?: string,
|
|
300
|
+
tools?: Tool[]
|
|
301
|
+
): Promise<void> {
|
|
302
|
+
logger.debug(`Adding mcp server ${mcpServerName}: ${url}`);
|
|
303
|
+
const sseTransportOptions: SSEClientTransportOptions = {};
|
|
304
|
+
if (apiKey) {
|
|
305
|
+
sseTransportOptions.authProvider = new TokenAuth(apiKey);
|
|
306
|
+
}
|
|
307
|
+
const urlO = new URL(url);
|
|
308
|
+
const transport = new SSEClientTransport(urlO, sseTransportOptions);
|
|
309
|
+
return this.addMcpServerWithTransport(mcpServerName, transport, tools);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
public async addMcpServerWithStreamableHTTPUrl(
|
|
313
|
+
mcpServerName: string,
|
|
314
|
+
url: string,
|
|
315
|
+
apiKey?: string
|
|
316
|
+
): Promise<void> {
|
|
317
|
+
logger.debug(`Adding mcp (s-http) server ${mcpServerName}: ${url}`);
|
|
318
|
+
const transportOptions: StreamableHTTPClientTransportOptions = {};
|
|
319
|
+
if (apiKey) {
|
|
320
|
+
transportOptions.authProvider = new TokenAuth(apiKey);
|
|
321
|
+
}
|
|
322
|
+
const urlO = new URL(url);
|
|
323
|
+
const transport = new StreamableHTTPClientTransport(urlO, transportOptions);
|
|
324
|
+
return this.addMcpServerWithTransport(mcpServerName, transport);
|
|
325
|
+
}
|
|
326
|
+
|
|
226
327
|
/**
|
|
227
328
|
* Add MCP server from an already connected McpClient.
|
|
228
329
|
*/
|
|
@@ -232,16 +333,23 @@ export class McpServerManager implements IMcpServerManager {
|
|
|
232
333
|
tools?: Tool[]
|
|
233
334
|
): Promise<void> {
|
|
234
335
|
try {
|
|
235
|
-
// TODO
|
|
336
|
+
// TODO: require the tools to be passed in.
|
|
236
337
|
|
|
338
|
+
const resourcesP = client.listResources().catch((err: unknown) => {
|
|
339
|
+
logger.warn(
|
|
340
|
+
`resources ${mcpServerName}: ${JSON.stringify(err)} ${String(err)}`
|
|
341
|
+
);
|
|
342
|
+
return { resources: [] };
|
|
343
|
+
});
|
|
237
344
|
if (!tools) {
|
|
238
345
|
const mcpTools = await client.listTools();
|
|
239
346
|
tools = mcpTools.tools;
|
|
240
347
|
}
|
|
241
348
|
|
|
349
|
+
const resources: Resource[] = (await resourcesP).resources;
|
|
242
350
|
this.mcpServers.set(
|
|
243
351
|
mcpServerName,
|
|
244
|
-
new McpServerInfoInternal(client, tools)
|
|
352
|
+
new McpServerInfoInternal(mcpServerName, client, tools, resources)
|
|
245
353
|
);
|
|
246
354
|
} catch (e) {
|
|
247
355
|
await client.close();
|
|
@@ -289,7 +397,7 @@ export class McpServerManager implements IMcpServerManager {
|
|
|
289
397
|
this.enabledToolsDirty = true;
|
|
290
398
|
}
|
|
291
399
|
|
|
292
|
-
public getOpenAITools():
|
|
400
|
+
public getOpenAITools(): ToolDescriptor[] {
|
|
293
401
|
if (this.enabledToolsDirty) {
|
|
294
402
|
this.enabledOpenAITools = computeOpenAIToolList(this.mcpServers);
|
|
295
403
|
this.enabledToolsDirty = false;
|
|
@@ -325,7 +433,7 @@ export class McpServerManager implements IMcpServerManager {
|
|
|
325
433
|
* Note the `qualifiedToolName` is the full `{mcpServerName}/{toolName}` as
|
|
326
434
|
* in the openai spec.
|
|
327
435
|
*/
|
|
328
|
-
public async invoke(toolCall: VerifiedMcpToolCall): Promise<
|
|
436
|
+
public async invoke(toolCall: VerifiedMcpToolCall): Promise<ToolCallResult> {
|
|
329
437
|
const server = this.getMcpServerInternal(toolCall.mcpServerName);
|
|
330
438
|
const cb = server.getCallback(toolCall.toolName);
|
|
331
439
|
if (!cb) {
|
|
@@ -335,6 +443,14 @@ export class McpServerManager implements IMcpServerManager {
|
|
|
335
443
|
return cb(JSON.stringify(toolCall.args));
|
|
336
444
|
}
|
|
337
445
|
|
|
446
|
+
public async getResource(
|
|
447
|
+
serverName: string,
|
|
448
|
+
uri: string
|
|
449
|
+
): Promise<ResourceContent[]> {
|
|
450
|
+
const server = this.getMcpServerInternal(serverName);
|
|
451
|
+
return server.readResource(uri);
|
|
452
|
+
}
|
|
453
|
+
|
|
338
454
|
/**
|
|
339
455
|
* "Settings" refers to the set of added servers and enabled tools.
|
|
340
456
|
*/
|
|
@@ -390,8 +506,8 @@ export function splitQualifiedName(
|
|
|
390
506
|
|
|
391
507
|
export function computeOpenAIToolList(
|
|
392
508
|
mcpServers: Map<string, McpServerInfoInternal>
|
|
393
|
-
):
|
|
394
|
-
const openaiTools:
|
|
509
|
+
): ToolDescriptor[] {
|
|
510
|
+
const openaiTools: ToolDescriptor[] = [];
|
|
395
511
|
|
|
396
512
|
for (const [mcpServerName, mcpServer] of mcpServers) {
|
|
397
513
|
const tools = mcpServer.getTools();
|
|
@@ -413,7 +529,7 @@ export function computeOpenAIToolList(
|
|
|
413
529
|
export function mcpToolToOpenAITool(
|
|
414
530
|
tool: Tool,
|
|
415
531
|
qualifiedName?: string
|
|
416
|
-
):
|
|
532
|
+
): ToolDescriptor {
|
|
417
533
|
return {
|
|
418
534
|
type: "function",
|
|
419
535
|
function: {
|