Tool Plugin — Go
A complete example of a remote tool plugin that provides a get_weather tool to the agent.
Project Setup
mkdir weather-plugin && cd weather-plugin
go mod init github.com/yourorg/weather-plugin
Generate Go code from the proto files:
protoc --go_out=. --go-grpc_out=. \
proto/plugin.proto proto/tool.proto
Or copy the generated code from gen/agentplatform/v1/ in the AgentPlatform repository.
Full Source — main.go
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net"
"os"
"google.golang.org/grpc"
pb "github.com/dbb1dev/agentplatform/gen/agentplatform/v1"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "9010"
}
lis, err := net.Listen("tcp", ":"+port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
srv := &server{}
pb.RegisterPluginServer(s, srv)
pb.RegisterToolProviderServer(s, srv)
log.Printf("weather-plugin listening on :%s", port)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
// server implements both PluginServer and ToolProviderServer.
type server struct {
pb.UnimplementedPluginServer
pb.UnimplementedToolProviderServer
}
// ---------------------------------------------------------------------------
// Plugin service — required by all plugins
// ---------------------------------------------------------------------------
func (s *server) Register(_ context.Context, req *pb.RegisterRequest) (*pb.RegisterResponse, error) {
return &pb.RegisterResponse{
Name: "weather",
Capabilities: []pb.Capability{pb.Capability_TOOL_PROVIDER},
Metadata: map[string]string{"version": "1.0.0"},
}, nil
}
func (s *server) Health(_ context.Context, _ *pb.HealthRequest) (*pb.HealthResponse, error) {
return &pb.HealthResponse{Status: pb.Status_SERVING}, nil
}
const manifestHCL = `plugin "weather" {
version = "1.0.0"
description = "Provides current weather data for any city."
author = "yourorg"
icon = "cloud"
type = "remote"
config_schema {
field "api_key_env" {
type = "string"
required = true
placeholder = "e.g. WEATHER_API_KEY"
description = "Environment variable containing your weather API key"
}
field "unit" {
type = "string"
options = ["fahrenheit", "celsius"]
default = "fahrenheit"
description = "Temperature unit"
}
field "max_retries" {
type = "number"
default = "3"
description = "Maximum retry attempts for API calls"
}
field "debug" {
type = "bool"
default = "false"
description = "Enable debug logging"
}
}
}
`
func (s *server) GetManifest(_ context.Context, _ *pb.GetManifestRequest) (*pb.GetManifestResponse, error) {
return &pb.GetManifestResponse{ManifestHcl: manifestHCL}, nil
}
// ---------------------------------------------------------------------------
// ToolProvider service — exposes tools to the agent
// ---------------------------------------------------------------------------
func (s *server) ListTools(_ context.Context, _ *pb.ListToolsRequest) (*pb.ListToolsResponse, error) {
return &pb.ListToolsResponse{
Tools: []*pb.ToolDefinition{
{
Name: "get_weather",
Description: "Get the current weather for a city.",
ParametersJsonSchema: `{
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name (e.g. 'San Francisco')"
}
},
"required": ["city"]
}`,
},
},
}, nil
}
func (s *server) ExecuteTool(_ context.Context, req *pb.ExecuteToolRequest) (*pb.ExecuteToolResponse, error) {
if req.GetName() != "get_weather" {
return &pb.ExecuteToolResponse{
ResultJson: fmt.Sprintf(`{"error":"unknown tool: %s"}`, req.GetName()),
IsError: true,
}, nil
}
// Parse arguments
var args struct {
City string `json:"city"`
}
if err := json.Unmarshal([]byte(req.GetArgumentsJson()), &args); err != nil {
return &pb.ExecuteToolResponse{
ResultJson: `{"error":"invalid arguments"}`,
IsError: true,
}, nil
}
// In a real plugin you'd call a weather API here.
result, _ := json.Marshal(map[string]any{
"city": args.City,
"temperature": 72,
"unit": "F",
"condition": "Sunny",
})
return &pb.ExecuteToolResponse{
ResultJson: string(result),
}, nil
}
Run It
go run main.go
# weather-plugin listening on :9010
Register in Workspace
Add to your workspace HCL config:
workspace "my-workspace" {
plugin "weather" {
source = "remote://localhost:9010"
version = "1.0.0"
}
}
Start the workspace. The agent now has access to the get_weather tool and will call it when users ask about weather.
How It Works
- Platform connects and calls
Register— your plugin reportsTOOL_PROVIDERcapability - Platform calls
ListTools— receives your tool definitions with JSON Schema parameters - When the agent decides to use
get_weather, platform callsExecuteToolwith the arguments - Your plugin returns the result as JSON — the agent incorporates it into its response