@iqai/adk 0.2.1 → 0.2.2
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/CHANGELOG.md +6 -0
- package/dist/index.d.mts +114 -113
- package/dist/index.d.ts +114 -113
- package/dist/index.js +1417 -1416
- package/dist/index.mjs +1373 -1372
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3879,7 +3879,6 @@ __export(tools_exports, {
|
|
|
3879
3879
|
McpAbi: () => McpAbi,
|
|
3880
3880
|
McpAtp: () => McpAtp,
|
|
3881
3881
|
McpBamm: () => McpBamm,
|
|
3882
|
-
McpClientService: () => McpClientService,
|
|
3883
3882
|
McpCoinGecko: () => McpCoinGecko,
|
|
3884
3883
|
McpDiscord: () => McpDiscord,
|
|
3885
3884
|
McpError: () => McpError,
|
|
@@ -3901,6 +3900,7 @@ __export(tools_exports, {
|
|
|
3901
3900
|
UserInteractionTool: () => UserInteractionTool,
|
|
3902
3901
|
adkToMcpToolType: () => adkToMcpToolType,
|
|
3903
3902
|
buildFunctionDeclaration: () => buildFunctionDeclaration,
|
|
3903
|
+
convertMcpToolToBaseTool: () => convertMcpToolToBaseTool,
|
|
3904
3904
|
createFunctionTool: () => createFunctionTool,
|
|
3905
3905
|
createSamplingHandler: () => createSamplingHandler,
|
|
3906
3906
|
createTool: () => createTool,
|
|
@@ -3909,637 +3909,111 @@ __export(tools_exports, {
|
|
|
3909
3909
|
mcpSchemaToParameters: () => mcpSchemaToParameters,
|
|
3910
3910
|
normalizeJsonSchema: () => normalizeJsonSchema
|
|
3911
3911
|
});
|
|
3912
|
+
init_base_tool();
|
|
3912
3913
|
|
|
3913
|
-
// src/tools/
|
|
3914
|
-
|
|
3915
|
-
import
|
|
3916
|
-
import {
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
McpErrorType2["TIMEOUT_ERROR"] = "timeout_error";
|
|
3933
|
-
McpErrorType2["INVALID_SCHEMA_ERROR"] = "invalid_schema_error";
|
|
3934
|
-
McpErrorType2["SAMPLING_ERROR"] = "SAMPLING_ERROR";
|
|
3935
|
-
McpErrorType2["INVALID_REQUEST_ERROR"] = "INVALID_REQUEST_ERROR";
|
|
3936
|
-
return McpErrorType2;
|
|
3937
|
-
})(McpErrorType || {});
|
|
3938
|
-
var McpError = class extends Error {
|
|
3939
|
-
type;
|
|
3940
|
-
originalError;
|
|
3941
|
-
constructor(message, type, originalError) {
|
|
3942
|
-
super(message);
|
|
3943
|
-
this.name = "McpError";
|
|
3944
|
-
this.type = type;
|
|
3945
|
-
this.originalError = originalError;
|
|
3946
|
-
}
|
|
3947
|
-
};
|
|
3948
|
-
|
|
3949
|
-
// src/tools/mcp/sampling-handler.ts
|
|
3950
|
-
var McpSamplingHandler = class {
|
|
3951
|
-
logger = new Logger({ name: "McpSamplingHandler" });
|
|
3952
|
-
samplingHandler;
|
|
3953
|
-
constructor(samplingHandler) {
|
|
3954
|
-
this.samplingHandler = samplingHandler;
|
|
3914
|
+
// src/tools/base/create-tool.ts
|
|
3915
|
+
init_base_tool();
|
|
3916
|
+
import * as z from "zod";
|
|
3917
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
3918
|
+
var CreatedTool = class extends BaseTool {
|
|
3919
|
+
func;
|
|
3920
|
+
schema;
|
|
3921
|
+
functionDeclaration;
|
|
3922
|
+
constructor(config) {
|
|
3923
|
+
super({
|
|
3924
|
+
name: config.name,
|
|
3925
|
+
description: config.description,
|
|
3926
|
+
isLongRunning: config.isLongRunning ?? false,
|
|
3927
|
+
shouldRetryOnFailure: config.shouldRetryOnFailure ?? false,
|
|
3928
|
+
maxRetryAttempts: config.maxRetryAttempts ?? 3
|
|
3929
|
+
});
|
|
3930
|
+
this.func = config.fn;
|
|
3931
|
+
this.schema = config.schema ?? z.object({});
|
|
3932
|
+
this.functionDeclaration = this.buildDeclaration();
|
|
3955
3933
|
}
|
|
3956
3934
|
/**
|
|
3957
|
-
*
|
|
3935
|
+
* Executes the tool function with validation
|
|
3958
3936
|
*/
|
|
3959
|
-
async
|
|
3937
|
+
async runAsync(args, context4) {
|
|
3960
3938
|
try {
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
);
|
|
3965
|
-
throw new McpError(
|
|
3966
|
-
`Invalid method: ${request.method}. This handler only processes sampling/createMessage requests.`,
|
|
3967
|
-
"INVALID_REQUEST_ERROR" /* INVALID_REQUEST_ERROR */
|
|
3968
|
-
);
|
|
3969
|
-
}
|
|
3970
|
-
const validationResult = CreateMessageRequestSchema.safeParse(request);
|
|
3971
|
-
if (!validationResult.success) {
|
|
3972
|
-
this.logger.error(
|
|
3973
|
-
"Invalid MCP sampling request:",
|
|
3974
|
-
validationResult.error
|
|
3975
|
-
);
|
|
3976
|
-
throw new McpError(
|
|
3977
|
-
`Invalid sampling request: ${validationResult.error.message}`,
|
|
3978
|
-
"INVALID_REQUEST_ERROR" /* INVALID_REQUEST_ERROR */
|
|
3979
|
-
);
|
|
3980
|
-
}
|
|
3981
|
-
const mcpParams = request.params;
|
|
3982
|
-
if (!mcpParams.messages || !Array.isArray(mcpParams.messages)) {
|
|
3983
|
-
throw new McpError(
|
|
3984
|
-
"Invalid sampling request: messages array is required",
|
|
3985
|
-
"INVALID_REQUEST_ERROR" /* INVALID_REQUEST_ERROR */
|
|
3986
|
-
);
|
|
3987
|
-
}
|
|
3988
|
-
if (!mcpParams.maxTokens || mcpParams.maxTokens <= 0) {
|
|
3989
|
-
throw new McpError(
|
|
3990
|
-
"Invalid sampling request: maxTokens must be a positive number",
|
|
3991
|
-
"INVALID_REQUEST_ERROR" /* INVALID_REQUEST_ERROR */
|
|
3992
|
-
);
|
|
3993
|
-
}
|
|
3994
|
-
this.logger.debug("Converting MCP request to ADK format");
|
|
3995
|
-
const adkContents = this.convertMcpMessagesToADK(
|
|
3996
|
-
mcpParams.messages,
|
|
3997
|
-
mcpParams.systemPrompt
|
|
3998
|
-
);
|
|
3999
|
-
const requestModel = mcpParams.model || "gemini-2.0-flash";
|
|
4000
|
-
const adkRequest = new LlmRequest({
|
|
4001
|
-
model: requestModel,
|
|
4002
|
-
contents: adkContents,
|
|
4003
|
-
config: {
|
|
4004
|
-
temperature: mcpParams.temperature,
|
|
4005
|
-
maxOutputTokens: mcpParams.maxTokens
|
|
4006
|
-
}
|
|
4007
|
-
});
|
|
4008
|
-
this.logger.debug("Calling ADK sampling handler");
|
|
4009
|
-
const adkResponse = await this.samplingHandler(adkRequest);
|
|
4010
|
-
this.logger.debug("Converting ADK response to MCP format");
|
|
4011
|
-
const mcpResponse = this.convertADKResponseToMcp(
|
|
4012
|
-
adkResponse,
|
|
4013
|
-
requestModel
|
|
4014
|
-
);
|
|
4015
|
-
const responseValidation = CreateMessageResultSchema.safeParse(mcpResponse);
|
|
4016
|
-
if (!responseValidation.success) {
|
|
4017
|
-
this.logger.error(
|
|
4018
|
-
"Invalid MCP response generated:",
|
|
4019
|
-
responseValidation.error
|
|
4020
|
-
);
|
|
4021
|
-
throw new McpError(
|
|
4022
|
-
`Invalid response generated: ${responseValidation.error.message}`,
|
|
4023
|
-
"SAMPLING_ERROR" /* SAMPLING_ERROR */
|
|
4024
|
-
);
|
|
4025
|
-
}
|
|
4026
|
-
return mcpResponse;
|
|
3939
|
+
const validatedArgs = this.schema.parse(args);
|
|
3940
|
+
const result = await Promise.resolve(this.func(validatedArgs, context4));
|
|
3941
|
+
return result ?? {};
|
|
4027
3942
|
} catch (error) {
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
3943
|
+
if (error instanceof z.ZodError) {
|
|
3944
|
+
return {
|
|
3945
|
+
error: `Invalid arguments for ${this.name}: ${error.message}`
|
|
3946
|
+
};
|
|
4031
3947
|
}
|
|
4032
|
-
|
|
4033
|
-
`
|
|
4034
|
-
|
|
4035
|
-
error instanceof Error ? error : void 0
|
|
4036
|
-
);
|
|
3948
|
+
return {
|
|
3949
|
+
error: `Error executing ${this.name}: ${error instanceof Error ? error.message : String(error)}`
|
|
3950
|
+
};
|
|
4037
3951
|
}
|
|
4038
3952
|
}
|
|
4039
3953
|
/**
|
|
4040
|
-
*
|
|
3954
|
+
* Returns the function declaration for this tool
|
|
4041
3955
|
*/
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
if (systemPrompt) {
|
|
4045
|
-
contents.push({
|
|
4046
|
-
role: "user",
|
|
4047
|
-
// System messages are typically sent as user role in content
|
|
4048
|
-
parts: [{ text: systemPrompt }]
|
|
4049
|
-
});
|
|
4050
|
-
}
|
|
4051
|
-
const transformedMessages = mcpMessages.map(
|
|
4052
|
-
(mcpMessage) => this.convertSingleMcpMessageToADK(mcpMessage)
|
|
4053
|
-
);
|
|
4054
|
-
contents.push(...transformedMessages);
|
|
4055
|
-
return contents;
|
|
3956
|
+
getDeclaration() {
|
|
3957
|
+
return this.functionDeclaration;
|
|
4056
3958
|
}
|
|
4057
3959
|
/**
|
|
4058
|
-
*
|
|
3960
|
+
* Builds the function declaration from the Zod schema
|
|
4059
3961
|
*/
|
|
4060
|
-
|
|
4061
|
-
const
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
3962
|
+
buildDeclaration() {
|
|
3963
|
+
const rawParameters = zodToJsonSchema(this.schema, {
|
|
3964
|
+
target: "jsonSchema7",
|
|
3965
|
+
$refStrategy: "none"
|
|
3966
|
+
});
|
|
3967
|
+
const { $schema, ...parameters } = rawParameters;
|
|
3968
|
+
return {
|
|
3969
|
+
name: this.name,
|
|
3970
|
+
description: this.description,
|
|
3971
|
+
parameters
|
|
4066
3972
|
};
|
|
4067
|
-
this.logger.debug(
|
|
4068
|
-
`Converted MCP message - role: ${mcpMessage.role} -> ${adkRole}, content type: ${mcpMessage.content.type}`
|
|
4069
|
-
);
|
|
4070
|
-
return adkContent;
|
|
4071
3973
|
}
|
|
3974
|
+
};
|
|
3975
|
+
function createTool(config) {
|
|
3976
|
+
return new CreatedTool(config);
|
|
3977
|
+
}
|
|
3978
|
+
|
|
3979
|
+
// src/tools/common/agent-tool.ts
|
|
3980
|
+
init_logger();
|
|
3981
|
+
import { Type } from "@google/genai";
|
|
3982
|
+
import { v4 as uuidv42 } from "uuid";
|
|
3983
|
+
|
|
3984
|
+
// src/agents/invocation-context.ts
|
|
3985
|
+
var LlmCallsLimitExceededError = class extends Error {
|
|
3986
|
+
constructor(message) {
|
|
3987
|
+
super(message);
|
|
3988
|
+
this.name = "LlmCallsLimitExceededError";
|
|
3989
|
+
}
|
|
3990
|
+
};
|
|
3991
|
+
var InvocationCostManager = class {
|
|
4072
3992
|
/**
|
|
4073
|
-
*
|
|
3993
|
+
* A counter that keeps track of number of llm calls made.
|
|
4074
3994
|
*/
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
if (mcpContent.data && typeof mcpContent.data === "string") {
|
|
4086
|
-
const mimeType = mcpContent.mimeType || "image/jpeg";
|
|
4087
|
-
parts.push({
|
|
4088
|
-
inlineData: {
|
|
4089
|
-
data: mcpContent.data,
|
|
4090
|
-
mimeType
|
|
4091
|
-
}
|
|
4092
|
-
});
|
|
4093
|
-
}
|
|
4094
|
-
return parts.length > 0 ? parts : [{ text: "" }];
|
|
3995
|
+
_numberOfLlmCalls = 0;
|
|
3996
|
+
/**
|
|
3997
|
+
* Increments _numberOfLlmCalls and enforces the limit.
|
|
3998
|
+
*/
|
|
3999
|
+
incrementAndEnforceLlmCallsLimit(runConfig) {
|
|
4000
|
+
this._numberOfLlmCalls += 1;
|
|
4001
|
+
if (runConfig && runConfig.maxLlmCalls > 0 && this._numberOfLlmCalls > runConfig.maxLlmCalls) {
|
|
4002
|
+
throw new LlmCallsLimitExceededError(
|
|
4003
|
+
`Max number of llm calls limit of \`${runConfig.maxLlmCalls}\` exceeded`
|
|
4004
|
+
);
|
|
4095
4005
|
}
|
|
4096
|
-
this.logger.warn(`Unknown MCP content type: ${mcpContent.type}`);
|
|
4097
|
-
const fallbackText = typeof mcpContent.data === "string" ? mcpContent.data : "";
|
|
4098
|
-
return [{ text: fallbackText }];
|
|
4099
4006
|
}
|
|
4007
|
+
};
|
|
4008
|
+
function newInvocationContextId() {
|
|
4009
|
+
return `e-${crypto.randomUUID()}`;
|
|
4010
|
+
}
|
|
4011
|
+
var InvocationContext = class _InvocationContext {
|
|
4012
|
+
artifactService;
|
|
4013
|
+
sessionService;
|
|
4014
|
+
memoryService;
|
|
4100
4015
|
/**
|
|
4101
|
-
*
|
|
4102
|
-
*/
|
|
4103
|
-
convertADKResponseToMcp(adkResponse, model) {
|
|
4104
|
-
let responseText = "";
|
|
4105
|
-
if (typeof adkResponse === "string") {
|
|
4106
|
-
responseText = adkResponse;
|
|
4107
|
-
} else {
|
|
4108
|
-
if (adkResponse.content) {
|
|
4109
|
-
if (typeof adkResponse.content === "string") {
|
|
4110
|
-
responseText = adkResponse.content;
|
|
4111
|
-
} else if (adkResponse.content.parts) {
|
|
4112
|
-
responseText = adkResponse.content.parts.map((part) => {
|
|
4113
|
-
return typeof part.text === "string" ? part.text : "";
|
|
4114
|
-
}).join("");
|
|
4115
|
-
}
|
|
4116
|
-
}
|
|
4117
|
-
}
|
|
4118
|
-
const mcpResponse = {
|
|
4119
|
-
model,
|
|
4120
|
-
// Use the model from the request
|
|
4121
|
-
role: "assistant",
|
|
4122
|
-
// ADK responses are always from assistant
|
|
4123
|
-
content: {
|
|
4124
|
-
type: "text",
|
|
4125
|
-
text: responseText
|
|
4126
|
-
}
|
|
4127
|
-
};
|
|
4128
|
-
this.logger.debug(`Received content: ${responseText}`);
|
|
4129
|
-
return mcpResponse;
|
|
4130
|
-
}
|
|
4131
|
-
/**
|
|
4132
|
-
* Update the ADK handler
|
|
4133
|
-
*/
|
|
4134
|
-
updateHandler(handler) {
|
|
4135
|
-
this.samplingHandler = handler;
|
|
4136
|
-
this.logger.debug("ADK sampling handler updated");
|
|
4137
|
-
}
|
|
4138
|
-
};
|
|
4139
|
-
function createSamplingHandler(handler) {
|
|
4140
|
-
return handler;
|
|
4141
|
-
}
|
|
4142
|
-
|
|
4143
|
-
// src/tools/mcp/utils.ts
|
|
4144
|
-
function withRetry(fn, instance, reinitMethod, maxRetries = 1) {
|
|
4145
|
-
return async (...args) => {
|
|
4146
|
-
let attempt = 0;
|
|
4147
|
-
while (attempt <= maxRetries) {
|
|
4148
|
-
try {
|
|
4149
|
-
return await fn.apply(instance, args);
|
|
4150
|
-
} catch (error) {
|
|
4151
|
-
const isClosedResourceError = error instanceof Error && (error.message.includes("closed") || error.message.includes("ECONNRESET") || error.message.includes("socket hang up"));
|
|
4152
|
-
if (!isClosedResourceError || attempt >= maxRetries) {
|
|
4153
|
-
throw error;
|
|
4154
|
-
}
|
|
4155
|
-
console.warn(
|
|
4156
|
-
`Resource closed, reinitializing (attempt ${attempt + 1}/${maxRetries + 1})...`
|
|
4157
|
-
);
|
|
4158
|
-
try {
|
|
4159
|
-
await reinitMethod(instance);
|
|
4160
|
-
} catch (reinitError) {
|
|
4161
|
-
console.error("Error reinitializing resources:", reinitError);
|
|
4162
|
-
throw new Error(`Failed to reinitialize resources: ${reinitError}`);
|
|
4163
|
-
}
|
|
4164
|
-
attempt++;
|
|
4165
|
-
}
|
|
4166
|
-
}
|
|
4167
|
-
throw new Error("Unexpected end of retry loop");
|
|
4168
|
-
};
|
|
4169
|
-
}
|
|
4170
|
-
|
|
4171
|
-
// src/tools/mcp/client.ts
|
|
4172
|
-
var McpClientService = class {
|
|
4173
|
-
config;
|
|
4174
|
-
client = null;
|
|
4175
|
-
transport = null;
|
|
4176
|
-
isClosing = false;
|
|
4177
|
-
mcpSamplingHandler = null;
|
|
4178
|
-
logger = new Logger({ name: "McpClientService" });
|
|
4179
|
-
constructor(config) {
|
|
4180
|
-
this.config = config;
|
|
4181
|
-
if (config.samplingHandler) {
|
|
4182
|
-
this.mcpSamplingHandler = new McpSamplingHandler(config.samplingHandler);
|
|
4183
|
-
}
|
|
4184
|
-
}
|
|
4185
|
-
/**
|
|
4186
|
-
* Initializes and returns an MCP client based on configuration.
|
|
4187
|
-
* Will create a new client if one doesn't exist yet.
|
|
4188
|
-
*/
|
|
4189
|
-
async initialize() {
|
|
4190
|
-
if (this.isClosing) {
|
|
4191
|
-
throw new McpError(
|
|
4192
|
-
"Cannot initialize a client that is being closed",
|
|
4193
|
-
"resource_closed_error" /* RESOURCE_CLOSED_ERROR */
|
|
4194
|
-
);
|
|
4195
|
-
}
|
|
4196
|
-
if (this.client) {
|
|
4197
|
-
return this.client;
|
|
4198
|
-
}
|
|
4199
|
-
try {
|
|
4200
|
-
if (!this.transport) {
|
|
4201
|
-
this.transport = await this.createTransport();
|
|
4202
|
-
}
|
|
4203
|
-
const client = new Client(
|
|
4204
|
-
{
|
|
4205
|
-
name: this.config.name,
|
|
4206
|
-
version: "0.0.1"
|
|
4207
|
-
},
|
|
4208
|
-
{
|
|
4209
|
-
capabilities: {
|
|
4210
|
-
prompts: {},
|
|
4211
|
-
resources: {},
|
|
4212
|
-
tools: {},
|
|
4213
|
-
sampling: {}
|
|
4214
|
-
// Enable sampling capability
|
|
4215
|
-
}
|
|
4216
|
-
}
|
|
4217
|
-
);
|
|
4218
|
-
const connectPromise = client.connect(this.transport);
|
|
4219
|
-
if (this.config.timeout) {
|
|
4220
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
4221
|
-
setTimeout(() => {
|
|
4222
|
-
reject(
|
|
4223
|
-
new McpError(
|
|
4224
|
-
`MCP client connection timed out after ${this.config.timeout}ms`,
|
|
4225
|
-
"timeout_error" /* TIMEOUT_ERROR */
|
|
4226
|
-
)
|
|
4227
|
-
);
|
|
4228
|
-
}, this.config.timeout);
|
|
4229
|
-
});
|
|
4230
|
-
await Promise.race([connectPromise, timeoutPromise]);
|
|
4231
|
-
} else {
|
|
4232
|
-
await connectPromise;
|
|
4233
|
-
}
|
|
4234
|
-
await this.setupSamplingHandler(client);
|
|
4235
|
-
this.logger.debug("\u2705 MCP client connected successfully");
|
|
4236
|
-
this.client = client;
|
|
4237
|
-
return client;
|
|
4238
|
-
} catch (error) {
|
|
4239
|
-
await this.cleanupResources();
|
|
4240
|
-
if (!(error instanceof McpError)) {
|
|
4241
|
-
this.logger.error("Failed to initialize MCP client:", error);
|
|
4242
|
-
throw new McpError(
|
|
4243
|
-
`Failed to initialize MCP client: ${error instanceof Error ? error.message : String(error)}`,
|
|
4244
|
-
"connection_error" /* CONNECTION_ERROR */,
|
|
4245
|
-
error instanceof Error ? error : void 0
|
|
4246
|
-
);
|
|
4247
|
-
}
|
|
4248
|
-
throw error;
|
|
4249
|
-
}
|
|
4250
|
-
}
|
|
4251
|
-
/**
|
|
4252
|
-
* Creates a transport based on the configuration.
|
|
4253
|
-
*/
|
|
4254
|
-
async createTransport() {
|
|
4255
|
-
try {
|
|
4256
|
-
if (this.config.transport.mode === "sse") {
|
|
4257
|
-
this.logger.debug(
|
|
4258
|
-
"\u{1F680} Initializing MCP client in SSE mode",
|
|
4259
|
-
this.config.transport.serverUrl
|
|
4260
|
-
);
|
|
4261
|
-
const headers = {
|
|
4262
|
-
...this.config.transport.headers || {},
|
|
4263
|
-
...this.config.headers || {}
|
|
4264
|
-
};
|
|
4265
|
-
return new SSEClientTransport(
|
|
4266
|
-
new URL(this.config.transport.serverUrl),
|
|
4267
|
-
{
|
|
4268
|
-
requestInit: {
|
|
4269
|
-
headers,
|
|
4270
|
-
...this.config.timeout ? { timeout: this.config.timeout } : {}
|
|
4271
|
-
}
|
|
4272
|
-
}
|
|
4273
|
-
);
|
|
4274
|
-
}
|
|
4275
|
-
this.logger.debug(
|
|
4276
|
-
"\u{1F680} Initializing MCP client in STDIO mode",
|
|
4277
|
-
this.config.transport.command
|
|
4278
|
-
);
|
|
4279
|
-
return new StdioClientTransport({
|
|
4280
|
-
command: this.config.transport.command,
|
|
4281
|
-
args: this.config.transport.args,
|
|
4282
|
-
env: this.config.transport.env
|
|
4283
|
-
});
|
|
4284
|
-
} catch (error) {
|
|
4285
|
-
throw new McpError(
|
|
4286
|
-
`Failed to create transport: ${error instanceof Error ? error.message : String(error)}`,
|
|
4287
|
-
"connection_error" /* CONNECTION_ERROR */,
|
|
4288
|
-
error instanceof Error ? error : void 0
|
|
4289
|
-
);
|
|
4290
|
-
}
|
|
4291
|
-
}
|
|
4292
|
-
/**
|
|
4293
|
-
* Re-initializes the MCP client when a session is closed.
|
|
4294
|
-
* Used by the retry mechanism.
|
|
4295
|
-
*/
|
|
4296
|
-
async reinitialize() {
|
|
4297
|
-
this.logger.debug("\u{1F504} Reinitializing MCP client after closed connection");
|
|
4298
|
-
await this.cleanupResources();
|
|
4299
|
-
this.client = null;
|
|
4300
|
-
this.transport = null;
|
|
4301
|
-
await this.initialize();
|
|
4302
|
-
}
|
|
4303
|
-
/**
|
|
4304
|
-
* Cleans up resources associated with this client service.
|
|
4305
|
-
* Similar to Python's AsyncExitStack.aclose() functionality.
|
|
4306
|
-
*/
|
|
4307
|
-
async cleanupResources() {
|
|
4308
|
-
try {
|
|
4309
|
-
this.isClosing = true;
|
|
4310
|
-
if (this.client) {
|
|
4311
|
-
try {
|
|
4312
|
-
if (typeof this.client.close === "function") {
|
|
4313
|
-
await this.client.close();
|
|
4314
|
-
}
|
|
4315
|
-
} catch (err) {
|
|
4316
|
-
}
|
|
4317
|
-
}
|
|
4318
|
-
if (this.transport && typeof this.transport.close === "function") {
|
|
4319
|
-
await this.transport.close();
|
|
4320
|
-
}
|
|
4321
|
-
this.logger.debug("\u{1F9F9} Cleaned up MCP client resources");
|
|
4322
|
-
} catch (error) {
|
|
4323
|
-
this.logger.error("Error cleaning up MCP resources:", error);
|
|
4324
|
-
} finally {
|
|
4325
|
-
this.client = null;
|
|
4326
|
-
this.transport = null;
|
|
4327
|
-
this.isClosing = false;
|
|
4328
|
-
}
|
|
4329
|
-
}
|
|
4330
|
-
/**
|
|
4331
|
-
* Call an MCP tool with retry capability if the session is closed.
|
|
4332
|
-
*/
|
|
4333
|
-
async callTool(name, args) {
|
|
4334
|
-
try {
|
|
4335
|
-
const wrappedCall = withRetry(
|
|
4336
|
-
async function() {
|
|
4337
|
-
const client = await this.initialize();
|
|
4338
|
-
return client.callTool({
|
|
4339
|
-
name,
|
|
4340
|
-
arguments: args
|
|
4341
|
-
});
|
|
4342
|
-
},
|
|
4343
|
-
this,
|
|
4344
|
-
async (instance) => await instance.reinitialize(),
|
|
4345
|
-
this.config.retryOptions?.maxRetries || 2
|
|
4346
|
-
);
|
|
4347
|
-
return await wrappedCall();
|
|
4348
|
-
} catch (error) {
|
|
4349
|
-
if (!(error instanceof McpError)) {
|
|
4350
|
-
throw new McpError(
|
|
4351
|
-
`Error calling tool "${name}": ${error instanceof Error ? error.message : String(error)}`,
|
|
4352
|
-
"tool_execution_error" /* TOOL_EXECUTION_ERROR */,
|
|
4353
|
-
error instanceof Error ? error : void 0
|
|
4354
|
-
);
|
|
4355
|
-
}
|
|
4356
|
-
throw error;
|
|
4357
|
-
}
|
|
4358
|
-
}
|
|
4359
|
-
/**
|
|
4360
|
-
* Closes and cleans up all resources.
|
|
4361
|
-
* Should be called when the service is no longer needed.
|
|
4362
|
-
* Similar to Python's close() method.
|
|
4363
|
-
*/
|
|
4364
|
-
async close() {
|
|
4365
|
-
this.logger.debug("\u{1F51A} Closing MCP client service");
|
|
4366
|
-
await this.cleanupResources();
|
|
4367
|
-
}
|
|
4368
|
-
/**
|
|
4369
|
-
* Checks if the client is currently connected
|
|
4370
|
-
*/
|
|
4371
|
-
isConnected() {
|
|
4372
|
-
return !!this.client && !this.isClosing;
|
|
4373
|
-
}
|
|
4374
|
-
async setupSamplingHandler(client) {
|
|
4375
|
-
if (!this.mcpSamplingHandler) {
|
|
4376
|
-
this.logger.debug(
|
|
4377
|
-
"\u26A0\uFE0F No sampling handler provided - sampling requests will be rejected"
|
|
4378
|
-
);
|
|
4379
|
-
return;
|
|
4380
|
-
}
|
|
4381
|
-
try {
|
|
4382
|
-
client.setRequestHandler(
|
|
4383
|
-
CreateMessageRequestSchema2,
|
|
4384
|
-
async (request) => {
|
|
4385
|
-
try {
|
|
4386
|
-
this.logger.debug("Received sampling request:", request);
|
|
4387
|
-
const response = await this.mcpSamplingHandler.handleSamplingRequest(request);
|
|
4388
|
-
this.logger.debug("\u2705 Sampling request completed successfully");
|
|
4389
|
-
return response;
|
|
4390
|
-
} catch (error) {
|
|
4391
|
-
this.logger.error("\u274C Error handling sampling request:", error);
|
|
4392
|
-
if (error instanceof McpError) {
|
|
4393
|
-
throw error;
|
|
4394
|
-
}
|
|
4395
|
-
throw new McpError(
|
|
4396
|
-
`Sampling request failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
4397
|
-
"SAMPLING_ERROR" /* SAMPLING_ERROR */,
|
|
4398
|
-
error instanceof Error ? error : void 0
|
|
4399
|
-
);
|
|
4400
|
-
}
|
|
4401
|
-
}
|
|
4402
|
-
);
|
|
4403
|
-
this.logger.debug("\u{1F3AF} Sampling handler registered successfully");
|
|
4404
|
-
} catch (error) {
|
|
4405
|
-
this.logger.error("Failed to setup sampling handler:", error);
|
|
4406
|
-
this.logger.debug(
|
|
4407
|
-
"\u26A0\uFE0F Sampling handler registration failed, continuing without sampling support"
|
|
4408
|
-
);
|
|
4409
|
-
}
|
|
4410
|
-
}
|
|
4411
|
-
/**
|
|
4412
|
-
* Set a new ADK sampling handler
|
|
4413
|
-
*/
|
|
4414
|
-
setSamplingHandler(handler) {
|
|
4415
|
-
this.mcpSamplingHandler = new McpSamplingHandler(handler);
|
|
4416
|
-
if (this.client) {
|
|
4417
|
-
this.setupSamplingHandler(this.client).catch((error) => {
|
|
4418
|
-
this.logger.error("Failed to update ADK sampling handler:", error);
|
|
4419
|
-
});
|
|
4420
|
-
}
|
|
4421
|
-
}
|
|
4422
|
-
/**
|
|
4423
|
-
* Remove the sampling handler
|
|
4424
|
-
*/
|
|
4425
|
-
removeSamplingHandler() {
|
|
4426
|
-
this.mcpSamplingHandler = null;
|
|
4427
|
-
if (this.client) {
|
|
4428
|
-
try {
|
|
4429
|
-
this.client.removeRequestHandler?.("sampling/createMessage");
|
|
4430
|
-
} catch (error) {
|
|
4431
|
-
this.logger.error("Failed to remove sampling handler:", error);
|
|
4432
|
-
}
|
|
4433
|
-
}
|
|
4434
|
-
}
|
|
4435
|
-
};
|
|
4436
|
-
|
|
4437
|
-
// src/tools/index.ts
|
|
4438
|
-
init_base_tool();
|
|
4439
|
-
|
|
4440
|
-
// src/tools/base/create-tool.ts
|
|
4441
|
-
init_base_tool();
|
|
4442
|
-
import * as z from "zod";
|
|
4443
|
-
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
4444
|
-
var CreatedTool = class extends BaseTool {
|
|
4445
|
-
func;
|
|
4446
|
-
schema;
|
|
4447
|
-
functionDeclaration;
|
|
4448
|
-
constructor(config) {
|
|
4449
|
-
super({
|
|
4450
|
-
name: config.name,
|
|
4451
|
-
description: config.description,
|
|
4452
|
-
isLongRunning: config.isLongRunning ?? false,
|
|
4453
|
-
shouldRetryOnFailure: config.shouldRetryOnFailure ?? false,
|
|
4454
|
-
maxRetryAttempts: config.maxRetryAttempts ?? 3
|
|
4455
|
-
});
|
|
4456
|
-
this.func = config.fn;
|
|
4457
|
-
this.schema = config.schema ?? z.object({});
|
|
4458
|
-
this.functionDeclaration = this.buildDeclaration();
|
|
4459
|
-
}
|
|
4460
|
-
/**
|
|
4461
|
-
* Executes the tool function with validation
|
|
4462
|
-
*/
|
|
4463
|
-
async runAsync(args, context4) {
|
|
4464
|
-
try {
|
|
4465
|
-
const validatedArgs = this.schema.parse(args);
|
|
4466
|
-
const result = await Promise.resolve(this.func(validatedArgs, context4));
|
|
4467
|
-
return result ?? {};
|
|
4468
|
-
} catch (error) {
|
|
4469
|
-
if (error instanceof z.ZodError) {
|
|
4470
|
-
return {
|
|
4471
|
-
error: `Invalid arguments for ${this.name}: ${error.message}`
|
|
4472
|
-
};
|
|
4473
|
-
}
|
|
4474
|
-
return {
|
|
4475
|
-
error: `Error executing ${this.name}: ${error instanceof Error ? error.message : String(error)}`
|
|
4476
|
-
};
|
|
4477
|
-
}
|
|
4478
|
-
}
|
|
4479
|
-
/**
|
|
4480
|
-
* Returns the function declaration for this tool
|
|
4481
|
-
*/
|
|
4482
|
-
getDeclaration() {
|
|
4483
|
-
return this.functionDeclaration;
|
|
4484
|
-
}
|
|
4485
|
-
/**
|
|
4486
|
-
* Builds the function declaration from the Zod schema
|
|
4487
|
-
*/
|
|
4488
|
-
buildDeclaration() {
|
|
4489
|
-
const rawParameters = zodToJsonSchema(this.schema, {
|
|
4490
|
-
target: "jsonSchema7",
|
|
4491
|
-
$refStrategy: "none"
|
|
4492
|
-
});
|
|
4493
|
-
const { $schema, ...parameters } = rawParameters;
|
|
4494
|
-
return {
|
|
4495
|
-
name: this.name,
|
|
4496
|
-
description: this.description,
|
|
4497
|
-
parameters
|
|
4498
|
-
};
|
|
4499
|
-
}
|
|
4500
|
-
};
|
|
4501
|
-
function createTool(config) {
|
|
4502
|
-
return new CreatedTool(config);
|
|
4503
|
-
}
|
|
4504
|
-
|
|
4505
|
-
// src/tools/common/agent-tool.ts
|
|
4506
|
-
init_logger();
|
|
4507
|
-
import { Type } from "@google/genai";
|
|
4508
|
-
import { v4 as uuidv42 } from "uuid";
|
|
4509
|
-
|
|
4510
|
-
// src/agents/invocation-context.ts
|
|
4511
|
-
var LlmCallsLimitExceededError = class extends Error {
|
|
4512
|
-
constructor(message) {
|
|
4513
|
-
super(message);
|
|
4514
|
-
this.name = "LlmCallsLimitExceededError";
|
|
4515
|
-
}
|
|
4516
|
-
};
|
|
4517
|
-
var InvocationCostManager = class {
|
|
4518
|
-
/**
|
|
4519
|
-
* A counter that keeps track of number of llm calls made.
|
|
4520
|
-
*/
|
|
4521
|
-
_numberOfLlmCalls = 0;
|
|
4522
|
-
/**
|
|
4523
|
-
* Increments _numberOfLlmCalls and enforces the limit.
|
|
4524
|
-
*/
|
|
4525
|
-
incrementAndEnforceLlmCallsLimit(runConfig) {
|
|
4526
|
-
this._numberOfLlmCalls += 1;
|
|
4527
|
-
if (runConfig && runConfig.maxLlmCalls > 0 && this._numberOfLlmCalls > runConfig.maxLlmCalls) {
|
|
4528
|
-
throw new LlmCallsLimitExceededError(
|
|
4529
|
-
`Max number of llm calls limit of \`${runConfig.maxLlmCalls}\` exceeded`
|
|
4530
|
-
);
|
|
4531
|
-
}
|
|
4532
|
-
}
|
|
4533
|
-
};
|
|
4534
|
-
function newInvocationContextId() {
|
|
4535
|
-
return `e-${crypto.randomUUID()}`;
|
|
4536
|
-
}
|
|
4537
|
-
var InvocationContext = class _InvocationContext {
|
|
4538
|
-
artifactService;
|
|
4539
|
-
sessionService;
|
|
4540
|
-
memoryService;
|
|
4541
|
-
/**
|
|
4542
|
-
* The id of this invocation context. Readonly.
|
|
4016
|
+
* The id of this invocation context. Readonly.
|
|
4543
4017
|
*/
|
|
4544
4018
|
invocationId;
|
|
4545
4019
|
/**
|
|
@@ -4656,265 +4130,630 @@ var InvocationContext = class _InvocationContext {
|
|
|
4656
4130
|
}
|
|
4657
4131
|
};
|
|
4658
4132
|
|
|
4659
|
-
// src/tools/common/agent-tool.ts
|
|
4133
|
+
// src/tools/common/agent-tool.ts
|
|
4134
|
+
init_base_tool();
|
|
4135
|
+
function isLlmAgent(agent) {
|
|
4136
|
+
return true;
|
|
4137
|
+
}
|
|
4138
|
+
var AgentTool = class extends BaseTool {
|
|
4139
|
+
/**
|
|
4140
|
+
* The agent used by this tool
|
|
4141
|
+
*/
|
|
4142
|
+
agent;
|
|
4143
|
+
/**
|
|
4144
|
+
* The function declaration schema
|
|
4145
|
+
*/
|
|
4146
|
+
functionDeclaration;
|
|
4147
|
+
/**
|
|
4148
|
+
* The key to store the tool output in the state
|
|
4149
|
+
*/
|
|
4150
|
+
outputKey;
|
|
4151
|
+
/**
|
|
4152
|
+
* Whether to skip summarization of the agent's response
|
|
4153
|
+
*/
|
|
4154
|
+
skipSummarization;
|
|
4155
|
+
logger = new Logger({ name: "AgentTool" });
|
|
4156
|
+
/**
|
|
4157
|
+
* Create a new agent tool
|
|
4158
|
+
*/
|
|
4159
|
+
constructor(config) {
|
|
4160
|
+
super({
|
|
4161
|
+
name: config.name,
|
|
4162
|
+
description: config.description || config.agent.description,
|
|
4163
|
+
isLongRunning: config.isLongRunning || false,
|
|
4164
|
+
shouldRetryOnFailure: config.shouldRetryOnFailure || false,
|
|
4165
|
+
maxRetryAttempts: config.maxRetryAttempts || 3
|
|
4166
|
+
});
|
|
4167
|
+
this.agent = config.agent;
|
|
4168
|
+
this.functionDeclaration = config.functionDeclaration;
|
|
4169
|
+
this.outputKey = config.outputKey;
|
|
4170
|
+
this.skipSummarization = config.skipSummarization || false;
|
|
4171
|
+
}
|
|
4172
|
+
/**
|
|
4173
|
+
* Get the function declaration for the tool
|
|
4174
|
+
*/
|
|
4175
|
+
getDeclaration() {
|
|
4176
|
+
if (this.functionDeclaration) {
|
|
4177
|
+
return this.functionDeclaration;
|
|
4178
|
+
}
|
|
4179
|
+
const description = isLlmAgent(this.agent) ? typeof this.agent.instruction === "string" ? this.agent.instruction : this.description : this.description;
|
|
4180
|
+
return {
|
|
4181
|
+
name: this.name,
|
|
4182
|
+
description,
|
|
4183
|
+
parameters: {
|
|
4184
|
+
type: Type.OBJECT,
|
|
4185
|
+
properties: {
|
|
4186
|
+
input: {
|
|
4187
|
+
type: Type.STRING,
|
|
4188
|
+
description: "The input to provide to the agent"
|
|
4189
|
+
}
|
|
4190
|
+
},
|
|
4191
|
+
required: ["input"]
|
|
4192
|
+
}
|
|
4193
|
+
};
|
|
4194
|
+
}
|
|
4195
|
+
/**
|
|
4196
|
+
* Execute the tool by running the agent with the provided input
|
|
4197
|
+
*/
|
|
4198
|
+
async runAsync(params, context4) {
|
|
4199
|
+
try {
|
|
4200
|
+
const input = params.input || Object.values(params)[0];
|
|
4201
|
+
if (!isLlmAgent(this.agent)) {
|
|
4202
|
+
throw new Error(
|
|
4203
|
+
`Agent ${this.name} does not support running as a tool`
|
|
4204
|
+
);
|
|
4205
|
+
}
|
|
4206
|
+
const parentInvocation = context4._invocationContext;
|
|
4207
|
+
const childInvocationContext = new InvocationContext({
|
|
4208
|
+
invocationId: uuidv42(),
|
|
4209
|
+
agent: this.agent,
|
|
4210
|
+
session: parentInvocation.session,
|
|
4211
|
+
artifactService: parentInvocation.artifactService,
|
|
4212
|
+
sessionService: parentInvocation.sessionService,
|
|
4213
|
+
memoryService: parentInvocation.memoryService,
|
|
4214
|
+
runConfig: parentInvocation.runConfig,
|
|
4215
|
+
userContent: {
|
|
4216
|
+
role: "user",
|
|
4217
|
+
parts: [{ text: String(input) }]
|
|
4218
|
+
},
|
|
4219
|
+
branch: parentInvocation.branch ? `${parentInvocation.branch}.${this.agent.name}` : this.agent.name
|
|
4220
|
+
});
|
|
4221
|
+
let lastEvent = null;
|
|
4222
|
+
for await (const event of this.agent.runAsync(childInvocationContext)) {
|
|
4223
|
+
if (!event.partial) {
|
|
4224
|
+
await childInvocationContext.sessionService.appendEvent(
|
|
4225
|
+
childInvocationContext.session,
|
|
4226
|
+
event
|
|
4227
|
+
);
|
|
4228
|
+
}
|
|
4229
|
+
if (event.content && event.author === this.agent.name) {
|
|
4230
|
+
lastEvent = event;
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
if (!lastEvent || !lastEvent.content || !lastEvent.content.parts) {
|
|
4234
|
+
return "";
|
|
4235
|
+
}
|
|
4236
|
+
const mergedText = lastEvent.content.parts.filter((part) => part.text !== void 0 && part.text !== null).map((part) => part.text).join("\n");
|
|
4237
|
+
let toolResult;
|
|
4238
|
+
try {
|
|
4239
|
+
toolResult = JSON.parse(mergedText);
|
|
4240
|
+
} catch {
|
|
4241
|
+
toolResult = mergedText;
|
|
4242
|
+
}
|
|
4243
|
+
if (this.outputKey && context4?.state) {
|
|
4244
|
+
context4.state[this.outputKey] = toolResult;
|
|
4245
|
+
}
|
|
4246
|
+
return toolResult;
|
|
4247
|
+
} catch (error) {
|
|
4248
|
+
this.logger.error(`Error executing agent tool ${this.name}:`, error);
|
|
4249
|
+
throw new Error(
|
|
4250
|
+
`Agent tool execution failed: ${error instanceof Error ? error.message : String(error)}`
|
|
4251
|
+
);
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
};
|
|
4255
|
+
|
|
4256
|
+
// src/tools/tool-context.ts
|
|
4257
|
+
var ToolContext = class extends CallbackContext {
|
|
4258
|
+
/**
|
|
4259
|
+
* The function call id of the current tool call. This id was
|
|
4260
|
+
* returned in the function call event from LLM to identify a function call.
|
|
4261
|
+
* If LLM didn't return this id, ADK will assign one to it. This id is used
|
|
4262
|
+
* to map function call response to the original function call.
|
|
4263
|
+
*/
|
|
4264
|
+
functionCallId;
|
|
4265
|
+
/**
|
|
4266
|
+
* Constructor for ToolContext
|
|
4267
|
+
*/
|
|
4268
|
+
constructor(invocationContext, options = {}) {
|
|
4269
|
+
super(invocationContext, { eventActions: options.eventActions });
|
|
4270
|
+
this.functionCallId = options.functionCallId;
|
|
4271
|
+
}
|
|
4272
|
+
/**
|
|
4273
|
+
* Gets the event actions of the current tool call
|
|
4274
|
+
*/
|
|
4275
|
+
get actions() {
|
|
4276
|
+
return this.eventActions;
|
|
4277
|
+
}
|
|
4278
|
+
/**
|
|
4279
|
+
* Lists the filenames of the artifacts attached to the current session
|
|
4280
|
+
*/
|
|
4281
|
+
async listArtifacts() {
|
|
4282
|
+
if (!this._invocationContext.artifactService) {
|
|
4283
|
+
throw new Error("Artifact service is not initialized.");
|
|
4284
|
+
}
|
|
4285
|
+
return await this._invocationContext.artifactService.listArtifactKeys({
|
|
4286
|
+
appName: this._invocationContext.appName,
|
|
4287
|
+
userId: this._invocationContext.userId,
|
|
4288
|
+
sessionId: this._invocationContext.session.id
|
|
4289
|
+
});
|
|
4290
|
+
}
|
|
4291
|
+
/**
|
|
4292
|
+
* Searches the memory of the current user
|
|
4293
|
+
*/
|
|
4294
|
+
async searchMemory(query) {
|
|
4295
|
+
if (!this._invocationContext.memoryService) {
|
|
4296
|
+
throw new Error("Memory service is not available.");
|
|
4297
|
+
}
|
|
4298
|
+
return await this._invocationContext.memoryService.searchMemory({
|
|
4299
|
+
query,
|
|
4300
|
+
appName: this._invocationContext.appName,
|
|
4301
|
+
userId: this._invocationContext.userId
|
|
4302
|
+
});
|
|
4303
|
+
}
|
|
4304
|
+
};
|
|
4305
|
+
|
|
4306
|
+
// src/tools/index.ts
|
|
4307
|
+
init_function_tool();
|
|
4308
|
+
|
|
4309
|
+
// src/tools/function/index.ts
|
|
4310
|
+
init_function_tool();
|
|
4311
|
+
init_function_utils();
|
|
4312
|
+
function createFunctionTool(func, options) {
|
|
4313
|
+
const { FunctionTool: FunctionTool2 } = (init_function_tool(), __toCommonJS(function_tool_exports));
|
|
4314
|
+
return new FunctionTool2(func, options);
|
|
4315
|
+
}
|
|
4316
|
+
|
|
4317
|
+
// src/tools/index.ts
|
|
4318
|
+
init_function_utils();
|
|
4319
|
+
|
|
4320
|
+
// src/tools/common/google-search.ts
|
|
4321
|
+
init_logger();
|
|
4322
|
+
init_base_tool();
|
|
4323
|
+
import { Type as Type3 } from "@google/genai";
|
|
4324
|
+
var GoogleSearch = class extends BaseTool {
|
|
4325
|
+
logger = new Logger({ name: "GoogleSearch" });
|
|
4326
|
+
/**
|
|
4327
|
+
* Constructor for GoogleSearch
|
|
4328
|
+
*/
|
|
4329
|
+
constructor() {
|
|
4330
|
+
super({
|
|
4331
|
+
name: "google_search",
|
|
4332
|
+
description: "Search the web using Google"
|
|
4333
|
+
});
|
|
4334
|
+
}
|
|
4335
|
+
/**
|
|
4336
|
+
* Get the function declaration for the tool
|
|
4337
|
+
*/
|
|
4338
|
+
getDeclaration() {
|
|
4339
|
+
return {
|
|
4340
|
+
name: this.name,
|
|
4341
|
+
description: this.description,
|
|
4342
|
+
parameters: {
|
|
4343
|
+
type: Type3.OBJECT,
|
|
4344
|
+
properties: {
|
|
4345
|
+
query: {
|
|
4346
|
+
type: Type3.STRING,
|
|
4347
|
+
description: "The search query to execute"
|
|
4348
|
+
},
|
|
4349
|
+
num_results: {
|
|
4350
|
+
type: Type3.INTEGER,
|
|
4351
|
+
description: "Number of results to return (max 10)",
|
|
4352
|
+
default: 5
|
|
4353
|
+
}
|
|
4354
|
+
},
|
|
4355
|
+
required: ["query"]
|
|
4356
|
+
}
|
|
4357
|
+
};
|
|
4358
|
+
}
|
|
4359
|
+
/**
|
|
4360
|
+
* Execute the search
|
|
4361
|
+
* This is a simplified implementation that doesn't actually search, just returns mock results
|
|
4362
|
+
*/
|
|
4363
|
+
async runAsync(args, _context) {
|
|
4364
|
+
this.logger.debug(
|
|
4365
|
+
`[GoogleSearch] Executing Google search for: ${args.query}`
|
|
4366
|
+
);
|
|
4367
|
+
return {
|
|
4368
|
+
results: [
|
|
4369
|
+
{
|
|
4370
|
+
title: `Result 1 for ${args.query}`,
|
|
4371
|
+
link: "https://example.com/1",
|
|
4372
|
+
snippet: `This is a sample result for the query "${args.query}".`
|
|
4373
|
+
},
|
|
4374
|
+
{
|
|
4375
|
+
title: `Result 2 for ${args.query}`,
|
|
4376
|
+
link: "https://example.com/2",
|
|
4377
|
+
snippet: `Another sample result for "${args.query}".`
|
|
4378
|
+
}
|
|
4379
|
+
]
|
|
4380
|
+
};
|
|
4381
|
+
}
|
|
4382
|
+
};
|
|
4383
|
+
|
|
4384
|
+
// src/tools/common/http-request-tool.ts
|
|
4660
4385
|
init_base_tool();
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
/**
|
|
4670
|
-
* The function declaration schema
|
|
4671
|
-
*/
|
|
4672
|
-
functionDeclaration;
|
|
4386
|
+
import { Type as Type4 } from "@google/genai";
|
|
4387
|
+
var HttpRequestTool = class extends BaseTool {
|
|
4388
|
+
constructor() {
|
|
4389
|
+
super({
|
|
4390
|
+
name: "http_request",
|
|
4391
|
+
description: "Make HTTP requests to external APIs and web services"
|
|
4392
|
+
});
|
|
4393
|
+
}
|
|
4673
4394
|
/**
|
|
4674
|
-
*
|
|
4395
|
+
* Get the function declaration for the tool
|
|
4675
4396
|
*/
|
|
4676
|
-
|
|
4397
|
+
getDeclaration() {
|
|
4398
|
+
return {
|
|
4399
|
+
name: this.name,
|
|
4400
|
+
description: this.description,
|
|
4401
|
+
parameters: {
|
|
4402
|
+
type: Type4.OBJECT,
|
|
4403
|
+
properties: {
|
|
4404
|
+
url: {
|
|
4405
|
+
type: Type4.STRING,
|
|
4406
|
+
description: "The URL to send the request to"
|
|
4407
|
+
},
|
|
4408
|
+
method: {
|
|
4409
|
+
type: Type4.STRING,
|
|
4410
|
+
description: "The HTTP method to use (GET, POST, PUT, DELETE, etc.)",
|
|
4411
|
+
enum: ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"],
|
|
4412
|
+
default: "GET"
|
|
4413
|
+
},
|
|
4414
|
+
headers: {
|
|
4415
|
+
type: Type4.OBJECT,
|
|
4416
|
+
description: "Request headers to include"
|
|
4417
|
+
},
|
|
4418
|
+
body: {
|
|
4419
|
+
type: Type4.STRING,
|
|
4420
|
+
description: "Request body content (as string, typically JSON)"
|
|
4421
|
+
},
|
|
4422
|
+
params: {
|
|
4423
|
+
type: Type4.OBJECT,
|
|
4424
|
+
description: "URL query parameters to include"
|
|
4425
|
+
},
|
|
4426
|
+
timeout: {
|
|
4427
|
+
type: Type4.INTEGER,
|
|
4428
|
+
description: "Request timeout in milliseconds",
|
|
4429
|
+
default: 1e4
|
|
4430
|
+
}
|
|
4431
|
+
},
|
|
4432
|
+
required: ["url"]
|
|
4433
|
+
}
|
|
4434
|
+
};
|
|
4435
|
+
}
|
|
4677
4436
|
/**
|
|
4678
|
-
*
|
|
4437
|
+
* Execute the HTTP request
|
|
4679
4438
|
*/
|
|
4680
|
-
|
|
4681
|
-
|
|
4439
|
+
async runAsync(args, _context) {
|
|
4440
|
+
try {
|
|
4441
|
+
const {
|
|
4442
|
+
url,
|
|
4443
|
+
method = "GET",
|
|
4444
|
+
headers = {},
|
|
4445
|
+
body,
|
|
4446
|
+
params,
|
|
4447
|
+
timeout = 1e4
|
|
4448
|
+
} = args;
|
|
4449
|
+
const urlObj = new URL(url);
|
|
4450
|
+
if (params) {
|
|
4451
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
4452
|
+
urlObj.searchParams.append(key, value);
|
|
4453
|
+
});
|
|
4454
|
+
}
|
|
4455
|
+
const requestHeaders = { ...headers };
|
|
4456
|
+
if (body && !requestHeaders["Content-Type"] && this.isValidJson(body)) {
|
|
4457
|
+
requestHeaders["Content-Type"] = "application/json";
|
|
4458
|
+
}
|
|
4459
|
+
const options = {
|
|
4460
|
+
method,
|
|
4461
|
+
headers: requestHeaders,
|
|
4462
|
+
body,
|
|
4463
|
+
signal: AbortSignal.timeout(timeout)
|
|
4464
|
+
};
|
|
4465
|
+
const response = await fetch(urlObj.toString(), options);
|
|
4466
|
+
const responseHeaders = {};
|
|
4467
|
+
response.headers.forEach((value, key) => {
|
|
4468
|
+
responseHeaders[key] = value;
|
|
4469
|
+
});
|
|
4470
|
+
const responseBody = await response.text();
|
|
4471
|
+
return {
|
|
4472
|
+
statusCode: response.status,
|
|
4473
|
+
headers: responseHeaders,
|
|
4474
|
+
body: responseBody
|
|
4475
|
+
};
|
|
4476
|
+
} catch (error) {
|
|
4477
|
+
return {
|
|
4478
|
+
statusCode: 0,
|
|
4479
|
+
headers: {},
|
|
4480
|
+
body: "",
|
|
4481
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4482
|
+
};
|
|
4483
|
+
}
|
|
4484
|
+
}
|
|
4682
4485
|
/**
|
|
4683
|
-
*
|
|
4486
|
+
* Check if a string is valid JSON
|
|
4684
4487
|
*/
|
|
4685
|
-
|
|
4488
|
+
isValidJson(str) {
|
|
4489
|
+
try {
|
|
4490
|
+
JSON.parse(str);
|
|
4491
|
+
return true;
|
|
4492
|
+
} catch (e) {
|
|
4493
|
+
return false;
|
|
4494
|
+
}
|
|
4495
|
+
}
|
|
4496
|
+
};
|
|
4497
|
+
|
|
4498
|
+
// src/tools/common/file-operations-tool.ts
|
|
4499
|
+
init_base_tool();
|
|
4500
|
+
import fs from "fs/promises";
|
|
4501
|
+
import path from "path";
|
|
4502
|
+
import { Type as Type5 } from "@google/genai";
|
|
4503
|
+
var FileOperationsTool = class extends BaseTool {
|
|
4504
|
+
basePath;
|
|
4505
|
+
constructor(options) {
|
|
4686
4506
|
super({
|
|
4687
|
-
name:
|
|
4688
|
-
description:
|
|
4689
|
-
isLongRunning: config.isLongRunning || false,
|
|
4690
|
-
shouldRetryOnFailure: config.shouldRetryOnFailure || false,
|
|
4691
|
-
maxRetryAttempts: config.maxRetryAttempts || 3
|
|
4507
|
+
name: "file_operations",
|
|
4508
|
+
description: "Perform file system operations like reading, writing, and managing files"
|
|
4692
4509
|
});
|
|
4693
|
-
this.
|
|
4694
|
-
this.functionDeclaration = config.functionDeclaration;
|
|
4695
|
-
this.outputKey = config.outputKey;
|
|
4696
|
-
this.skipSummarization = config.skipSummarization || false;
|
|
4510
|
+
this.basePath = options?.basePath || process.cwd();
|
|
4697
4511
|
}
|
|
4698
4512
|
/**
|
|
4699
4513
|
* Get the function declaration for the tool
|
|
4700
4514
|
*/
|
|
4701
4515
|
getDeclaration() {
|
|
4702
|
-
if (this.functionDeclaration) {
|
|
4703
|
-
return this.functionDeclaration;
|
|
4704
|
-
}
|
|
4705
|
-
const description = isLlmAgent(this.agent) ? typeof this.agent.instruction === "string" ? this.agent.instruction : this.description : this.description;
|
|
4706
4516
|
return {
|
|
4707
4517
|
name: this.name,
|
|
4708
|
-
description,
|
|
4518
|
+
description: this.description,
|
|
4709
4519
|
parameters: {
|
|
4710
|
-
type:
|
|
4520
|
+
type: Type5.OBJECT,
|
|
4711
4521
|
properties: {
|
|
4712
|
-
|
|
4713
|
-
type:
|
|
4714
|
-
description: "The
|
|
4522
|
+
operation: {
|
|
4523
|
+
type: Type5.STRING,
|
|
4524
|
+
description: "The file operation to perform",
|
|
4525
|
+
enum: [
|
|
4526
|
+
"read",
|
|
4527
|
+
"write",
|
|
4528
|
+
"append",
|
|
4529
|
+
"delete",
|
|
4530
|
+
"exists",
|
|
4531
|
+
"list",
|
|
4532
|
+
"mkdir"
|
|
4533
|
+
]
|
|
4534
|
+
},
|
|
4535
|
+
filepath: {
|
|
4536
|
+
type: Type5.STRING,
|
|
4537
|
+
description: "Path to the file or directory (relative to the base path)"
|
|
4538
|
+
},
|
|
4539
|
+
content: {
|
|
4540
|
+
type: Type5.STRING,
|
|
4541
|
+
description: "Content to write to the file (for write and append operations)"
|
|
4542
|
+
},
|
|
4543
|
+
encoding: {
|
|
4544
|
+
type: Type5.STRING,
|
|
4545
|
+
description: "File encoding to use",
|
|
4546
|
+
default: "utf8"
|
|
4715
4547
|
}
|
|
4716
4548
|
},
|
|
4717
|
-
required: ["
|
|
4549
|
+
required: ["operation", "filepath"]
|
|
4718
4550
|
}
|
|
4719
4551
|
};
|
|
4720
4552
|
}
|
|
4721
4553
|
/**
|
|
4722
|
-
* Execute the
|
|
4554
|
+
* Execute the file operation
|
|
4723
4555
|
*/
|
|
4724
|
-
async runAsync(
|
|
4556
|
+
async runAsync(args, _context) {
|
|
4725
4557
|
try {
|
|
4726
|
-
const
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
artifactService: parentInvocation.artifactService,
|
|
4738
|
-
sessionService: parentInvocation.sessionService,
|
|
4739
|
-
memoryService: parentInvocation.memoryService,
|
|
4740
|
-
runConfig: parentInvocation.runConfig,
|
|
4741
|
-
userContent: {
|
|
4742
|
-
role: "user",
|
|
4743
|
-
parts: [{ text: String(input) }]
|
|
4744
|
-
},
|
|
4745
|
-
branch: parentInvocation.branch ? `${parentInvocation.branch}.${this.agent.name}` : this.agent.name
|
|
4746
|
-
});
|
|
4747
|
-
let lastEvent = null;
|
|
4748
|
-
for await (const event of this.agent.runAsync(childInvocationContext)) {
|
|
4749
|
-
if (!event.partial) {
|
|
4750
|
-
await childInvocationContext.sessionService.appendEvent(
|
|
4751
|
-
childInvocationContext.session,
|
|
4752
|
-
event
|
|
4558
|
+
const resolvedPath = this.resolvePath(args.filepath);
|
|
4559
|
+
this.validatePath(resolvedPath);
|
|
4560
|
+
const encoding = args.encoding || "utf8";
|
|
4561
|
+
switch (args.operation) {
|
|
4562
|
+
case "read":
|
|
4563
|
+
return await this.readFile(resolvedPath, encoding);
|
|
4564
|
+
case "write":
|
|
4565
|
+
return await this.writeFile(
|
|
4566
|
+
resolvedPath,
|
|
4567
|
+
args.content || "",
|
|
4568
|
+
encoding
|
|
4753
4569
|
);
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
context4.state[this.outputKey] = toolResult;
|
|
4570
|
+
case "append":
|
|
4571
|
+
return await this.appendFile(
|
|
4572
|
+
resolvedPath,
|
|
4573
|
+
args.content || "",
|
|
4574
|
+
encoding
|
|
4575
|
+
);
|
|
4576
|
+
case "delete":
|
|
4577
|
+
return await this.deleteFile(resolvedPath);
|
|
4578
|
+
case "exists":
|
|
4579
|
+
return await this.fileExists(resolvedPath);
|
|
4580
|
+
case "list":
|
|
4581
|
+
return await this.listDirectory(resolvedPath);
|
|
4582
|
+
case "mkdir":
|
|
4583
|
+
return await this.makeDirectory(resolvedPath);
|
|
4584
|
+
default:
|
|
4585
|
+
throw new Error(`Unsupported operation: ${args.operation}`);
|
|
4771
4586
|
}
|
|
4772
|
-
return toolResult;
|
|
4773
4587
|
} catch (error) {
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4588
|
+
return {
|
|
4589
|
+
success: false,
|
|
4590
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4591
|
+
};
|
|
4778
4592
|
}
|
|
4779
4593
|
}
|
|
4780
|
-
};
|
|
4781
|
-
|
|
4782
|
-
// src/tools/tool-context.ts
|
|
4783
|
-
var ToolContext = class extends CallbackContext {
|
|
4784
4594
|
/**
|
|
4785
|
-
*
|
|
4786
|
-
* returned in the function call event from LLM to identify a function call.
|
|
4787
|
-
* If LLM didn't return this id, ADK will assign one to it. This id is used
|
|
4788
|
-
* to map function call response to the original function call.
|
|
4595
|
+
* Resolve a file path relative to the base path
|
|
4789
4596
|
*/
|
|
4790
|
-
|
|
4597
|
+
resolvePath(filepath) {
|
|
4598
|
+
return path.isAbsolute(filepath) ? filepath : path.resolve(this.basePath, filepath);
|
|
4599
|
+
}
|
|
4791
4600
|
/**
|
|
4792
|
-
*
|
|
4601
|
+
* Validate that a path is within the base path for security
|
|
4793
4602
|
*/
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4603
|
+
validatePath(filepath) {
|
|
4604
|
+
const normalizedPath = path.normalize(filepath);
|
|
4605
|
+
const normalizedBasePath = path.normalize(this.basePath);
|
|
4606
|
+
if (!normalizedPath.startsWith(normalizedBasePath)) {
|
|
4607
|
+
throw new Error(
|
|
4608
|
+
`Access denied: Can't access paths outside the base directory`
|
|
4609
|
+
);
|
|
4610
|
+
}
|
|
4797
4611
|
}
|
|
4798
4612
|
/**
|
|
4799
|
-
*
|
|
4613
|
+
* Read a file
|
|
4800
4614
|
*/
|
|
4801
|
-
|
|
4802
|
-
|
|
4615
|
+
async readFile(filepath, encoding) {
|
|
4616
|
+
try {
|
|
4617
|
+
const content = await fs.readFile(filepath, { encoding });
|
|
4618
|
+
return {
|
|
4619
|
+
success: true,
|
|
4620
|
+
data: content
|
|
4621
|
+
};
|
|
4622
|
+
} catch (error) {
|
|
4623
|
+
return {
|
|
4624
|
+
success: false,
|
|
4625
|
+
error: `Failed to read file: ${error instanceof Error ? error.message : String(error)}`
|
|
4626
|
+
};
|
|
4627
|
+
}
|
|
4803
4628
|
}
|
|
4804
4629
|
/**
|
|
4805
|
-
*
|
|
4630
|
+
* Write to a file
|
|
4806
4631
|
*/
|
|
4807
|
-
async
|
|
4808
|
-
|
|
4809
|
-
|
|
4632
|
+
async writeFile(filepath, content, encoding) {
|
|
4633
|
+
try {
|
|
4634
|
+
const dir = path.dirname(filepath);
|
|
4635
|
+
await fs.mkdir(dir, { recursive: true });
|
|
4636
|
+
await fs.writeFile(filepath, content, { encoding });
|
|
4637
|
+
return {
|
|
4638
|
+
success: true
|
|
4639
|
+
};
|
|
4640
|
+
} catch (error) {
|
|
4641
|
+
return {
|
|
4642
|
+
success: false,
|
|
4643
|
+
error: `Failed to write to file: ${error instanceof Error ? error.message : String(error)}`
|
|
4644
|
+
};
|
|
4810
4645
|
}
|
|
4811
|
-
return await this._invocationContext.artifactService.listArtifactKeys({
|
|
4812
|
-
appName: this._invocationContext.appName,
|
|
4813
|
-
userId: this._invocationContext.userId,
|
|
4814
|
-
sessionId: this._invocationContext.session.id
|
|
4815
|
-
});
|
|
4816
4646
|
}
|
|
4817
4647
|
/**
|
|
4818
|
-
*
|
|
4648
|
+
* Append to a file
|
|
4819
4649
|
*/
|
|
4820
|
-
async
|
|
4821
|
-
|
|
4822
|
-
|
|
4650
|
+
async appendFile(filepath, content, encoding) {
|
|
4651
|
+
try {
|
|
4652
|
+
const dir = path.dirname(filepath);
|
|
4653
|
+
await fs.mkdir(dir, { recursive: true });
|
|
4654
|
+
await fs.appendFile(filepath, content, { encoding });
|
|
4655
|
+
return {
|
|
4656
|
+
success: true
|
|
4657
|
+
};
|
|
4658
|
+
} catch (error) {
|
|
4659
|
+
return {
|
|
4660
|
+
success: false,
|
|
4661
|
+
error: `Failed to append to file: ${error instanceof Error ? error.message : String(error)}`
|
|
4662
|
+
};
|
|
4823
4663
|
}
|
|
4824
|
-
return await this._invocationContext.memoryService.searchMemory({
|
|
4825
|
-
query,
|
|
4826
|
-
appName: this._invocationContext.appName,
|
|
4827
|
-
userId: this._invocationContext.userId
|
|
4828
|
-
});
|
|
4829
4664
|
}
|
|
4830
|
-
};
|
|
4831
|
-
|
|
4832
|
-
// src/tools/index.ts
|
|
4833
|
-
init_function_tool();
|
|
4834
|
-
|
|
4835
|
-
// src/tools/function/index.ts
|
|
4836
|
-
init_function_tool();
|
|
4837
|
-
init_function_utils();
|
|
4838
|
-
function createFunctionTool(func, options) {
|
|
4839
|
-
const { FunctionTool: FunctionTool2 } = (init_function_tool(), __toCommonJS(function_tool_exports));
|
|
4840
|
-
return new FunctionTool2(func, options);
|
|
4841
|
-
}
|
|
4842
|
-
|
|
4843
|
-
// src/tools/index.ts
|
|
4844
|
-
init_function_utils();
|
|
4845
|
-
|
|
4846
|
-
// src/tools/common/google-search.ts
|
|
4847
|
-
init_logger();
|
|
4848
|
-
init_base_tool();
|
|
4849
|
-
import { Type as Type3 } from "@google/genai";
|
|
4850
|
-
var GoogleSearch = class extends BaseTool {
|
|
4851
|
-
logger = new Logger({ name: "GoogleSearch" });
|
|
4852
4665
|
/**
|
|
4853
|
-
*
|
|
4666
|
+
* Delete a file
|
|
4854
4667
|
*/
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4668
|
+
async deleteFile(filepath) {
|
|
4669
|
+
try {
|
|
4670
|
+
await fs.unlink(filepath);
|
|
4671
|
+
return {
|
|
4672
|
+
success: true
|
|
4673
|
+
};
|
|
4674
|
+
} catch (error) {
|
|
4675
|
+
return {
|
|
4676
|
+
success: false,
|
|
4677
|
+
error: `Failed to delete file: ${error instanceof Error ? error.message : String(error)}`
|
|
4678
|
+
};
|
|
4679
|
+
}
|
|
4860
4680
|
}
|
|
4861
4681
|
/**
|
|
4862
|
-
*
|
|
4682
|
+
* Check if a file exists
|
|
4863
4683
|
*/
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
description: "Number of results to return (max 10)",
|
|
4878
|
-
default: 5
|
|
4879
|
-
}
|
|
4880
|
-
},
|
|
4881
|
-
required: ["query"]
|
|
4882
|
-
}
|
|
4883
|
-
};
|
|
4684
|
+
async fileExists(filepath) {
|
|
4685
|
+
try {
|
|
4686
|
+
await fs.access(filepath);
|
|
4687
|
+
return {
|
|
4688
|
+
success: true,
|
|
4689
|
+
data: true
|
|
4690
|
+
};
|
|
4691
|
+
} catch {
|
|
4692
|
+
return {
|
|
4693
|
+
success: true,
|
|
4694
|
+
data: false
|
|
4695
|
+
};
|
|
4696
|
+
}
|
|
4884
4697
|
}
|
|
4885
4698
|
/**
|
|
4886
|
-
*
|
|
4887
|
-
* This is a simplified implementation that doesn't actually search, just returns mock results
|
|
4699
|
+
* List directory contents
|
|
4888
4700
|
*/
|
|
4889
|
-
async
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4701
|
+
async listDirectory(dirpath) {
|
|
4702
|
+
try {
|
|
4703
|
+
const entries = await fs.readdir(dirpath, { withFileTypes: true });
|
|
4704
|
+
const results = await Promise.all(
|
|
4705
|
+
entries.map(async (entry) => {
|
|
4706
|
+
const entryPath = path.join(dirpath, entry.name);
|
|
4707
|
+
const stats = await fs.stat(entryPath);
|
|
4708
|
+
return {
|
|
4709
|
+
name: entry.name,
|
|
4710
|
+
path: entryPath,
|
|
4711
|
+
isFile: entry.isFile(),
|
|
4712
|
+
isDirectory: entry.isDirectory(),
|
|
4713
|
+
size: stats.size,
|
|
4714
|
+
created: stats.birthtime,
|
|
4715
|
+
modified: stats.mtime
|
|
4716
|
+
};
|
|
4717
|
+
})
|
|
4718
|
+
);
|
|
4719
|
+
return {
|
|
4720
|
+
success: true,
|
|
4721
|
+
data: results
|
|
4722
|
+
};
|
|
4723
|
+
} catch (error) {
|
|
4724
|
+
return {
|
|
4725
|
+
success: false,
|
|
4726
|
+
error: `Failed to list directory: ${error instanceof Error ? error.message : String(error)}`
|
|
4727
|
+
};
|
|
4728
|
+
}
|
|
4729
|
+
}
|
|
4730
|
+
/**
|
|
4731
|
+
* Create a directory
|
|
4732
|
+
*/
|
|
4733
|
+
async makeDirectory(dirpath) {
|
|
4734
|
+
try {
|
|
4735
|
+
await fs.mkdir(dirpath, { recursive: true });
|
|
4736
|
+
return {
|
|
4737
|
+
success: true
|
|
4738
|
+
};
|
|
4739
|
+
} catch (error) {
|
|
4740
|
+
return {
|
|
4741
|
+
success: false,
|
|
4742
|
+
error: `Failed to create directory: ${error instanceof Error ? error.message : String(error)}`
|
|
4743
|
+
};
|
|
4744
|
+
}
|
|
4907
4745
|
}
|
|
4908
4746
|
};
|
|
4909
4747
|
|
|
4910
|
-
// src/tools/common/
|
|
4748
|
+
// src/tools/common/user-interaction-tool.ts
|
|
4911
4749
|
init_base_tool();
|
|
4912
|
-
import { Type as
|
|
4913
|
-
var
|
|
4750
|
+
import { Type as Type6 } from "@google/genai";
|
|
4751
|
+
var UserInteractionTool = class extends BaseTool {
|
|
4914
4752
|
constructor() {
|
|
4915
|
-
super({
|
|
4916
|
-
name: "
|
|
4917
|
-
description: "
|
|
4753
|
+
super({
|
|
4754
|
+
name: "user_interaction",
|
|
4755
|
+
description: "Prompt the user for input during agent execution",
|
|
4756
|
+
isLongRunning: true
|
|
4918
4757
|
});
|
|
4919
4758
|
}
|
|
4920
4759
|
/**
|
|
@@ -4925,115 +4764,102 @@ var HttpRequestTool = class extends BaseTool {
|
|
|
4925
4764
|
name: this.name,
|
|
4926
4765
|
description: this.description,
|
|
4927
4766
|
parameters: {
|
|
4928
|
-
type:
|
|
4767
|
+
type: Type6.OBJECT,
|
|
4929
4768
|
properties: {
|
|
4930
|
-
|
|
4931
|
-
type:
|
|
4932
|
-
description: "The
|
|
4933
|
-
},
|
|
4934
|
-
method: {
|
|
4935
|
-
type: Type4.STRING,
|
|
4936
|
-
description: "The HTTP method to use (GET, POST, PUT, DELETE, etc.)",
|
|
4937
|
-
enum: ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"],
|
|
4938
|
-
default: "GET"
|
|
4939
|
-
},
|
|
4940
|
-
headers: {
|
|
4941
|
-
type: Type4.OBJECT,
|
|
4942
|
-
description: "Request headers to include"
|
|
4943
|
-
},
|
|
4944
|
-
body: {
|
|
4945
|
-
type: Type4.STRING,
|
|
4946
|
-
description: "Request body content (as string, typically JSON)"
|
|
4769
|
+
prompt: {
|
|
4770
|
+
type: Type6.STRING,
|
|
4771
|
+
description: "The prompt message to display to the user"
|
|
4947
4772
|
},
|
|
4948
|
-
|
|
4949
|
-
type:
|
|
4950
|
-
description: "
|
|
4773
|
+
options: {
|
|
4774
|
+
type: Type6.ARRAY,
|
|
4775
|
+
description: "Optional array of choices to present to the user",
|
|
4776
|
+
items: {
|
|
4777
|
+
type: Type6.STRING
|
|
4778
|
+
}
|
|
4951
4779
|
},
|
|
4952
|
-
|
|
4953
|
-
type:
|
|
4954
|
-
description: "
|
|
4955
|
-
default: 1e4
|
|
4780
|
+
defaultValue: {
|
|
4781
|
+
type: Type6.STRING,
|
|
4782
|
+
description: "Optional default value for the input field"
|
|
4956
4783
|
}
|
|
4957
4784
|
},
|
|
4958
|
-
required: ["
|
|
4785
|
+
required: ["prompt"]
|
|
4959
4786
|
}
|
|
4960
4787
|
};
|
|
4961
4788
|
}
|
|
4962
4789
|
/**
|
|
4963
|
-
* Execute the
|
|
4790
|
+
* Execute the user interaction
|
|
4964
4791
|
*/
|
|
4965
|
-
async runAsync(args,
|
|
4792
|
+
async runAsync(args, context4) {
|
|
4966
4793
|
try {
|
|
4967
|
-
const
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
timeout = 1e4
|
|
4974
|
-
} = args;
|
|
4975
|
-
const urlObj = new URL(url);
|
|
4976
|
-
if (params) {
|
|
4977
|
-
Object.entries(params).forEach(([key, value]) => {
|
|
4978
|
-
urlObj.searchParams.append(key, value);
|
|
4979
|
-
});
|
|
4794
|
+
const actions = context4.actions;
|
|
4795
|
+
if (!actions || !actions.promptUser) {
|
|
4796
|
+
return {
|
|
4797
|
+
success: false,
|
|
4798
|
+
error: "User interaction is not supported in the current environment"
|
|
4799
|
+
};
|
|
4980
4800
|
}
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
requestHeaders["Content-Type"] = "application/json";
|
|
4801
|
+
if (actions.skipSummarization) {
|
|
4802
|
+
actions.skipSummarization(true);
|
|
4984
4803
|
}
|
|
4985
|
-
const
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
const responseHeaders = {};
|
|
4993
|
-
response.headers.forEach((value, key) => {
|
|
4994
|
-
responseHeaders[key] = value;
|
|
4804
|
+
const promptOptions = args.options && args.options.length > 0 ? {
|
|
4805
|
+
choices: args.options
|
|
4806
|
+
} : void 0;
|
|
4807
|
+
const response = await actions.promptUser({
|
|
4808
|
+
prompt: args.prompt,
|
|
4809
|
+
defaultValue: args.defaultValue,
|
|
4810
|
+
options: promptOptions
|
|
4995
4811
|
});
|
|
4996
|
-
const responseBody = await response.text();
|
|
4997
4812
|
return {
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
body: responseBody
|
|
4813
|
+
success: true,
|
|
4814
|
+
userInput: response
|
|
5001
4815
|
};
|
|
5002
4816
|
} catch (error) {
|
|
5003
4817
|
return {
|
|
5004
|
-
|
|
5005
|
-
headers: {},
|
|
5006
|
-
body: "",
|
|
4818
|
+
success: false,
|
|
5007
4819
|
error: error instanceof Error ? error.message : String(error)
|
|
5008
4820
|
};
|
|
5009
4821
|
}
|
|
5010
4822
|
}
|
|
4823
|
+
};
|
|
4824
|
+
|
|
4825
|
+
// src/tools/common/exit-loop-tool.ts
|
|
4826
|
+
init_logger();
|
|
4827
|
+
init_base_tool();
|
|
4828
|
+
var ExitLoopTool = class extends BaseTool {
|
|
4829
|
+
logger = new Logger({ name: "ExitLoopTool" });
|
|
5011
4830
|
/**
|
|
5012
|
-
*
|
|
4831
|
+
* Constructor for ExitLoopTool
|
|
5013
4832
|
*/
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
}
|
|
5019
|
-
|
|
5020
|
-
|
|
4833
|
+
constructor() {
|
|
4834
|
+
super({
|
|
4835
|
+
name: "exit_loop",
|
|
4836
|
+
description: "Exits the loop. Call this function only when you are instructed to do so."
|
|
4837
|
+
});
|
|
4838
|
+
}
|
|
4839
|
+
/**
|
|
4840
|
+
* Execute the exit loop action
|
|
4841
|
+
*/
|
|
4842
|
+
async runAsync(_args, context4) {
|
|
4843
|
+
this.logger.debug("Executing exit loop tool");
|
|
4844
|
+
context4.actions.escalate = true;
|
|
5021
4845
|
}
|
|
5022
4846
|
};
|
|
5023
4847
|
|
|
5024
|
-
// src/tools/common/
|
|
4848
|
+
// src/tools/common/get-user-choice-tool.ts
|
|
4849
|
+
init_logger();
|
|
5025
4850
|
init_base_tool();
|
|
5026
|
-
import
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
4851
|
+
import { Type as Type7 } from "@google/genai";
|
|
4852
|
+
var GetUserChoiceTool = class extends BaseTool {
|
|
4853
|
+
logger = new Logger({ name: "GetUserChoiceTool" });
|
|
4854
|
+
/**
|
|
4855
|
+
* Constructor for GetUserChoiceTool
|
|
4856
|
+
*/
|
|
4857
|
+
constructor() {
|
|
5032
4858
|
super({
|
|
5033
|
-
name: "
|
|
5034
|
-
description: "
|
|
4859
|
+
name: "get_user_choice",
|
|
4860
|
+
description: "This tool provides the options to the user and asks them to choose one. Use this tool when you need the user to make a selection between multiple options. Do not list options in your response - use this tool instead.",
|
|
4861
|
+
isLongRunning: true
|
|
5035
4862
|
});
|
|
5036
|
-
this.basePath = options?.basePath || process.cwd();
|
|
5037
4863
|
}
|
|
5038
4864
|
/**
|
|
5039
4865
|
* Get the function declaration for the tool
|
|
@@ -5043,601 +4869,773 @@ var FileOperationsTool = class extends BaseTool {
|
|
|
5043
4869
|
name: this.name,
|
|
5044
4870
|
description: this.description,
|
|
5045
4871
|
parameters: {
|
|
5046
|
-
type:
|
|
4872
|
+
type: Type7.OBJECT,
|
|
5047
4873
|
properties: {
|
|
5048
|
-
|
|
5049
|
-
type:
|
|
5050
|
-
description: "
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
"append",
|
|
5055
|
-
"delete",
|
|
5056
|
-
"exists",
|
|
5057
|
-
"list",
|
|
5058
|
-
"mkdir"
|
|
5059
|
-
]
|
|
5060
|
-
},
|
|
5061
|
-
filepath: {
|
|
5062
|
-
type: Type5.STRING,
|
|
5063
|
-
description: "Path to the file or directory (relative to the base path)"
|
|
5064
|
-
},
|
|
5065
|
-
content: {
|
|
5066
|
-
type: Type5.STRING,
|
|
5067
|
-
description: "Content to write to the file (for write and append operations)"
|
|
4874
|
+
options: {
|
|
4875
|
+
type: Type7.ARRAY,
|
|
4876
|
+
description: "List of options for the user to choose from",
|
|
4877
|
+
items: {
|
|
4878
|
+
type: Type7.STRING
|
|
4879
|
+
}
|
|
5068
4880
|
},
|
|
5069
|
-
|
|
5070
|
-
type:
|
|
5071
|
-
description: "
|
|
5072
|
-
default: "utf8"
|
|
4881
|
+
question: {
|
|
4882
|
+
type: Type7.STRING,
|
|
4883
|
+
description: "The question or prompt to show the user before presenting options"
|
|
5073
4884
|
}
|
|
5074
4885
|
},
|
|
5075
|
-
required: ["
|
|
4886
|
+
required: ["options"]
|
|
5076
4887
|
}
|
|
5077
4888
|
};
|
|
5078
4889
|
}
|
|
5079
4890
|
/**
|
|
5080
|
-
* Execute the
|
|
4891
|
+
* Execute the user choice action
|
|
4892
|
+
* This is a long running operation that will return null initially
|
|
4893
|
+
* and the actual choice will be provided asynchronously
|
|
5081
4894
|
*/
|
|
5082
|
-
async runAsync(args,
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
4895
|
+
async runAsync(args, context4) {
|
|
4896
|
+
this.logger.debug(
|
|
4897
|
+
`Executing get_user_choice with options: ${args.options.join(", ")}`
|
|
4898
|
+
);
|
|
4899
|
+
if (args.question) {
|
|
4900
|
+
this.logger.debug(`Question: ${args.question}`);
|
|
4901
|
+
}
|
|
4902
|
+
context4.actions.skipSummarization = true;
|
|
4903
|
+
return null;
|
|
4904
|
+
}
|
|
4905
|
+
};
|
|
4906
|
+
|
|
4907
|
+
// src/tools/common/transfer-to-agent-tool.ts
|
|
4908
|
+
init_logger();
|
|
4909
|
+
init_base_tool();
|
|
4910
|
+
import { Type as Type8 } from "@google/genai";
|
|
4911
|
+
var TransferToAgentTool = class extends BaseTool {
|
|
4912
|
+
logger = new Logger({ name: "TransferToAgentTool" });
|
|
4913
|
+
/**
|
|
4914
|
+
* Constructor for TransferToAgentTool
|
|
4915
|
+
*/
|
|
4916
|
+
constructor() {
|
|
4917
|
+
super({
|
|
4918
|
+
name: "transfer_to_agent",
|
|
4919
|
+
description: "Transfer the question to another agent when it's more suitable to answer the user's question according to the agent's description. Use this function when you determine that another agent in the system would be better equipped to handle the user's request based on their specialized capabilities and expertise areas."
|
|
4920
|
+
});
|
|
4921
|
+
}
|
|
4922
|
+
/**
|
|
4923
|
+
* Get the function declaration for the tool
|
|
4924
|
+
*/
|
|
4925
|
+
getDeclaration() {
|
|
4926
|
+
return {
|
|
4927
|
+
name: this.name,
|
|
4928
|
+
description: this.description,
|
|
4929
|
+
parameters: {
|
|
4930
|
+
type: Type8.OBJECT,
|
|
4931
|
+
properties: {
|
|
4932
|
+
agent_name: {
|
|
4933
|
+
type: Type8.STRING,
|
|
4934
|
+
description: "The name of the agent to transfer control to"
|
|
4935
|
+
}
|
|
4936
|
+
},
|
|
4937
|
+
required: ["agent_name"]
|
|
5112
4938
|
}
|
|
5113
|
-
}
|
|
5114
|
-
return {
|
|
5115
|
-
success: false,
|
|
5116
|
-
error: error instanceof Error ? error.message : String(error)
|
|
5117
|
-
};
|
|
5118
|
-
}
|
|
4939
|
+
};
|
|
5119
4940
|
}
|
|
5120
4941
|
/**
|
|
5121
|
-
*
|
|
4942
|
+
* Execute the transfer to agent action
|
|
5122
4943
|
*/
|
|
5123
|
-
|
|
5124
|
-
|
|
4944
|
+
async runAsync(args, context4) {
|
|
4945
|
+
this.logger.debug(`Executing transfer to agent: ${args.agent_name}`);
|
|
4946
|
+
context4.actions.transferToAgent = args.agent_name;
|
|
5125
4947
|
}
|
|
4948
|
+
};
|
|
4949
|
+
|
|
4950
|
+
// src/tools/common/load-memory-tool.ts
|
|
4951
|
+
init_logger();
|
|
4952
|
+
init_base_tool();
|
|
4953
|
+
import { Type as Type9 } from "@google/genai";
|
|
4954
|
+
var LoadMemoryTool = class extends BaseTool {
|
|
4955
|
+
logger = new Logger({ name: "LoadMemoryTool" });
|
|
5126
4956
|
/**
|
|
5127
|
-
*
|
|
4957
|
+
* Constructor for LoadMemoryTool
|
|
5128
4958
|
*/
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
`Access denied: Can't access paths outside the base directory`
|
|
5135
|
-
);
|
|
5136
|
-
}
|
|
4959
|
+
constructor() {
|
|
4960
|
+
super({
|
|
4961
|
+
name: "load_memory",
|
|
4962
|
+
description: "Loads the memory for the current user based on a query."
|
|
4963
|
+
});
|
|
5137
4964
|
}
|
|
5138
4965
|
/**
|
|
5139
|
-
*
|
|
4966
|
+
* Get the function declaration for the tool
|
|
5140
4967
|
*/
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
4968
|
+
getDeclaration() {
|
|
4969
|
+
return {
|
|
4970
|
+
name: this.name,
|
|
4971
|
+
description: this.description,
|
|
4972
|
+
parameters: {
|
|
4973
|
+
type: Type9.OBJECT,
|
|
4974
|
+
properties: {
|
|
4975
|
+
query: {
|
|
4976
|
+
type: Type9.STRING,
|
|
4977
|
+
description: "The query to load memories for"
|
|
4978
|
+
}
|
|
4979
|
+
},
|
|
4980
|
+
required: ["query"]
|
|
4981
|
+
}
|
|
4982
|
+
};
|
|
5154
4983
|
}
|
|
5155
4984
|
/**
|
|
5156
|
-
*
|
|
4985
|
+
* Execute the memory loading action
|
|
5157
4986
|
*/
|
|
5158
|
-
async
|
|
4987
|
+
async runAsync(args, context4) {
|
|
4988
|
+
this.logger.debug(`Executing load_memory with query: ${args.query}`);
|
|
5159
4989
|
try {
|
|
5160
|
-
const
|
|
5161
|
-
await fs.mkdir(dir, { recursive: true });
|
|
5162
|
-
await fs.writeFile(filepath, content, { encoding });
|
|
4990
|
+
const searchResult = await context4.searchMemory(args.query);
|
|
5163
4991
|
return {
|
|
5164
|
-
|
|
4992
|
+
memories: searchResult.memories || [],
|
|
4993
|
+
count: searchResult.memories?.length || 0
|
|
5165
4994
|
};
|
|
5166
4995
|
} catch (error) {
|
|
4996
|
+
console.error("Error searching memory:", error);
|
|
5167
4997
|
return {
|
|
5168
|
-
|
|
5169
|
-
|
|
4998
|
+
error: "Memory search failed",
|
|
4999
|
+
message: error instanceof Error ? error.message : String(error)
|
|
5170
5000
|
};
|
|
5171
5001
|
}
|
|
5172
5002
|
}
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
} catch (error) {
|
|
5185
|
-
return {
|
|
5186
|
-
success: false,
|
|
5187
|
-
error: `Failed to append to file: ${error instanceof Error ? error.message : String(error)}`
|
|
5188
|
-
};
|
|
5189
|
-
}
|
|
5003
|
+
};
|
|
5004
|
+
|
|
5005
|
+
// src/tools/common/load-artifacts-tool.ts
|
|
5006
|
+
init_base_tool();
|
|
5007
|
+
import { Type as Type10 } from "@google/genai";
|
|
5008
|
+
var LoadArtifactsTool = class extends BaseTool {
|
|
5009
|
+
constructor() {
|
|
5010
|
+
super({
|
|
5011
|
+
name: "load_artifacts",
|
|
5012
|
+
description: "Loads the artifacts and adds them to the session."
|
|
5013
|
+
});
|
|
5190
5014
|
}
|
|
5191
5015
|
/**
|
|
5192
|
-
*
|
|
5016
|
+
* Get the function declaration for the tool
|
|
5193
5017
|
*/
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5018
|
+
getDeclaration() {
|
|
5019
|
+
return {
|
|
5020
|
+
name: this.name,
|
|
5021
|
+
description: this.description,
|
|
5022
|
+
parameters: {
|
|
5023
|
+
type: Type10.OBJECT,
|
|
5024
|
+
properties: {
|
|
5025
|
+
artifact_names: {
|
|
5026
|
+
type: Type10.ARRAY,
|
|
5027
|
+
items: {
|
|
5028
|
+
type: Type10.STRING
|
|
5029
|
+
},
|
|
5030
|
+
description: "List of artifact names to load"
|
|
5031
|
+
}
|
|
5032
|
+
},
|
|
5033
|
+
required: []
|
|
5034
|
+
}
|
|
5035
|
+
};
|
|
5206
5036
|
}
|
|
5207
5037
|
/**
|
|
5208
|
-
*
|
|
5038
|
+
* Execute the load artifacts operation
|
|
5209
5039
|
*/
|
|
5210
|
-
async
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
return {
|
|
5214
|
-
success: true,
|
|
5215
|
-
data: true
|
|
5216
|
-
};
|
|
5217
|
-
} catch {
|
|
5218
|
-
return {
|
|
5219
|
-
success: true,
|
|
5220
|
-
data: false
|
|
5221
|
-
};
|
|
5222
|
-
}
|
|
5040
|
+
async runAsync(args, context4) {
|
|
5041
|
+
const artifactNames = args.artifact_names || [];
|
|
5042
|
+
return { artifact_names: artifactNames };
|
|
5223
5043
|
}
|
|
5224
5044
|
/**
|
|
5225
|
-
*
|
|
5045
|
+
* Processes the outgoing LLM request for this tool.
|
|
5226
5046
|
*/
|
|
5227
|
-
async
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
const results = await Promise.all(
|
|
5231
|
-
entries.map(async (entry) => {
|
|
5232
|
-
const entryPath = path.join(dirpath, entry.name);
|
|
5233
|
-
const stats = await fs.stat(entryPath);
|
|
5234
|
-
return {
|
|
5235
|
-
name: entry.name,
|
|
5236
|
-
path: entryPath,
|
|
5237
|
-
isFile: entry.isFile(),
|
|
5238
|
-
isDirectory: entry.isDirectory(),
|
|
5239
|
-
size: stats.size,
|
|
5240
|
-
created: stats.birthtime,
|
|
5241
|
-
modified: stats.mtime
|
|
5242
|
-
};
|
|
5243
|
-
})
|
|
5244
|
-
);
|
|
5245
|
-
return {
|
|
5246
|
-
success: true,
|
|
5247
|
-
data: results
|
|
5248
|
-
};
|
|
5249
|
-
} catch (error) {
|
|
5250
|
-
return {
|
|
5251
|
-
success: false,
|
|
5252
|
-
error: `Failed to list directory: ${error instanceof Error ? error.message : String(error)}`
|
|
5253
|
-
};
|
|
5254
|
-
}
|
|
5047
|
+
async processLlmRequest(toolContext, llmRequest) {
|
|
5048
|
+
await super.processLlmRequest(toolContext, llmRequest);
|
|
5049
|
+
await this.appendArtifactsToLlmRequest(toolContext, llmRequest);
|
|
5255
5050
|
}
|
|
5256
5051
|
/**
|
|
5257
|
-
*
|
|
5052
|
+
* Appends artifacts information to the LLM request
|
|
5258
5053
|
*/
|
|
5259
|
-
async
|
|
5054
|
+
async appendArtifactsToLlmRequest(toolContext, llmRequest) {
|
|
5260
5055
|
try {
|
|
5261
|
-
await
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
}
|
|
5056
|
+
const artifactNames = await toolContext.listArtifacts();
|
|
5057
|
+
if (!artifactNames || artifactNames.length === 0) {
|
|
5058
|
+
return;
|
|
5059
|
+
}
|
|
5060
|
+
const instructions = [
|
|
5061
|
+
`You have a list of artifacts:
|
|
5062
|
+
${JSON.stringify(artifactNames)}
|
|
5063
|
+
|
|
5064
|
+
When the user asks questions about any of the artifacts, you should call the
|
|
5065
|
+
\`load_artifacts\` function to load the artifact. Do not generate any text other
|
|
5066
|
+
than the function call.
|
|
5067
|
+
`
|
|
5068
|
+
];
|
|
5069
|
+
if (llmRequest.appendInstructions) {
|
|
5070
|
+
llmRequest.appendInstructions(instructions);
|
|
5071
|
+
}
|
|
5072
|
+
if (llmRequest.contents && llmRequest.contents.length > 0) {
|
|
5073
|
+
const lastContent = llmRequest.contents[llmRequest.contents.length - 1];
|
|
5074
|
+
if (lastContent.parts && lastContent.parts.length > 0) {
|
|
5075
|
+
const firstPart = lastContent.parts[0];
|
|
5076
|
+
const functionResponse = this.extractFunctionResponse(firstPart);
|
|
5077
|
+
if (functionResponse && functionResponse.name === "load_artifacts") {
|
|
5078
|
+
const requestedArtifactNames = functionResponse.response.artifact_names || [];
|
|
5079
|
+
for (const artifactName of requestedArtifactNames) {
|
|
5080
|
+
try {
|
|
5081
|
+
const artifact = await toolContext.loadArtifact(artifactName);
|
|
5082
|
+
if (artifact) {
|
|
5083
|
+
llmRequest.contents.push({
|
|
5084
|
+
role: "user",
|
|
5085
|
+
parts: [
|
|
5086
|
+
{
|
|
5087
|
+
text: `Artifact ${artifactName} is:`
|
|
5088
|
+
},
|
|
5089
|
+
artifact
|
|
5090
|
+
]
|
|
5091
|
+
});
|
|
5092
|
+
}
|
|
5093
|
+
} catch (error) {
|
|
5094
|
+
console.error(
|
|
5095
|
+
`Failed to load artifact ${artifactName}:`,
|
|
5096
|
+
error
|
|
5097
|
+
);
|
|
5098
|
+
}
|
|
5099
|
+
}
|
|
5100
|
+
}
|
|
5101
|
+
}
|
|
5102
|
+
}
|
|
5265
5103
|
} catch (error) {
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5104
|
+
console.error("Error appending artifacts to LLM request:", error);
|
|
5105
|
+
}
|
|
5106
|
+
}
|
|
5107
|
+
/**
|
|
5108
|
+
* Extracts function response from a part if it exists
|
|
5109
|
+
*/
|
|
5110
|
+
extractFunctionResponse(part) {
|
|
5111
|
+
if ("functionResponse" in part && part.functionResponse) {
|
|
5112
|
+
return part.functionResponse;
|
|
5270
5113
|
}
|
|
5114
|
+
return null;
|
|
5271
5115
|
}
|
|
5272
5116
|
};
|
|
5273
5117
|
|
|
5274
|
-
// src/tools/
|
|
5275
|
-
|
|
5276
|
-
import {
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5118
|
+
// src/tools/mcp/client.ts
|
|
5119
|
+
init_logger();
|
|
5120
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
5121
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
5122
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
5123
|
+
import { CreateMessageRequestSchema as CreateMessageRequestSchema2 } from "@modelcontextprotocol/sdk/types.js";
|
|
5124
|
+
|
|
5125
|
+
// src/tools/mcp/sampling-handler.ts
|
|
5126
|
+
init_logger();
|
|
5127
|
+
import {
|
|
5128
|
+
CreateMessageRequestSchema,
|
|
5129
|
+
CreateMessageResultSchema
|
|
5130
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
5131
|
+
|
|
5132
|
+
// src/tools/mcp/types.ts
|
|
5133
|
+
var McpErrorType = /* @__PURE__ */ ((McpErrorType2) => {
|
|
5134
|
+
McpErrorType2["CONNECTION_ERROR"] = "connection_error";
|
|
5135
|
+
McpErrorType2["TOOL_EXECUTION_ERROR"] = "tool_execution_error";
|
|
5136
|
+
McpErrorType2["RESOURCE_CLOSED_ERROR"] = "resource_closed_error";
|
|
5137
|
+
McpErrorType2["TIMEOUT_ERROR"] = "timeout_error";
|
|
5138
|
+
McpErrorType2["INVALID_SCHEMA_ERROR"] = "invalid_schema_error";
|
|
5139
|
+
McpErrorType2["SAMPLING_ERROR"] = "SAMPLING_ERROR";
|
|
5140
|
+
McpErrorType2["INVALID_REQUEST_ERROR"] = "INVALID_REQUEST_ERROR";
|
|
5141
|
+
return McpErrorType2;
|
|
5142
|
+
})(McpErrorType || {});
|
|
5143
|
+
var McpError = class extends Error {
|
|
5144
|
+
type;
|
|
5145
|
+
originalError;
|
|
5146
|
+
constructor(message, type, originalError) {
|
|
5147
|
+
super(message);
|
|
5148
|
+
this.name = "McpError";
|
|
5149
|
+
this.type = type;
|
|
5150
|
+
this.originalError = originalError;
|
|
5284
5151
|
}
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
type: Type6.OBJECT,
|
|
5294
|
-
properties: {
|
|
5295
|
-
prompt: {
|
|
5296
|
-
type: Type6.STRING,
|
|
5297
|
-
description: "The prompt message to display to the user"
|
|
5298
|
-
},
|
|
5299
|
-
options: {
|
|
5300
|
-
type: Type6.ARRAY,
|
|
5301
|
-
description: "Optional array of choices to present to the user",
|
|
5302
|
-
items: {
|
|
5303
|
-
type: Type6.STRING
|
|
5304
|
-
}
|
|
5305
|
-
},
|
|
5306
|
-
defaultValue: {
|
|
5307
|
-
type: Type6.STRING,
|
|
5308
|
-
description: "Optional default value for the input field"
|
|
5309
|
-
}
|
|
5310
|
-
},
|
|
5311
|
-
required: ["prompt"]
|
|
5312
|
-
}
|
|
5313
|
-
};
|
|
5152
|
+
};
|
|
5153
|
+
|
|
5154
|
+
// src/tools/mcp/sampling-handler.ts
|
|
5155
|
+
var McpSamplingHandler = class {
|
|
5156
|
+
logger = new Logger({ name: "McpSamplingHandler" });
|
|
5157
|
+
samplingHandler;
|
|
5158
|
+
constructor(samplingHandler) {
|
|
5159
|
+
this.samplingHandler = samplingHandler;
|
|
5314
5160
|
}
|
|
5315
5161
|
/**
|
|
5316
|
-
*
|
|
5162
|
+
* Handle MCP sampling request and convert between formats
|
|
5317
5163
|
*/
|
|
5318
|
-
async
|
|
5164
|
+
async handleSamplingRequest(request) {
|
|
5319
5165
|
try {
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5166
|
+
if (request.method !== "sampling/createMessage") {
|
|
5167
|
+
this.logger.error(
|
|
5168
|
+
`Invalid method for sampling handler: ${request.method}. Expected: sampling/createMessage`
|
|
5169
|
+
);
|
|
5170
|
+
throw new McpError(
|
|
5171
|
+
`Invalid method: ${request.method}. This handler only processes sampling/createMessage requests.`,
|
|
5172
|
+
"INVALID_REQUEST_ERROR" /* INVALID_REQUEST_ERROR */
|
|
5173
|
+
);
|
|
5326
5174
|
}
|
|
5327
|
-
|
|
5328
|
-
|
|
5175
|
+
const validationResult = CreateMessageRequestSchema.safeParse(request);
|
|
5176
|
+
if (!validationResult.success) {
|
|
5177
|
+
this.logger.error(
|
|
5178
|
+
"Invalid MCP sampling request:",
|
|
5179
|
+
validationResult.error
|
|
5180
|
+
);
|
|
5181
|
+
throw new McpError(
|
|
5182
|
+
`Invalid sampling request: ${validationResult.error.message}`,
|
|
5183
|
+
"INVALID_REQUEST_ERROR" /* INVALID_REQUEST_ERROR */
|
|
5184
|
+
);
|
|
5329
5185
|
}
|
|
5330
|
-
const
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5186
|
+
const mcpParams = request.params;
|
|
5187
|
+
if (!mcpParams.messages || !Array.isArray(mcpParams.messages)) {
|
|
5188
|
+
throw new McpError(
|
|
5189
|
+
"Invalid sampling request: messages array is required",
|
|
5190
|
+
"INVALID_REQUEST_ERROR" /* INVALID_REQUEST_ERROR */
|
|
5191
|
+
);
|
|
5192
|
+
}
|
|
5193
|
+
if (!mcpParams.maxTokens || mcpParams.maxTokens <= 0) {
|
|
5194
|
+
throw new McpError(
|
|
5195
|
+
"Invalid sampling request: maxTokens must be a positive number",
|
|
5196
|
+
"INVALID_REQUEST_ERROR" /* INVALID_REQUEST_ERROR */
|
|
5197
|
+
);
|
|
5198
|
+
}
|
|
5199
|
+
this.logger.debug("Converting MCP request to ADK format");
|
|
5200
|
+
const adkContents = this.convertMcpMessagesToADK(
|
|
5201
|
+
mcpParams.messages,
|
|
5202
|
+
mcpParams.systemPrompt
|
|
5203
|
+
);
|
|
5204
|
+
const requestModel = mcpParams.model || "gemini-2.0-flash";
|
|
5205
|
+
const adkRequest = new LlmRequest({
|
|
5206
|
+
model: requestModel,
|
|
5207
|
+
contents: adkContents,
|
|
5208
|
+
config: {
|
|
5209
|
+
temperature: mcpParams.temperature,
|
|
5210
|
+
maxOutputTokens: mcpParams.maxTokens
|
|
5211
|
+
}
|
|
5337
5212
|
});
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5213
|
+
this.logger.debug("Calling ADK sampling handler");
|
|
5214
|
+
const adkResponse = await this.samplingHandler(adkRequest);
|
|
5215
|
+
this.logger.debug("Converting ADK response to MCP format");
|
|
5216
|
+
const mcpResponse = this.convertADKResponseToMcp(
|
|
5217
|
+
adkResponse,
|
|
5218
|
+
requestModel
|
|
5219
|
+
);
|
|
5220
|
+
const responseValidation = CreateMessageResultSchema.safeParse(mcpResponse);
|
|
5221
|
+
if (!responseValidation.success) {
|
|
5222
|
+
this.logger.error(
|
|
5223
|
+
"Invalid MCP response generated:",
|
|
5224
|
+
responseValidation.error
|
|
5225
|
+
);
|
|
5226
|
+
throw new McpError(
|
|
5227
|
+
`Invalid response generated: ${responseValidation.error.message}`,
|
|
5228
|
+
"SAMPLING_ERROR" /* SAMPLING_ERROR */
|
|
5229
|
+
);
|
|
5230
|
+
}
|
|
5231
|
+
return mcpResponse;
|
|
5342
5232
|
} catch (error) {
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
}
|
|
5233
|
+
this.logger.error("Error handling sampling request:", error);
|
|
5234
|
+
if (error instanceof McpError) {
|
|
5235
|
+
throw error;
|
|
5236
|
+
}
|
|
5237
|
+
throw new McpError(
|
|
5238
|
+
`Sampling request failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
5239
|
+
"SAMPLING_ERROR" /* SAMPLING_ERROR */,
|
|
5240
|
+
error instanceof Error ? error : void 0
|
|
5241
|
+
);
|
|
5347
5242
|
}
|
|
5348
5243
|
}
|
|
5349
|
-
};
|
|
5350
|
-
|
|
5351
|
-
// src/tools/common/exit-loop-tool.ts
|
|
5352
|
-
init_logger();
|
|
5353
|
-
init_base_tool();
|
|
5354
|
-
var ExitLoopTool = class extends BaseTool {
|
|
5355
|
-
logger = new Logger({ name: "ExitLoopTool" });
|
|
5356
|
-
/**
|
|
5357
|
-
* Constructor for ExitLoopTool
|
|
5358
|
-
*/
|
|
5359
|
-
constructor() {
|
|
5360
|
-
super({
|
|
5361
|
-
name: "exit_loop",
|
|
5362
|
-
description: "Exits the loop. Call this function only when you are instructed to do so."
|
|
5363
|
-
});
|
|
5364
|
-
}
|
|
5365
5244
|
/**
|
|
5366
|
-
*
|
|
5245
|
+
* Convert MCP messages to ADK Content format
|
|
5367
5246
|
*/
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5247
|
+
convertMcpMessagesToADK(mcpMessages, systemPrompt) {
|
|
5248
|
+
const contents = [];
|
|
5249
|
+
if (systemPrompt) {
|
|
5250
|
+
contents.push({
|
|
5251
|
+
role: "user",
|
|
5252
|
+
// System messages are typically sent as user role in content
|
|
5253
|
+
parts: [{ text: systemPrompt }]
|
|
5254
|
+
});
|
|
5255
|
+
}
|
|
5256
|
+
const transformedMessages = mcpMessages.map(
|
|
5257
|
+
(mcpMessage) => this.convertSingleMcpMessageToADK(mcpMessage)
|
|
5258
|
+
);
|
|
5259
|
+
contents.push(...transformedMessages);
|
|
5260
|
+
return contents;
|
|
5371
5261
|
}
|
|
5372
|
-
};
|
|
5373
|
-
|
|
5374
|
-
// src/tools/common/get-user-choice-tool.ts
|
|
5375
|
-
init_logger();
|
|
5376
|
-
init_base_tool();
|
|
5377
|
-
import { Type as Type7 } from "@google/genai";
|
|
5378
|
-
var GetUserChoiceTool = class extends BaseTool {
|
|
5379
|
-
logger = new Logger({ name: "GetUserChoiceTool" });
|
|
5380
5262
|
/**
|
|
5381
|
-
*
|
|
5263
|
+
* Convert a single MCP message to ADK Content format
|
|
5382
5264
|
*/
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5265
|
+
convertSingleMcpMessageToADK(mcpMessage) {
|
|
5266
|
+
const adkRole = mcpMessage.role === "assistant" ? "model" : "user";
|
|
5267
|
+
const adkParts = this.convertMcpContentToADKParts(mcpMessage.content);
|
|
5268
|
+
const adkContent = {
|
|
5269
|
+
role: adkRole,
|
|
5270
|
+
parts: adkParts
|
|
5271
|
+
};
|
|
5272
|
+
this.logger.debug(
|
|
5273
|
+
`Converted MCP message - role: ${mcpMessage.role} -> ${adkRole}, content type: ${mcpMessage.content.type}`
|
|
5274
|
+
);
|
|
5275
|
+
return adkContent;
|
|
5389
5276
|
}
|
|
5390
5277
|
/**
|
|
5391
|
-
*
|
|
5278
|
+
* Convert MCP message content to ADK parts format
|
|
5392
5279
|
*/
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
description: "The question or prompt to show the user before presenting options"
|
|
5280
|
+
convertMcpContentToADKParts(mcpContent) {
|
|
5281
|
+
if (mcpContent.type === "text") {
|
|
5282
|
+
const textContent = mcpContent.text || "";
|
|
5283
|
+
return [{ text: textContent }];
|
|
5284
|
+
}
|
|
5285
|
+
if (mcpContent.type === "image") {
|
|
5286
|
+
const parts = [];
|
|
5287
|
+
if (mcpContent.text && typeof mcpContent.text === "string") {
|
|
5288
|
+
parts.push({ text: mcpContent.text });
|
|
5289
|
+
}
|
|
5290
|
+
if (mcpContent.data && typeof mcpContent.data === "string") {
|
|
5291
|
+
const mimeType = mcpContent.mimeType || "image/jpeg";
|
|
5292
|
+
parts.push({
|
|
5293
|
+
inlineData: {
|
|
5294
|
+
data: mcpContent.data,
|
|
5295
|
+
mimeType
|
|
5410
5296
|
}
|
|
5411
|
-
}
|
|
5412
|
-
required: ["options"]
|
|
5297
|
+
});
|
|
5413
5298
|
}
|
|
5414
|
-
|
|
5299
|
+
return parts.length > 0 ? parts : [{ text: "" }];
|
|
5300
|
+
}
|
|
5301
|
+
this.logger.warn(`Unknown MCP content type: ${mcpContent.type}`);
|
|
5302
|
+
const fallbackText = typeof mcpContent.data === "string" ? mcpContent.data : "";
|
|
5303
|
+
return [{ text: fallbackText }];
|
|
5415
5304
|
}
|
|
5416
5305
|
/**
|
|
5417
|
-
*
|
|
5418
|
-
* This is a long running operation that will return null initially
|
|
5419
|
-
* and the actual choice will be provided asynchronously
|
|
5306
|
+
* Convert ADK response to MCP response format
|
|
5420
5307
|
*/
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5308
|
+
convertADKResponseToMcp(adkResponse, model) {
|
|
5309
|
+
let responseText = "";
|
|
5310
|
+
if (typeof adkResponse === "string") {
|
|
5311
|
+
responseText = adkResponse;
|
|
5312
|
+
} else {
|
|
5313
|
+
if (adkResponse.content) {
|
|
5314
|
+
if (typeof adkResponse.content === "string") {
|
|
5315
|
+
responseText = adkResponse.content;
|
|
5316
|
+
} else if (adkResponse.content.parts) {
|
|
5317
|
+
responseText = adkResponse.content.parts.map((part) => {
|
|
5318
|
+
return typeof part.text === "string" ? part.text : "";
|
|
5319
|
+
}).join("");
|
|
5320
|
+
}
|
|
5321
|
+
}
|
|
5427
5322
|
}
|
|
5428
|
-
|
|
5429
|
-
|
|
5323
|
+
const mcpResponse = {
|
|
5324
|
+
model,
|
|
5325
|
+
// Use the model from the request
|
|
5326
|
+
role: "assistant",
|
|
5327
|
+
// ADK responses are always from assistant
|
|
5328
|
+
content: {
|
|
5329
|
+
type: "text",
|
|
5330
|
+
text: responseText
|
|
5331
|
+
}
|
|
5332
|
+
};
|
|
5333
|
+
this.logger.debug(`Received content: ${responseText}`);
|
|
5334
|
+
return mcpResponse;
|
|
5430
5335
|
}
|
|
5431
|
-
};
|
|
5432
|
-
|
|
5433
|
-
// src/tools/common/transfer-to-agent-tool.ts
|
|
5434
|
-
init_logger();
|
|
5435
|
-
init_base_tool();
|
|
5436
|
-
import { Type as Type8 } from "@google/genai";
|
|
5437
|
-
var TransferToAgentTool = class extends BaseTool {
|
|
5438
|
-
logger = new Logger({ name: "TransferToAgentTool" });
|
|
5439
5336
|
/**
|
|
5440
|
-
*
|
|
5337
|
+
* Update the ADK handler
|
|
5441
5338
|
*/
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5339
|
+
updateHandler(handler) {
|
|
5340
|
+
this.samplingHandler = handler;
|
|
5341
|
+
this.logger.debug("ADK sampling handler updated");
|
|
5342
|
+
}
|
|
5343
|
+
};
|
|
5344
|
+
function createSamplingHandler(handler) {
|
|
5345
|
+
return handler;
|
|
5346
|
+
}
|
|
5347
|
+
|
|
5348
|
+
// src/tools/mcp/utils.ts
|
|
5349
|
+
function withRetry(fn, instance, reinitMethod, maxRetries = 1) {
|
|
5350
|
+
return async (...args) => {
|
|
5351
|
+
let attempt = 0;
|
|
5352
|
+
while (attempt <= maxRetries) {
|
|
5353
|
+
try {
|
|
5354
|
+
return await fn.apply(instance, args);
|
|
5355
|
+
} catch (error) {
|
|
5356
|
+
const isClosedResourceError = error instanceof Error && (error.message.includes("closed") || error.message.includes("ECONNRESET") || error.message.includes("socket hang up"));
|
|
5357
|
+
if (!isClosedResourceError || attempt >= maxRetries) {
|
|
5358
|
+
throw error;
|
|
5359
|
+
}
|
|
5360
|
+
console.warn(
|
|
5361
|
+
`Resource closed, reinitializing (attempt ${attempt + 1}/${maxRetries + 1})...`
|
|
5362
|
+
);
|
|
5363
|
+
try {
|
|
5364
|
+
await reinitMethod(instance);
|
|
5365
|
+
} catch (reinitError) {
|
|
5366
|
+
console.error("Error reinitializing resources:", reinitError);
|
|
5367
|
+
throw new Error(`Failed to reinitialize resources: ${reinitError}`);
|
|
5368
|
+
}
|
|
5369
|
+
attempt++;
|
|
5370
|
+
}
|
|
5371
|
+
}
|
|
5372
|
+
throw new Error("Unexpected end of retry loop");
|
|
5373
|
+
};
|
|
5374
|
+
}
|
|
5375
|
+
|
|
5376
|
+
// src/tools/mcp/client.ts
|
|
5377
|
+
var McpClientService = class {
|
|
5378
|
+
config;
|
|
5379
|
+
client = null;
|
|
5380
|
+
transport = null;
|
|
5381
|
+
isClosing = false;
|
|
5382
|
+
mcpSamplingHandler = null;
|
|
5383
|
+
logger = new Logger({ name: "McpClientService" });
|
|
5384
|
+
constructor(config) {
|
|
5385
|
+
this.config = config;
|
|
5386
|
+
if (config.samplingHandler) {
|
|
5387
|
+
this.mcpSamplingHandler = new McpSamplingHandler(config.samplingHandler);
|
|
5388
|
+
}
|
|
5447
5389
|
}
|
|
5448
5390
|
/**
|
|
5449
|
-
*
|
|
5391
|
+
* Initializes and returns an MCP client based on configuration.
|
|
5392
|
+
* Will create a new client if one doesn't exist yet.
|
|
5450
5393
|
*/
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5394
|
+
async initialize() {
|
|
5395
|
+
if (this.isClosing) {
|
|
5396
|
+
throw new McpError(
|
|
5397
|
+
"Cannot initialize a client that is being closed",
|
|
5398
|
+
"resource_closed_error" /* RESOURCE_CLOSED_ERROR */
|
|
5399
|
+
);
|
|
5400
|
+
}
|
|
5401
|
+
if (this.client) {
|
|
5402
|
+
return this.client;
|
|
5403
|
+
}
|
|
5404
|
+
try {
|
|
5405
|
+
if (!this.transport) {
|
|
5406
|
+
this.transport = await this.createTransport();
|
|
5407
|
+
}
|
|
5408
|
+
const client = new Client(
|
|
5409
|
+
{
|
|
5410
|
+
name: this.config.name,
|
|
5411
|
+
version: "0.0.1"
|
|
5462
5412
|
},
|
|
5463
|
-
|
|
5413
|
+
{
|
|
5414
|
+
capabilities: {
|
|
5415
|
+
prompts: {},
|
|
5416
|
+
resources: {},
|
|
5417
|
+
tools: {},
|
|
5418
|
+
sampling: {}
|
|
5419
|
+
// Enable sampling capability
|
|
5420
|
+
}
|
|
5421
|
+
}
|
|
5422
|
+
);
|
|
5423
|
+
const connectPromise = client.connect(this.transport);
|
|
5424
|
+
if (this.config.timeout) {
|
|
5425
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
5426
|
+
setTimeout(() => {
|
|
5427
|
+
reject(
|
|
5428
|
+
new McpError(
|
|
5429
|
+
`MCP client connection timed out after ${this.config.timeout}ms`,
|
|
5430
|
+
"timeout_error" /* TIMEOUT_ERROR */
|
|
5431
|
+
)
|
|
5432
|
+
);
|
|
5433
|
+
}, this.config.timeout);
|
|
5434
|
+
});
|
|
5435
|
+
await Promise.race([connectPromise, timeoutPromise]);
|
|
5436
|
+
} else {
|
|
5437
|
+
await connectPromise;
|
|
5464
5438
|
}
|
|
5465
|
-
|
|
5439
|
+
await this.setupSamplingHandler(client);
|
|
5440
|
+
this.logger.debug("\u2705 MCP client connected successfully");
|
|
5441
|
+
this.client = client;
|
|
5442
|
+
return client;
|
|
5443
|
+
} catch (error) {
|
|
5444
|
+
await this.cleanupResources();
|
|
5445
|
+
if (!(error instanceof McpError)) {
|
|
5446
|
+
this.logger.error("Failed to initialize MCP client:", error);
|
|
5447
|
+
throw new McpError(
|
|
5448
|
+
`Failed to initialize MCP client: ${error instanceof Error ? error.message : String(error)}`,
|
|
5449
|
+
"connection_error" /* CONNECTION_ERROR */,
|
|
5450
|
+
error instanceof Error ? error : void 0
|
|
5451
|
+
);
|
|
5452
|
+
}
|
|
5453
|
+
throw error;
|
|
5454
|
+
}
|
|
5466
5455
|
}
|
|
5467
5456
|
/**
|
|
5468
|
-
*
|
|
5457
|
+
* Creates a transport based on the configuration.
|
|
5469
5458
|
*/
|
|
5470
|
-
async
|
|
5471
|
-
|
|
5472
|
-
|
|
5459
|
+
async createTransport() {
|
|
5460
|
+
try {
|
|
5461
|
+
if (this.config.transport.mode === "sse") {
|
|
5462
|
+
this.logger.debug(
|
|
5463
|
+
"\u{1F680} Initializing MCP client in SSE mode",
|
|
5464
|
+
this.config.transport.serverUrl
|
|
5465
|
+
);
|
|
5466
|
+
const headers = {
|
|
5467
|
+
...this.config.transport.headers || {},
|
|
5468
|
+
...this.config.headers || {}
|
|
5469
|
+
};
|
|
5470
|
+
return new SSEClientTransport(
|
|
5471
|
+
new URL(this.config.transport.serverUrl),
|
|
5472
|
+
{
|
|
5473
|
+
requestInit: {
|
|
5474
|
+
headers,
|
|
5475
|
+
...this.config.timeout ? { timeout: this.config.timeout } : {}
|
|
5476
|
+
}
|
|
5477
|
+
}
|
|
5478
|
+
);
|
|
5479
|
+
}
|
|
5480
|
+
this.logger.debug(
|
|
5481
|
+
"\u{1F680} Initializing MCP client in STDIO mode",
|
|
5482
|
+
this.config.transport.command
|
|
5483
|
+
);
|
|
5484
|
+
return new StdioClientTransport({
|
|
5485
|
+
command: this.config.transport.command,
|
|
5486
|
+
args: this.config.transport.args,
|
|
5487
|
+
env: this.config.transport.env
|
|
5488
|
+
});
|
|
5489
|
+
} catch (error) {
|
|
5490
|
+
throw new McpError(
|
|
5491
|
+
`Failed to create transport: ${error instanceof Error ? error.message : String(error)}`,
|
|
5492
|
+
"connection_error" /* CONNECTION_ERROR */,
|
|
5493
|
+
error instanceof Error ? error : void 0
|
|
5494
|
+
);
|
|
5495
|
+
}
|
|
5473
5496
|
}
|
|
5474
|
-
};
|
|
5475
|
-
|
|
5476
|
-
// src/tools/common/load-memory-tool.ts
|
|
5477
|
-
init_logger();
|
|
5478
|
-
init_base_tool();
|
|
5479
|
-
import { Type as Type9 } from "@google/genai";
|
|
5480
|
-
var LoadMemoryTool = class extends BaseTool {
|
|
5481
|
-
logger = new Logger({ name: "LoadMemoryTool" });
|
|
5482
5497
|
/**
|
|
5483
|
-
*
|
|
5498
|
+
* Re-initializes the MCP client when a session is closed.
|
|
5499
|
+
* Used by the retry mechanism.
|
|
5484
5500
|
*/
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5501
|
+
async reinitialize() {
|
|
5502
|
+
this.logger.debug("\u{1F504} Reinitializing MCP client after closed connection");
|
|
5503
|
+
await this.cleanupResources();
|
|
5504
|
+
this.client = null;
|
|
5505
|
+
this.transport = null;
|
|
5506
|
+
await this.initialize();
|
|
5490
5507
|
}
|
|
5491
5508
|
/**
|
|
5492
|
-
*
|
|
5509
|
+
* Cleans up resources associated with this client service.
|
|
5510
|
+
* Similar to Python's AsyncExitStack.aclose() functionality.
|
|
5493
5511
|
*/
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
query: {
|
|
5502
|
-
type: Type9.STRING,
|
|
5503
|
-
description: "The query to load memories for"
|
|
5512
|
+
async cleanupResources() {
|
|
5513
|
+
try {
|
|
5514
|
+
this.isClosing = true;
|
|
5515
|
+
if (this.client) {
|
|
5516
|
+
try {
|
|
5517
|
+
if (typeof this.client.close === "function") {
|
|
5518
|
+
await this.client.close();
|
|
5504
5519
|
}
|
|
5505
|
-
}
|
|
5506
|
-
|
|
5520
|
+
} catch (err) {
|
|
5521
|
+
}
|
|
5507
5522
|
}
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
*/
|
|
5513
|
-
async runAsync(args, context4) {
|
|
5514
|
-
this.logger.debug(`Executing load_memory with query: ${args.query}`);
|
|
5515
|
-
try {
|
|
5516
|
-
const searchResult = await context4.searchMemory(args.query);
|
|
5517
|
-
return {
|
|
5518
|
-
memories: searchResult.memories || [],
|
|
5519
|
-
count: searchResult.memories?.length || 0
|
|
5520
|
-
};
|
|
5523
|
+
if (this.transport && typeof this.transport.close === "function") {
|
|
5524
|
+
await this.transport.close();
|
|
5525
|
+
}
|
|
5526
|
+
this.logger.debug("\u{1F9F9} Cleaned up MCP client resources");
|
|
5521
5527
|
} catch (error) {
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5528
|
+
this.logger.error("Error cleaning up MCP resources:", error);
|
|
5529
|
+
} finally {
|
|
5530
|
+
this.client = null;
|
|
5531
|
+
this.transport = null;
|
|
5532
|
+
this.isClosing = false;
|
|
5527
5533
|
}
|
|
5528
5534
|
}
|
|
5529
|
-
};
|
|
5530
|
-
|
|
5531
|
-
// src/tools/common/load-artifacts-tool.ts
|
|
5532
|
-
init_base_tool();
|
|
5533
|
-
import { Type as Type10 } from "@google/genai";
|
|
5534
|
-
var LoadArtifactsTool = class extends BaseTool {
|
|
5535
|
-
constructor() {
|
|
5536
|
-
super({
|
|
5537
|
-
name: "load_artifacts",
|
|
5538
|
-
description: "Loads the artifacts and adds them to the session."
|
|
5539
|
-
});
|
|
5540
|
-
}
|
|
5541
5535
|
/**
|
|
5542
|
-
*
|
|
5536
|
+
* Call an MCP tool with retry capability if the session is closed.
|
|
5543
5537
|
*/
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
items: {
|
|
5554
|
-
type: Type10.STRING
|
|
5555
|
-
},
|
|
5556
|
-
description: "List of artifact names to load"
|
|
5557
|
-
}
|
|
5538
|
+
async callTool(name, args) {
|
|
5539
|
+
try {
|
|
5540
|
+
const wrappedCall = withRetry(
|
|
5541
|
+
async function() {
|
|
5542
|
+
const client = await this.initialize();
|
|
5543
|
+
return client.callTool({
|
|
5544
|
+
name,
|
|
5545
|
+
arguments: args
|
|
5546
|
+
});
|
|
5558
5547
|
},
|
|
5559
|
-
|
|
5548
|
+
this,
|
|
5549
|
+
async (instance) => await instance.reinitialize(),
|
|
5550
|
+
this.config.retryOptions?.maxRetries || 2
|
|
5551
|
+
);
|
|
5552
|
+
return await wrappedCall();
|
|
5553
|
+
} catch (error) {
|
|
5554
|
+
if (!(error instanceof McpError)) {
|
|
5555
|
+
throw new McpError(
|
|
5556
|
+
`Error calling tool "${name}": ${error instanceof Error ? error.message : String(error)}`,
|
|
5557
|
+
"tool_execution_error" /* TOOL_EXECUTION_ERROR */,
|
|
5558
|
+
error instanceof Error ? error : void 0
|
|
5559
|
+
);
|
|
5560
5560
|
}
|
|
5561
|
-
|
|
5561
|
+
throw error;
|
|
5562
|
+
}
|
|
5562
5563
|
}
|
|
5563
5564
|
/**
|
|
5564
|
-
*
|
|
5565
|
+
* Closes and cleans up all resources.
|
|
5566
|
+
* Should be called when the service is no longer needed.
|
|
5567
|
+
* Similar to Python's close() method.
|
|
5565
5568
|
*/
|
|
5566
|
-
async
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
+
async close() {
|
|
5570
|
+
this.logger.debug("\u{1F51A} Closing MCP client service");
|
|
5571
|
+
await this.cleanupResources();
|
|
5569
5572
|
}
|
|
5570
5573
|
/**
|
|
5571
|
-
*
|
|
5574
|
+
* Checks if the client is currently connected
|
|
5572
5575
|
*/
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
await this.appendArtifactsToLlmRequest(toolContext, llmRequest);
|
|
5576
|
+
isConnected() {
|
|
5577
|
+
return !!this.client && !this.isClosing;
|
|
5576
5578
|
}
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5579
|
+
async setupSamplingHandler(client) {
|
|
5580
|
+
if (!this.mcpSamplingHandler) {
|
|
5581
|
+
this.logger.debug(
|
|
5582
|
+
"\u26A0\uFE0F No sampling handler provided - sampling requests will be rejected"
|
|
5583
|
+
);
|
|
5584
|
+
return;
|
|
5585
|
+
}
|
|
5581
5586
|
try {
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
];
|
|
5595
|
-
if (llmRequest.appendInstructions) {
|
|
5596
|
-
llmRequest.appendInstructions(instructions);
|
|
5597
|
-
}
|
|
5598
|
-
if (llmRequest.contents && llmRequest.contents.length > 0) {
|
|
5599
|
-
const lastContent = llmRequest.contents[llmRequest.contents.length - 1];
|
|
5600
|
-
if (lastContent.parts && lastContent.parts.length > 0) {
|
|
5601
|
-
const firstPart = lastContent.parts[0];
|
|
5602
|
-
const functionResponse = this.extractFunctionResponse(firstPart);
|
|
5603
|
-
if (functionResponse && functionResponse.name === "load_artifacts") {
|
|
5604
|
-
const requestedArtifactNames = functionResponse.response.artifact_names || [];
|
|
5605
|
-
for (const artifactName of requestedArtifactNames) {
|
|
5606
|
-
try {
|
|
5607
|
-
const artifact = await toolContext.loadArtifact(artifactName);
|
|
5608
|
-
if (artifact) {
|
|
5609
|
-
llmRequest.contents.push({
|
|
5610
|
-
role: "user",
|
|
5611
|
-
parts: [
|
|
5612
|
-
{
|
|
5613
|
-
text: `Artifact ${artifactName} is:`
|
|
5614
|
-
},
|
|
5615
|
-
artifact
|
|
5616
|
-
]
|
|
5617
|
-
});
|
|
5618
|
-
}
|
|
5619
|
-
} catch (error) {
|
|
5620
|
-
console.error(
|
|
5621
|
-
`Failed to load artifact ${artifactName}:`,
|
|
5622
|
-
error
|
|
5623
|
-
);
|
|
5624
|
-
}
|
|
5587
|
+
client.setRequestHandler(
|
|
5588
|
+
CreateMessageRequestSchema2,
|
|
5589
|
+
async (request) => {
|
|
5590
|
+
try {
|
|
5591
|
+
this.logger.debug("Received sampling request:", request);
|
|
5592
|
+
const response = await this.mcpSamplingHandler.handleSamplingRequest(request);
|
|
5593
|
+
this.logger.debug("\u2705 Sampling request completed successfully");
|
|
5594
|
+
return response;
|
|
5595
|
+
} catch (error) {
|
|
5596
|
+
this.logger.error("\u274C Error handling sampling request:", error);
|
|
5597
|
+
if (error instanceof McpError) {
|
|
5598
|
+
throw error;
|
|
5625
5599
|
}
|
|
5600
|
+
throw new McpError(
|
|
5601
|
+
`Sampling request failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
5602
|
+
"SAMPLING_ERROR" /* SAMPLING_ERROR */,
|
|
5603
|
+
error instanceof Error ? error : void 0
|
|
5604
|
+
);
|
|
5626
5605
|
}
|
|
5627
5606
|
}
|
|
5628
|
-
|
|
5607
|
+
);
|
|
5608
|
+
this.logger.debug("\u{1F3AF} Sampling handler registered successfully");
|
|
5629
5609
|
} catch (error) {
|
|
5630
|
-
|
|
5610
|
+
this.logger.error("Failed to setup sampling handler:", error);
|
|
5611
|
+
this.logger.debug(
|
|
5612
|
+
"\u26A0\uFE0F Sampling handler registration failed, continuing without sampling support"
|
|
5613
|
+
);
|
|
5631
5614
|
}
|
|
5632
5615
|
}
|
|
5633
5616
|
/**
|
|
5634
|
-
*
|
|
5617
|
+
* Set a new ADK sampling handler
|
|
5635
5618
|
*/
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5619
|
+
setSamplingHandler(handler) {
|
|
5620
|
+
this.mcpSamplingHandler = new McpSamplingHandler(handler);
|
|
5621
|
+
if (this.client) {
|
|
5622
|
+
this.setupSamplingHandler(this.client).catch((error) => {
|
|
5623
|
+
this.logger.error("Failed to update ADK sampling handler:", error);
|
|
5624
|
+
});
|
|
5625
|
+
}
|
|
5626
|
+
}
|
|
5627
|
+
/**
|
|
5628
|
+
* Remove the sampling handler
|
|
5629
|
+
*/
|
|
5630
|
+
removeSamplingHandler() {
|
|
5631
|
+
this.mcpSamplingHandler = null;
|
|
5632
|
+
if (this.client) {
|
|
5633
|
+
try {
|
|
5634
|
+
this.client.removeRequestHandler?.("sampling/createMessage");
|
|
5635
|
+
} catch (error) {
|
|
5636
|
+
this.logger.error("Failed to remove sampling handler:", error);
|
|
5637
|
+
}
|
|
5639
5638
|
}
|
|
5640
|
-
return null;
|
|
5641
5639
|
}
|
|
5642
5640
|
};
|
|
5643
5641
|
|
|
@@ -5818,7 +5816,7 @@ function mcpSchemaToParameters(mcpTool) {
|
|
|
5818
5816
|
}
|
|
5819
5817
|
|
|
5820
5818
|
// src/tools/mcp/create-tool.ts
|
|
5821
|
-
async function
|
|
5819
|
+
async function convertMcpToolToBaseTool(mcpTool, client) {
|
|
5822
5820
|
try {
|
|
5823
5821
|
return new McpToolAdapter(mcpTool, client);
|
|
5824
5822
|
} catch (error) {
|
|
@@ -5848,7 +5846,7 @@ var McpToolAdapter = class extends BaseTool {
|
|
|
5848
5846
|
});
|
|
5849
5847
|
this.mcpTool = mcpTool;
|
|
5850
5848
|
this.client = client;
|
|
5851
|
-
if (client.reinitialize && typeof client.reinitialize === "function") {
|
|
5849
|
+
if (client && client.reinitialize && typeof client.reinitialize === "function") {
|
|
5852
5850
|
this.clientService = client;
|
|
5853
5851
|
}
|
|
5854
5852
|
}
|
|
@@ -6160,7 +6158,10 @@ var McpToolset = class {
|
|
|
6160
6158
|
for (const mcpTool of toolsResponse.tools) {
|
|
6161
6159
|
if (this.isSelected(mcpTool, context4)) {
|
|
6162
6160
|
try {
|
|
6163
|
-
const tool = await
|
|
6161
|
+
const tool = await convertMcpToolToBaseTool(
|
|
6162
|
+
mcpTool,
|
|
6163
|
+
client
|
|
6164
|
+
);
|
|
6164
6165
|
tools.push(tool);
|
|
6165
6166
|
} catch (toolError) {
|
|
6166
6167
|
console.error(
|
|
@@ -13233,7 +13234,6 @@ export {
|
|
|
13233
13234
|
McpAbi,
|
|
13234
13235
|
McpAtp,
|
|
13235
13236
|
McpBamm,
|
|
13236
|
-
McpClientService,
|
|
13237
13237
|
McpCoinGecko,
|
|
13238
13238
|
McpDiscord,
|
|
13239
13239
|
McpError,
|
|
@@ -13286,6 +13286,7 @@ export {
|
|
|
13286
13286
|
requestProcessor3 as codeExecutionRequestProcessor,
|
|
13287
13287
|
responseProcessor as codeExecutionResponseProcessor,
|
|
13288
13288
|
requestProcessor4 as contentRequestProcessor,
|
|
13289
|
+
convertMcpToolToBaseTool,
|
|
13289
13290
|
createAuthToolArguments,
|
|
13290
13291
|
createBranchContextForSubAgent,
|
|
13291
13292
|
createDatabaseSessionService,
|