linear-toon-mcp 0.3.0 → 0.4.0
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 +4 -4
- data/README.md +6 -0
- data/lib/linear_toon_mcp/tools/list_cycles.rb +49 -0
- data/lib/linear_toon_mcp/tools/list_issue_labels.rb +54 -0
- data/lib/linear_toon_mcp/tools/list_issue_statuses.rb +49 -0
- data/lib/linear_toon_mcp/tools/list_projects.rb +54 -0
- data/lib/linear_toon_mcp/tools/list_teams.rb +44 -0
- data/lib/linear_toon_mcp/tools/list_users.rb +61 -0
- data/lib/linear_toon_mcp/version.rb +1 -1
- data/lib/linear_toon_mcp.rb +7 -1
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc4a8872f9db60b36c89019d376e06b28c95ea1d881c62996c0cf05e65e8ccb5
|
|
4
|
+
data.tar.gz: 479e37c017575c68fda311020066518211956fe2416d76c16e337114d20d9cc0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e4967b288c7ee386c448b03acecc0c74b9d7134c88efa23179341a7ab6433e6e58372a84df06a0a835c60bde9522d699a0f57572e56365541521a1161f8e52e5
|
|
7
|
+
data.tar.gz: '078f01ebb0c2b14ba592e065b3bc131768e1ec1d51df887cbae0abd4dcb8b32f48c89e15d54b027d889d6b88ba524c95f8a9ff40685853d1b9244d496f122ffa'
|
data/README.md
CHANGED
|
@@ -46,6 +46,12 @@ claude mcp add -e LINEAR_API_KEY=lin_api_xxxxx linear-toon -- linear-toon-mcp
|
|
|
46
46
|
|------|-------------|
|
|
47
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. |
|
|
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
|
+
| `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
|
+
| `list_teams` | List all teams in the workspace. Returns team id, name, and key. |
|
|
51
|
+
| `list_users` | List users in the workspace, optionally scoped to a team. Returns user id, name, and email. |
|
|
52
|
+
| `list_issue_labels` | List issue labels, optionally scoped to a team. Returns label id and name. |
|
|
53
|
+
| `list_projects` | List projects, optionally scoped to a team. Returns project id, name, and state. |
|
|
54
|
+
| `list_cycles` | List cycles for a team. Returns cycle id, name, number, startsAt, and endsAt. Requires team name or UUID. |
|
|
49
55
|
| `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. |
|
|
50
56
|
| `update_issue` | Update an existing Linear issue by ID. Supports partial updates, null to remove fields, and relation replacement. |
|
|
51
57
|
| `create_comment` | Create a comment on a Linear issue. Supports Markdown content and threaded replies via parentId. |
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "toon"
|
|
4
|
+
|
|
5
|
+
module LinearToonMcp
|
|
6
|
+
module Tools
|
|
7
|
+
# List cycles for a Linear team.
|
|
8
|
+
# Returns TOON-encoded array of cycles with id, name, number, startsAt, and endsAt.
|
|
9
|
+
class ListCycles < MCP::Tool
|
|
10
|
+
description "List cycles for a team"
|
|
11
|
+
|
|
12
|
+
annotations(
|
|
13
|
+
read_only_hint: true,
|
|
14
|
+
destructive_hint: false,
|
|
15
|
+
idempotent_hint: true
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
input_schema(
|
|
19
|
+
properties: {
|
|
20
|
+
team: {type: "string", description: "Team name or ID"}
|
|
21
|
+
},
|
|
22
|
+
required: ["team"],
|
|
23
|
+
additionalProperties: false
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
QUERY = <<~GRAPHQL
|
|
27
|
+
query($filter: CycleFilter) {
|
|
28
|
+
cycles(filter: $filter) { nodes { id name number startsAt endsAt } }
|
|
29
|
+
}
|
|
30
|
+
GRAPHQL
|
|
31
|
+
|
|
32
|
+
class << self
|
|
33
|
+
# @param team [String] team name or UUID
|
|
34
|
+
# @param server_context [Hash, nil] must contain +:client+ key with a {Client}
|
|
35
|
+
# @return [MCP::Tool::Response] TOON-encoded cycle list or error
|
|
36
|
+
def call(team:, server_context: nil)
|
|
37
|
+
client = server_context&.dig(:client) or raise Error, "client missing from server_context"
|
|
38
|
+
team_id = Resolvers.resolve_team(client, team)
|
|
39
|
+
data = client.query(QUERY, variables: {filter: {team: {id: {eq: team_id}}}})
|
|
40
|
+
cycles = data["cycles"] or raise Error, "Unexpected response: missing cycles field"
|
|
41
|
+
text = Toon.encode(cycles)
|
|
42
|
+
MCP::Tool::Response.new([{type: "text", text:}])
|
|
43
|
+
rescue Error => e
|
|
44
|
+
MCP::Tool::Response.new([{type: "text", text: e.message}], error: true)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "toon"
|
|
4
|
+
|
|
5
|
+
module LinearToonMcp
|
|
6
|
+
module Tools
|
|
7
|
+
# List issue labels in the Linear workspace, optionally scoped to a team.
|
|
8
|
+
# Returns TOON-encoded array of labels with id and name.
|
|
9
|
+
class ListIssueLabels < MCP::Tool
|
|
10
|
+
description "List issue labels, optionally scoped to a team"
|
|
11
|
+
|
|
12
|
+
annotations(
|
|
13
|
+
read_only_hint: true,
|
|
14
|
+
destructive_hint: false,
|
|
15
|
+
idempotent_hint: true
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
input_schema(
|
|
19
|
+
properties: {
|
|
20
|
+
team: {type: "string", description: "Team name or ID"}
|
|
21
|
+
},
|
|
22
|
+
additionalProperties: false
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
QUERY = <<~GRAPHQL
|
|
26
|
+
query($filter: IssueLabelFilter) {
|
|
27
|
+
issueLabels(filter: $filter) { nodes { id name } }
|
|
28
|
+
}
|
|
29
|
+
GRAPHQL
|
|
30
|
+
|
|
31
|
+
class << self
|
|
32
|
+
# @param team [String, nil] team name or UUID (optional scope)
|
|
33
|
+
# @param server_context [Hash, nil] must contain +:client+ key with a {Client}
|
|
34
|
+
# @return [MCP::Tool::Response] TOON-encoded label list or error
|
|
35
|
+
def call(team: nil, server_context: nil)
|
|
36
|
+
client = server_context&.dig(:client) or raise Error, "client missing from server_context"
|
|
37
|
+
|
|
38
|
+
variables = {}
|
|
39
|
+
if team
|
|
40
|
+
team_id = Resolvers.resolve_team(client, team)
|
|
41
|
+
variables[:filter] = {team: {id: {eq: team_id}}}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
data = client.query(QUERY, variables:)
|
|
45
|
+
labels = data["issueLabels"] or raise Error, "Unexpected response: missing issueLabels field"
|
|
46
|
+
text = Toon.encode(labels)
|
|
47
|
+
MCP::Tool::Response.new([{type: "text", text:}])
|
|
48
|
+
rescue Error => e
|
|
49
|
+
MCP::Tool::Response.new([{type: "text", text: e.message}], error: true)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "toon"
|
|
4
|
+
|
|
5
|
+
module LinearToonMcp
|
|
6
|
+
module Tools
|
|
7
|
+
# List available workflow states for a Linear team.
|
|
8
|
+
# Returns TOON-encoded array of statuses with id, type, and name.
|
|
9
|
+
class ListIssueStatuses < MCP::Tool
|
|
10
|
+
description "List available issue statuses in a Linear team"
|
|
11
|
+
|
|
12
|
+
annotations(
|
|
13
|
+
read_only_hint: true,
|
|
14
|
+
destructive_hint: false,
|
|
15
|
+
idempotent_hint: true
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
input_schema(
|
|
19
|
+
properties: {
|
|
20
|
+
team: {type: "string", description: "Team name or ID"}
|
|
21
|
+
},
|
|
22
|
+
required: ["team"],
|
|
23
|
+
additionalProperties: false
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
QUERY = <<~GRAPHQL
|
|
27
|
+
query($filter: WorkflowStateFilter) {
|
|
28
|
+
workflowStates(filter: $filter) { nodes { id type name } }
|
|
29
|
+
}
|
|
30
|
+
GRAPHQL
|
|
31
|
+
|
|
32
|
+
class << self
|
|
33
|
+
# @param team [String] team name or UUID
|
|
34
|
+
# @param server_context [Hash, nil] must contain +:client+ key with a {Client}
|
|
35
|
+
# @return [MCP::Tool::Response] TOON-encoded status list or error
|
|
36
|
+
def call(team:, server_context: nil)
|
|
37
|
+
client = server_context&.dig(:client) or raise Error, "client missing from server_context"
|
|
38
|
+
team_id = Resolvers.resolve_team(client, team)
|
|
39
|
+
data = client.query(QUERY, variables: {filter: {team: {id: {eq: team_id}}}})
|
|
40
|
+
states = data["workflowStates"] or raise Error, "Unexpected response: missing workflowStates field"
|
|
41
|
+
text = Toon.encode(states)
|
|
42
|
+
MCP::Tool::Response.new([{type: "text", text:}])
|
|
43
|
+
rescue Error => e
|
|
44
|
+
MCP::Tool::Response.new([{type: "text", text: e.message}], error: true)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "toon"
|
|
4
|
+
|
|
5
|
+
module LinearToonMcp
|
|
6
|
+
module Tools
|
|
7
|
+
# List projects in the Linear workspace, optionally scoped to a team.
|
|
8
|
+
# Returns TOON-encoded array of projects with id, name, and state.
|
|
9
|
+
class ListProjects < MCP::Tool
|
|
10
|
+
description "List projects, optionally scoped to a team"
|
|
11
|
+
|
|
12
|
+
annotations(
|
|
13
|
+
read_only_hint: true,
|
|
14
|
+
destructive_hint: false,
|
|
15
|
+
idempotent_hint: true
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
input_schema(
|
|
19
|
+
properties: {
|
|
20
|
+
team: {type: "string", description: "Team name or ID"}
|
|
21
|
+
},
|
|
22
|
+
additionalProperties: false
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
QUERY = <<~GRAPHQL
|
|
26
|
+
query($filter: ProjectFilter) {
|
|
27
|
+
projects(filter: $filter) { nodes { id name state } }
|
|
28
|
+
}
|
|
29
|
+
GRAPHQL
|
|
30
|
+
|
|
31
|
+
class << self
|
|
32
|
+
# @param team [String, nil] team name or UUID (optional scope)
|
|
33
|
+
# @param server_context [Hash, nil] must contain +:client+ key with a {Client}
|
|
34
|
+
# @return [MCP::Tool::Response] TOON-encoded project list or error
|
|
35
|
+
def call(team: nil, server_context: nil)
|
|
36
|
+
client = server_context&.dig(:client) or raise Error, "client missing from server_context"
|
|
37
|
+
|
|
38
|
+
variables = {}
|
|
39
|
+
if team
|
|
40
|
+
team_id = Resolvers.resolve_team(client, team)
|
|
41
|
+
variables[:filter] = {accessibleTeams: {id: {eq: team_id}}}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
data = client.query(QUERY, variables:)
|
|
45
|
+
projects = data["projects"] or raise Error, "Unexpected response: missing projects field"
|
|
46
|
+
text = Toon.encode(projects)
|
|
47
|
+
MCP::Tool::Response.new([{type: "text", text:}])
|
|
48
|
+
rescue Error => e
|
|
49
|
+
MCP::Tool::Response.new([{type: "text", text: e.message}], error: true)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "toon"
|
|
4
|
+
|
|
5
|
+
module LinearToonMcp
|
|
6
|
+
module Tools
|
|
7
|
+
# List all teams in the Linear workspace.
|
|
8
|
+
# Returns TOON-encoded array of teams with id, name, and key.
|
|
9
|
+
class ListTeams < MCP::Tool
|
|
10
|
+
description "List teams in the workspace"
|
|
11
|
+
|
|
12
|
+
annotations(
|
|
13
|
+
read_only_hint: true,
|
|
14
|
+
destructive_hint: false,
|
|
15
|
+
idempotent_hint: true
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
input_schema(
|
|
19
|
+
properties: {},
|
|
20
|
+
additionalProperties: false
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
QUERY = <<~GRAPHQL
|
|
24
|
+
query {
|
|
25
|
+
teams { nodes { id name key } }
|
|
26
|
+
}
|
|
27
|
+
GRAPHQL
|
|
28
|
+
|
|
29
|
+
class << self
|
|
30
|
+
# @param server_context [Hash, nil] must contain +:client+ key with a {Client}
|
|
31
|
+
# @return [MCP::Tool::Response] TOON-encoded team list or error
|
|
32
|
+
def call(server_context: nil)
|
|
33
|
+
client = server_context&.dig(:client) or raise Error, "client missing from server_context"
|
|
34
|
+
data = client.query(QUERY)
|
|
35
|
+
teams = data["teams"] or raise Error, "Unexpected response: missing teams field"
|
|
36
|
+
text = Toon.encode(teams)
|
|
37
|
+
MCP::Tool::Response.new([{type: "text", text:}])
|
|
38
|
+
rescue Error => e
|
|
39
|
+
MCP::Tool::Response.new([{type: "text", text: e.message}], error: true)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "toon"
|
|
4
|
+
|
|
5
|
+
module LinearToonMcp
|
|
6
|
+
module Tools
|
|
7
|
+
# List users in the Linear workspace, optionally scoped to a team.
|
|
8
|
+
# Returns TOON-encoded array of users with id, name, and email.
|
|
9
|
+
class ListUsers < MCP::Tool
|
|
10
|
+
description "List users, optionally scoped to a team"
|
|
11
|
+
|
|
12
|
+
annotations(
|
|
13
|
+
read_only_hint: true,
|
|
14
|
+
destructive_hint: false,
|
|
15
|
+
idempotent_hint: true
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
input_schema(
|
|
19
|
+
properties: {
|
|
20
|
+
team: {type: "string", description: "Team name or ID"}
|
|
21
|
+
},
|
|
22
|
+
additionalProperties: false
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
QUERY = <<~GRAPHQL
|
|
26
|
+
query {
|
|
27
|
+
users { nodes { id name email } }
|
|
28
|
+
}
|
|
29
|
+
GRAPHQL
|
|
30
|
+
|
|
31
|
+
TEAM_MEMBERS_QUERY = <<~GRAPHQL
|
|
32
|
+
query($id: String!) {
|
|
33
|
+
team(id: $id) { members { nodes { id name email } } }
|
|
34
|
+
}
|
|
35
|
+
GRAPHQL
|
|
36
|
+
|
|
37
|
+
class << self
|
|
38
|
+
# @param team [String, nil] team name or UUID (optional scope)
|
|
39
|
+
# @param server_context [Hash, nil] must contain +:client+ key with a {Client}
|
|
40
|
+
# @return [MCP::Tool::Response] TOON-encoded user list or error
|
|
41
|
+
def call(team: nil, server_context: nil)
|
|
42
|
+
client = server_context&.dig(:client) or raise Error, "client missing from server_context"
|
|
43
|
+
|
|
44
|
+
users = if team
|
|
45
|
+
team_id = Resolvers.resolve_team(client, team)
|
|
46
|
+
data = client.query(TEAM_MEMBERS_QUERY, variables: {id: team_id})
|
|
47
|
+
data.dig("team", "members") or raise Error, "Unexpected response: missing team members field"
|
|
48
|
+
else
|
|
49
|
+
data = client.query(QUERY)
|
|
50
|
+
data["users"] or raise Error, "Unexpected response: missing users field"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
text = Toon.encode(users)
|
|
54
|
+
MCP::Tool::Response.new([{type: "text", text:}])
|
|
55
|
+
rescue Error => e
|
|
56
|
+
MCP::Tool::Response.new([{type: "text", text: e.message}], error: true)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/lib/linear_toon_mcp.rb
CHANGED
|
@@ -9,6 +9,12 @@ require_relative "linear_toon_mcp/tools/list_issues"
|
|
|
9
9
|
require_relative "linear_toon_mcp/tools/create_comment"
|
|
10
10
|
require_relative "linear_toon_mcp/tools/create_issue"
|
|
11
11
|
require_relative "linear_toon_mcp/tools/update_issue"
|
|
12
|
+
require_relative "linear_toon_mcp/tools/list_issue_statuses"
|
|
13
|
+
require_relative "linear_toon_mcp/tools/list_teams"
|
|
14
|
+
require_relative "linear_toon_mcp/tools/list_users"
|
|
15
|
+
require_relative "linear_toon_mcp/tools/list_issue_labels"
|
|
16
|
+
require_relative "linear_toon_mcp/tools/list_projects"
|
|
17
|
+
require_relative "linear_toon_mcp/tools/list_cycles"
|
|
12
18
|
|
|
13
19
|
# Token-efficient MCP server for Linear. Wraps Linear's GraphQL API
|
|
14
20
|
# and returns TOON-formatted responses for ~40-60% token savings.
|
|
@@ -24,7 +30,7 @@ module LinearToonMcp
|
|
|
24
30
|
name: "linear-toon-mcp",
|
|
25
31
|
version: VERSION,
|
|
26
32
|
description: "Manage Linear issues, projects, and teams",
|
|
27
|
-
tools: [Tools::GetIssue, Tools::ListIssues, Tools::CreateComment, Tools::CreateIssue, Tools::UpdateIssue],
|
|
33
|
+
tools: [Tools::GetIssue, Tools::ListIssues, Tools::ListIssueStatuses, Tools::ListTeams, Tools::ListUsers, Tools::ListIssueLabels, Tools::ListProjects, Tools::ListCycles, Tools::CreateComment, Tools::CreateIssue, Tools::UpdateIssue],
|
|
28
34
|
server_context: {client:}
|
|
29
35
|
)
|
|
30
36
|
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.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yevhenii Hurin
|
|
@@ -69,7 +69,13 @@ files:
|
|
|
69
69
|
- lib/linear_toon_mcp/tools/create_comment.rb
|
|
70
70
|
- lib/linear_toon_mcp/tools/create_issue.rb
|
|
71
71
|
- lib/linear_toon_mcp/tools/get_issue.rb
|
|
72
|
+
- lib/linear_toon_mcp/tools/list_cycles.rb
|
|
73
|
+
- lib/linear_toon_mcp/tools/list_issue_labels.rb
|
|
74
|
+
- lib/linear_toon_mcp/tools/list_issue_statuses.rb
|
|
72
75
|
- lib/linear_toon_mcp/tools/list_issues.rb
|
|
76
|
+
- lib/linear_toon_mcp/tools/list_projects.rb
|
|
77
|
+
- lib/linear_toon_mcp/tools/list_teams.rb
|
|
78
|
+
- lib/linear_toon_mcp/tools/list_users.rb
|
|
73
79
|
- lib/linear_toon_mcp/tools/update_issue.rb
|
|
74
80
|
- lib/linear_toon_mcp/version.rb
|
|
75
81
|
homepage: https://github.com/hoblin/linear-toon-mcp
|