Skip to main content
Unlisted page
This page is unlisted. Search engines will not index it, and only users having a direct link can access it.

Molecule SDK Reference

The Molecule SDK provides the types, interfaces, and helpers for building molecule plugins in Go. Molecules can provide discovery, MCP tools, skills, and visualizations.

Installation

go get github.com/sixdegree-ai/molecule-sdk

Requirements:

  • Go 1.24 or later

Core Interfaces

Molecule

The base interface that all molecules must implement:

type Molecule interface {
// GetMetadata returns the molecule's identity and capabilities.
GetMetadata(ctx context.Context) (*MoleculeMetadata, error)

// Configure sets up the molecule with per-capability configuration.
Configure(ctx context.Context, config *MoleculeConfig) error

// Shutdown gracefully stops the molecule.
Shutdown(ctx context.Context) error
}

Discoverer

For molecules that discover entities. Extends Molecule:

type Discoverer interface {
Molecule

// Discover executes discovery and streams results via a channel.
Discover(ctx context.Context, opts *DiscoveryOptions) (<-chan *DiscoveryResult, error)
}

ToolProvider

For molecules that provide MCP tools. Extends Molecule:

type ToolProvider interface {
Molecule

// ListTools returns the available MCP tools.
ListTools(ctx context.Context) ([]ToolDefinition, error)

// CallTool executes an MCP tool and returns the result.
CallTool(ctx context.Context, req *ToolCallRequest) (*ToolCallResponse, error)
}

VisualizationProvider

For molecules that render visualizations. Extends Molecule:

type VisualizationProvider interface {
Molecule

// ListVisualizations returns the available visualizations.
ListVisualizations(ctx context.Context) ([]VisualizationDefinition, error)

// RenderVisualization renders a visualization for an entity.
RenderVisualization(ctx context.Context, req *VisualizationRenderRequest) (*VisualizationRenderResponse, error)
}

WebhookHandler

Optional interface for real-time event handling:

type WebhookHandler interface {
// HandleWebhook processes an incoming webhook event.
HandleWebhook(ctx context.Context, req *WebhookRequest) (*WebhookResponse, error)
}

FullMolecule

Convenience interface for molecules that implement all capabilities:

type FullMolecule interface {
Discoverer
ToolProvider
VisualizationProvider
ConfigWidgetProvider
}

BaseMolecule

Embed BaseMolecule in your struct to get default implementations for GetMetadata, Configure, and Shutdown:

type MyMolecule struct {
*molecule.BaseMolecule
}

func NewMyMolecule() *MyMolecule {
return &MyMolecule{
BaseMolecule: molecule.NewBaseMolecule(&molecule.MoleculeMetadata{
Namespace: "molecules.sixdegree.ai",
Name: "my-molecule",
Version: "1.0.0",
DisplayName: "My Molecule",
Description: "Discovers entities from my service",
Discovery: &molecule.DiscoveryCapability{
Supported: true,
Modes: []molecule.DiscoveryMode{molecule.DiscoveryModePolling},
EntityTypes: []molecule.EntityTypeDefinition{
{
Group: "entities.sixdegree.ai",
Version: "v1",
Kind: "MyResource",
Description: "A resource from my service",
},
},
},
}),
}
}

Data Structures

Entity

Follows a Kubernetes-inspired structure:

type Entity struct {
APIVersion string `json:"apiVersion"` // e.g., "entities.sixdegree.ai/v1"
Kind string `json:"kind"` // e.g., "GithubRepository"
Metadata EntityMetadata `json:"metadata"`
Spec map[string]any `json:"spec"`
Status *EntityStatus `json:"status,omitempty"`
}

type EntityMetadata struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
UID string `json:"uid,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
}

Relation

type Relation struct {
Namespace string `json:"namespace"`
RelationType string `json:"relation"` // e.g., "OWNS", "DEPLOYS"
Source EntityReference `json:"source"`
Target EntityReference `json:"target"`
Labels map[string]string `json:"labels,omitempty"`
Spec map[string]any `json:"spec,omitempty"`
Weight *float64 `json:"weight,omitempty"` // For graph algorithms
}

type EntityReference struct {
APIVersion string `json:"apiVersion"`
Kind string `json:"kind"`
Name string `json:"name"`
Namespace string `json:"namespace"`
}

MoleculeMetadata

type MoleculeMetadata struct {
Namespace string `json:"namespace"`
Name string `json:"name"`
Version string `json:"version"`
DisplayName string `json:"display_name"`
Description string `json:"description,omitempty"`

Author *AuthorInfo `json:"author,omitempty"`
License string `json:"license,omitempty"`

// Structured capabilities
Discovery *DiscoveryCapability `json:"discovery,omitempty"`
MCP *MCPCapability `json:"mcp,omitempty"`
Visualizations *VisualizationCapability `json:"visualizations,omitempty"`

HomepageURL string `json:"homepage_url,omitempty"`
DocsURL string `json:"docs_url,omitempty"`
Logo []byte `json:"logo,omitempty"`
}

DiscoveryOptions

Controls how discovery runs:

type DiscoveryOptions struct {
// IncrementalFrom limits discovery to changes since this time.
// If nil, performs a full discovery.
IncrementalFrom *time.Time `json:"incremental_from,omitempty"`

// Filters restricts discovery scope.
Filters map[string]string `json:"filters,omitempty"`

// DryRun validates without persisting entities.
DryRun bool `json:"dry_run"`
}

DiscoveryResult

Streamed back from Discover():

type DiscoveryResult struct {
Entity *Entity `json:"entity,omitempty"`
Relation *Relation `json:"relation,omitempty"`
Error *DiscoveryError `json:"error,omitempty"`
Progress int `json:"progress"` // 0-100
Message string `json:"message,omitempty"`
}

ToolDefinition

Describes an MCP tool:

type ToolDefinition struct {
Name string `json:"name"`
Description string `json:"description"`
InputSchema json.RawMessage `json:"input_schema"`
// EntityTypes for late tool disclosure — tool is only shown to the LLM
// after these entity types appear in the conversation.
EntityTypes []string `json:"entity_types,omitempty"`
}

Builders

The SDK provides builder helpers to reduce boilerplate.

EntityBuilder

entity := molecule.NewEntityBuilder("entities.sixdegree.ai/v1", "GithubRepository").
Name("api-service").
Namespace("default").
Label("language", "go").
Label("visibility", "private").
Annotation("source-url", "https://github.com/org/api-service").
Spec("url", "https://github.com/org/api-service").
Spec("language", "Go").
Spec("stars", 42).
Build()

RelationBuilder

relation := molecule.NewRelationBuilder("OWNS").
Namespace("default").
From("entities.sixdegree.ai/v1", "GithubOrganization", "default", "my-org").
To("entities.sixdegree.ai/v1", "GithubRepository", "default", "api-service").
Label("role", "owner").
Build()

You can also use entities directly:

relation := molecule.NewRelationBuilder("CONTRIBUTES_TO").
Namespace("default").
FromEntity(userEntity).
ToEntity(repoEntity).
Build()

NewEntityRef

Helper for creating entity references:

ref := molecule.NewEntityRef("GithubRepository", "api-service", "default")
// → EntityReference{APIVersion: "entities.sixdegree.ai/v1", Kind: "GithubRepository", ...}

ResultChannel

Helper for streaming discovery results:

func (m *MyMolecule) Discover(ctx context.Context, opts *molecule.DiscoveryOptions) (<-chan *molecule.DiscoveryResult, error) {
rc := molecule.NewResultChannel(100) // buffered channel

go func() {
defer rc.Close()

rc.SendProgress(0, "Starting discovery...")

// Discover entities
repos, err := m.fetchRepos(ctx)
if err != nil {
rc.SendError("FETCH_FAILED", err.Error())
return
}

for i, repo := range repos {
entity := molecule.NewEntityBuilder("entities.sixdegree.ai/v1", "MyResource").
Name(repo.Name).
Namespace(m.Config().Namespace).
Spec("url", repo.URL).
Build()

rc.SendEntity(entity)
rc.SendProgress(((i + 1) * 100) / len(repos), fmt.Sprintf("Discovered %d/%d", i+1, len(repos)))
}
}()

return rc.Chan(), nil
}

Configuration Helpers

Reading Settings

func (m *MyMolecule) Configure(ctx context.Context, config *molecule.MoleculeConfig) error {
// Store config via BaseMolecule
m.BaseMolecule.Configure(ctx, config)

// Read required settings
token, err := molecule.RequireCapabilitySetting[string](config.Discovery, "token")
if err != nil {
return err
}

// Read optional settings with defaults
pageSize := molecule.GetCapabilitySetting(config.Discovery, "page_size", 100)
org := molecule.GetCapabilitySetting(config.Discovery, "organization", "")

// Check which capabilities are enabled
if molecule.IsCapabilityEnabled(config, molecule.CapabilityDiscovery) {
m.setupDiscovery(token, org, pageSize)
}

if molecule.IsCapabilityEnabled(config, molecule.CapabilityMCP) {
m.setupMCPTools(token)
}

return nil
}

Config Schema

Define a JSON Schema for your configuration so the UI can render a configuration form:

Discovery: &molecule.DiscoveryCapability{
Supported: true,
ConfigSchema: json.RawMessage(`{
"type": "object",
"required": ["token"],
"properties": {
"token": {
"type": "string",
"title": "API Token",
"description": "Authentication token for the service",
"x-sixdegree": {"sensitive": true}
},
"organization": {
"type": "string",
"title": "Organization",
"description": "Organization to discover resources from"
},
"page_size": {
"type": "integer",
"title": "Page Size",
"default": 100,
"minimum": 10,
"maximum": 1000
}
}
}`),
},

MCP Tools

Defining Tools

MCP: &molecule.MCPCapability{
Supported: true,
Tools: []molecule.ToolDefinition{
{
Name: "my_search",
Description: "Search for resources in my service",
InputSchema: json.RawMessage(`{
"type": "object",
"required": ["query"],
"properties": {
"query": {"type": "string", "description": "Search query"}
}
}`),
EntityTypes: []string{"entities.sixdegree.ai/v1/MyResource"},
},
},
},

Handling Tool Calls

func (m *MyMolecule) CallTool(ctx context.Context, req *molecule.ToolCallRequest) (*molecule.ToolCallResponse, error) {
switch req.Name {
case "my_search":
query, _ := req.Arguments["query"].(string)
results, err := m.client.Search(ctx, query)
if err != nil {
return &molecule.ToolCallResponse{
Success: false,
Error: err.Error(),
}, nil
}
return &molecule.ToolCallResponse{
Success: true,
Result: map[string]any{"results": results},
}, nil

default:
return &molecule.ToolCallResponse{
Success: false,
Error: fmt.Sprintf("unknown tool: %s", req.Name),
}, nil
}
}

Webhooks

Handling Webhook Events

func (m *MyMolecule) HandleWebhook(ctx context.Context, req *molecule.WebhookRequest) (*molecule.WebhookResponse, error) {
switch req.EventType {
case "resource.created", "resource.updated":
return &molecule.WebhookResponse{
Acknowledged: true,
TriggerDiscovery: true, // triggers incremental discovery
}, nil

case "resource.deleted":
var payload struct {
ResourceName string `json:"resource_name"`
}
json.Unmarshal(req.Payload, &payload)

return &molecule.WebhookResponse{
Acknowledged: true,
AffectedEntities: []molecule.EntityReference{
molecule.NewEntityRef("MyResource", payload.ResourceName, "default"),
},
}, nil

default:
return &molecule.WebhookResponse{Acknowledged: true}, nil
}
}

Discovery Modes

Declare supported discovery modes in metadata:

Discovery: &molecule.DiscoveryCapability{
Supported: true,
Modes: []molecule.DiscoveryMode{
molecule.DiscoveryModePolling, // interval-based
molecule.DiscoveryModeWebhook, // event-driven
molecule.DiscoveryModeBoth, // both simultaneously
},
},

Next Steps