linear

package
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 22, 2026 License: MIT Imports: 29 Imported by: 0

Documentation

Overview

Package linear provides a Go client for the Linear API with comprehensive GraphQL support, automatic rate limiting, and robust error handling.

The client supports all major Linear operations including issue management, comments, notifications, team operations, and custom metadata storage using a description-based approach.

Authentication

The client requires a Linear API token for authentication. You can provide the token directly or load it from a file:

// Direct token
client := linear.NewClient("your-api-token")

// Token from file with fallback to environment variable
client := linear.NewClientWithTokenPath("/path/to/token")

Basic Usage

Get an issue:

issue, err := client.GetIssue("issue-id")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Issue: %s - %s\n", issue.Identifier, issue.Title)

Create an issue:

issue, err := client.CreateIssue("team-id", "Bug Report", "Description here")
if err != nil {
    log.Fatal(err)
}

Update issue state:

err := client.UpdateIssueState("issue-id", "state-id")
if err != nil {
    log.Fatal(err)
}

Metadata Management

This client supports storing custom metadata in Linear issue and project descriptions using a collapsible markdown format. Metadata is automatically extracted when fetching issues/projects and preserved when updating descriptions.

Update metadata for an issue:

err := client.UpdateIssueMetadataKey("issue-id", "priority", "high")
if err != nil {
    log.Fatal(err)
}

Remove metadata:

err := client.RemoveIssueMetadataKey("issue-id", "priority")
if err != nil {
    log.Fatal(err)
}

Error Handling

The package defines custom error types for better error handling:

err := client.UpdateIssueState("issue-id", "invalid-state")
if linear.IsValidationError(err) {
    // Handle validation error
}
if linear.IsRateLimitError(err) {
    // Handle rate limit with retry
    retryAfter := linear.GetRetryAfter(err)
}

Rate Limiting

The client automatically handles rate limiting with exponential backoff and respects Linear's rate limit headers. When rate limits are exceeded, a RateLimitError is returned with retry information.

Network Resilience

The client includes automatic retry logic for transient network errors with exponential backoff. Connection failures, timeouts, and temporary DNS issues are automatically retried.

Index

Constants

View Source
const (
	// MaxTitleLength is the maximum length for issue titles in Linear
	MaxTitleLength = 255
	// MaxDescriptionLength is the maximum length for descriptions in Linear
	MaxDescriptionLength = 100000
	// MaxNotificationLimit is the maximum number of notifications that can be fetched
	MaxNotificationLimit = 100
)

Constants for validation limits

View Source
const MCPSizeLimit = 1024 * 1024 // 1MB

MCPSizeLimit is the 1MB limit for MCP content (Claude Desktop constraint)

Variables

This section is empty.

Functions

func AuthenticationRequiredError

func AuthenticationRequiredError(operation string) error

AuthenticationRequiredError creates an error when authentication is needed

func BatchOperationError

func BatchOperationError(operation string, err error) error

BatchOperationError creates an error for batch operations

func ConfigurationError

func ConfigurationError(issue string, err error) error

ConfigurationError creates an error for configuration issues

func EnhanceGenericError

func EnhanceGenericError(operation string, err error) error

EnhanceGenericError takes a generic error and adds context

func GetImageDimensions

func GetImageDimensions(imageData []byte) (width, height int, err error)

GetImageDimensions returns dimensions of image data

func GetRetryAfter

func GetRetryAfter(err error) time.Duration

GetRetryAfter extracts the retry duration from a RateLimitError Returns 0 if the error is not a RateLimitError or has no retry duration

func GetSharedHTTPClient

func GetSharedHTTPClient() *http.Client

GetSharedHTTPClient returns the shared HTTP client instance This should be used for OAuth and other non-API operations that don't need per-client authentication headers

func InvalidStateIDError

func InvalidStateIDError(stateID string, err error) error

InvalidStateIDError creates an error for invalid state IDs with guidance

func IsAuthenticationError

func IsAuthenticationError(err error) bool

IsAuthenticationError checks if an error is an AuthenticationError It uses errors.As to handle wrapped errors

func IsGraphQLError

func IsGraphQLError(err error) bool

IsGraphQLError checks if an error is a GraphQLError It uses errors.As to handle wrapped errors

func IsImageAttachment

func IsImageAttachment(attachment Attachment) bool

IsImageAttachment checks if an attachment is an image based on content type

func IsNotFoundError

func IsNotFoundError(err error) bool

IsNotFoundError checks if an error is a NotFoundError It uses errors.As to handle wrapped errors

func IsRateLimitError

func IsRateLimitError(err error) bool

IsRateLimitError checks if an error is a RateLimitError It uses errors.As to handle wrapped errors

func IsValidationError

func IsValidationError(err error) bool

IsValidationError checks if an error is a ValidationError It uses errors.As to handle wrapped errors

func MapSortDirection

func MapSortDirection(direction string) string

MapSortDirection converts user-facing direction to GraphQL direction

func MapSortField

func MapSortField(sort string) string

MapSortField converts user-facing sort names to Linear's PaginationOrderBy enum Returns empty string for priority (client-side sorting required)

func NetworkRetryError

func NetworkRetryError(operation string, err error) error

NetworkRetryError creates an error with retry guidance

func NewOptimizedHTTPClient

func NewOptimizedHTTPClient() *http.Client

NewOptimizedHTTPClient creates an HTTP client optimized for Linear API usage

func OperationFailedError

func OperationFailedError(operation, resourceType string, guidance []string) error

OperationFailedError creates an error when an operation fails without clear reason

func RateLimitErrorWithGuidance

func RateLimitErrorWithGuidance(operation string, retryAfter int) error

RateLimitErrorWithGuidance creates an error with rate limit guidance

func ResourceNotFoundError

func ResourceNotFoundError(resourceType, resourceID string, err error) error

ResourceNotFoundError creates an error when a resource is not found

func ValidationErrorWithExample

func ValidationErrorWithExample(field, requirement, example string) error

ValidationError creates an error for invalid input with examples

Types

type Attachment

type Attachment struct {
	ID              string                 `json:"id"`
	URL             string                 `json:"url"`
	Title           string                 `json:"title"`
	Subtitle        string                 `json:"subtitle,omitempty"`
	Filename        string                 `json:"filename,omitempty"`    // From UploadFile if available
	ContentType     string                 `json:"contentType,omitempty"` // From UploadFile if available
	Size            int64                  `json:"size,omitempty"`        // From UploadFile if available
	CreatedAt       string                 `json:"createdAt"`
	UpdatedAt       string                 `json:"updatedAt"`
	ArchivedAt      *string                `json:"archivedAt,omitempty"`
	Creator         *User                  `json:"creator,omitempty"`
	ExternalCreator *ExternalUser          `json:"externalUserCreator,omitempty"`
	Metadata        map[string]interface{} `json:"metadata,omitempty"` // Custom metadata
	Source          map[string]interface{} `json:"source,omitempty"`   // Source information
	SourceType      string                 `json:"sourceType,omitempty"`
	GroupBySource   bool                   `json:"groupBySource"`
	Issue           *Issue                 `json:"issue,omitempty"`         // Parent issue
	OriginalIssue   *Issue                 `json:"originalIssue,omitempty"` // If moved/copied
}

Attachment represents a file attachment on an issue Based on Linear's GraphQL schema research

func FilterImageAttachments

func FilterImageAttachments(attachments []Attachment) []Attachment

FilterImageAttachments returns only image attachments from a slice

type AttachmentCache

type AttachmentCache struct {
	// contains filtered or unexported fields
}

AttachmentCache provides in-memory caching for processed attachments

func NewAttachmentCache

func NewAttachmentCache(ttl time.Duration) *AttachmentCache

NewAttachmentCache creates a new attachment cache with specified TTL

func (*AttachmentCache) Clear

func (cache *AttachmentCache) Clear()

Clear removes all entries from the cache

func (*AttachmentCache) Get

func (cache *AttachmentCache) Get(key string) *CacheEntry

Get retrieves a cache entry if it exists and hasn't expired

func (*AttachmentCache) Set

func (cache *AttachmentCache) Set(key string, entry *CacheEntry)

Set stores a cache entry

func (*AttachmentCache) Size

func (cache *AttachmentCache) Size() int

Size returns the current number of cached entries

type AttachmentClient

type AttachmentClient struct {
	// contains filtered or unexported fields
}

AttachmentClient handles attachment download and processing operations

func NewAttachmentClient

func NewAttachmentClient(base *BaseClient) *AttachmentClient

NewAttachmentClient creates a new attachment client

func (*AttachmentClient) GetAttachment

func (ac *AttachmentClient) GetAttachment(attachmentURL string, format AttachmentFormat) (*AttachmentResponse, error)

GetAttachment downloads and processes an attachment with the specified format Note: This is a simplified implementation that expects the full attachment URL to be passed as attachmentID In practice, this would be enhanced to properly resolve attachment IDs to URLs

func (*AttachmentClient) UploadFile

func (ac *AttachmentClient) UploadFile(filename string, content []byte, contentType string) (string, error)

UploadFile uploads a file to Linear and returns the asset URL This implements the full upload flow: 1. Call fileUpload mutation to get upload URL and headers 2. PUT the file content to the upload URL 3. Return the asset URL for use in markdown

func (*AttachmentClient) UploadFileFromPath

func (ac *AttachmentClient) UploadFileFromPath(filepath string) (string, error)

UploadFileFromPath uploads a file from a filesystem path

type AttachmentConnection

type AttachmentConnection struct {
	Nodes []Attachment `json:"nodes,omitempty"`
}

AttachmentConnection represents a paginated collection of attachments

type AttachmentFormat

type AttachmentFormat string

AttachmentFormat defines the supported return formats for attachments

const (
	FormatBase64   AttachmentFormat = "base64"   // Base64 encoded content (default for MCP)
	FormatURL      AttachmentFormat = "url"      // Direct URL (for large files)
	FormatMetadata AttachmentFormat = "metadata" // Metadata only, no download
)

type AttachmentResponse

type AttachmentResponse struct {
	Format      AttachmentFormat `json:"format"`
	Content     string           `json:"content,omitempty"`     // Base64 content or URL
	URL         string           `json:"url,omitempty"`         // Original URL
	ContentType string           `json:"contentType,omitempty"` // MIME type
	Size        int64            `json:"size,omitempty"`        // Content size in bytes
	Width       int              `json:"width,omitempty"`       // Image width (if image)
	Height      int              `json:"height,omitempty"`      // Image height (if image)
	Resized     bool             `json:"resized,omitempty"`     // True if image was resized for MCP limits
	Error       string           `json:"error,omitempty"`       // Error message if download failed
}

AttachmentResponse represents the response from GetAttachment

type AuthenticationError

type AuthenticationError struct {
	Message string // Human-readable error message
	Code    string // Error code from the API (e.g., "INVALID_TOKEN", "TOKEN_EXPIRED")
}

AuthenticationError represents an authentication failure It provides details about why authentication failed

func (*AuthenticationError) Error

func (e *AuthenticationError) Error() string

Error implements the error interface for AuthenticationError

type BaseClient

type BaseClient struct {
	// contains filtered or unexported fields
}

BaseClient contains the shared HTTP client and common request functionality that all sub-clients will use. This ensures we have a single HTTP client instance and consistent request handling across all Linear API operations.

func NewBaseClient

func NewBaseClient(apiToken string) *BaseClient

NewBaseClient creates a new base client with a shared HTTP client. For backward compatibility, this wraps the API token in a StaticProvider.

func NewBaseClientWithProvider added in v1.1.0

func NewBaseClientWithProvider(provider token.TokenProvider) *BaseClient

NewBaseClientWithProvider creates a new base client with a custom token provider. This enables automatic token refresh for OAuth apps with expiring tokens.

func NewTestBaseClient

func NewTestBaseClient(apiToken string, baseURL string, httpClient *http.Client) *BaseClient

NewTestBaseClient creates a new base client for testing with custom URL

func (*BaseClient) SetHTTPClient

func (bc *BaseClient) SetHTTPClient(client *http.Client)

SetHTTPClient sets a custom HTTP client for testing purposes

type BatchIssueUpdate

type BatchIssueUpdate struct {
	StateID    string   `json:"stateId,omitempty"`
	AssigneeID string   `json:"assigneeId,omitempty"`
	LabelIDs   []string `json:"labelIds,omitempty"`
	Priority   *int     `json:"priority,omitempty"`
	ProjectID  string   `json:"projectId,omitempty"`
}

BatchIssueUpdate represents update fields for batch operations

type BatchIssueUpdateResult

type BatchIssueUpdateResult struct {
	Success       bool    `json:"success"`
	UpdatedIssues []Issue `json:"updatedIssues"`
}

BatchIssueUpdateResult represents the result of a batch update operation

type CacheEntry

type CacheEntry struct {
	Content     []byte
	ContentType string
	Size        int64
	Width       int
	Height      int
	Resized     bool
	ExpiresAt   time.Time
}

CacheEntry represents a cached attachment with expiration

type CapacityRecommendation

type CapacityRecommendation struct {
	ConservativeScope  int // P80 velocity
	TargetScope        int // Median velocity
	OptimisticScope    int // P20 velocity
	ConservativeIssues int // Based on avg points/issue
	TargetIssues       int
	OptimisticIssues   int
	Rationale          string // Explanation of calculation
}

CapacityRecommendation represents suggested cycle scope based on historical data

func GenerateCapacityRecommendation

func GenerateCapacityRecommendation(analysis *CycleAnalysis, isPerUser bool) *CapacityRecommendation

GenerateCapacityRecommendation creates scope recommendations based on analysis

type ChildrenNodes

type ChildrenNodes struct {
	Nodes []SubIssue `json:"nodes"`
}

ChildrenNodes represents the children structure from GraphQL

type Client

type Client struct {

	// Sub-clients for different domains
	Issues        *IssueClient
	Projects      *ProjectClient
	Comments      *CommentClient
	Teams         *TeamClient
	Notifications *NotificationClient
	Workflows     *WorkflowClient
	Attachments   *AttachmentClient
	Cycles        *CycleClient
	// contains filtered or unexported fields
}

Client represents the main Linear API client that orchestrates all sub-clients. It provides a single entry point for all Linear API operations while delegating to specialized sub-clients for specific functionality.

func NewClient

func NewClient(apiToken string) *Client

NewClient creates a new Linear API client with all sub-clients initialized

func NewClientWithTokenPath

func NewClientWithTokenPath(tokenPath string) *Client

NewClientWithTokenPath creates a new Linear API client with token loading. It intelligently selects between static and refreshing token providers based on: - Whether a refresh token is available - Whether OAuth credentials are configured

func NewDefaultClient

func NewDefaultClient() *Client

NewDefaultClient creates a new Linear API client using default token path

func (*Client) AddReaction

func (c *Client) AddReaction(targetID, emoji string) error

func (*Client) ArchiveCycle

func (c *Client) ArchiveCycle(cycleID string) error

func (*Client) AssignIssue

func (c *Client) AssignIssue(identifierOrID, assigneeNameOrEmail string) error

func (*Client) CreateComment

func (c *Client) CreateComment(issueID, body string) (string, error)

Comment operations

func (*Client) CreateCommentReply

func (c *Client) CreateCommentReply(issueID, parentID, body string) (string, error)

func (*Client) CreateCycle

func (c *Client) CreateCycle(input *CreateCycleInput) (*Cycle, error)

func (*Client) CreateIssue

func (c *Client) CreateIssue(title, description, teamKeyOrName string) (*Issue, error)

Issue operations

func (*Client) CreateProject

func (c *Client) CreateProject(name, description, teamKeyOrName string) (*Project, error)

Project operations

func (*Client) GetAPIToken

func (c *Client) GetAPIToken() string

GetAPIToken returns the current API token Why: Some operations may need direct access to the token, such as checking authentication status.

func (*Client) GetActiveCycle

func (c *Client) GetActiveCycle(teamKeyOrName string) (*Cycle, error)

func (*Client) GetAppUserID

func (c *Client) GetAppUserID() (string, error)

func (*Client) GetBase

func (c *Client) GetBase() *BaseClient

GetBase returns the base client (for testing purposes)

func (*Client) GetCommentWithReplies

func (c *Client) GetCommentWithReplies(commentID string) (*CommentWithReplies, error)

func (*Client) GetCycle

func (c *Client) GetCycle(cycleID string) (*Cycle, error)

Cycle operations

func (*Client) GetCycleIssues

func (c *Client) GetCycleIssues(cycleID string, limit int) ([]Issue, error)

func (*Client) GetHTTPClient

func (c *Client) GetHTTPClient() *http.Client

GetHTTPClient returns the underlying HTTP client for testing purposes

func (*Client) GetIssue

func (c *Client) GetIssue(identifierOrID string) (*Issue, error)

GetIssue retrieves an issue with the best context automatically determined This is the preferred method for getting issues as it intelligently chooses whether to include parent or project context based on the issue's relationships.

func (*Client) GetIssueBasic

func (c *Client) GetIssueBasic(issueID string) (*Issue, error)

GetIssueBasic retrieves basic issue information without additional context Use this when you only need basic issue data without parent/project details.

func (*Client) GetIssueSimplified

func (c *Client) GetIssueSimplified(issueID string) (*Issue, error)

GetIssueSimplified retrieves basic issue information using a simplified query Use this as a fallback when the full context queries fail due to server issues.

func (*Client) GetIssueWithParentContext

func (c *Client) GetIssueWithParentContext(issueID string) (*Issue, error)

DEPRECATED: Use GetIssue() instead, which automatically determines the best context

func (*Client) GetIssueWithProjectContext

func (c *Client) GetIssueWithProjectContext(issueID string) (*Issue, error)

DEPRECATED: Use GetIssue() instead, which automatically determines the best context

func (*Client) GetNotifications

func (c *Client) GetNotifications(includeRead bool, limit int) ([]Notification, error)

Notification operations

func (*Client) GetProject

func (c *Client) GetProject(projectID string) (*Project, error)

func (*Client) GetSubIssues

func (c *Client) GetSubIssues(parentIssueID string) ([]SubIssue, error)

func (*Client) GetTeam

func (c *Client) GetTeam(keyOrName string) (*Team, error)

func (*Client) GetTeamEstimateScale

func (c *Client) GetTeamEstimateScale(keyOrName string) (*EstimateScale, error)

func (*Client) GetTeams

func (c *Client) GetTeams() ([]Team, error)

Team operations

func (*Client) GetUser

func (c *Client) GetUser(idOrEmail string) (*User, error)

func (*Client) GetViewer

func (c *Client) GetViewer() (*User, error)

func (*Client) GetWorkflowStateByName

func (c *Client) GetWorkflowStateByName(teamID, stateName string) (*WorkflowState, error)

func (*Client) GetWorkflowStates

func (c *Client) GetWorkflowStates(teamID string) ([]WorkflowState, error)

Workflow operations

func (*Client) ListAllIssues

func (c *Client) ListAllIssues(filter *IssueFilter) (*ListAllIssuesResult, error)

func (*Client) ListAllProjects

func (c *Client) ListAllProjects() ([]Project, error)

func (*Client) ListAssignedIssues

func (c *Client) ListAssignedIssues(userID string) ([]Issue, error)

func (*Client) ListCycles

func (c *Client) ListCycles(filter *CycleFilter) (*CycleSearchResult, error)

func (*Client) ListUserProjects

func (c *Client) ListUserProjects(userID string) ([]Project, error)

func (*Client) ListUsers

func (c *Client) ListUsers(filter *UserFilter) ([]User, error)

User operations

func (*Client) ListUsersWithPagination

func (c *Client) ListUsersWithPagination(filter *UserFilter) (*ListUsersResult, error)

func (*Client) MarkNotificationAsRead

func (c *Client) MarkNotificationAsRead(notificationID string) error

func (*Client) RemoveIssueMetadataKey

func (c *Client) RemoveIssueMetadataKey(issueID, key string) error

func (*Client) RemoveProjectMetadataKey

func (c *Client) RemoveProjectMetadataKey(projectID, key string) error

func (*Client) ResolveCycleIdentifier

func (c *Client) ResolveCycleIdentifier(numberOrNameOrID string, teamID string) (string, error)

func (*Client) ResolveIssueIdentifier

func (c *Client) ResolveIssueIdentifier(identifier string) (string, error)

func (*Client) ResolveTeamIdentifier

func (c *Client) ResolveTeamIdentifier(keyOrName string) (string, error)

Resolver operations (expose resolver functionality)

func (*Client) ResolveUserIdentifier

func (c *Client) ResolveUserIdentifier(nameOrEmail string) (string, error)

func (*Client) SearchIssues

func (c *Client) SearchIssues(filters *IssueSearchFilters) (*IssueSearchResult, error)

Issue search operations

func (*Client) SetBase

func (c *Client) SetBase(base *BaseClient)

SetBase sets the base client (for testing purposes)

func (*Client) TestConnection

func (c *Client) TestConnection() error

TestConnection tests if the client can connect to Linear API Why: Users need to verify their authentication and network connectivity before attempting other operations.

func (*Client) UpdateCycle

func (c *Client) UpdateCycle(cycleID string, input *UpdateCycleInput) (*Cycle, error)

func (*Client) UpdateIssue

func (c *Client) UpdateIssue(identifierOrID string, input UpdateIssueInput) (*Issue, error)

func (*Client) UpdateIssueDescription

func (c *Client) UpdateIssueDescription(issueID, newDescription string) error

func (*Client) UpdateIssueMetadataKey

func (c *Client) UpdateIssueMetadataKey(issueID, key string, value interface{}) error

func (*Client) UpdateIssueState

func (c *Client) UpdateIssueState(identifierOrID, stateID string) error

func (*Client) UpdateProjectDescription

func (c *Client) UpdateProjectDescription(projectID, newDescription string) error

func (*Client) UpdateProjectMetadataKey

func (c *Client) UpdateProjectMetadataKey(projectID, key string, value interface{}) error

func (*Client) UpdateProjectState

func (c *Client) UpdateProjectState(projectID, state string) error

type Comment

type Comment struct {
	ID        string         `json:"id"`
	Body      string         `json:"body"`
	CreatedAt string         `json:"createdAt"`
	UpdatedAt string         `json:"updatedAt"`
	User      User           `json:"user"`
	Issue     CommentIssue   `json:"issue"`
	Parent    *CommentParent `json:"parent,omitempty"`
}

Comment represents a Linear comment

type CommentClient

type CommentClient struct {
	// contains filtered or unexported fields
}

CommentClient handles all comment and reaction operations for the Linear API. It uses the shared BaseClient for HTTP communication and focuses on collaboration features like comments, replies, and reactions.

func NewCommentClient

func NewCommentClient(base *BaseClient) *CommentClient

NewCommentClient creates a new comment client with the provided base client

func (*CommentClient) AddReaction

func (cc *CommentClient) AddReaction(targetID, emoji string) error

AddReaction adds an emoji reaction to an issue or comment Why: Reactions provide quick, non-verbal feedback on issues and comments. They're useful for acknowledging, agreeing, or expressing sentiment.

func (*CommentClient) CreateComment

func (cc *CommentClient) CreateComment(issueID, body string) (*Comment, error)

CreateComment creates a new comment on an issue Why: Comments are essential for collaboration on issues. This method enables users to add context, updates, and discussions to issues.

func (*CommentClient) CreateCommentReply

func (cc *CommentClient) CreateCommentReply(issueID, parentID, body string) (*Comment, error)

CreateCommentReply creates a reply to an existing comment Why: Threaded discussions allow for more organized conversations. This method enables users to reply directly to specific comments.

func (*CommentClient) GetCommentWithReplies

func (cc *CommentClient) GetCommentWithReplies(commentID string) (*CommentWithReplies, error)

GetCommentWithReplies retrieves a comment and all its replies Why: Understanding the full context of a discussion requires seeing all replies. This method provides the complete comment thread.

func (*CommentClient) GetIssueComments

func (cc *CommentClient) GetIssueComments(issueID string) ([]Comment, error)

GetIssueComments retrieves all comments for an issue

type CommentConnection

type CommentConnection struct {
	Nodes []Comment `json:"nodes,omitempty"`
}

CommentConnection represents a paginated collection of comments

type CommentIssue

type CommentIssue struct {
	ID         string `json:"id"`
	Identifier string `json:"identifier"`
	Title      string `json:"title"`
}

CommentIssue represents the issue a comment belongs to

type CommentParent

type CommentParent struct {
	ID string `json:"id"`
}

CommentParent represents the parent of a comment reply

type CommentWithReplies

type CommentWithReplies struct {
	Comment Comment   `json:"comment"`
	Replies []Comment `json:"replies"`
}

CommentWithReplies represents a comment with all its replies

type CreateCycleInput

type CreateCycleInput struct {
	TeamID      string `json:"teamId"`
	Name        string `json:"name,omitempty"`
	Description string `json:"description,omitempty"`
	StartsAt    string `json:"startsAt"`
	EndsAt      string `json:"endsAt"`
}

CreateCycleInput represents the input for creating a cycle

type Cycle

type Cycle struct {
	ID                         string  `json:"id"`
	Name                       string  `json:"name"`
	Number                     int     `json:"number"`
	Description                string  `json:"description,omitempty"`
	StartsAt                   string  `json:"startsAt"`
	EndsAt                     string  `json:"endsAt"`
	CompletedAt                *string `json:"completedAt,omitempty"`
	Progress                   float64 `json:"progress"`
	Team                       *Team   `json:"team,omitempty"`
	IsActive                   bool    `json:"isActive"`
	IsFuture                   bool    `json:"isFuture"`
	IsPast                     bool    `json:"isPast"`
	IsNext                     bool    `json:"isNext"`
	IsPrevious                 bool    `json:"isPrevious"`
	ScopeHistory               []int   `json:"scopeHistory,omitempty"`
	CompletedScopeHistory      []int   `json:"completedScopeHistory,omitempty"`
	CompletedIssueCountHistory []int   `json:"completedIssueCountHistory,omitempty"`
	InProgressScopeHistory     []int   `json:"inProgressScopeHistory,omitempty"`
	IssueCountHistory          []int   `json:"issueCountHistory,omitempty"`
	CreatedAt                  string  `json:"createdAt"`
	UpdatedAt                  string  `json:"updatedAt"`
	ArchivedAt                 *string `json:"archivedAt,omitempty"`
	AutoArchivedAt             *string `json:"autoArchivedAt,omitempty"`
}

Cycle represents a Linear cycle (sprint/iteration)

func (*Cycle) ToCompact

func (c *Cycle) ToCompact() CycleCompact

ToCompact converts a full Cycle to CycleCompact

func (*Cycle) ToMinimal

func (c *Cycle) ToMinimal() CycleMinimal

ToMinimal converts a full Cycle to CycleMinimal

type CycleAnalysis

type CycleAnalysis struct {
	CycleCount int
	Metrics    []CycleMetrics

	// Summary statistics
	AvgVelocity          float64 // average completedScope
	AvgCompletionRate    float64
	AvgScopeCreep        float64
	AvgScopeCreepPercent float64
	AvgThroughput        float64

	// Statistical measures
	StdDevVelocity float64
	MedianVelocity float64
	P80Velocity    float64 // 80th percentile (conservative)
	P20Velocity    float64 // 20th percentile (optimistic)
}

CycleAnalysis represents aggregated metrics across multiple cycles

func AnalyzeMultipleCycles

func AnalyzeMultipleCycles(cycles []*Cycle, userIssues map[string][]Issue) *CycleAnalysis

AnalyzeMultipleCycles aggregates metrics across multiple cycles

type CycleClient

type CycleClient struct {
	// contains filtered or unexported fields
}

CycleClient handles all cycle-related operations for the Linear API. It uses the shared BaseClient for HTTP communication and focuses on cycle (sprint/iteration) management functionality.

func NewCycleClient

func NewCycleClient(base *BaseClient) *CycleClient

NewCycleClient creates a new cycle client with the provided base client

func (*CycleClient) ArchiveCycle

func (cc *CycleClient) ArchiveCycle(cycleID string) error

ArchiveCycle archives a cycle Why: Completed or obsolete cycles should be archived to keep the workspace clean.

func (*CycleClient) CreateCycle

func (cc *CycleClient) CreateCycle(input *CreateCycleInput) (*Cycle, error)

CreateCycle creates a new cycle in Linear Why: Teams need to create new sprints/iterations for planning work.

func (*CycleClient) GetActiveCycle

func (cc *CycleClient) GetActiveCycle(teamID string) (*Cycle, error)

GetActiveCycle retrieves the current active cycle for a team Why: Most common use case is finding what cycle is currently active for sprint planning and issue assignment.

func (*CycleClient) GetCycle

func (cc *CycleClient) GetCycle(cycleID string) (*Cycle, error)

GetCycle retrieves a single cycle by ID Why: This is the primary method for fetching detailed cycle information including progress metrics and state indicators.

func (*CycleClient) GetCycleIssues

func (cc *CycleClient) GetCycleIssues(cycleID string, limit int) ([]Issue, error)

GetCycleIssues retrieves issues for a specific cycle Why: Users need to see all issues in a cycle for sprint planning, progress tracking, and workload analysis.

func (*CycleClient) ListCycles

func (cc *CycleClient) ListCycles(filter *CycleFilter) (*CycleSearchResult, error)

ListCycles retrieves cycles with optional filtering Why: Users need to discover and browse cycles by team, status (active/future/past), with pagination support for large cycle histories.

func (*CycleClient) UpdateCycle

func (cc *CycleClient) UpdateCycle(cycleID string, input *UpdateCycleInput) (*Cycle, error)

UpdateCycle updates an existing cycle Why: Teams need to modify cycle dates, names, or mark cycles as completed.

type CycleCompact

type CycleCompact struct {
	ID       string  `json:"id"`
	Name     string  `json:"name"`
	Number   int     `json:"number"`
	StartsAt string  `json:"startsAt"`
	EndsAt   string  `json:"endsAt"`
	Progress float64 `json:"progress"`
	IsActive bool    `json:"isActive"`
	IsFuture bool    `json:"isFuture"`
	IsPast   bool    `json:"isPast"`
}

CycleCompact represents a compact cycle (~80 tokens) Use this when you need more context than minimal but not full details

type CycleFilter

type CycleFilter struct {
	TeamID          string         `json:"teamId,omitempty"`
	IsActive        *bool          `json:"isActive,omitempty"`
	IsFuture        *bool          `json:"isFuture,omitempty"`
	IsPast          *bool          `json:"isPast,omitempty"`
	IncludeArchived bool           `json:"includeArchived,omitempty"`
	Limit           int            `json:"limit"`
	After           string         `json:"after,omitempty"`
	Format          ResponseFormat `json:"format,omitempty"`
}

CycleFilter represents filter options for listing cycles

type CycleMetrics

type CycleMetrics struct {
	CycleID     string
	CycleName   string
	StartDate   string
	EndDate     string
	CompletedAt *string

	// Scope metrics
	InitialScope      int     // scopeHistory[0] or sum of initial issue estimates
	FinalScope        int     // scopeHistory[last] or sum of final issue estimates
	CompletedScope    int     // completedScopeHistory[last] or sum of completed issue estimates
	ScopeCreep        int     // finalScope - initialScope
	ScopeCreepPercent float64 // (scopeCreep / initialScope) * 100

	// Completion metrics
	CompletionRate float64 // (completedScope / finalScope) * 100

	// Issue metrics
	InitialIssueCount int // issueCountHistory[0] or count of initial issues
	CompletedIssues   int // completedIssueCountHistory[last] or count of completed issues
	Throughput        int // completedIssues
}

CycleMetrics represents calculated metrics for a single cycle

func CalculateCycleMetrics

func CalculateCycleMetrics(cycle *Cycle, userIssues []Issue) *CycleMetrics

CalculateCycleMetrics calculates metrics for a single cycle If userIssues is nil/empty, uses cycle history arrays (team-wide analysis) If userIssues is provided, calculates from filtered issues (per-user analysis)

type CycleMinimal

type CycleMinimal struct {
	ID       string `json:"id"`
	Name     string `json:"name"`
	Number   int    `json:"number"`
	IsActive bool   `json:"isActive"`
}

CycleMinimal represents a minimal cycle (~30 tokens) Use this for efficient browsing and list operations

type CycleReference

type CycleReference struct {
	ID     string `json:"id"`
	Name   string `json:"name"`
	Number int    `json:"number"`
}

CycleReference represents a minimal cycle reference for Issue.Cycle field

type CycleSearchResult

type CycleSearchResult struct {
	Cycles      []Cycle `json:"cycles"`
	HasNextPage bool    `json:"hasNextPage"`
	EndCursor   string  `json:"endCursor"`
}

CycleSearchResult represents the result of searching cycles with pagination

type ErrorWithGuidance

type ErrorWithGuidance struct {
	Operation   string   // What operation failed
	Reason      string   // Why it failed
	Guidance    []string // Steps to resolve
	Tools       []string // MCP tools that might help
	Example     string   // Example of correct usage
	OriginalErr error    // Original error for debugging
}

ErrorWithGuidance creates an error with actionable guidance for agents

func (*ErrorWithGuidance) Error

func (e *ErrorWithGuidance) Error() string

func (*ErrorWithGuidance) Unwrap

func (e *ErrorWithGuidance) Unwrap() error

Unwrap returns the original error for errors.As/Is/Unwrap support

type EstimateScale

type EstimateScale struct {
	Type            string    `json:"type"`            // notUsed, exponential, fibonacci, linear, tShirt
	AllowZero       bool      `json:"allowZero"`       // Whether 0 is allowed
	Extended        bool      `json:"extended"`        // Whether extended values are enabled
	DefaultEstimate *float64  `json:"defaultEstimate"` // Default value
	Values          []float64 `json:"values"`          // Available estimate values
	Labels          []string  `json:"labels"`          // Human-readable labels (for tShirt)
}

EstimateScale represents the available estimate values for a team

type ExternalUser

type ExternalUser struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	DisplayName string `json:"displayName"`
	Email       string `json:"email,omitempty"`
}

ExternalUser represents a user from external systems (like Slack)

type FileUploadResponse

type FileUploadResponse struct {
	UploadURL string            `json:"uploadUrl"`
	AssetURL  string            `json:"assetUrl"`
	Headers   map[string]string `json:"headers"`
}

FileUploadResponse represents the response from the fileUpload mutation

type GraphQLError

type GraphQLError struct {
	Message    string                 // The error message from GraphQL
	Extensions map[string]interface{} // Additional error context from the API
}

GraphQLError represents an error returned by the Linear GraphQL API It includes the error message and any extensions provided by the API

func (*GraphQLError) Error

func (e *GraphQLError) Error() string

Error implements the error interface for GraphQLError

type HTTPError

type HTTPError struct {
	StatusCode int
	Body       string
}

HTTPError represents an HTTP-level error

func (*HTTPError) Error

func (e *HTTPError) Error() string

type Issue

type Issue struct {
	ID          string `json:"id"`
	Identifier  string `json:"identifier"`
	Title       string `json:"title"`
	Description string `json:"description"`
	State       struct {
		ID   string `json:"id"`
		Name string `json:"name"`
	} `json:"state"`
	Project   *Project               `json:"project,omitempty"`
	Creator   *User                  `json:"creator,omitempty"`
	Assignee  *User                  `json:"assignee,omitempty"`
	Parent    *ParentIssue           `json:"parent,omitempty"`
	Children  ChildrenNodes          `json:"children,omitempty"`
	Cycle     *CycleReference        `json:"cycle,omitempty"`
	Labels    *LabelConnection       `json:"labels,omitempty"`
	Metadata  map[string]interface{} `json:"metadata,omitempty"`
	Priority  *int                   `json:"priority,omitempty"`
	Estimate  *float64               `json:"estimate,omitempty"`
	DueDate   *string                `json:"dueDate,omitempty"`
	CreatedAt string                 `json:"createdAt"`
	UpdatedAt string                 `json:"updatedAt"`
	URL       string                 `json:"url"`

	// Attachment support
	AttachmentCount int                   `json:"attachmentCount"` // Total number of attachments
	HasAttachments  bool                  `json:"hasAttachments"`  // Computed: AttachmentCount > 0
	Attachments     *AttachmentConnection `json:"attachments,omitempty"`
	Comments        *CommentConnection    `json:"comments,omitempty"`
}

Issue represents a Linear issue

func (*Issue) ToCompact

func (i *Issue) ToCompact() IssueCompact

ToCompact converts a full Issue to IssueCompact

func (*Issue) ToMinimal

func (i *Issue) ToMinimal() IssueMinimal

ToMinimal converts a full Issue to IssueMinimal

type IssueClient

type IssueClient struct {
	// contains filtered or unexported fields
}

IssueClient handles all issue-related operations for the Linear API. It uses the shared BaseClient for HTTP communication and focuses solely on issue management functionality.

func NewIssueClient

func NewIssueClient(base *BaseClient) *IssueClient

NewIssueClient creates a new issue client with the provided base client

func (*IssueClient) AssignIssue

func (ic *IssueClient) AssignIssue(issueID, assigneeID string) error

AssignIssue assigns or unassigns an issue to/from a user Why: Issue assignment is crucial for workload management. Passing an empty assigneeID unassigns the issue, providing flexibility in one method.

func (*IssueClient) BatchUpdateIssues

func (ic *IssueClient) BatchUpdateIssues(issueIDs []string, update BatchIssueUpdate) (*BatchIssueUpdateResult, error)

BatchUpdateIssues updates multiple issues with the same changes Why: Bulk operations are common in issue management, such as moving multiple issues to a new state, assigning them to someone, or applying labels in bulk.

func (*IssueClient) CreateIssue

func (ic *IssueClient) CreateIssue(title, description, teamID string) (*Issue, error)

CreateIssue creates a new issue in Linear Why: This is a core function for creating work items in Linear. It requires a team assignment and supports optional descriptions for detailed context.

func (*IssueClient) GetIssue

func (ic *IssueClient) GetIssue(issueID string) (*Issue, error)

GetIssue retrieves a single issue by ID Why: This is the primary method for fetching detailed issue information. It automatically extracts metadata from the description for easy access.

func (*IssueClient) GetIssueSimplified

func (ic *IssueClient) GetIssueSimplified(issueID string) (*Issue, error)

GetIssueSimplified retrieves a single issue by ID with reduced query complexity Why: The full GetIssue query can sometimes hit server complexity limits for issues with many children or complex data. This simplified version excludes children nodes to reduce query complexity.

func (*IssueClient) GetIssueWithBestContext

func (ic *IssueClient) GetIssueWithBestContext(issueID string) (*Issue, error)

GetIssueWithBestContext retrieves an issue with the most appropriate context Why: This intelligently determines whether to fetch parent or project context based on the issue's actual relationships, avoiding unnecessary API calls.

func (*IssueClient) GetIssueWithFallback

func (ic *IssueClient) GetIssueWithFallback(issueID string) (*Issue, error)

GetIssueWithFallback attempts to get an issue with full details, falling back to simplified query if the full query fails with a server error. Why: This provides resilience against server-side complexity limits while still attempting to get full data when possible.

func (*IssueClient) GetIssueWithParentContext

func (ic *IssueClient) GetIssueWithParentContext(issueID string) (*Issue, error)

GetIssueWithParentContext retrieves an issue with parent issue details Why: Understanding issue hierarchy is important for sub-tasks. This method provides parent context needed for proper sub-task management.

func (*IssueClient) GetIssueWithProjectContext

func (ic *IssueClient) GetIssueWithProjectContext(issueID string) (*Issue, error)

GetIssueWithProjectContext retrieves an issue with additional project information Why: When working within a project context, we need more project details like metadata and state. This method provides that extended information in one call.

func (*IssueClient) GetIssueWithRelations

func (ic *IssueClient) GetIssueWithRelations(issueID string) (*IssueWithRelations, error)

GetIssueWithRelations retrieves an issue with its blocking/blocked-by relations for dependency graph visualization.

func (*IssueClient) GetSubIssues

func (ic *IssueClient) GetSubIssues(parentIssueID string) ([]SubIssue, error)

GetSubIssues retrieves all sub-issues for a given parent issue Why: Sub-task management requires fetching all children of an issue. This dedicated method provides that functionality efficiently.

func (*IssueClient) GetTeamIssuesWithRelations

func (ic *IssueClient) GetTeamIssuesWithRelations(teamID string, limit int) ([]IssueWithRelations, error)

GetTeamIssuesWithRelations retrieves all issues for a team with their relations for building a complete dependency graph.

func (*IssueClient) ListAllIssues

func (ic *IssueClient) ListAllIssues(filter *IssueFilter) (*ListAllIssuesResult, error)

ListAllIssues retrieves issues with comprehensive filtering, pagination, and sorting options Why: Users need flexible ways to query issues across teams, projects, states, etc. This method provides a powerful search interface with metadata support.

func (*IssueClient) ListAssignedIssues

func (ic *IssueClient) ListAssignedIssues(limit int) ([]Issue, error)

ListAssignedIssues retrieves issues assigned to the authenticated user Why: Users need to see their workload. This method provides a focused view of assigned work with configurable result limits.

func (*IssueClient) ListIssueAttachments

func (ic *IssueClient) ListIssueAttachments(issueID string) ([]Attachment, error)

ListIssueAttachments retrieves all attachments for a specific issue Why: Agents need to access UI specs, mockups, and screenshots attached to issues for implementation guidance. This method provides comprehensive attachment metadata including filename, size, content type, and URLs.

func (*IssueClient) RemoveIssueMetadataKey

func (ic *IssueClient) RemoveIssueMetadataKey(issueID, key string) error

RemoveIssueMetadataKey removes a specific metadata key from an issue Why: Sometimes metadata keys become obsolete or need to be cleaned up. This method provides that capability without affecting other metadata.

func (*IssueClient) SearchIssuesEnhanced

func (ic *IssueClient) SearchIssuesEnhanced(filters *IssueSearchFilters) (*IssueSearchResult, error)

SearchIssuesEnhanced searches for issues with advanced filtering options Why: Users need flexible search capabilities to find issues based on multiple criteria like team, state, labels, assignee, priority, and date ranges.

func (*IssueClient) UpdateIssue

func (ic *IssueClient) UpdateIssue(issueID string, input UpdateIssueInput) (*Issue, error)

UpdateIssue updates an issue with the provided fields Why: Issues need to be updated with various fields like title, description, priority, etc. This method provides a flexible way to update any combination of fields while preserving existing data like metadata.

func (*IssueClient) UpdateIssueDescription

func (ic *IssueClient) UpdateIssueDescription(issueID, newDescription string) error

UpdateIssueDescription updates an issue's description while preserving metadata Why: Descriptions may contain both user content and hidden metadata. This method ensures metadata is preserved when users update descriptions.

func (*IssueClient) UpdateIssueMetadataKey

func (ic *IssueClient) UpdateIssueMetadataKey(issueID, key string, value interface{}) error

UpdateIssueMetadataKey updates a specific metadata key for an issue Why: Granular metadata updates are more efficient than replacing all metadata. This method allows updating individual keys without affecting others.

func (*IssueClient) UpdateIssueState

func (ic *IssueClient) UpdateIssueState(issueID, stateID string) error

UpdateIssueState updates the workflow state of an issue Why: Moving issues through workflow states is a core project management action. This method provides that capability with proper validation.

type IssueCompact

type IssueCompact struct {
	ID         string `json:"id"`
	Identifier string `json:"identifier"`
	Title      string `json:"title"`
	State      struct {
		ID   string `json:"id"`
		Name string `json:"name"`
	} `json:"state"`
	Assignee  *User  `json:"assignee,omitempty"`
	Priority  *int   `json:"priority,omitempty"`
	CreatedAt string `json:"createdAt"`
	UpdatedAt string `json:"updatedAt"`
	Parent    *struct {
		ID         string `json:"id"`
		Identifier string `json:"identifier"`
		Title      string `json:"title"`
	} `json:"parent,omitempty"`
	Children []struct {
		ID         string `json:"id"`
		Identifier string `json:"identifier"`
		Title      string `json:"title"`
	} `json:"children,omitempty"`
}

IssueCompact represents a compact issue with commonly needed fields (~150 tokens) Use this when you need more context than minimal but not full details

type IssueConnection

type IssueConnection struct {
	Nodes []ProjectIssue `json:"nodes"`
}

IssueConnection represents the GraphQL connection for issues

type IssueFilter

type IssueFilter struct {
	// Pagination
	First int    `json:"first"`           // Number of items to fetch (required, max 250)
	After string `json:"after,omitempty"` // Cursor for pagination

	// Filters
	StateIDs   []string `json:"stateIds,omitempty"`   // Filter by workflow state IDs
	AssigneeID string   `json:"assigneeId,omitempty"` // Filter by assignee user ID
	LabelIDs   []string `json:"labelIds,omitempty"`   // Filter by label IDs
	ProjectID  string   `json:"projectId,omitempty"`  // Filter by project ID
	TeamID     string   `json:"teamId,omitempty"`     // Filter by team ID

	// Sorting
	OrderBy   string `json:"orderBy,omitempty"`   // Sort field: "createdAt", "updatedAt", "priority"
	Direction string `json:"direction,omitempty"` // Sort direction: "asc" or "desc"

	// Response format (minimal, compact, or full)
	Format ResponseFormat `json:"format,omitempty"`
}

IssueFilter represents filter options for listing issues

type IssueMinimal

type IssueMinimal struct {
	ID         string `json:"id"`
	Identifier string `json:"identifier"`
	Title      string `json:"title"`
	State      struct {
		ID   string `json:"id"`
		Name string `json:"name"`
	} `json:"state"`
	ParentIdentifier string `json:"parentIdentifier,omitempty"` // Parent issue identifier (e.g., "CEN-123")
}

IssueMinimal represents a minimal issue with only essential fields (~50 tokens) Use this for efficient browsing and list operations where full details aren't needed

type IssueRelation

type IssueRelation struct {
	ID           string            `json:"id"`
	Type         IssueRelationType `json:"type"`
	Issue        *IssueMinimal     `json:"issue"`
	RelatedIssue *IssueMinimal     `json:"relatedIssue"`
	CreatedAt    string            `json:"createdAt"`
	UpdatedAt    string            `json:"updatedAt"`
}

IssueRelation represents a dependency relationship between two issues

type IssueRelationConnection

type IssueRelationConnection struct {
	Nodes []IssueRelation `json:"nodes"`
}

IssueRelationConnection represents a collection of issue relations

type IssueRelationType

type IssueRelationType string

IssueRelationType represents the type of relationship between issues

const (
	// RelationBlocks indicates this issue blocks another
	RelationBlocks IssueRelationType = "blocks"
	// RelationDuplicate indicates this issue is a duplicate
	RelationDuplicate IssueRelationType = "duplicate"
	// RelationRelated indicates a general relationship
	RelationRelated IssueRelationType = "related"
)

type IssueSearchFilters

type IssueSearchFilters struct {
	// Team filter
	TeamID string `json:"teamId,omitempty"`

	// Identifier filter (e.g., "CEN-123")
	Identifier string `json:"identifier,omitempty"`

	// State filters
	StateIDs []string `json:"stateIds,omitempty"`

	// Label filters
	LabelIDs []string `json:"labelIds,omitempty"`

	// Assignee filter
	AssigneeID string `json:"assigneeId,omitempty"`

	// Cycle filter
	CycleID string `json:"cycleId,omitempty"`

	// Priority filter (0-4, where 0 is no priority, 1 is urgent, 4 is low)
	Priority *int `json:"priority,omitempty"`

	// Text search
	SearchTerm string `json:"searchTerm,omitempty"`

	// Include archived issues
	IncludeArchived bool `json:"includeArchived,omitempty"`

	// Date filters
	CreatedAfter  string `json:"createdAfter,omitempty"`
	CreatedBefore string `json:"createdBefore,omitempty"`
	UpdatedAfter  string `json:"updatedAfter,omitempty"`
	UpdatedBefore string `json:"updatedBefore,omitempty"`

	// Pagination
	Limit int    `json:"limit"`
	After string `json:"after,omitempty"`

	// Response format (minimal, compact, or full)
	Format ResponseFormat `json:"format,omitempty"`
}

IssueSearchFilters represents enhanced filter options for searching issues

type IssueSearchResult

type IssueSearchResult struct {
	Issues      []Issue `json:"issues"`
	HasNextPage bool    `json:"hasNextPage"`
	EndCursor   string  `json:"endCursor"`
}

IssueSearchResult represents the result of searching issues with pagination

type IssueWithDetails

type IssueWithDetails struct {
	ID          string                  `json:"id"`
	Identifier  string                  `json:"identifier"`
	Title       string                  `json:"title"`
	Description string                  `json:"description"`
	Priority    int                     `json:"priority"`
	CreatedAt   string                  `json:"createdAt"`
	UpdatedAt   string                  `json:"updatedAt"`
	State       WorkflowState           `json:"state"`
	Assignee    *User                   `json:"assignee,omitempty"`
	Labels      []Label                 `json:"labels"`
	Project     *Project                `json:"project,omitempty"`
	Team        Team                    `json:"team"`
	Metadata    *map[string]interface{} `json:"metadata,omitempty"`
}

IssueWithDetails represents an issue with full details including metadata

type IssueWithRelations

type IssueWithRelations struct {
	ID         string `json:"id"`
	Identifier string `json:"identifier"`
	Title      string `json:"title"`
	State      struct {
		ID   string `json:"id"`
		Name string `json:"name"`
	} `json:"state"`
	Relations        IssueRelationConnection `json:"relations"`
	InverseRelations IssueRelationConnection `json:"inverseRelations"`
}

IssueWithRelations extends Issue with relation information

type Label

type Label struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Color       string `json:"color"`
	Description string `json:"description"`
}

Label represents a Linear label

type LabelConnection

type LabelConnection struct {
	Nodes []Label `json:"nodes"`
}

LabelConnection represents a connection to labels

type ListAllIssuesResult

type ListAllIssuesResult struct {
	Issues      []IssueWithDetails `json:"issues"`
	HasNextPage bool               `json:"hasNextPage"`
	EndCursor   string             `json:"endCursor"`
	TotalCount  int                `json:"totalCount"`
}

ListAllIssuesResult represents the result of listing all issues

type ListUsersResult

type ListUsersResult struct {
	Users       []User `json:"users"`
	HasNextPage bool   `json:"hasNextPage"`
	EndCursor   string `json:"endCursor"`
}

ListUsersResult represents the result of listing users with pagination

type NotFoundError

type NotFoundError struct {
	ResourceType string // Type of resource (e.g., "issue", "project", "user")
	ResourceID   string // ID of the resource that wasn't found
}

NotFoundError represents a resource not found error It indicates that a requested resource doesn't exist

func (*NotFoundError) Error

func (e *NotFoundError) Error() string

Error implements the error interface for NotFoundError

type Notification

type Notification struct {
	ID             string               `json:"id"`
	Type           string               `json:"type"`
	CreatedAt      string               `json:"createdAt"`
	ReadAt         *string              `json:"readAt,omitempty"`
	ArchivedAt     *string              `json:"archivedAt,omitempty"`
	SnoozedUntilAt *string              `json:"snoozedUntilAt,omitempty"`
	User           *User                `json:"user,omitempty"`
	Issue          *NotificationIssue   `json:"issue,omitempty"`
	Comment        *NotificationComment `json:"comment,omitempty"`
}

Notification represents a Linear notification

type NotificationClient

type NotificationClient struct {
	// contains filtered or unexported fields
}

NotificationClient handles all notification-related operations for the Linear API. It uses the shared BaseClient for HTTP communication and manages user notifications including mentions and updates.

func NewNotificationClient

func NewNotificationClient(base *BaseClient) *NotificationClient

NewNotificationClient creates a new notification client with the provided base client

func (*NotificationClient) GetNotifications

func (nc *NotificationClient) GetNotifications(includeRead bool, limit int) ([]Notification, error)

GetNotifications retrieves notifications for the authenticated user Why: Notifications keep users informed about mentions, assignments, and updates. This method provides access to that activity stream.

func (*NotificationClient) MarkNotificationRead

func (nc *NotificationClient) MarkNotificationRead(notificationID string) error

MarkNotificationRead marks a notification as read Why: Users need to acknowledge notifications to keep their inbox manageable. This method provides that acknowledgment capability.

type NotificationComment

type NotificationComment struct {
	ID   string `json:"id"`
	Body string `json:"body"`
}

NotificationComment represents comment info in a notification

type NotificationIssue

type NotificationIssue struct {
	ID         string `json:"id"`
	Identifier string `json:"identifier"`
	Title      string `json:"title"`
}

NotificationIssue represents issue info in a notification

type PaginationInput

type PaginationInput struct {
	Start     int    `json:"start"`     // Starting position (0-indexed)
	Limit     int    `json:"limit"`     // Number of items per page
	Sort      string `json:"sort"`      // Sort field: priority|created|updated
	Direction string `json:"direction"` // Sort direction: asc|desc
}

PaginationInput represents offset-based pagination parameters

func ValidatePagination

func ValidatePagination(input *PaginationInput) *PaginationInput

ValidatePagination validates and normalizes pagination input

type ParentIssue

type ParentIssue struct {
	ID          string `json:"id"`
	Identifier  string `json:"identifier"`
	Title       string `json:"title"`
	Description string `json:"description"`
	State       struct {
		ID   string `json:"id"`
		Name string `json:"name"`
	} `json:"state"`
	Metadata map[string]interface{} `json:"metadata,omitempty"`
}

ParentIssue represents a parent issue

type Project

type Project struct {
	ID          string                 `json:"id"`
	Name        string                 `json:"name"`
	Description string                 `json:"description"`       // Short description (255 char limit)
	Content     string                 `json:"content,omitempty"` // Long markdown content (no limit)
	State       string                 `json:"state"`             // planned, started, completed, etc.
	Issues      *IssueConnection       `json:"issues,omitempty"`
	Metadata    map[string]interface{} `json:"metadata,omitempty"`
	CreatedAt   string                 `json:"createdAt"`
	UpdatedAt   string                 `json:"updatedAt"`
}

Project represents a Linear project

func (*Project) GetIssues

func (p *Project) GetIssues() []ProjectIssue

GetIssues returns the issues from a project, handling nil Issues field

type ProjectClient

type ProjectClient struct {
	// contains filtered or unexported fields
}

ProjectClient handles all project-related operations for the Linear API. It uses the shared BaseClient for HTTP communication and focuses on project management functionality.

func NewProjectClient

func NewProjectClient(base *BaseClient) *ProjectClient

NewProjectClient creates a new project client with the provided base client

func (*ProjectClient) CreateProject

func (pc *ProjectClient) CreateProject(name, description, teamID string) (*Project, error)

CreateProject creates a new project in Linear Why: Projects are containers for organizing related issues. This method enables project creation with proper team assignment.

func (*ProjectClient) GetProject

func (pc *ProjectClient) GetProject(projectID string) (*Project, error)

GetProject retrieves a single project by ID Why: This is the primary method for fetching detailed project information including associated issues and metadata.

func (*ProjectClient) ListAllProjects

func (pc *ProjectClient) ListAllProjects(limit int) ([]Project, error)

ListAllProjects retrieves all projects in the workspace Why: Users need to discover available projects. This method provides a complete list with optional limiting for performance.

func (*ProjectClient) ListUserProjects

func (pc *ProjectClient) ListUserProjects(userID string, limit int) ([]Project, error)

ListUserProjects retrieves projects that have issues assigned to a specific user Why: Users often want to see only projects they're actively working on. This method filters projects based on issue assignments.

func (*ProjectClient) RemoveProjectMetadataKey

func (pc *ProjectClient) RemoveProjectMetadataKey(projectID, key string) error

RemoveProjectMetadataKey removes a specific metadata key from a project Why: Metadata keys may become obsolete. This method allows selective removal without affecting other metadata. Note: Uses 'content' field instead of 'description' to avoid 255 char limit.

func (*ProjectClient) UpdateProject

func (pc *ProjectClient) UpdateProject(projectID string, input UpdateProjectInput) (*Project, error)

UpdateProject updates a project with the provided input Supports updating name, description, state, lead, start date, and target date

func (*ProjectClient) UpdateProjectDescription

func (pc *ProjectClient) UpdateProjectDescription(projectID, newContent string) error

UpdateProjectDescription updates a project's content while preserving metadata Why: Project content may contain both user content and metadata. This method ensures metadata is preserved during content updates. Note: Linear has two fields - 'description' (255 char limit) and 'content' (no limit). We use 'content' for longer text to avoid the character limit.

func (*ProjectClient) UpdateProjectMetadataKey

func (pc *ProjectClient) UpdateProjectMetadataKey(projectID, key string, value interface{}) error

UpdateProjectMetadataKey updates a specific metadata key for a project Why: Granular metadata updates allow changing individual values without affecting other metadata. This is more efficient than full replacements. Note: Uses 'content' field instead of 'description' to avoid 255 char limit.

func (*ProjectClient) UpdateProjectState

func (pc *ProjectClient) UpdateProjectState(projectID, state string) error

UpdateProjectState updates the state of a project Why: Projects have states (planned, started, completed, etc.) that need to be updated as work progresses. This method provides that capability.

type ProjectIssue

type ProjectIssue struct {
	ID         string `json:"id"`
	Identifier string `json:"identifier"`
	Title      string `json:"title"`
	State      struct {
		ID   string `json:"id"`
		Name string `json:"name"`
	} `json:"state"`
	Assignee *User `json:"assignee,omitempty"`
}

ProjectIssue represents a minimal issue in a project context

type RateLimitError

type RateLimitError struct {
	RetryAfter time.Duration // How long to wait before retrying
}

RateLimitError represents a rate limit error from the Linear API It includes information about when to retry the request

func (*RateLimitError) Error

func (e *RateLimitError) Error() string

Error implements the error interface for RateLimitError

type Resolver

type Resolver struct {
	// contains filtered or unexported fields
}

Resolver handles intelligent resolution of human-readable identifiers to UUIDs It manages caching and provides smart matching with ambiguity detection

Why: The MCP server should accept human-readable inputs (emails, names, CEN-123) instead of forcing clients to look up UUIDs. The resolver handles this translation.

func NewResolver

func NewResolver(client *Client) *Resolver

NewResolver creates a new resolver with the default cache TTL

func (*Resolver) ResolveCycle

func (r *Resolver) ResolveCycle(numberOrNameOrID string, teamID string) (string, error)

ResolveCycle resolves a cycle identifier (number or name) to a cycle UUID Supports: - Cycle numbers: "62" (fastest lookup) - Cycle names: "Cycle 67" or "Sprint Planning"

Returns error with suggestions if multiple cycles match

func (*Resolver) ResolveIssue

func (r *Resolver) ResolveIssue(identifier string) (string, error)

ResolveIssue resolves an issue identifier (CEN-123) to an issue UUID Only accepts Linear identifiers in format TEAM-NUMBER

Returns error if identifier invalid or issue not found

func (*Resolver) ResolveTeam

func (r *Resolver) ResolveTeam(keyOrName string) (string, error)

ResolveTeam resolves a team identifier (name or key) to a team UUID Supports: - Team keys: "ENG", "PRODUCT" - Team names: "Engineering", "Product Team"

Returns error if team not found

func (*Resolver) ResolveUser

func (r *Resolver) ResolveUser(nameOrEmail string) (string, error)

ResolveUser resolves a user identifier (email or name) to a user UUID Supports: - Email addresses: "john@company.com" - Display names: "John Doe" - First names: "John" (errors if ambiguous)

Returns error with suggestions if multiple users match

type ResponseFormat

type ResponseFormat string

ResponseFormat specifies the level of detail in API responses

const (
	// FormatMinimal returns only essential fields (~50 tokens per issue)
	FormatMinimal ResponseFormat = "minimal"
	// FormatCompact returns commonly needed fields (~150 tokens per issue)
	FormatCompact ResponseFormat = "compact"
	// FormatFull returns all fields (~ 1500 tokens per issue)
	FormatFull ResponseFormat = "full"
)

func ParseResponseFormat

func ParseResponseFormat(s string) (ResponseFormat, error)

ParseResponseFormat parses a string into a ResponseFormat with validation

type SubIssue

type SubIssue struct {
	ID         string `json:"id"`
	Identifier string `json:"identifier"`
	Title      string `json:"title"`
	State      struct {
		ID   string `json:"id"`
		Name string `json:"name"`
	} `json:"state"`
}

SubIssue represents a minimal sub-issue with its state

type Team

type Team struct {
	ID                       string   `json:"id"`
	Name                     string   `json:"name"`
	Key                      string   `json:"key"`
	Description              string   `json:"description"`
	IssueEstimationType      string   `json:"issueEstimationType,omitempty"`      // notUsed, exponential, fibonacci, linear, tShirt
	IssueEstimationAllowZero bool     `json:"issueEstimationAllowZero,omitempty"` // Whether 0 is allowed as estimate
	IssueEstimationExtended  bool     `json:"issueEstimationExtended,omitempty"`  // Whether extended estimates are enabled
	DefaultIssueEstimate     *float64 `json:"defaultIssueEstimate,omitempty"`     // Default estimate for new issues
}

Team represents a Linear team

func (*Team) GetEstimateScale

func (t *Team) GetEstimateScale() *EstimateScale

GetEstimateScale returns the available estimate values based on team settings

type TeamClient

type TeamClient struct {
	// contains filtered or unexported fields
}

TeamClient handles all team and user-related operations for the Linear API. It uses the shared BaseClient for HTTP communication and focuses on organizational structure queries.

func NewTeamClient

func NewTeamClient(base *BaseClient) *TeamClient

NewTeamClient creates a new team client with the provided base client

func (*TeamClient) GetTeam

func (tc *TeamClient) GetTeam(teamID string) (*Team, error)

GetTeam retrieves a single team by ID with estimate settings Why: Users need to get details about a specific team including the estimate scale configuration for that team.

func (*TeamClient) GetTeams

func (tc *TeamClient) GetTeams() ([]Team, error)

GetTeams retrieves all teams in the workspace Why: Teams are the primary organizational unit in Linear. Users need to discover available teams for issue creation and assignment.

func (*TeamClient) GetUser

func (tc *TeamClient) GetUser(userID string) (*User, error)

GetUser retrieves a specific user by ID with optional team memberships and preferences Why: Users need to look up specific team members by their unique ID to view profile details, team associations, and notification preferences.

func (*TeamClient) GetUserByEmail

func (tc *TeamClient) GetUserByEmail(email string) (*User, error)

GetUserByEmail retrieves a user by their email address Why: Email is a common identifier for users. This allows finding users when only their email is known, useful for mentions and assignments.

func (*TeamClient) GetViewer

func (tc *TeamClient) GetViewer() (*User, error)

GetViewer retrieves information about the authenticated user Why: Users need to know their own identity and capabilities within the system. This is essential for determining permissions and context.

func (*TeamClient) ListLabels

func (tc *TeamClient) ListLabels(teamID string) ([]Label, error)

ListLabels retrieves all labels for a specific team Why: Labels are used to categorize and filter issues. Teams need to discover available labels for issue tagging and organization.

func (*TeamClient) ListUsers

func (tc *TeamClient) ListUsers(filter *UserFilter) ([]User, error)

ListUsers retrieves users based on the provided filter Why: Users need to discover team members, find assignees, and understand organizational structure. Filters allow focusing on specific subsets.

func (*TeamClient) ListUsersWithDisplayNameFilter

func (tc *TeamClient) ListUsersWithDisplayNameFilter(displayName string, activeOnly *bool, limit int) ([]User, error)

ListUsersWithDisplayNameFilter retrieves users by displayName filter with optional active status Why: Efficient server-side filtering for user name resolution. Uses displayName filter to avoid downloading all users when searching by name.

func (*TeamClient) ListUsersWithPagination

func (tc *TeamClient) ListUsersWithPagination(filter *UserFilter) (*ListUsersResult, error)

ListUsersWithPagination retrieves users with pagination information Why: Large organizations may have many users, requiring pagination to efficiently retrieve and display user lists.

type UpdateCycleInput

type UpdateCycleInput struct {
	Name        *string `json:"name,omitempty"`
	Description *string `json:"description,omitempty"`
	StartsAt    *string `json:"startsAt,omitempty"`
	EndsAt      *string `json:"endsAt,omitempty"`
	CompletedAt *string `json:"completedAt,omitempty"`
}

UpdateCycleInput represents the input for updating a cycle

type UpdateIssueInput

type UpdateIssueInput struct {
	Title       *string  `json:"title,omitempty"`
	Description *string  `json:"description,omitempty"`
	Priority    *int     `json:"priority,omitempty"`   // 0 = No priority, 1 = Urgent, 2 = High, 3 = Medium, 4 = Low
	Estimate    *float64 `json:"estimate,omitempty"`   // Story points estimate
	DueDate     *string  `json:"dueDate,omitempty"`    // ISO 8601 date format
	StateID     *string  `json:"stateId,omitempty"`    // Workflow state ID
	AssigneeID  *string  `json:"assigneeId,omitempty"` // User ID to assign to
	ProjectID   *string  `json:"projectId,omitempty"`  // Project ID to move issue to
	ParentID    *string  `json:"parentId,omitempty"`   // Parent issue ID for sub-issues
	TeamID      *string  `json:"teamId,omitempty"`     // Team ID to move issue to
	CycleID     *string  `json:"cycleId,omitempty"`    // Cycle ID to move issue to
	LabelIDs    []string `json:"labelIds,omitempty"`   // Label IDs to apply
}

UpdateIssueInput represents the input for updating an issue All fields are optional to support partial updates

type UpdateProjectInput

type UpdateProjectInput struct {
	Name        *string `json:"name,omitempty"`
	Description *string `json:"description,omitempty"`
	Content     *string `json:"content,omitempty"`
	State       *string `json:"state,omitempty"`
	LeadID      *string `json:"leadId,omitempty"`
	StartDate   *string `json:"startDate,omitempty"`
	TargetDate  *string `json:"targetDate,omitempty"`
}

UpdateProjectInput represents the input for updating a project

type User

type User struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	DisplayName string `json:"displayName"`
	Email       string `json:"email"`
	AvatarURL   string `json:"avatarUrl"`
	Active      bool   `json:"active"`
	Admin       bool   `json:"admin"`
	CreatedAt   string `json:"createdAt"`
	IsMe        bool   `json:"isMe"`
	Teams       []Team `json:"teams,omitempty"`
}

User represents a Linear user with full information

func (*User) UnmarshalJSON

func (u *User) UnmarshalJSON(data []byte) error

UnmarshalJSON handles custom unmarshaling for User to support both direct teams array and nested teams.nodes structure from GraphQL

type UserFilter

type UserFilter struct {
	// Filter by team membership
	TeamID string `json:"teamId,omitempty"`

	// Filter by active status (nil means include all)
	ActiveOnly *bool `json:"activeOnly,omitempty"`

	// Pagination
	First int    `json:"first"`           // Number of items to fetch (default 50, max 250)
	After string `json:"after,omitempty"` // Cursor for pagination
}

UserFilter represents filter options for listing users

type ValidationError

type ValidationError struct {
	Field   string      // The field that failed validation
	Value   interface{} // The invalid value that was provided
	Message string      // Simple error message
	Reason  string      // Why the validation failed
}

ValidationError represents an input validation failure It provides details about which field failed validation and why

func (*ValidationError) Error

func (e *ValidationError) Error() string

Error implements the error interface for ValidationError

type WorkflowClient

type WorkflowClient struct {
	// contains filtered or unexported fields
}

WorkflowClient handles all workflow-related operations for the Linear API. It uses the shared BaseClient for HTTP communication and manages workflow states and transitions.

func NewWorkflowClient

func NewWorkflowClient(base *BaseClient) *WorkflowClient

NewWorkflowClient creates a new workflow client with the provided base client

func (*WorkflowClient) GetWorkflowStateByName

func (wc *WorkflowClient) GetWorkflowStateByName(teamID, stateName string) (*WorkflowState, error)

GetWorkflowStateByName retrieves a specific workflow state by name for a team Why: When updating issue states, users often know the state name (e.g., "In Progress") but need the state ID. This helper provides a convenient way to look up states by name. The search is case-insensitive to improve usability.

func (*WorkflowClient) GetWorkflowStates

func (wc *WorkflowClient) GetWorkflowStates(teamID string) ([]WorkflowState, error)

GetWorkflowStates retrieves all available workflow states with caching Why: Understanding available workflow states is crucial for proper issue state management. This method provides all possible states an issue can transition to, optionally filtered by team. Results are cached per team to reduce API calls.

type WorkflowState

type WorkflowState struct {
	ID          string  `json:"id"`
	Name        string  `json:"name"`
	Type        string  `json:"type"` // backlog, unstarted, started, completed, canceled
	Color       string  `json:"color"`
	Position    float64 `json:"position"`
	Description string  `json:"description"`
	Team        *Team   `json:"team,omitempty"`
}

WorkflowState represents a workflow state in Linear

Directories

Path Synopsis
Package schema contains the Linear GraphQL schema for reference and validation.
Package schema contains the Linear GraphQL schema for reference and validation.
Package testutil provides testing utilities for Linear client tests.
Package testutil provides testing utilities for Linear client tests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL