> ## Documentation Index
> Fetch the complete documentation index at: https://docs.plugged.in/llms.txt
> Use this file to discover all available pages before exploring further.

# Go SDK

> Official Go SDK for the Plugged.in Library API with context support

# Go SDK

[![Go Reference](https://pkg.go.dev/badge/github.com/veriteknik/pluggedinkit-go.svg)](https://pkg.go.dev/github.com/veriteknik/pluggedinkit-go)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

The official Go SDK for interacting with Plugged.in's Library API. Features context-aware operations, comprehensive error handling, and full type safety.

<CardGroup cols={2}>
  <Card title="Go Package" icon="golang" href="https://pkg.go.dev/github.com/veriteknik/pluggedinkit-go">
    View on pkg.go.dev
  </Card>

  <Card title="GitHub Repository" icon="github" href="https://github.com/VeriTeknik/pluggedinkit-go">
    Source code and examples
  </Card>
</CardGroup>

## Installation

```bash theme={null}
go get github.com/veriteknik/pluggedinkit-go
```

### Requirements

* Go 1.18 or higher
* No external dependencies beyond the standard library

## Quick Start

```go theme={null}
package main

import (
    "context"
    "fmt"
    "log"

    pluggedin "github.com/veriteknik/pluggedinkit-go"
)

func main() {
    // Initialize client
    client := pluggedin.NewClient("your-api-key")
    // For local development:
    // client := pluggedin.NewClientWithOptions("your-api-key", "http://localhost:12005", nil)

    ctx := context.Background()

    // List documents
    docs, err := client.Documents.List(ctx, &pluggedin.DocumentFilters{
        Source: pluggedin.SourceAll,
        Limit:  10,
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Found %d documents\n", docs.Total)

    // Search documents
    results, err := client.Documents.Search(ctx, "machine learning", nil, 10, 0)
    if err != nil {
        log.Fatal(err)
    }
    for _, result := range results.Results {
        fmt.Printf("%s - Relevance: %.2f\n", result.Title, result.RelevanceScore)
    }

    // Query RAG
    answer, err := client.RAG.AskQuestion(ctx, "What are the main features?")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(answer)
}
```

## Authentication

Get your API key from your [Plugged.in Profile](https://plugged.in/profile/settings):

```go theme={null}
import (
    "os"
    pluggedin "github.com/veriteknik/pluggedinkit-go"
)

// Using environment variables (recommended)
client := pluggedin.NewClient(os.Getenv("PLUGGEDIN_API_KEY"))

// With custom configuration
import (
    "net/http"
    "time"
)

httpClient := &http.Client{
    Timeout: 120 * time.Second,
}

client := pluggedin.NewClientWithOptions(
    os.Getenv("PLUGGEDIN_API_KEY"),
    os.Getenv("PLUGGEDIN_BASE_URL"),
    httpClient,
)

// Update API key at runtime
client.SetAPIKey("new-api-key")

// Change base URL
client.SetBaseURL("http://localhost:12005")
```

## Core Features

### Document Management

#### List Documents

```go theme={null}
filters := &pluggedin.DocumentFilters{
    Source:        pluggedin.SourceAIGenerated,
    Tags:          []string{"report", "analysis"},
    Category:      pluggedin.CategoryDocumentation,
    Sort:          pluggedin.SortDateDesc,
    Limit:         20,
    Offset:        0,
    ModelProvider: "anthropic",
}

// Time filters
from := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
to := time.Date(2024, 12, 31, 23, 59, 59, 0, time.UTC)
filters.DateFrom = &from
filters.DateTo = &to

response, err := client.Documents.List(ctx, filters)
if err != nil {
    log.Fatal(err)
}

for _, doc := range response.Documents {
    fmt.Printf("%s (%d bytes)\n", doc.Title, doc.FileSize)
    fmt.Printf("  Created: %s\n", doc.CreatedAt)
    fmt.Printf("  Tags: %v\n", doc.Tags)
}
```

#### Get Document

```go theme={null}
// Get document metadata only
doc, err := client.Documents.Get(ctx, "document-id", false, false)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Title: %s\n", doc.Title)
fmt.Printf("Version: %d\n", doc.Version)

// Get document with content and version history
docWithContent, err := client.Documents.Get(ctx, "document-id", true, true)
if err != nil {
    log.Fatal(err)
}
fmt.Println(docWithContent.Content)

// Check last access time
if docWithContent.LastAccessAt != nil {
    fmt.Printf("Last accessed: %s\n", *docWithContent.LastAccessAt)
}
```

#### Search Documents

```go theme={null}
filters := map[string]interface{}{
    "modelProvider": "anthropic",
    "dateFrom":      "2024-01-01T00:00:00Z",
    "tags":          "finance,q4",
}

results, err := client.Documents.Search(ctx, "quarterly report", filters, 10, 0)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Found %d results for query: %s\n", results.Total, results.Query)

for _, result := range results.Results {
    fmt.Printf("\n%s\n", result.Title)
    fmt.Printf("  Document ID: %s\n", result.DocumentID)
    fmt.Printf("  Relevance: %.2f\n", result.RelevanceScore)
    fmt.Printf("  Snippet: %s\n", result.Snippet)
    fmt.Printf("  Tags: %v\n", result.Tags)
}
```

#### Create AI-Generated Document

```go theme={null}
request := &pluggedin.CreateDocumentRequest{
    Title:   "API Integration Guide",
    Content: "# API Integration Guide\n\n## Introduction\n\n...",
    Metadata: map[string]interface{}{
        "format":   "md",
        "category": "documentation",
        "tags":     []string{"api", "guide"},
        "model": map[string]string{
            "name":     "claude-3-opus",
            "provider": "anthropic",
            "version":  "20240229",
        },
        "prompt":     "Create an API integration guide",
        "visibility": "workspace",
    },
}

doc, err := client.Documents.Create(ctx, request)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Created document: %s\n", doc.ID)
fmt.Printf("Version: %d\n", doc.Version)
```

#### Update Document

```go theme={null}
request := &pluggedin.UpdateDocumentRequest{
    Operation: pluggedin.OpAppend,
    Content:   "\n\n## New Section\n\nAdditional content here.",
    Metadata: map[string]interface{}{
        "changeSummary": "Added implementation details section",
        "model": map[string]string{
            "name":     "gpt-4",
            "provider": "openai",
            "version":  "0613",
        },
    },
}

response, err := client.Documents.Update(ctx, "document-id", request)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Updated to version %d\n", response.Version)
fmt.Printf("Updated at: %s\n", response.UpdatedAt)
```

#### Delete Document

```go theme={null}
err := client.Documents.Delete(ctx, "document-id")
if err != nil {
    log.Fatal(err)
}
fmt.Println("Document deleted successfully")
```

### RAG Operations

#### Simple Query

```go theme={null}
// Basic question answering
answer, err := client.RAG.AskQuestion(ctx, "What are the deployment procedures?")
if err != nil {
    log.Fatal(err)
}
fmt.Println("Answer:", answer)

// Check availability
status, err := client.RAG.CheckAvailability(ctx)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("RAG Available: %v\n", status["available"])

// Get storage statistics
stats, err := client.RAG.GetStorageStats(ctx, "user-id-from-dashboard")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Documents: %d\n", stats.DocumentsCount)
fmt.Printf("Estimated storage: %.2f MB\n", stats.EstimatedStorageMB)
```

#### Query with Sources

```go theme={null}
result, err := client.RAG.QueryWithSources(
    ctx,
    "Explain the authentication flow",
    "",
)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Answer: %s\n", result.Answer)
fmt.Println("\nSources:")
for index, source := range result.Sources {
    var documentID string
    if index < len(result.DocumentIDs) {
        documentID = result.DocumentIDs[index]
    }
    fmt.Printf("- %s (%s)\n", source, documentID)
}
```

#### Find Relevant Documents

```go theme={null}
documents, err := client.RAG.FindRelevantDocuments(
    ctx,
    "user authentication",
    "",
    5, // Top 5 documents
)
if err != nil {
    log.Fatal(err)
}

for _, doc := range documents {
    fmt.Printf("- %s (%s)\n", doc.Source, doc.DocumentID)
}
```

### File Uploads

> **Important:** The Go SDK no longer exposes binary upload helpers. All previous
> upload functions now return descriptive errors so legacy code fails fast.
> Continue using `DocumentsService.Create` for AI-generated content and the
> Plugged.in web interface (or forthcoming ingestion tools) for binary files.

## Error Handling

The SDK provides typed error structs for comprehensive error handling:

```go theme={null}
doc, err := client.Documents.Get(ctx, "invalid-id", false, false)
if err != nil {
    switch e := err.(type) {
    case *pluggedin.AuthenticationError:
        fmt.Println("Invalid API key - please check your credentials")
        // Refresh API key logic

    case *pluggedin.RateLimitError:
        fmt.Printf("Rate limited. Retry after %d seconds\n", e.RetryAfter)
        // Implement retry with backoff
        time.Sleep(time.Duration(e.RetryAfter) * time.Second)

    case *pluggedin.NotFoundError:
        fmt.Printf("Document not found: %s\n", e.ID)
        // Handle missing document

    case *pluggedin.ValidationError:
        fmt.Printf("Validation error on field '%s': %s\n", e.Field, e.Message)
        // Fix validation issues

    case *pluggedin.APIError:
        fmt.Printf("API error (%d): %s\n", e.StatusCode, e.Message)
        if e.Details != nil {
            fmt.Printf("Details: %v\n", e.Details)
        }

    default:
        fmt.Printf("Unexpected error: %v\n", err)
    }
}
```

## PAP Agents

The SDK provides full support for managing PAP (Plugged.in Agent Protocol) agents:

### Creating Agents

```go theme={null}
result, err := client.Agents.Create(ctx, pluggedinkit.CreateAgentRequest{
    Name:        "my-agent",
    Description: stringPtr("My autonomous PAP agent"),
    Resources: &pluggedinkit.ResourceRequirements{
        CPURequest:    stringPtr("100m"),
        MemoryRequest: stringPtr("256Mi"),
        CPULimit:      stringPtr("1000m"),
        MemoryLimit:   stringPtr("1Gi"),
    },
})
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Agent created: %s\n", result.Agent.UUID)
fmt.Printf("DNS: %s\n", result.Agent.DNSName) // my-agent.is.plugged.in

func stringPtr(s string) *string { return &s }
```

### Listing and Managing Agents

```go theme={null}
// List all agents
agents, err := client.Agents.List(ctx)

// Get agent details
details, err := client.Agents.Get(ctx, agentID)
fmt.Println(details.RecentHeartbeats)
fmt.Println(details.RecentMetrics)
fmt.Println(details.LifecycleEvents)

// Export agent data
exportData, err := client.Agents.Export(ctx, agentID, true, 1000)

// Delete agent
deleteResp, err := client.Agents.Delete(ctx, agentID)
```

### Heartbeats and Metrics

> **CRITICAL**: Heartbeats contain ONLY liveness data. Resource telemetry goes in metrics (separate channel). This separation is PAP's zombie prevention superpower.

```go theme={null}
// Send heartbeat (liveness only)
_, err = client.Agents.Heartbeat(ctx, agentID, "IDLE", 330.0)

// Send metrics (resource telemetry)
customMetrics := map[string]interface{}{
    "queue_depth": 3,
    "cache_hit_rate": 0.85,
}
_, err = client.Agents.Metrics(ctx, agentID, 12.5, 128, 45, customMetrics)
```

### Concurrent Agent Operations

```go theme={null}
func monitorAgents(client *pluggedinkit.Client, agentIDs []string) {
    ctx := context.Background()

    var wg sync.WaitGroup

    for _, agentID := range agentIDs {
        wg.Add(1)
        go func(id string) {
            defer wg.Done()

            // Send heartbeat and metrics concurrently
            errChan := make(chan error, 2)

            go func() {
                _, err := client.Agents.Heartbeat(ctx, id, "IDLE", 330.0)
                errChan <- err
            }()

            go func() {
                _, err := client.Agents.Metrics(ctx, id, 10.0, 100, 20, nil)
                errChan <- err
            }()

            // Wait for both to complete
            for i := 0; i < 2; i++ {
                if err := <-errChan; err != nil {
                    log.Printf("Error for agent %s: %v", id, err)
                }
            }
        }(agentID)
    }

    wg.Wait()
}
```

### Agent States

Agents follow the PAP lifecycle state machine:

```
NEW → PROVISIONED → ACTIVE ↔ DRAINING → TERMINATED
                       ↓ (error)
                     KILLED
```

```go theme={null}
// Poll until agent is active
for {
    details, err := client.Agents.Get(ctx, agentID)
    if err != nil {
        return err
    }

    if details.Agent.State == "ACTIVE" {
        fmt.Println("Agent is ready!")
        break
    }

    time.Sleep(5 * time.Second)
}
```

See [PAP Agents Documentation](/agents/overview) for complete details on the Plugged.in Agent Protocol.

## Context and Cancellation

All SDK methods accept a context for cancellation and timeout control:

```go theme={null}
// With timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

docs, err := client.Documents.List(ctx, nil)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        fmt.Println("Request timed out")
    }
}

// With cancellation
ctx, cancel := context.WithCancel(context.Background())

// Cancel from another goroutine
go func() {
    time.Sleep(5 * time.Second)
    cancel()
}()

// This will be cancelled after 5 seconds
answer, err := client.RAG.AskQuestion(ctx, "Long running query")
if err != nil {
    if errors.Is(err, context.Canceled) {
        fmt.Println("Request was cancelled")
    }
}
```

## Concurrent Operations

Leverage Go's concurrency for parallel operations:

```go theme={null}
func fetchDocumentsConcurrently(client *pluggedin.Client) {
    ctx := context.Background()
    documentIDs := []string{"doc1", "doc2", "doc3", "doc4", "doc5"}

    type result struct {
        doc *pluggedin.Document
        err error
    }

    results := make(chan result, len(documentIDs))

    // Fetch documents concurrently
    for _, id := range documentIDs {
        go func(docID string) {
            doc, err := client.Documents.Get(ctx, docID, true, false)
            results <- result{doc, err}
        }(id)
    }

    // Collect results
    for i := 0; i < len(documentIDs); i++ {
        r := <-results
        if r.err != nil {
            fmt.Printf("Error fetching document: %v\n", r.err)
        } else {
            fmt.Printf("Fetched: %s\n", r.doc.Title)
        }
    }
}
```

## Testing

### Unit Testing with Interfaces

```go theme={null}
// Define interface for testing
type DocumentService interface {
    List(ctx context.Context, filters *DocumentFilters) (*ListDocumentsResponse, error)
    Get(ctx context.Context, id string, includeContent, includeVersions bool) (*Document, error)
}

// Test function
func TestDocumentList(t *testing.T) {
    // Mock implementation
    mockService := &MockDocumentService{
        ListFunc: func(ctx context.Context, filters *DocumentFilters) (*ListDocumentsResponse, error) {
            return &ListDocumentsResponse{
                Documents: []Document{{Title: "Test Doc"}},
                Total:     1,
            }, nil
        },
    }

    // Test
    response, err := mockService.List(context.Background(), nil)
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }

    if response.Total != 1 {
        t.Errorf("expected 1 document, got %d", response.Total)
    }
}
```

### Integration Testing

```go theme={null}
func TestDocumentLifecycle(t *testing.T) {
    if testing.Short() {
        t.Skip("skipping integration test")
    }

    client := pluggedin.NewClient(os.Getenv("PLUGGEDIN_API_KEY"))
    ctx := context.Background()

    // Create document
    doc, err := client.Documents.Create(ctx, &CreateDocumentRequest{
        Title:   "Test Document",
        Content: "Test content",
        Metadata: map[string]interface{}{
            "tags": []string{"test"},
        },
    })
    if err != nil {
        t.Fatalf("failed to create document: %v", err)
    }

    // Cleanup
    defer func() {
        _ = client.Documents.Delete(ctx, doc.ID)
    }()

    // Update document
    updateResp, err := client.Documents.Update(ctx, doc.ID, &UpdateDocumentRequest{
        Operation: OpAppend,
        Content:   "\nUpdated content",
    })
    if err != nil {
        t.Fatalf("failed to update document: %v", err)
    }

    if updateResp.Version <= doc.Version {
        t.Errorf("expected version to increase, got %d", updateResp.Version)
    }
}
```

### Benchmarking

```go theme={null}
func BenchmarkDocumentSearch(b *testing.B) {
    client := pluggedin.NewClient(os.Getenv("PLUGGEDIN_API_KEY"))
    ctx := context.Background()

    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        _, err := client.Documents.Search(ctx, "test query", nil, 10, 0)
        if err != nil {
            b.Fatalf("search failed: %v", err)
        }
    }
}
```

## Advanced Configuration

### Custom HTTP Transport

```go theme={null}
import (
    "net/http"
    "net/url"
    "time"
)

// Custom transport with proxy
proxyURL, _ := url.Parse("http://proxy.example.com:8080")
transport := &http.Transport{
    Proxy:           http.ProxyURL(proxyURL),
    MaxIdleConns:    100,
    IdleConnTimeout: 90 * time.Second,
}

httpClient := &http.Client{
    Transport: transport,
    Timeout:   2 * time.Minute,
}

client := pluggedin.NewClientWithOptions(
    "your-api-key",
    "https://plugged.in",
    httpClient,
)
```

### Environment Variables

```go theme={null}
// Load from environment
type Config struct {
    APIKey  string
    BaseURL string
    Timeout time.Duration
}

func LoadConfig() *Config {
    timeout, _ := time.ParseDuration(os.Getenv("PLUGGEDIN_TIMEOUT"))
    if timeout == 0 {
        timeout = 60 * time.Second
    }

    return &Config{
        APIKey:  os.Getenv("PLUGGEDIN_API_KEY"),
        BaseURL: os.Getenv("PLUGGEDIN_BASE_URL"),
        Timeout: timeout,
    }
}

// Use configuration
config := LoadConfig()
httpClient := &http.Client{Timeout: config.Timeout}
client := pluggedin.NewClientWithOptions(config.APIKey, config.BaseURL, httpClient)
```

## Rate Limiting

The SDK handles rate limiting automatically:

* **API Endpoints**: 60 requests per minute
* **Document Search**: 10 requests per hour for AI documents
* **RAG Queries**: Subject to plan limits

```go theme={null}
// Implement retry with exponential backoff
func retryWithBackoff(ctx context.Context, fn func() error) error {
    maxRetries := 5
    baseDelay := 1 * time.Second

    for i := 0; i < maxRetries; i++ {
        err := fn()
        if err == nil {
            return nil
        }

        // Check if it's a rate limit error
        if rateLimitErr, ok := err.(*pluggedin.RateLimitError); ok {
            delay := time.Duration(rateLimitErr.RetryAfter) * time.Second
            fmt.Printf("Rate limited, waiting %v\n", delay)
            time.Sleep(delay)
            continue
        }

        // Exponential backoff for other errors
        delay := baseDelay * time.Duration(1<<i)
        fmt.Printf("Error: %v, retrying in %v\n", err, delay)
        time.Sleep(delay)
    }

    return fmt.Errorf("max retries exceeded")
}
```

## Examples

Complete working examples are available in the [GitHub repository](https://github.com/VeriTeknik/pluggedinkit-go/tree/main/examples):

* [Basic Usage](https://github.com/VeriTeknik/pluggedinkit-go/blob/main/examples/basic/main.go)
* [Document Management](https://github.com/VeriTeknik/pluggedinkit-go/blob/main/examples/documents/main.go)
* [RAG Queries](https://github.com/VeriTeknik/pluggedinkit-go/blob/main/examples/rag/main.go)
* [File Upload (deprecated)](https://github.com/VeriTeknik/pluggedinkit-go/blob/main/examples/upload/main.go)
* [Error Handling](https://github.com/VeriTeknik/pluggedinkit-go/blob/main/examples/errors/main.go)
* [Concurrent Operations](https://github.com/VeriTeknik/pluggedinkit-go/blob/main/examples/concurrent/main.go)

## API Reference

### Client Functions

| Function                                            | Description                               |
| --------------------------------------------------- | ----------------------------------------- |
| `NewClient(apiKey)`                                 | Create a new client with default settings |
| `NewClientWithOptions(apiKey, baseURL, httpClient)` | Create a client with custom options       |

### Client Methods

| Method                | Description         |
| --------------------- | ------------------- |
| `SetAPIKey(apiKey)`   | Update the API key  |
| `SetBaseURL(baseURL)` | Update the base URL |

### Document Service Methods

| Method                                          | Description                          |
| ----------------------------------------------- | ------------------------------------ |
| `List(ctx, filters)`                            | List documents with optional filters |
| `Get(ctx, id, includeContent, includeVersions)` | Get a specific document              |
| `Search(ctx, query, filters, limit, offset)`    | Search documents                     |
| `Create(ctx, request)`                          | Create a new document                |
| `Update(ctx, id, request)`                      | Update an existing document          |
| `Delete(ctx, id)`                               | Delete a document                    |

### RAG Service Methods

| Method                                                  | Description                                            |
| ------------------------------------------------------- | ------------------------------------------------------ |
| `Query(ctx, query)`                                     | Returns `RAGResponse` with answer and metadata         |
| `AskQuestion(ctx, query)`                               | Simple RAG query (answer text only)                    |
| `QueryWithSources(ctx, query, projectUUID)`             | Query with source metadata (project argument optional) |
| `FindRelevantDocuments(ctx, query, projectUUID, limit)` | Find relevant documents                                |
| `CheckAvailability(ctx)`                                | Check RAG service availability                         |
| `GetStorageStats(ctx, userID)`                          | Get storage statistics                                 |

### Upload Service Methods

Upload helpers now emit descriptive errors because binary ingestion is no longer exposed via the public API.

### Clipboard Service Methods

| Method                    | Description                      |
| ------------------------- | -------------------------------- |
| `List(ctx)`               | List all clipboard entries       |
| `Get(ctx, filters)`       | Get entry by name or index       |
| `GetByName(ctx, name)`    | Get entry by name                |
| `GetByIndex(ctx, idx)`    | Get entry by stack index         |
| `Set(ctx, request)`       | Create or update named entry     |
| `Push(ctx, request)`      | Push value to indexed stack      |
| `Pop(ctx)`                | Pop and remove most recent entry |
| `Delete(ctx, request)`    | Delete specific entry            |
| `DeleteByName(ctx, name)` | Delete entry by name             |
| `DeleteByIndex(ctx, idx)` | Delete entry by index            |
| `ClearAll(ctx)`           | Delete all entries               |

## Clipboard (Memory) Service

The Clipboard service provides persistent key-value storage for MCP tools and AI agents.

### Named Access

```go theme={null}
ctx := context.Background()

// Set a named entry
entry, err := client.Clipboard.Set(ctx, &pluggedin.ClipboardSetRequest{
    Name:        "user_preferences",
    Value:       `{"theme": "dark", "lang": "en"}`,
    ContentType: "application/json",
    Encoding:    pluggedin.EncodingUTF8,
    Visibility:  pluggedin.ClipboardVisibilityPrivate,
    TTLSeconds:  86400, // 24 hours
})
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Created entry: %s\n", entry.UUID)

// Get by name
entry, err = client.Clipboard.GetByName(ctx, "user_preferences")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Value: %s\n", entry.Value)

// Delete by name
deleted, err := client.Clipboard.DeleteByName(ctx, "user_preferences")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Deleted: %v\n", deleted)
```

### Indexed (Stack) Access

```go theme={null}
ctx := context.Background()

// Push to stack
_, err := client.Clipboard.Push(ctx, &pluggedin.ClipboardPushRequest{
    Value:       "Step 1 result",
    ContentType: "text/plain",
})

_, err = client.Clipboard.Push(ctx, &pluggedin.ClipboardPushRequest{
    Value:       "Step 2 result",
    ContentType: "text/plain",
})

// Get by index (0 = most recent)
latest, err := client.Clipboard.GetByIndex(ctx, 0)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Latest: %s\n", latest.Value) // "Step 2 result"

// Pop removes and returns the most recent
popped, err := client.Clipboard.Pop(ctx)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Popped: %s\n", popped.Value) // "Step 2 result"
```

### List All Entries

```go theme={null}
ctx := context.Background()

entries, err := client.Clipboard.List(ctx)
if err != nil {
    log.Fatal(err)
}

for _, entry := range entries {
    var nameOrIdx string
    if entry.Name != nil {
        nameOrIdx = *entry.Name
    } else if entry.Idx != nil {
        nameOrIdx = fmt.Sprintf("idx:%d", *entry.Idx)
    }
    fmt.Printf("%s: %s\n", nameOrIdx, entry.Value)
    if entry.ExpiresAt != nil {
        fmt.Printf("  Expires: %s\n", entry.ExpiresAt.Format(time.RFC3339))
    }
}
```

### Type Definitions

```go theme={null}
// ClipboardEntry represents a clipboard entry
type ClipboardEntry struct {
    UUID           string              `json:"uuid"`
    Name           *string             `json:"name,omitempty"`
    Idx            *int                `json:"idx,omitempty"`
    Value          string              `json:"value"`
    ContentType    string              `json:"contentType"`
    Encoding       ClipboardEncoding   `json:"encoding"`
    SizeBytes      int                 `json:"sizeBytes"`
    Visibility     ClipboardVisibility `json:"visibility"`
    Source         ClipboardSource     `json:"source"`
    CreatedByTool  *string             `json:"createdByTool,omitempty"`
    CreatedByModel *string             `json:"createdByModel,omitempty"`
    CreatedAt      time.Time           `json:"createdAt"`
    UpdatedAt      time.Time           `json:"updatedAt"`
    ExpiresAt      *time.Time          `json:"expiresAt,omitempty"`
}

// Source options - tracks where entries originate
const (
    ClipboardSourceUI  ClipboardSource = "ui"   // Created via web interface
    ClipboardSourceSDK ClipboardSource = "sdk"  // Created via SDK (automatically set)
    ClipboardSourceMCP ClipboardSource = "mcp"  // Created via MCP tools
)

// The SDK automatically sets Source to ClipboardSourceSDK for all entries.
// DefaultClipboardSource is ClipboardSourceUI for backward compatibility.

// Encoding options
const (
    EncodingUTF8   ClipboardEncoding = "utf-8"
    EncodingBase64 ClipboardEncoding = "base64"
    EncodingHex    ClipboardEncoding = "hex"
)

// Visibility options
const (
    ClipboardVisibilityPrivate   ClipboardVisibility = "private"
    ClipboardVisibilityWorkspace ClipboardVisibility = "workspace"
    ClipboardVisibilityPublic    ClipboardVisibility = "public"
)
```

## Support

* **Go Package**: [https://pkg.go.dev/github.com/veriteknik/pluggedinkit-go](https://pkg.go.dev/github.com/veriteknik/pluggedinkit-go)
* **GitHub Issues**: [Report bugs and request features](https://github.com/VeriTeknik/pluggedinkit-go/issues)
* **Documentation**: [https://docs.plugged.in](https://docs.plugged.in)
* **Discord**: [Join our community](https://discord.gg/pluggedin)
