### Run Function Tool and MCP Tool examples Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Run the Function Tool example by entering the examples/tool directory and executing go run . . For the MCP tool example, enter examples/mcp_tool, start the external server in streamalbe_server with go run main.go in the background, then run the main program with the -model flag set to deepseek-v4-flash. ```shell cd examples/tool go run . ``` ```shell cd examples/mcp_tool # Start the external server. cd streamalbe_server && go run main.go & # Run the main program. go run main.go -model="deepseek-v4-flash" ``` -------------------------------- ### Streamable function tool with weather streaming example Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Shows how to define a streamable function tool with input/output structs, implement a streaming function that sends chunks via tool.StreamWriter, create the tool with function.NewStreamableFunctionTool, and consume the stream using StreamableCall and reader.Recv. The example simulates latency with time.Sleep and handles io.EOF to end the stream. ```go // 1. Define input and output structures. type weatherInput struct { Location string `json:"location" jsonschema:"description=Location to query e.g. city name or coordinates"` } type weatherOutput struct { Weather string `json:"weather"` } // 2. Implement the streaming tool function. func getStreamableWeather(input weatherInput) *tool.StreamReader { stream := tool.NewStream(10) go func() { defer stream.Writer.Close() // Simulate progressively returning weather data. result := "Sunny, 25°C in " + input.Location for i := 0; i < len(result); i++ { chunk := tool.StreamChunk{ Content: weatherOutput{ Weather: result[i : i+1], }, Metadata: tool.Metadata{CreatedAt: time.Now()}, } if closed := stream.Writer.Send(chunk, nil); closed { break } time.Sleep(10 * time.Millisecond) // Simulate latency. } }() return stream.Reader } // 3. Create the streaming tool. weatherStreamTool := function.NewStreamableFunctionTool[weatherInput, weatherOutput]( getStreamableWeather, function.WithName("get_weather_stream"), function.WithDescription("Get weather information as a stream."), ) // 4. Use the streaming tool. reader, err := weatherStreamTool.StreamableCall(ctx, jsonArgs) if err != nil { return err } // Receive streaming data. for { chunk, err := reader.Recv() if err == io.EOF { break // End of stream. } if err != nil { return err } // Process each chunk. fmt.Printf("Received: %v\n", chunk.Content) } reader.Close() ``` -------------------------------- ### Register toolsearch plugin with Toolboxes and Runner Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Register the toolsearch plugin on a Runner, group deferred tools into namespaced Toolboxes, and inject the catalog into the system prompt. Preset tools are always advertised; deferred tools are loaded on demand via tool_search. The example caps search results to 5 and namespaces toolboxes to avoid collisions. ```go import ( "trpc.group/trpc-go/trpc-agent-go/agent/llmagent" "trpc.group/trpc-go/trpc-agent-go/plugin/toolsearch" "trpc.group/trpc-go/trpc-agent-go/runner" "trpc.group/trpc-go/trpc-agent-go/tool" ) ag := llmagent.New("assistant", llmagent.WithModel(modelInstance), // Preset tools are always available; deferred tools are loaded on demand. llmagent.WithTools(presetTools), llmagent.WithInstruction(` You can browse deferred tools in this catalog and load them via tool_search: {deferred_tools_section} `), ) ts := toolsearch.New( presetTools, toolsearch.WithToolboxes([]toolsearch.Toolbox{ { Name: "billing", Description: "Invoices, payments and refunds.", Tools: billingTools, }, { Name: "crm", Description: "Customer, contact and opportunity management.", Tools: crmTools, }, }), toolsearch.WithMaxResults(5), // cap the number of tools returned per search ) r := runner.NewRunner("app", ag, runner.WithPlugins(ts)) ``` -------------------------------- ### mcp_list_servers output with description Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Example output of the mcp_list_servers tool showing the server description field. The description is optional; when omitted, the output simply does not include a description property. ```json { "servers": [ { "name": "local_stdio_code", "transport": "stdio", "description": "Project management, documentation, and calendar tools." } ] } ``` -------------------------------- ### Hybrid Pattern Graph Creation Source: https://trpc-group.github.io/trpc-agent-go/graph?q= Hybrid pattern example: creates a state graph with the given schema. The code is incomplete in the source, showing only the import and graph creation. ```go import ( "trpc.group/trpc-go/trpc-agent-go/agent" "trpc.group/trpc-go/trpc-agent-go/agent/chainagent" "trpc.group/trpc-go/trpc-agent-go/agent/graphagent" "trpc.group/trpc-go/trpc-agent-go/graph" ) sg := graph.NewStateGraph(schema) ``` -------------------------------- ### Create AgentTool with HistoryScopeParentBranch Source: https://trpc-group.github.io/trpc-agent-go/tool?q= The first block is a numbered list placeholder (1-8) that appears to be a template for steps, but it is not actual code. The second block is a Go example showing how to create an AgentTool with persistent history options, including WithHistoryScope(HistoryScopeParentBranch). It is used to configure a child agent's history scope to share lineage with the parent. ```text 1 2 3 4 5 6 7 8 ``` ```go child := agenttool.NewTool( childAgent, agenttool.WithSkipSummarization(false), agenttool.WithStreamInner(true), agenttool.WithInnerTextMode(agenttool.InnerTextModeExclude), agenttool.WithResponseMode(agenttool.ResponseModeFinalOnly), agenttool.WithHistoryScope(agenttool.HistoryScopeParentBranch), ) ``` -------------------------------- ### Configure child Agent with independent parallel tool limits Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Configure a child Agent with its own parallel tool limits. Each child Agent invocation gets independent limits, so concurrent invocations can each run up to the configured number of search and fetch calls. Limits must be set on every child Agent that provides these tools. ```go child := llmagent.New( "worker", llmagent.WithModel(model), llmagent.WithTools([]tool.Tool{searchTool, fetchTool}), llmagent.WithEnableParallelTools(true), llmagent.WithToolConcurrencyConfig(tool.ConcurrencyConfig{ Groups: []tool.ConcurrencyGroup{ {ToolNames: []string{"search"}, Limit: 1}, {ToolNames: []string{"fetch"}, Limit: 1}, }, }), ) ``` -------------------------------- ### Human-in-the-Loop: Interrupt and Resume with graph.Interrupt Source: https://trpc-group.github.io/trpc-agent-go/graph?q= Use this snippet to implement a human-in-the-loop flow with an interrupt and resume. It shows how to add a node that interrupts for human review, then resumes execution with a resume command that provides the human's approval. The example uses graph.Interrupt to pause and wait for input, and graph.NewResumeCommand to supply the resume value. ```go import ( "context" "fmt" "trpc.group/trpc-go/trpc-agent-go/agent" "trpc.group/trpc-go/trpc-agent-go/graph" "trpc.group/trpc-go/trpc-agent-go/model" ) const ( stateKeyContent = "content" stateKeyDecision = "decision" interruptKeyReview = "review_key" nodeReview = "review" ) sg.AddNode(nodeReview, func(ctx context.Context, s graph.State) (any, error) { content := s[stateKeyContent].(string) // Interrupt and wait for human input result, err := graph.Interrupt(ctx, s, interruptKeyReview, fmt.Sprintf("Please review: %s", content)) if err != nil { return nil, err } return graph.State{stateKeyDecision: result}, nil }) // Resume execution (requires the agent package) cmd := graph.NewResumeCommand(). AddResumeValue(interruptKeyReview, "approved") eventCh, err := r.Run(ctx, userID, sessionID, model.NewUserMessage("resume"), agent.WithRuntimeState(map[string]any{ graph.CfgKeyLineageID: lineageID, graph.CfgKeyCheckpointID: checkpointID, graph.StateKeyCommand: cmd, }), ) ``` -------------------------------- ### Create and initialize an MCP ToolSet (STDIO) Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Create an MCP ToolSet using STDIO transport, optionally filter tools, initialize it, and attach it to an LLMAgent. The Init call fails fast on connection or tool-loading errors. The tool filter is optional and uses the include filter for the names "echo" and "add". ```go import "trpc.group/trpc-go/trpc-agent-go/tool/mcp" // Create an MCP ToolSet (STDIO example). mcpToolSet := mcp.NewMCPToolSet( mcp.ConnectionConfig{ Transport: "stdio", // Transport method. Command: "go", // Command to execute. Args: []string{"run", "./stdio_server/main.go"}, Timeout: 10 * time.Second, }, mcp.WithToolFilterFunc(tool.NewIncludeToolNamesFilter("echo", "add")), // Optional: tool filter. ) // (Optional but recommended) Explicitly initialize MCP: connect + initialize + list tools. if err := mcpToolSet.Init(ctx); err != nil { log.Fatalf("failed to initialize MCP toolset: %v", err) } // Integrate into an Agent. agent := llmagent.New("mcp-assistant", llmagent.WithModel(model), llmagent.WithToolSets([]tool.ToolSet{mcpToolSet})) ``` -------------------------------- ### Custom FilterFunc: allow tools with names starting with safe_ Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Custom filter function that only allows tools whose names start with "safe_". Option 1 applies the filter directly to runner.Run; Option 2 configures the agent with the same filter via llmagent.WithToolFilter. ```go // Option 1: // Custom filter: only allow tools with names starting with "safe_" filter := func(ctx context.Context, t tool.Tool) bool { declaration := t.Declaration() if declaration == nil { return false } return strings.HasPrefix(declaration.Name, "safe_") } eventChan, err := runner.Run(ctx, userID, sessionID, message, agent.WithToolFilter(filter), ) // Option 2: agent := llmagent.New("ai-assistant", llmagent.WithModel(model), llmagent.WithInstruction("You are a helpful AI assistant that can use various tools to help users."), llmagent.WithTools([]tool.Tool{ calculatorTool, timeTool, searchTool, }), llmagent.WithToolSets([]tool.ToolSet{ stdioToolSet, sseToolSet, streamableToolSet, }), llmagent.WithToolFilter(filter), ``` -------------------------------- ### Create Agent and Integrate Tools with MCP ToolSets Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Creates an Agent with function tools, a built-in DuckDuckGo tool, and MCP ToolSets for stdio, SSE, and streamable HTTP transports. Each MCP ToolSet is initialized with a 10-second timeout and returns an error if initialization fails. The Agent is configured with a model, instruction, and both single tools and ToolSets. ```go import ( "trpc.group/trpc-go/trpc-agent-go/agent/llmagent" "trpc.group/trpc-go/trpc-agent-go/tool" "trpc.group/trpc-go/trpc-agent-go/tool/function" "trpc.group/trpc-go/trpc-agent-go/tool/duckduckgo" "trpc.group/trpc-go/trpc-agent-go/tool/mcp" ) // Create function tools. calculatorTool := function.NewFunctionTool(calculator, function.WithName("calculator"), function.WithDescription("Perform basic mathematical operations.")) timeTool := function.NewFunctionTool(getCurrentTime, function.WithName("current_time"), function.WithDescription("Get the current time.")) // Create a built-in tool. searchTool := duckduckgo.NewTool() // Create MCP ToolSets (examples for different transports). stdioToolSet := mcp.NewMCPToolSet( mcp.ConnectionConfig{ Transport: "stdio", Command: "python", Args: []string{"-m", "my_mcp_server"}, Timeout: 10 * time.Second, }, ) if err := stdioToolSet.Init(ctx); err != nil { return fmt.Errorf("failed to initialize stdio MCP toolset: %w", err) } sseToolSet := mcp.NewMCPToolSet( mcp.ConnectionConfig{ Transport: "sse", ServerURL: "http://localhost:8080/sse", Timeout: 10 * time.Second, }, ) if err := sseToolSet.Init(ctx); err != nil { return fmt.Errorf("failed to initialize sse MCP toolset: %w", err) } streamableToolSet := mcp.NewMCPToolSet( mcp.ConnectionConfig{ Transport: "streamable_http", ServerURL: "http://localhost:3000/mcp", Timeout: 10 * time.Second, }, ) if err := streamableToolSet.Init(ctx); err != nil { return fmt.Errorf("failed to initialize streamable MCP toolset: %w", err) } // Create an Agent and integrate all tools. agent := llmagent.New("ai-assistant", llmagent.WithModel(model), llmagent.WithInstruction("You are a helpful AI assistant that can use various tools to help users."), // Add single tools (Tool interface). llmagent.WithTools([]tool.Tool{ calculatorTool, timeTool, searchTool, }), // Add ToolSets (ToolSet interface). llmagent.WithToolSets([]tool.ToolSet{ stdioToolSet, sseToolSet, streamableToolSet, }), ) ``` -------------------------------- ### Create a Function Tool with calculator and integrate into an Agent Source: https://trpc-group.github.io/trpc-agent-go/tool Shows how to define a Go function as a tool, wrap it with function.NewFunctionTool, and integrate it into an agent using llmagent.New. The function uses jsonschema tags for argument descriptions and returns a map with the result. ```go import "trpc.group/trpc-go/trpc-agent-go/tool/function" // 1. Define a tool function. func calculator(ctx context.Context, req struct { Operation string `json:"operation" jsonschema:"description=Operation type e.g. add/multiply"` A float64 `json:"a" jsonschema:"description=First operand"` B float64 `json:"b" jsonschema:"description=Second operand"` }) (map[string]interface{}, error) { switch req.Operation { case "add": return map[string]interface{}{"result": req.A + req.B}, nil case "multiply": return map[string]interface{}{"result": req.A * req.B}, nil default: return nil, fmt.Errorf("unsupported operation: %s", req.Operation) } } // 2. Create the tool. calculatorTool := function.NewFunctionTool( calculator, function.WithName("calculator"), function.WithDescription("Perform mathematical operations."), ) // 3. Integrate into an Agent. agent := llmagent.New("math-assistant", llmagent.WithModel(model), llmagent.WithTools([]tool.Tool{calculatorTool})) ``` -------------------------------- ### Per-Agent Filtering with agent.InvocationFromContext Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Per-agent filtering: uses agent.InvocationFromContext to get the current Agent name and only allows tools listed in agentAllowedTools for that Agent. Falls back to allowing all tools when context is missing or the Agent has no entry. ```go // Define allowed tools for each Agent agentAllowedTools := map[string]map[string]bool{ "math-agent": { "calculator": true, }, "time-agent": { "time_tool": true, }, } // Custom filter function: filter based on current Agent name filter := func(ctx context.Context, t tool.Tool) bool { declaration := t.Declaration() if declaration == nil { return false } toolName := declaration.Name // Get current Agent information from context inv, ok := agent.InvocationFromContext(ctx) if !ok || inv == nil { return true // fallback: allow all tools } agentName := inv.AgentName // Check if this tool is in the current Agent's allowed list allowedTools, exists := agentAllowedTools[agentName] if !exists { return true // fallback: allow all tools } return allowedTools[toolName] } eventChan, err := runner.Run(ctx, userID, sessionID, message, agent.WithToolFilter(filter), ) ``` -------------------------------- ### Simple calculator agent with streaming Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Complete Go program that creates a calculator tool, configures an LLM agent with streaming, and runs a session to handle tool calls and streamed responses. ```go package main import ( "context" "fmt" "trpc.group/trpc-go/trpc-agent-go/runner" "trpc.group/trpc-go/trpc-agent-go/agent/llmagent" "trpc.group/trpc-go/trpc-agent-go/model/openai" "trpc.group/trpc-go/trpc-agent-go/model" "trpc.group/trpc-go/trpc-agent-go/tool/function" ) func main() { // 1. Create a simple tool. calculatorTool := function.NewFunctionTool( func(ctx context.Context, req struct { Operation string `json:"operation" jsonschema:"description=Operation type e.g. add/multiply"` A float64 `json:"a" jsonschema:"description=First operand"` B float64 `json:"b" jsonschema:"description=Second operand"` }) (map[string]interface{}, error) { var result float64 switch req.Operation { case "add": result = req.A + req.B case "multiply": result = req.A * req.B default: return nil, fmt.Errorf("unsupported operation") } return map[string]interface{}{"result": result}, nil }, function.WithName("calculator"), function.WithDescription("Simple calculator."), ) // 2. Create model and Agent. llmModel := openai.New("deepseek-v4-flash") agent := llmagent.New("calculator-assistant", llmagent.WithModel(llmModel), llmagent.WithInstruction("You are a math assistant."), llmagent.WithTools([]tool.Tool{calculatorTool}), llmagent.WithGenerationConfig(model.GenerationConfig{Stream: true}), // Enable streaming output. ) // 3. Create Runner and execute. r := runner.NewRunner("math-app", agent) ctx := context.Background() userMessage := model.NewUserMessage("Please calculate 25 times 4.") eventChan, err := r.Run(ctx, "user1", "session1", userMessage) if err != nil { panic(err) } // 4. Handle responses. for event := range eventChan { if event.Error != nil { fmt.Printf("Error: %s\n", event.Error.Message) continue } // Display tool calls. if len(event.Response.Choices) > 0 && len(event.Response.Choices[0].Message.ToolCalls) > 0 { for _, toolCall := range event.Response.Choices[0].Message.ToolCalls { fmt.Printf("🔧 Call tool: %s\n", toolCall.Function.Name) fmt.Printf(" Params: %s\n", string(toolCall.Function.Arguments)) } } // Display streaming content. if len(event.Response.Choices) > 0 { fmt.Print(event.Response.Choices[0].Delta.Content) } if event.Done { break } } } ``` -------------------------------- ### Basic MCP Broker Integration with LLMAgent Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Basic integration of an MCP broker with an LLMAgent. Creates a broker with a stdio server and allows ad-hoc HTTP, then passes the broker's tools to the agent. Use this when you want to dynamically discover remote MCP tools rather than mounting them up front. ```go import ( "time" "trpc.group/trpc-go/trpc-agent-go/agent/llmagent" "trpc.group/trpc-go/trpc-agent-go/tool/mcp" "trpc.group/trpc-go/trpc-agent-go/tool/mcpbroker" ) broker := mcpbroker.New( mcpbroker.WithServers(map[string]mcp.ConnectionConfig{ "local_stdio_code": { Transport: "stdio", Command: "go", Args: []string{"run", "./stdio_server/main.go"}, Timeout: 10 * time.Second, Description: "Project management, documentation, and calendar tools.", }, }), mcpbroker.WithAllowAdHocHTTP(true), ) agent := llmagent.New( "assistant", llmagent.WithModel(model), llmagent.WithTools(broker.Tools()), ) ``` -------------------------------- ### Enable await_user_reply with llmagent.WithAwaitUserReplyTool and runner.WithAwaitUserReplyRouting Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Enable the optional await_user_reply framework tool and route the next user message to the same Agent. Requires both llmagent.WithAwaitUserReplyTool(true) and runner.WithAwaitUserReplyRouting(true). The route is one-shot: Runner consumes it on the next user turn and then clears it automatically. ```go profileAgent := llmagent.New("profile-agent", llmagent.WithAwaitUserReplyTool(true), llmagent.WithInstruction(` If you must ask the user for a missing field, call await_user_reply immediately before your question. `), ) r := runner.NewRunner( "crm-app", profileAgent, runner.WithAwaitUserReplyRouting(true), ) ``` -------------------------------- ### Register MCP server with toolsearch.WithMCPToolboxes Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Registers a live MCP server as a deferred namespace using the WithMCPToolboxes option. The ServerName field is used (not Name), and the ToolSet must implement tool.ToolSet. Tools from this server appear in the catalog as mcp__github__. ```go import ( "trpc.group/trpc-go/trpc-agent-go/plugin/toolsearch" ) ts := toolsearch.New( presetTools, toolsearch.WithMCPToolboxes([]toolsearch.MCPToolbox{ { ServerName: "github", // note: the field is ServerName, NOT Name Description: "GitHub issues / PR / repo", ToolSet: githubMCPToolSet, // a MCP client that implements tool.ToolSet }, }), ) // Tools from this server appear in the catalog as mcp__github__. ``` -------------------------------- ### Create LLMAgent with lookup_record tool and Runner Source: https://trpc-group.github.io/trpc-agent-go/promptiter Constructs an LLMAgent with a local function tool named lookup_record and creates a Runner for it. The tool is created with function.NewFunctionTool, using getFlightStatus as the underlying function and a description that the agent uses to decide when to call it. The agent is configured with a model, an instruction, and the tool, then wrapped in a Runner. ```go import ( "trpc.group/trpc-go/trpc-agent-go/agent/llmagent" "trpc.group/trpc-go/trpc-agent-go/runner" "trpc.group/trpc-go/trpc-agent-go/tool" "trpc.group/trpc-go/trpc-agent-go/tool/function" ) travelLookupTool := function.NewFunctionTool( getFlightStatus, function.WithName("lookup_record"), function.WithDescription("Look up a traveler loyalty-profile record."), ) candidateAgent := llmagent.New( candidateAgentName, llmagent.WithModel(candidateModel), llmagent.WithInstruction("Answer travel operations questions concisely. Use a tool only when its declaration clearly matches the request."), llmagent.WithTools([]tool.Tool{travelLookupTool}), ) candidateRunner := runner.NewRunner(candidateAppName, candidateAgent) ``` -------------------------------- ### Enforce todo completion with todoenforcer extension Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Install the todoenforcer extension to enforce that the agent completes all open todos before producing a final response. The extension contributes both todo_write and todo_declare_blocker; do not also pass a separate todo.New() through WithTools. To reuse tool/todo options such as WithStateKeyPrefix, WithClearOnAllDone, or WithNudgeHook, construct the tool yourself and pass it with todoenforcer.WithTodoTool(todo.New(...)). ```go import ( "trpc.group/trpc-go/trpc-agent-go/agent/extension/todoenforcer" "trpc.group/trpc-go/trpc-agent-go/agent/llmagent" "trpc.group/trpc-go/trpc-agent-go/tool/todo" ) agent := llmagent.New("todo-assistant", llmagent.WithModel(model), llmagent.WithInstruction(todo.DefaultToolPrompt), llmagent.WithExtensions(todoenforcer.New()), ) ``` -------------------------------- ### ToolMetadata and MetadataProvider Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Defines optional metadata for tools to describe execution behavior. Use MetadataProvider to expose ToolMetadata. Only explicit MCP hints are mapped; missing hints default to false. ```go type ToolMetadata struct { ReadOnly bool Destructive bool ConcurrencySafe bool SearchOrRead bool OpenWorld bool MaxResultSize int } type MetadataProvider interface { ToolMetadata() tool.ToolMetadata } ``` -------------------------------- ### Basic DuckDuckGo search tool with duckduckgo.NewTool and llmagent.New Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Creates a DuckDuckGo search tool using the default configuration and integrates it into an LLM agent. The tool is based on the DuckDuckGo Instant Answer API. ```go import "trpc.group/trpc-go/trpc-agent-go/tool/duckduckgo" // Create a DuckDuckGo search tool. searchTool := duckduckgo.NewTool() // Integrate into an Agent. searchAgent := llmagent.New("search-assistant", llmagent.WithModel(model), llmagent.WithTools([]tool.Tool{searchTool})) ``` -------------------------------- ### Attach Todo Tool to LLMAgent Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Creates a Todo tool and attaches it to an LLMAgent with a model and the default tool prompt. The Todo tool provides a persistent checklist for multi-step tasks. Replace todo.DefaultToolPrompt with your own instruction if needed. ```go import ( "trpc.group/trpc-go/trpc-agent-go/agent/llmagent" "trpc.group/trpc-go/trpc-agent-go/tool" "trpc.group/trpc-go/trpc-agent-go/tool/todo" ) todoTool := todo.New() agent := llmagent.New("todo-assistant", llmagent.WithModel(model), llmagent.WithInstruction(todo.DefaultToolPrompt), llmagent.WithTools([]tool.Tool{todoTool}), ) ``` -------------------------------- ### Create dynamic agent tool and register with parent agent Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Creates a dynamic agent tool and registers it with a parent agent. The parent agent can then invoke the dynamic agent tool, which runs a short-lived child agent invocation. The child agent's tools can be narrowed per call via the `tools` argument. ```go dynamicAgent := agenttool.NewDynamicTool() parent := llmagent.New( "assistant", llmagent.WithModel(modelInstance), llmagent.WithTools([]tool.Tool{ readFileTool, searchCodeTool, dynamicAgent, }), ) ``` -------------------------------- ### Enable dynamic MCP tool discovery with llmagent.WithRefreshToolSetsOnRun Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Configures an LLMAgent to dynamically refresh MCP ToolSets on each run using llmagent.WithRefreshToolSetsOnRun(true). The agent uses an OpenAI model and includes the MCP ToolSet. This ensures the agent sees the latest tools from the ToolSet when they change. ```go import ( "trpc.group/trpc-go/trpc-agent-go/agent/llmagent" "trpc.group/trpc-go/trpc-agent-go/model/openai" "trpc.group/trpc-go/trpc-agent-go/tool" "trpc.group/trpc-go/trpc-agent-go/tool/mcp" ) // 1. Create an MCP ToolSet (can be STDIO, SSE, or Streamable HTTP). mcpToolSet := mcp.NewMCPToolSet(connectionConfig) // 2. Create an LLMAgent and enable dynamic ToolSets refresh. agent := llmagent.New( "mcp-assistant", llmagent.WithModel(openai.New("gpt-4o-mini")), llmagent.WithToolSets([]tool.ToolSet{mcpToolSet}), llmagent.WithRefreshToolSetsOnRun(true), ) ``` -------------------------------- ### Enable parallel tools on LLMAgent with concurrency groups Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Enable parallel tool execution on an LLMAgent with per-group concurrency limits. The configuration has no effect unless parallel execution is enabled; non-positive group limits are ignored, and duplicate tool names in positive-limit groups cause WithToolConcurrencyConfig to panic. ```go agent := llmagent.New("ai-assistant", llmagent.WithModel(model), llmagent.WithTools(tools), llmagent.WithToolSets(toolSets), llmagent.WithEnableParallelTools(true), // Enable parallel execution. llmagent.WithToolConcurrencyConfig(tool.ConcurrencyConfig{ Groups: []tool.ConcurrencyGroup{ { ToolNames: []string{"subagent"}, Limit: 3, }, { // search calls are serial. ToolNames: []string{"search"}, Limit: 1, }, { // fetch calls are serial. ToolNames: []string{"fetch"}, Limit: 1, }, }, }), ) ``` -------------------------------- ### Wrap an Agent as a tool with agenttool.NewTool Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Wrap a child Agent as a reusable tool with AgentTool. Configure streaming, summarization, inner text mode, and response mode via the listed options. The parent Agent uses the tool through llmagent.WithTools. ```go import ( "trpc.group/trpc-go/trpc-agent-go/agent/llmagent" "trpc.group/trpc-go/trpc-agent-go/model" "trpc.group/trpc-go/trpc-agent-go/tool" agenttool "trpc.group/trpc-go/trpc-agent-go/tool/agent" ) // 1) Define a reusable child Agent (streaming recommended) mathAgent := llmagent.New( "math-specialist", llmagent.WithModel(modelInstance), llmagent.WithInstruction("You are a math specialist..."), llmagent.WithGenerationConfig(model.GenerationConfig{Stream: true}), ) // 2) Wrap as an Agent tool mathTool := agenttool.NewTool( mathAgent, // Optional, defaults to false. When set to true, the outer model summary will be skipped, // and the current round will end directly after tool.response. agenttool.WithSkipSummarization(false), // forward child Agent streaming events to parent flow. agenttool.WithStreamInner(true), // hide child assistant prose while still forwarding inner tool progress. agenttool.WithInnerTextMode(agenttool.InnerTextModeExclude), // return only the child Agent's final assistant message as the tool result. agenttool.WithResponseMode(agenttool.ResponseModeFinalOnly), ) // 3) Use in parent Agent parent := llmagent.New( "assistant", llmagent.WithModel(modelInstance), llmagent.WithGenerationConfig(model.GenerationConfig{Stream: true}), llmagent.WithTools([]tool.Tool{mathTool}), ) ``` -------------------------------- ### Use DispatchToolCalls mode with toolsearch.New Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Configures the toolsearch plugin to use DispatchToolCalls invocation mode, collapsing deferred tools behind tool_search and call_tool. Preset tools remain advertised separately. Each tool_search result includes the target tool's input_schema for building the call_tool params. ```go ts := toolsearch.New( presetTools, toolsearch.WithToolboxes(boxes), toolsearch.WithInvocationMode(toolsearch.DispatchToolCalls), ) // The deferred tool surface always consists of tool_search + call_tool; preset tools remain advertised separately. // Each tool_search result includes the target tool's input_schema so the // model can build the params object for the following call_tool invocation. ``` -------------------------------- ### Attach claudecode ToolSet to LLMAgent Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Creates a Claude Code ToolSet with a base directory and attaches it to an LLMAgent. The toolset is closed after use, and errors are fatal. Use this to expose Claude Code-style tools (Bash, Read, Write, etc.) to an agent. ```go import ( "log" "trpc.group/trpc-go/trpc-agent-go/agent/llmagent" "trpc.group/trpc-go/trpc-agent-go/tool" "trpc.group/trpc-go/trpc-agent-go/tool/claudecode" ) toolSet, err := claudecode.NewToolSet( claudecode.WithBaseDir("."), ) if err != nil { log.Fatal(err) } defer toolSet.Close() agent := llmagent.New( "claude-style-agent", llmagent.WithToolSets([]tool.ToolSet{toolSet}), ) ``` -------------------------------- ### Dynamic routing and fan-out with graph.Command Source: https://trpc-group.github.io/trpc-agent-go/graph?q= Shows command-based dynamic routing and fan-out in a graph. The decide node returns a Command with GoTo to route to A or B based on a state flag; the fanout node returns multiple Commands to dispatch parallel tasks to the same worker. When using command-based routing, static edges to GoTo targets are not needed; ensure target nodes exist and call SetFinishPoint where appropriate. ```go const ( nodeDecide = "decide" nodeA = "A" nodeB = "B" stateKeyFlag = "flag" ) sg.AddNode(nodeDecide, func(ctx context.Context, s graph.State) (any, error) { if s[stateKeyFlag].(bool) { return &graph.Command{Update: graph.State{"routed": nodeA}, GoTo: nodeA}, nil } return &graph.Command{Update: graph.State{"routed": nodeB}, GoTo: nodeB}, nil }) // Fan-out: dispatch multiple tasks to the same worker in parallel const ( nodeFanout = "fanout" nodeWorker = "worker" ) sg.AddNode(nodeFanout, func(ctx context.Context, s graph.State) (any, error) { cmds := []*graph.Command{ {Update: graph.State{"param": "A"}, GoTo: nodeWorker}, {Update: graph.State{"param": "B"}, GoTo: nodeWorker}, {Update: graph.State{"param": "C"}, GoTo: nodeWorker}, } return cmds, nil }) ``` -------------------------------- ### Configure AgentTool with ResponseModeFinalOnly and WithSkipSummarization Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Combines ResponseModeFinalOnly with WithSkipSummarization(true) to keep the child Agent's chain-of-work out of the tool result and to end the parent turn after tool.response instead of asking the parent model to summarize again. ```go childTool := agenttool.NewTool( childAgent, // Keep the child Agent's chain-of-work out of the tool result. agenttool.WithResponseMode(agenttool.ResponseModeFinalOnly), // End the parent turn after tool.response instead of asking the parent // model to summarize the result again. agenttool.WithSkipSummarization(true), ) ``` -------------------------------- ### mcpbroker WithClientOptionsProvider: URL allowlist via tmcp.WithHTTPBeforeRequest Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Use WithClientOptionsProvider to enforce egress control for ad-hoc HTTP targets. It runs after target resolution and WithHTTPHeaderInjector but before the underlying trpc-mcp-go client is created. Return (nil, nil) for no extra options; a non-nil error aborts before connect. For named stdio targets, populate ClientOptions.Stdio. ```go mcpbroker.New( mcpbroker.WithAllowAdHocHTTP(true), mcpbroker.WithClientOptionsProvider(func(ctx context.Context, req *mcpbroker.ClientOptionsRequest) (*mcpbroker.ClientOptions, error) { if req.Origin == mcpbroker.OriginAdhoc { return &mcpbroker.ClientOptions{ HTTP: []tmcp.ClientOption{ tmcp.WithHTTPBeforeRequest(func(ctx context.Context, httpReq *http.Request) error { if !hostAllowed(httpReq.URL) { return fmt.Errorf("MCP endpoint not allowed: %s", httpReq.URL.Host) } return nil }), }, }, nil } return nil, nil }), ) ``` -------------------------------- ### Advanced DuckDuckGo tool configuration with WithBaseURL, WithUserAgent, and WithHTTPClient Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Creates a DuckDuckGo search tool with custom configuration: a custom base URL, user agent, and HTTP client with a 15-second timeout. This allows overriding the default API endpoint and client behavior. ```go import ( "net/http" "time" "trpc.group/trpc-go/trpc-agent-go/tool/duckduckgo" ) // Custom configuration. searchTool := duckduckgo.NewTool( duckduckgo.WithBaseURL("https://api.duckduckgo.com"), duckduckgo.WithUserAgent("my-app/1.0"), duckduckgo.WithHTTPClient(&http.Client{ Timeout: 15 * time.Second, }), ) ``` -------------------------------- ### Customize todo tool with options Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Configures the todo tool with custom options: a state-key prefix, disabling auto-clear on all done, a custom nudge message, and a read-only nudge hook. The comments note that the default branch isolation usually makes the prefix unnecessary and that hooks must not modify state. ```go todoTool := todo.New( // Persist the list under a different state-key namespace. The // default already isolates by branch, so most users do not need // this. todo.WithStateKeyPrefix("temp:plan"), // By default the list is cleared once every item is completed, // so the next planning cycle starts from scratch. Set false to // keep the completed items visible. todo.WithClearOnAllDone(false), // Replace the default "after each write" nudge string. todo.WithNudgeMessage("Keep going; mark items as you finish them."), // Append extra guidance derived from the diff between the // previous list and the one just submitted (custom prompts, // telemetry, metrics, ...). Hooks are expected to be read-only. todo.WithNudgeHook(func(ctx context.Context, oldTodos, submitted []todo.Item) string { return "" }), ) ``` -------------------------------- ### Enable retry in LLMAgent with llmagent.WithToolCallRetryPolicy Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Creates an LLMAgent with a model, tools, and a tool call retry policy. The policy is applied to the agent's tool calls, enabling retry for transient failures. ```go agent := llmagent.New( "assistant", llmagent.WithModel(modelInstance), llmagent.WithTools([]tool.Tool{myTool}), llmagent.WithToolCallRetryPolicy(policy), ) ``` -------------------------------- ### Configure tool.RetryPolicy with MaxAttempts, InitialInterval, BackoffFactor, MaxInterval, and Jitter Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Configures a retry policy with a maximum of 3 attempts, an initial delay of 200ms, a backoff factor of 2.0, a maximum delay of 2 seconds, and jitter enabled. This policy is used to retry tool calls that fail due to transient issues. ```go policy := &tool.RetryPolicy{ MaxAttempts: 3, InitialInterval: 200 * time.Millisecond, BackoffFactor: 2.0, MaxInterval: 2 * time.Second, Jitter: true, } ``` -------------------------------- ### Manual tool execution with WithExternalTools and model.NewToolMessage Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Manual tool execution flow: declare an external tool, run the agent with WithExternalTools so the model can see it, read tool_calls from the response, execute externally, then send a role=tool message back so the model continues. The tool is not executed by the framework; the caller owns execution. ```go type declarationOnlyTool struct { decl *tool.Declaration } func (t *declarationOnlyTool) Declaration() *tool.Declaration { return t.decl } externalSearch := &declarationOnlyTool{ decl: &tool.Declaration{ Name: "external_search", Description: "Search a caller-owned system.", InputSchema: &tool.Schema{ Type: "object", Properties: map[string]*tool.Schema{ "query": {Type: "string"}, }, Required: []string{"query"}, }, }, } // Step 1: model returns tool_calls, but the tool is NOT executed. ch, err := r.Run(ctx, userID, sessionID, model.NewUserMessage("search ..."), agent.WithExternalTools([]tool.Tool{externalSearch}), ) // Step 2: extract tool_call_id + arguments from events (omitted). toolCallID := "call_123" toolResultJSON := `{"status":"ok","data":"..."}` // Step 3/4: send tool result as role=tool, then model continues. toolMsg := model.NewToolMessage(toolCallID, "external_search", toolResultJSON) ch, err = r.Run(ctx, userID, sessionID, toolMsg, agent.WithExternalTools([]tool.Tool{externalSearch}), ) ``` -------------------------------- ### Enable ad-hoc HTTP MCP targets with mcpbroker Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Enables ad-hoc HTTP MCP targets by setting WithAllowAdHocHTTP(true). In production, validate or allowlist the URL, domain, and path before treating ad-hoc HTTP as a trusted integration path. ```go broker := mcpbroker.New( mcpbroker.WithAllowAdHocHTTP(true), ) ``` -------------------------------- ### Exclude Specific Tools with tool.NewExcludeToolNamesFilter Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Use a blacklist approach to exclude unwanted tools. Option 1 creates an exclude filter and passes it to runner.Run; Option 2 builds an agent with the same filter. Both require the tool package import. ```go import "trpc.group/trpc-go/trpc-agent-go/tool" // Option 1: // Exclude text_tool and dangerous_tool, all other tools available filter := tool.NewExcludeToolNamesFilter("text_tool", "dangerous_tool") eventChan, err := runner.Run(ctx, userID, sessionID, message, agent.WithToolFilter(filter), ) // Option 2: agent := llmagent.New("ai-assistant", llmagent.WithModel(model), llmagent.WithInstruction("You are a helpful AI assistant that can use various tools to help users."), llmagent.WithTools([]tool.Tool{ calculatorTool, timeTool, searchTool, }), llmagent.WithToolSets([]tool.ToolSet{ stdioToolSet, sseToolSet, streamableToolSet, }), llmagent.WithToolFilter(filter), ) ``` -------------------------------- ### WithToolPermissionFilter: Gate deferred tools per caller Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Creates a tool search instance with a per-caller permission filter. The filter receives the context and candidate tool names, and returns a map indicating which tools the authenticated user is allowed to use. This filter is applied at catalog, search, and call time, but does not affect preset tools. ```go ts := toolsearch.New(presetTools, toolsearch.WithToolboxes(boxes), toolsearch.WithToolPermissionFilter(func(ctx context.Context, names []string) map[string]bool { user := auth.UserFromContext(ctx) allowed := make(map[string]bool, len(names)) for _, n := range names { allowed[n] = rbac.CanUse(user, n) } return allowed }), ) ``` -------------------------------- ### MCP ToolSet with SSE Transport Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Creates an MCP ToolSet using the SSE transport to connect to a remote server at the given URL. Includes an authorization header and a 10-second timeout. ```go mcpToolSet := mcp.NewMCPToolSet( mcp.ConnectionConfig{ Transport: "sse", ServerURL: "http://localhost:8080/sse", Timeout: 10 * time.Second, Headers: map[string]string{ "Authorization": "Bearer your-token", }, }, ) ``` -------------------------------- ### Attach multiple MCP ToolSets with distinct names Source: https://trpc-group.github.io/trpc-agent-go/tool?q= Attach multiple MCP ToolSets to an agent, each with a distinct name to avoid duplicate prefixed tool names. The default ToolSet name is "mcp", so a remote tool "search" becomes "mcp_search"; using WithName("github") and WithName("slack") produces names like "github_search" and "slack_search". If ToolSet.Name() is empty, no prefix is applied. ```go githubToolSet := mcp.NewMCPToolSet(githubCfg, mcp.WithName("github")) slackToolSet := mcp.NewMCPToolSet(slackCfg, mcp.WithName("slack")) agent := llmagent.New("multi-mcp", llmagent.WithModel(model), llmagent.WithToolSets([]tool.ToolSet{githubToolSet, slackToolSet}), ) // Model-visible names: github_search, slack_search, ... ```