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

  1. Platform calls ListViews — discovers the "hello" view with scope "workspace"
  2. Platform adds a tab labeled "Hello World" to the workspace page
  3. When the user clicks the tab, platform calls RenderView(view_name="hello")
  4. Your plugin returns HCL component definitions
  5. Platform parses the HCL and renders it to HTML using its component library
  6. 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: