View Plugin — Go
A complete example of a remote view plugin that adds a "Hello World" tab to the workspace dashboard.
Project Setup
mkdir hello-view-plugin && cd hello-view-plugin
go mod init github.com/yourorg/hello-view-plugin
Full Source — main.go
package main
import (
"context"
"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 = "9011"
}
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.RegisterViewProviderServer(s, srv)
log.Printf("hello-view plugin listening on :%s", port)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
type server struct {
pb.UnimplementedPluginServer
pb.UnimplementedViewProviderServer
}
// ---------------------------------------------------------------------------
// Plugin service
// ---------------------------------------------------------------------------
func (s *server) Register(_ context.Context, req *pb.RegisterRequest) (*pb.RegisterResponse, error) {
return &pb.RegisterResponse{
Name: "hello-view",
Capabilities: []pb.Capability{pb.Capability_VIEW_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 "hello-view" {
version = "1.0.0"
description = "A minimal view plugin that displays Hello World."
author = "yourorg"
icon = "eye"
type = "remote"
}
`
func (s *server) GetManifest(_ context.Context, _ *pb.GetManifestRequest) (*pb.GetManifestResponse, error) {
return &pb.GetManifestResponse{ManifestHcl: manifestHCL}, nil
}
// ---------------------------------------------------------------------------
// ViewProvider service
// ---------------------------------------------------------------------------
// ListViews tells the platform what views this plugin provides.
// Each view becomes a tab on the workspace page.
func (s *server) ListViews(_ context.Context, _ *pb.ListViewsRequest) (*pb.ListViewsResponse, error) {
return &pb.ListViewsResponse{
Views: []*pb.ViewDefinition{
{
Name: "hello",
Label: "Hello World",
Icon: "sparkles",
Scope: "workspace", // appears as a tab on the workspace page
},
},
}, nil
}
// RenderView returns HCL component definitions. The platform parses
// this HCL and renders it to HTML using its built-in component library.
// Plugins never produce HTML directly.
func (s *server) RenderView(_ context.Context, req *pb.RenderViewRequest) (*pb.RenderViewResponse, error) {
if req.GetViewName() != "hello" {
return &pb.RenderViewResponse{}, nil
}
wsID := req.GetWorkspaceId()
hcl := fmt.Sprintf(`
component "greeting" {
type = "text"
content = "Hello, World!"
style = "heading"
}
component "info" {
type = "text"
content = "This view is rendered by the hello-view plugin for workspace %s."
style = "muted"
}
component "status" {
type = "stat-group"
stat "uptime" {
label = "Status"
value = "Online"
}
stat "version" {
label = "Version"
value = "1.0.0"
}
}
component "details" {
type = "section"
title = "About This Plugin"
component "description" {
type = "text"
content = "This is a minimal example of a VIEW_PROVIDER plugin. It uses the platform's HCL component library to render UI without writing any HTML or JavaScript."
}
component "tip" {
type = "alert"
message = "View plugins can compose any combination of text, stats, tables, forms, code blocks, and chat components."
level = "info"
}
}
`, wsID)
return &pb.RenderViewResponse{ComponentHcl: hcl}, nil
}
// HandleRoute handles proxied HTTP requests. Return 404 if your
// view plugin doesn't need custom API routes.
func (s *server) HandleRoute(_ context.Context, _ *pb.RouteRequest) (*pb.RouteResponse, error) {
return &pb.RouteResponse{StatusCode: 404, Body: []byte(`{"error":"not found"}`)}, nil
}
// HandleStream handles bidirectional WebSocket streams. Return an
// error if your view plugin doesn't use streaming.
func (s *server) HandleStream(_ pb.ViewProvider_HandleStreamServer) error {
return fmt.Errorf("streaming not supported")
}
Run It
go run main.go
# hello-view plugin listening on :9011
Register in Workspace
workspace "my-workspace" {
plugin "hello-view" {
source = "remote://localhost:9011"
version = "1.0.0"
}
}
Start the workspace and navigate to its page. You'll see a "Hello World" tab alongside the Overview tab. Click it to see the rendered view.
How It Works
- Platform calls
ListViews— discovers the"hello"view with scope"workspace" - Platform adds a tab labeled "Hello World" to the workspace page
- When the user clicks the tab, platform calls
RenderView(view_name="hello") - Your plugin returns HCL component definitions
- Platform parses the HCL and renders it to HTML using its component library
- The rendered HTML is injected into the tab content area
Adding Interactivity
View plugins can go far beyond static content. See the HCL Component Reference for:
chat— WebSocket-powered chat with the agentform— Forms that POST to your plugin's HandleRoutetable— Data tables that fetch from your plugin's API routesbutton-group— Action buttons that trigger API calls