linear-toon-mcp 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c8a4dfd15f2122d8742273f0a2b305d9f52f15b26173f177589348efcaceffb
4
- data.tar.gz: 5f6f12e5bb56fe7c62cdabf552e110bd6b91dfabe0d0a6c241de074c5d1a02b3
3
+ metadata.gz: 0daf1704fb96cea3eec8dbd60cb0c51a0facdd3e2ede5c3575b4f312fdde23c6
4
+ data.tar.gz: 8f45de9bd2c1a409b506cd87cfa47479b4aeaf731b0f928d84882eb21aeed9fd
5
5
  SHA512:
6
- metadata.gz: b163ae42837c5ae08eecf3b3897345f8c81312b7692a937ad0ef9586a42978155e2e1260fe7aeaa91d5fde1dddf29f0d0c5b8bad40658351dc925c017e4507ad
7
- data.tar.gz: abd70a25debba62a651736fc0bfc018bda3171b3dc71070a2a4c17e5f2bd3476519def4f80dfb4c51853ad513ef9ecf898fc40aa3ad5630415144a9dd7e4e4a9
6
+ metadata.gz: 9a1609b91fc4d8a4dd5fa5f46b77fe3bd38cf48a022dd1de9f00432ac29ab55a7c55ada94b0acc9342dc50134890f272db9531a6e6fb7e7e7736f766ef4c58c1
7
+ data.tar.gz: 7b9adcc7084b17bb7167ff469679559a033f217bcb523591bd5a74cee79f51798aa9c5f22695937738f4175bab45691c59da8e16b341f182e53f4f68d74561cf
data/README.md CHANGED
@@ -44,7 +44,7 @@ claude mcp add linear-toon -e LINEAR_API_KEY=lin_api_xxxxx -- linear-toon-mcp
44
44
 
45
45
  | Tool | Description |
46
46
  |------|-------------|
47
- | `get_issue` | Retrieve a Linear issue by ID or identifier (e.g., `LIN-123`). Returns issue details including title, description, state, assignee, labels, project, and attachments. |
47
+ | `get_issue` | Retrieve a Linear issue by ID or identifier (e.g., `LIN-123`). Returns issue details including title, description, state, assignee, labels, project, attachments, and the parent and direct child issues (each with identifier, title, state, and url). |
48
48
  | `list_issues` | List issues with optional filters (team, assignee, state, label, priority, project, cycle) and cursor-based pagination. Supports name or UUID for most filters. |
49
49
  | `list_issue_statuses` | List available workflow states for a team. Returns status id, type (backlog/unstarted/started/completed/canceled), and name. Accepts team name or UUID. |
50
50
  | `list_teams` | List all teams in the workspace. Returns team id, name, and key. |
@@ -53,8 +53,8 @@ claude mcp add linear-toon -e LINEAR_API_KEY=lin_api_xxxxx -- linear-toon-mcp
53
53
  | `list_projects` | List projects, optionally scoped to a team. Returns project id, name, and state. |
54
54
  | `list_cycles` | List cycles for a team. Returns cycle id, name, number, startsAt, and endsAt. Requires team name or UUID. |
55
55
  | `get_project` | Retrieve a specific project by name, ID, or slug. Returns project details including state, priority, dates, progress, and lead. Optional includes for members, milestones, and resources. |
56
- | `create_issue` | Create a new Linear issue. Accepts human-friendly names for team, assignee, state, labels, project, cycle, and milestone (resolved to IDs automatically). Supports issue relations and link attachments. |
57
- | `update_issue` | Update an existing Linear issue by ID. Supports partial updates, null to remove fields, and relation replacement. |
56
+ | `create_issue` | Create a new Linear issue. Accepts human-friendly names for team, assignee, state, labels, project, cycle, and milestone (resolved to IDs automatically; label names resolve against the target team or workspace-wide labels). Relation params (`blocks`, `relatedTo`, `duplicateOf`) and `parentId` accept either issue UUIDs or human identifiers (e.g., `LIN-123`). Supports issue relations and link attachments. |
57
+ | `update_issue` | Update an existing Linear issue by ID. Supports partial updates, null to remove fields, and relation replacement. Relation params (`blocks`, `relatedTo`, `duplicateOf`) and `parentId` accept either issue UUIDs or human identifiers (e.g., `LIN-123`). Label names resolve against the issue's team or workspace-wide labels. |
58
58
  | `create_comment` | Create a comment on a Linear issue. Supports Markdown content and threaded replies via parentId. |
59
59
  | `list_comments` | List comments for a specific Linear issue in chronological order. Returns each comment's id, body, author, and timestamps. |
60
60
 
@@ -92,21 +92,45 @@ module LinearToonMcp
92
92
  data.dig("workflowStates", "nodes", 0, "id") or raise Error, "State not found: #{value}"
93
93
  end
94
94
 
95
+ # Resolve a single label name or UUID to its UUID.
96
+ # When +team_id+ is supplied, restricts the lookup to labels scoped to that
97
+ # team or to workspace-wide labels (where the label's team relation is null),
98
+ # so a name like "Bug" matches the right team-scoped label and not a same-named
99
+ # label on another team.
95
100
  # @param client [Client]
96
101
  # @param value [String] label UUID or name
102
+ # @param team_id [String, nil] optional team UUID to scope the lookup
97
103
  # @return [String] label UUID
98
104
  # @raise [Error] when label not found
99
- def resolve_label(client, value)
105
+ def resolve_label(client, value, team_id: nil)
100
106
  return value if value.match?(UUID_RE)
101
- data = client.query(LABEL_QUERY, variables: {filter: {name: {eqIgnoreCase: value}}})
102
- data.dig("issueLabels", "nodes", 0, "id") or raise Error, "Label not found: #{value}"
107
+
108
+ filter = {name: {eqIgnoreCase: value}}
109
+ if team_id
110
+ filter[:or] = [
111
+ {team: {null: true}},
112
+ {team: {id: {eq: team_id}}}
113
+ ]
114
+ end
115
+
116
+ data = client.query(LABEL_QUERY, variables: {filter:})
117
+ id = data.dig("issueLabels", "nodes", 0, "id")
118
+ return id if id
119
+
120
+ raise Error, label_not_found_message(value, team_id)
103
121
  end
104
122
 
105
123
  # @param client [Client]
106
124
  # @param values [Array<String>] label UUIDs or names
125
+ # @param team_id [String, nil] optional team UUID to scope the lookup
107
126
  # @return [Array<String>] label UUIDs
108
- def resolve_labels(client, values)
109
- values.map { |v| resolve_label(client, v) }
127
+ def resolve_labels(client, values, team_id: nil)
128
+ values.map { |v| resolve_label(client, v, team_id:) }
129
+ end
130
+
131
+ def label_not_found_message(value, team_id)
132
+ return "Label not found: #{value}" unless team_id
133
+ "Label not found on target team or workspace: #{value}"
110
134
  end
111
135
 
112
136
  # @param client [Client]
@@ -6,7 +6,10 @@ module LinearToonMcp
6
6
  module Tools
7
7
  # Create a new Linear issue with full parameter support.
8
8
  # Resolves human-friendly names to IDs for team, assignee, state, labels,
9
- # project, cycle, and milestone. Supports post-mutation relations and links.
9
+ # project, cycle, and milestone. Relation params (blocks, relatedTo,
10
+ # duplicateOf) and parentId accept either issue UUIDs or human identifiers
11
+ # (e.g., LIN-123); both are passed through to Linear unchanged.
12
+ # Supports post-mutation relations and links.
10
13
  class CreateIssue < MCP::Tool
11
14
  description "Create a new Linear issue"
12
15
 
@@ -30,10 +33,10 @@ module LinearToonMcp
30
33
  cycle: {type: "string", description: "Cycle name, number, or ID"},
31
34
  estimate: {type: "number", description: "Issue estimate value"},
32
35
  dueDate: {type: "string", description: "Due date (ISO format)"},
33
- parentId: {type: "string", description: "Parent issue ID"},
34
- blocks: {type: "array", items: {type: "string"}, description: "Issue IDs/identifiers this blocks"},
35
- relatedTo: {type: "array", items: {type: "string"}, description: "Related issue IDs/identifiers"},
36
- duplicateOf: {type: "string", description: "Duplicate of issue ID/identifier"},
36
+ parentId: {type: "string", description: "Parent issue UUID or identifier (e.g., LIN-123)"},
37
+ blocks: {type: "array", items: {type: "string"}, description: "Issue UUIDs or identifiers this blocks"},
38
+ relatedTo: {type: "array", items: {type: "string"}, description: "Related issue UUIDs or identifiers"},
39
+ duplicateOf: {type: "string", description: "Duplicate-of issue UUID or identifier"},
37
40
  milestone: {type: "string", description: "Milestone name or ID"},
38
41
  delegate: {type: "string", description: "Agent name or ID"},
39
42
  links: {type: "array", items: {type: "object", properties: {url: {type: "string"}, title: {type: "string"}}, required: ["url", "title"]}, description: "Link attachments [{url, title}]"}
@@ -88,7 +91,7 @@ module LinearToonMcp
88
91
  resolve_fields(input, client, team_id, **kwargs)
89
92
 
90
93
  data = client.query(MUTATION, variables: {input:})
91
- result = data["issueCreate"]
94
+ result = data["issueCreate"] or raise Error, "Issue creation failed: no result returned"
92
95
  raise Error, "Issue creation failed" unless result["success"]
93
96
 
94
97
  issue = result["issue"]
@@ -131,7 +134,7 @@ module LinearToonMcp
131
134
  project: nil, cycle: nil, milestone: nil, delegate: nil, **)
132
135
  input[:assigneeId] = Resolvers.resolve_user(client, delegate || assignee) if assignee || delegate
133
136
  input[:stateId] = Resolvers.resolve_state(client, team_id, state) if state
134
- input[:labelIds] = Resolvers.resolve_labels(client, labels) if labels
137
+ input[:labelIds] = Resolvers.resolve_labels(client, labels, team_id:) if labels
135
138
  project_id = Resolvers.resolve_project(client, project) if project
136
139
  input[:projectId] = project_id if project_id
137
140
  input[:cycleId] = Resolvers.resolve_cycle(client, team_id, cycle) if cycle
@@ -5,9 +5,10 @@ require "toon"
5
5
  module LinearToonMcp
6
6
  module Tools
7
7
  # Fetch a single Linear issue by ID or identifier and return it as TOON.
8
- # Includes metadata, state, assignee, labels, project, team, and attachments.
8
+ # Includes metadata, state, assignee, labels, project, team, attachments,
9
+ # parent issue, and direct child issues.
9
10
  class GetIssue < MCP::Tool
10
- description "Retrieve a Linear issue by ID"
11
+ description "Retrieve a Linear issue by ID, including its parent and direct child issues"
11
12
 
12
13
  annotations(
13
14
  read_only_hint: true,
@@ -46,6 +47,8 @@ module LinearToonMcp
46
47
  project { id name }
47
48
  team { id name }
48
49
  attachments { nodes { id title url } }
50
+ parent { identifier title url state { name } }
51
+ children(first: 50) { nodes { identifier title url state { name } } }
49
52
  }
50
53
  }
51
54
  GRAPHQL
@@ -5,7 +5,10 @@ require "toon"
5
5
  module LinearToonMcp
6
6
  module Tools
7
7
  # Update an existing Linear issue by ID. Supports partial updates,
8
- # null to remove fields, and relation replacement semantics.
8
+ # null to remove fields, and relation replacement semantics. Relation
9
+ # params (blocks, relatedTo, duplicateOf) and parentId accept either
10
+ # issue UUIDs or human identifiers (e.g., LIN-123); both are passed
11
+ # through to Linear unchanged.
9
12
  class UpdateIssue < MCP::Tool
10
13
  description "Update an existing Linear issue"
11
14
 
@@ -30,10 +33,10 @@ module LinearToonMcp
30
33
  cycle: {type: "string", description: "Cycle name, number, or ID"},
31
34
  estimate: {type: "number", description: "Issue estimate value"},
32
35
  dueDate: {type: "string", description: "Due date (ISO format)"},
33
- parentId: {type: ["string", "null"], description: "Parent issue ID. Null to remove"},
34
- blocks: {type: "array", items: {type: "string"}, description: "Issue IDs this blocks. Replaces existing; omit to keep unchanged"},
35
- relatedTo: {type: "array", items: {type: "string"}, description: "Related issue IDs. Replaces existing; omit to keep unchanged"},
36
- duplicateOf: {type: ["string", "null"], description: "Duplicate of issue ID. Null to remove"},
36
+ parentId: {type: ["string", "null"], description: "Parent issue UUID or identifier (e.g., LIN-123). Null to remove"},
37
+ blocks: {type: "array", items: {type: "string"}, description: "Issue UUIDs or identifiers this blocks. Replaces existing; omit to keep unchanged"},
38
+ relatedTo: {type: "array", items: {type: "string"}, description: "Related issue UUIDs or identifiers. Replaces existing; omit to keep unchanged"},
39
+ duplicateOf: {type: ["string", "null"], description: "Duplicate-of issue UUID or identifier. Null to remove"},
37
40
  milestone: {type: "string", description: "Milestone name or ID"},
38
41
  delegate: {type: ["string", "null"], description: "Agent name or ID. Null to remove"},
39
42
  links: {type: "array", items: {type: "object", properties: {url: {type: "string"}, title: {type: "string"}}, required: ["url", "title"]}, description: "Link attachments [{url, title}]"}
@@ -111,7 +114,7 @@ module LinearToonMcp
111
114
  build_input(input, client, team_id, kwargs)
112
115
 
113
116
  data = client.query(MUTATION, variables: {id:, input:})
114
- result = data["issueUpdate"]
117
+ result = data["issueUpdate"] or raise Error, "Issue update failed: no result returned"
115
118
  raise Error, "Issue update failed" unless result["success"]
116
119
 
117
120
  issue = result["issue"]
@@ -150,7 +153,7 @@ module LinearToonMcp
150
153
  end
151
154
 
152
155
  def needs_team_id?(kwargs)
153
- kwargs.key?(:state) || kwargs.key?(:cycle)
156
+ kwargs.key?(:state) || kwargs.key?(:cycle) || kwargs.key?(:labels)
154
157
  end
155
158
 
156
159
  def build_input(input, client, team_id, kwargs)
@@ -179,7 +182,9 @@ module LinearToonMcp
179
182
  def add_resolved_fields(input, client, team_id, kwargs)
180
183
  input[:teamId] = team_id if kwargs.key?(:team) && team_id
181
184
  input[:stateId] = Resolvers.resolve_state(client, team_id, kwargs[:state]) if kwargs.key?(:state) && team_id
182
- input[:labelIds] = Resolvers.resolve_labels(client, kwargs[:labels]) if kwargs.key?(:labels)
185
+ if kwargs.key?(:labels) && team_id
186
+ input[:labelIds] = Resolvers.resolve_labels(client, kwargs[:labels], team_id:)
187
+ end
183
188
  project_id = nil
184
189
  if kwargs.key?(:project) && kwargs[:project]
185
190
  project_id = Resolvers.resolve_project(client, kwargs[:project])
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LinearToonMcp
4
- VERSION = "0.6.0"
4
+ VERSION = "0.6.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: linear-toon-mcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yevhenii Hurin