6464import io .reactivex .rxjava3 .core .Maybe ;
6565import io .reactivex .rxjava3 .core .Single ;
6666import java .lang .reflect .Constructor ;
67+ import java .lang .reflect .Method ;
6768import java .util .ArrayList ;
6869import java .util .List ;
6970import java .util .Map ;
@@ -782,6 +783,13 @@ public List<BaseTool> tools() {
782783 return canonicalTools ().toList ().blockingGet ();
783784 }
784785
786+ public List <BaseToolset > toolsets () {
787+ return toolsUnion .stream ()
788+ .filter (t -> t instanceof BaseToolset )
789+ .map (t -> (BaseToolset ) t )
790+ .collect (ImmutableList .toImmutableList ());
791+ }
792+
785793 public List <Object > toolsUnion () {
786794 return toolsUnion ;
787795 }
@@ -939,14 +947,14 @@ public static LlmAgent fromConfig(LlmAgentConfig config, String configAbsPath)
939947 return agent ;
940948 }
941949
942- private static ImmutableList <BaseTool > resolveTools (
950+ private static ImmutableList <Object > resolveTools (
943951 List <ToolConfig > toolConfigs , String configAbsPath ) throws ConfigurationException {
944952
945953 if (toolConfigs == null || toolConfigs .isEmpty ()) {
946954 return ImmutableList .of ();
947955 }
948956
949- List <BaseTool > resolvedTools = new ArrayList <>();
957+ List <Object > resolvedTools = new ArrayList <>();
950958
951959 for (ToolConfig toolConfig : toolConfigs ) {
952960 try {
@@ -956,20 +964,20 @@ private static ImmutableList<BaseTool> resolveTools(
956964 }
957965
958966 toolName = toolName .trim ();
959- BaseTool tool ;
967+ Object toolOrToolset ;
960968
961969 if (!toolName .contains ("." )) {
962- tool = resolveBuiltInTool (toolName , toolConfig );
970+ toolOrToolset = resolveBuiltInTool (toolName , toolConfig , configAbsPath );
963971 } else {
964972 // TODO: Support user-defined tools
965973 logger .debug ("configAbsPath is: {}" , configAbsPath );
966974 throw new ConfigurationException ("User-defined tools are not yet supported" );
967975 }
968976
969- resolvedTools .add (tool );
970- logger .debug ("Successfully resolved tool: {}" , toolConfig .name ());
977+ resolvedTools .add (toolOrToolset );
978+ logger .debug ("Successfully resolved tool/toolset : {}" , toolConfig .name ());
971979 } catch (Exception e ) {
972- String errorMsg = "Failed to resolve tool: " + toolConfig .name ();
980+ String errorMsg = "Failed to resolve tool/toolset : " + toolConfig .name ();
973981 logger .error (errorMsg , e );
974982 throw new ConfigurationException (errorMsg , e );
975983 }
@@ -978,25 +986,29 @@ private static ImmutableList<BaseTool> resolveTools(
978986 return ImmutableList .copyOf (resolvedTools );
979987 }
980988
981- private static BaseTool resolveBuiltInTool (String toolName , ToolConfig toolConfig )
982- throws ConfigurationException {
989+ private static Object resolveBuiltInTool (
990+ String toolName , ToolConfig toolConfig , String configAbsPath ) throws ConfigurationException {
983991 try {
984- logger .debug ("Resolving built-in tool: {}" , toolName );
992+ logger .debug ("Resolving built-in tool/toolset : {}" , toolName );
985993 // TODO: Handle built-in tool name end with Tool while config yaml file does not.
986994 // e.g.google_search in config yaml file and GoogleSearchTool in tool class name.
987995 String pascalCaseToolName = CaseFormat .LOWER_UNDERSCORE .to (CaseFormat .UPPER_CAMEL , toolName );
988996 String className = "com.google.adk.tools." + pascalCaseToolName ;
997+ // TODO: use tool registry to handle this instead of hardcoding.
998+ if (toolName .equals ("McpToolset" )) {
999+ className = "com.google.adk.tools.mcp.McpToolset" ;
1000+ }
9891001 Class <?> toolClass ;
9901002 try {
9911003 toolClass = Class .forName (className );
992- logger .debug ("Successfully loaded tool class: {}" , className );
1004+ logger .debug ("Successfully loaded tool/toolset class: {}" , className );
9931005 } catch (ClassNotFoundException e ) {
9941006 String fallbackClassName = "com.google.adk.tools." + toolName ;
9951007 try {
9961008 toolClass = Class .forName (fallbackClassName );
9971009 } catch (ClassNotFoundException e2 ) {
9981010 throw new ConfigurationException (
999- "Built-in tool not found: "
1011+ "Built-in tool/toolset not found: "
10001012 + toolName
10011013 + ". Expected class: "
10021014 + className
@@ -1006,48 +1018,87 @@ private static BaseTool resolveBuiltInTool(String toolName, ToolConfig toolConfi
10061018 }
10071019 }
10081020
1009- if (!BaseTool .class .isAssignableFrom (toolClass )) {
1021+ if (BaseTool .class .isAssignableFrom (toolClass )) {
1022+ logger .debug ("Tool {} is a sub-class of BaseTool." , toolConfig .name ());
1023+ @ SuppressWarnings ("unchecked" )
1024+ Class <? extends BaseTool > baseToolClass = (Class <? extends BaseTool >) toolClass ;
1025+ BaseTool tool = createToolInstance (baseToolClass , toolConfig , configAbsPath );
1026+ logger .info (
1027+ "Successfully created built-in tool: {} (class: {})" , toolName , toolClass .getName ());
1028+ return tool ;
1029+ } else if (BaseToolset .class .isAssignableFrom (toolClass )) {
1030+ logger .debug ("Tool {} is a sub-class of BaseToolset." , toolConfig .name ());
1031+ @ SuppressWarnings ("unchecked" )
1032+ Class <? extends BaseToolset > baseToolsetClass = (Class <? extends BaseToolset >) toolClass ;
1033+ BaseToolset toolset = createToolsetInstance (baseToolsetClass , toolConfig , configAbsPath );
1034+ logger .info (
1035+ "Successfully created built-in toolset: {} (class: {})" , toolName , toolClass .getName ());
1036+ return toolset ;
1037+ } else {
1038+ logger .info ("configAbsPath is: {}" , configAbsPath );
10101039 throw new ConfigurationException (
1011- "Built-in tool class " + toolClass .getName () + " does not extend BaseTool" );
1040+ "Built-in tool class "
1041+ + toolClass .getName ()
1042+ + " does not extend BaseTool or BaseToolset" );
10121043 }
10131044
1014- @ SuppressWarnings ("unchecked" )
1015- Class <? extends BaseTool > baseToolClass = (Class <? extends BaseTool >) toolClass ;
1016-
1017- BaseTool tool = createToolInstance (baseToolClass , toolConfig );
1018- logger .info (
1019- "Successfully created built-in tool: {} (class: {})" , toolName , toolClass .getName ());
1020-
1021- return tool ;
1022-
10231045 } catch (Exception e ) {
1024- logger .error ("Failed to create built-in tool: {}" , toolName , e );
1025- throw new ConfigurationException ("Failed to create built-in tool: " + toolName , e );
1046+ logger .error ("Failed to create built-in tool/toolset : {}" , toolName , e );
1047+ throw new ConfigurationException ("Failed to create built-in tool/toolset : " + toolName , e );
10261048 }
10271049 }
10281050
10291051 private static BaseTool createToolInstance (
1030- Class <? extends BaseTool > toolClass , ToolConfig toolConfig )
1052+ Class <? extends BaseTool > toolClass , ToolConfig toolConfig , String configAbsPath )
10311053 throws ConfigAgentUtils .ConfigurationException {
10321054
10331055 try {
1034- // TODO:implement constructor with ToolArgsConfig
1035- logger .debug ("ToolConfig is: {}" , toolConfig );
1056+ // First, try to use the fromConfig static method
1057+ try {
1058+ Method fromConfigMethod = toolClass .getMethod ("fromConfig" , ToolConfig .class , String .class );
1059+ return (BaseTool ) fromConfigMethod .invoke (null , toolConfig , configAbsPath );
1060+ } catch (ReflectiveOperationException e ) {
1061+ // Continue
1062+ }
10361063
1037- // Try default constructor
1064+ // Second, try default constructor
10381065 try {
1039- Constructor <? extends BaseTool > constructor = toolClass .getConstructor ();
1040- return constructor .newInstance ();
1066+ Constructor <? extends BaseTool > defaultConstructor = toolClass .getConstructor ();
1067+ return defaultConstructor .newInstance ();
10411068 } catch (NoSuchMethodException e ) {
10421069 // Continue
10431070 }
10441071
1072+ // Third, try constructor with ToolArgsConfig
1073+ // TODO: implement constructor with ToolArgsConfig
1074+
1075+ logger .debug ("ToolConfig is: {}" , toolConfig );
1076+ logger .debug ("configAbsPath is: {}" , configAbsPath );
10451077 throw new ConfigAgentUtils .ConfigurationException (
1046- "No suitable constructor found for tool class: " + toolClass .getName ());
1078+ "No suitable constructor or fromConfig method found for tool class: "
1079+ + toolClass .getName ());
10471080
1081+ } catch (ConfigAgentUtils .ConfigurationException e ) {
1082+ throw e ;
10481083 } catch (Exception e ) {
10491084 throw new ConfigAgentUtils .ConfigurationException (
10501085 "Failed to instantiate tool class: " + toolClass .getName (), e );
10511086 }
10521087 }
1088+
1089+ private static BaseToolset createToolsetInstance (
1090+ Class <? extends BaseToolset > toolsetClass , ToolConfig toolConfig , String configAbsPath )
1091+ throws ConfigAgentUtils .ConfigurationException {
1092+ try {
1093+ Method fromConfigMethod =
1094+ toolsetClass .getMethod ("fromConfig" , ToolConfig .class , String .class );
1095+ return (BaseToolset ) fromConfigMethod .invoke (null , toolConfig , configAbsPath );
1096+ } catch (ReflectiveOperationException e ) {
1097+ throw new ConfigAgentUtils .ConfigurationException (
1098+ "No suitable fromConfig method found for toolset class: " + toolsetClass .getName (), e );
1099+ } catch (RuntimeException e ) {
1100+ throw new ConfigAgentUtils .ConfigurationException (
1101+ "Failed to instantiate toolset class: " + toolsetClass .getName (), e );
1102+ }
1103+ }
10531104}
0 commit comments