linear-toon-mcp 0.4.0 → 0.5.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 +2 -1
- data/lib/linear_toon_mcp/resolvers.rb +7 -1
- data/lib/linear_toon_mcp/tools/get_project.rb +99 -0
- data/lib/linear_toon_mcp/version.rb +1 -1
- data/lib/linear_toon_mcp.rb +2 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b7241fefeb39d4de3f8649fa66bede65615fa0eda60aa1206f121f3c2e30d0d8
|
|
4
|
+
data.tar.gz: d0eefcd97e9ff4fd27ca48efde69eeb6124a082b81f619671c5a67c8d3cc817e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3275b9582f4ea6d2a800e2dbc08f64e81e82932a5ec9a545ccdc7dea5d3935fd65a18aec825c464e17cbd724dfc04f239f9cb187a5c70cb2df25299576cdbb9d
|
|
7
|
+
data.tar.gz: b65c95f035164b0ea5eddc6f703716779040828e20d42b889992244de18bb95053178933af1a7a5f9406a4c567d8a6fed80ed8966a04641a8700ff9dec122b8f
|
data/README.md
CHANGED
|
@@ -37,7 +37,7 @@ Get your API key from [Linear Settings > API](https://linear.app/settings/api).
|
|
|
37
37
|
### Claude Code
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
claude mcp add -e LINEAR_API_KEY=lin_api_xxxxx
|
|
40
|
+
claude mcp add linear-toon -e LINEAR_API_KEY=lin_api_xxxxx -- linear-toon-mcp
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
## Tools
|
|
@@ -52,6 +52,7 @@ claude mcp add -e LINEAR_API_KEY=lin_api_xxxxx linear-toon -- linear-toon-mcp
|
|
|
52
52
|
| `list_issue_labels` | List issue labels, optionally scoped to a team. Returns label id and name. |
|
|
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
|
+
| `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. |
|
|
55
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. |
|
|
56
57
|
| `update_issue` | Update an existing Linear issue by ID. Supports partial updates, null to remove fields, and relation replacement. |
|
|
57
58
|
| `create_comment` | Create a comment on a Linear issue. Supports Markdown content and threaded replies via parentId. |
|
|
@@ -110,12 +110,18 @@ module LinearToonMcp
|
|
|
110
110
|
end
|
|
111
111
|
|
|
112
112
|
# @param client [Client]
|
|
113
|
-
# @param value [String] project UUID or
|
|
113
|
+
# @param value [String] project UUID, name, or slug
|
|
114
114
|
# @return [String] project UUID
|
|
115
115
|
# @raise [Error] when project not found
|
|
116
116
|
def resolve_project(client, value)
|
|
117
117
|
return value if value.match?(UUID_RE)
|
|
118
|
+
|
|
119
|
+
# Try name first, then slug
|
|
118
120
|
data = client.query(PROJECT_QUERY, variables: {filter: {name: {eqIgnoreCase: value}}})
|
|
121
|
+
id = data.dig("projects", "nodes", 0, "id")
|
|
122
|
+
return id if id
|
|
123
|
+
|
|
124
|
+
data = client.query(PROJECT_QUERY, variables: {filter: {slugId: {eqIgnoreCase: value}}})
|
|
119
125
|
data.dig("projects", "nodes", 0, "id") or raise Error, "Project not found: #{value}"
|
|
120
126
|
end
|
|
121
127
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "toon"
|
|
4
|
+
|
|
5
|
+
module LinearToonMcp
|
|
6
|
+
module Tools
|
|
7
|
+
# Fetch a single Linear project by ID, name, or slug and return it as TOON.
|
|
8
|
+
# Supports optional includes for members, milestones, and resources.
|
|
9
|
+
class GetProject < MCP::Tool
|
|
10
|
+
description "Retrieve details of a specific project in Linear"
|
|
11
|
+
|
|
12
|
+
annotations(
|
|
13
|
+
read_only_hint: true,
|
|
14
|
+
destructive_hint: false,
|
|
15
|
+
idempotent_hint: true
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
input_schema(
|
|
19
|
+
properties: {
|
|
20
|
+
query: {type: "string", description: "Project name, ID, or slug"},
|
|
21
|
+
includeMembers: {type: "boolean", description: "Include project members (default: false)"},
|
|
22
|
+
includeMilestones: {type: "boolean", description: "Include milestones (default: false)"},
|
|
23
|
+
includeResources: {type: "boolean", description: "Include resources (documents) (default: false)"}
|
|
24
|
+
},
|
|
25
|
+
required: ["query"],
|
|
26
|
+
additionalProperties: false
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
BASE_FIELDS = <<~GRAPHQL
|
|
30
|
+
id
|
|
31
|
+
name
|
|
32
|
+
slugId
|
|
33
|
+
icon
|
|
34
|
+
color
|
|
35
|
+
url
|
|
36
|
+
description
|
|
37
|
+
state
|
|
38
|
+
priority
|
|
39
|
+
priorityLabel
|
|
40
|
+
startDate
|
|
41
|
+
targetDate
|
|
42
|
+
createdAt
|
|
43
|
+
updatedAt
|
|
44
|
+
archivedAt
|
|
45
|
+
progress
|
|
46
|
+
scope
|
|
47
|
+
completedScopeHistory
|
|
48
|
+
lead { id name }
|
|
49
|
+
status { id name }
|
|
50
|
+
teams { nodes { id name } }
|
|
51
|
+
labels { nodes { id name } }
|
|
52
|
+
initiatives { nodes { id name } }
|
|
53
|
+
GRAPHQL
|
|
54
|
+
|
|
55
|
+
MEMBERS_FIELDS = "members { nodes { id name email } }"
|
|
56
|
+
MILESTONES_FIELDS = "projectMilestones { nodes { id name targetDate } }"
|
|
57
|
+
RESOURCES_FIELDS = "documents { nodes { id title } }"
|
|
58
|
+
|
|
59
|
+
# standard:disable Naming/VariableName
|
|
60
|
+
class << self
|
|
61
|
+
# @param query [String] Project ID, name, or slug
|
|
62
|
+
# @param includeMembers [Boolean] Include project members
|
|
63
|
+
# @param includeMilestones [Boolean] Include project milestones
|
|
64
|
+
# @param includeResources [Boolean] Include documents
|
|
65
|
+
# @param server_context [Hash, nil] must contain +:client+ key with a {Client}
|
|
66
|
+
# @return [MCP::Tool::Response] TOON-encoded project or error
|
|
67
|
+
def call(query:, includeMembers: false, includeMilestones: false, includeResources: false, server_context: nil)
|
|
68
|
+
client = server_context&.dig(:client) or raise Error, "client missing from server_context"
|
|
69
|
+
project_id = Resolvers.resolve_project(client, query)
|
|
70
|
+
graphql = build_query(includeMembers:, includeMilestones:, includeResources:)
|
|
71
|
+
data = client.query(graphql, variables: {id: project_id})
|
|
72
|
+
project = data["project"] or raise Error, "Project not found: #{query}"
|
|
73
|
+
text = Toon.encode(project)
|
|
74
|
+
MCP::Tool::Response.new([{type: "text", text:}])
|
|
75
|
+
rescue Error => e
|
|
76
|
+
MCP::Tool::Response.new([{type: "text", text: e.message}], error: true)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def build_query(includeMembers:, includeMilestones:, includeResources:)
|
|
82
|
+
fields = [BASE_FIELDS.strip]
|
|
83
|
+
fields << MEMBERS_FIELDS if includeMembers
|
|
84
|
+
fields << MILESTONES_FIELDS if includeMilestones
|
|
85
|
+
fields << RESOURCES_FIELDS if includeResources
|
|
86
|
+
|
|
87
|
+
<<~GRAPHQL
|
|
88
|
+
query($id: String!) {
|
|
89
|
+
project(id: $id) {
|
|
90
|
+
#{fields.join("\n ")}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
GRAPHQL
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
# standard:enable Naming/VariableName
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
data/lib/linear_toon_mcp.rb
CHANGED
|
@@ -15,6 +15,7 @@ require_relative "linear_toon_mcp/tools/list_users"
|
|
|
15
15
|
require_relative "linear_toon_mcp/tools/list_issue_labels"
|
|
16
16
|
require_relative "linear_toon_mcp/tools/list_projects"
|
|
17
17
|
require_relative "linear_toon_mcp/tools/list_cycles"
|
|
18
|
+
require_relative "linear_toon_mcp/tools/get_project"
|
|
18
19
|
|
|
19
20
|
# Token-efficient MCP server for Linear. Wraps Linear's GraphQL API
|
|
20
21
|
# and returns TOON-formatted responses for ~40-60% token savings.
|
|
@@ -30,7 +31,7 @@ module LinearToonMcp
|
|
|
30
31
|
name: "linear-toon-mcp",
|
|
31
32
|
version: VERSION,
|
|
32
33
|
description: "Manage Linear issues, projects, and teams",
|
|
33
|
-
tools: [Tools::GetIssue, Tools::ListIssues, Tools::ListIssueStatuses, Tools::ListTeams, Tools::ListUsers, Tools::ListIssueLabels, Tools::ListProjects, Tools::ListCycles, Tools::CreateComment, Tools::CreateIssue, Tools::UpdateIssue],
|
|
34
|
+
tools: [Tools::GetIssue, Tools::ListIssues, Tools::ListIssueStatuses, Tools::ListTeams, Tools::ListUsers, Tools::ListIssueLabels, Tools::ListProjects, Tools::ListCycles, Tools::GetProject, Tools::CreateComment, Tools::CreateIssue, Tools::UpdateIssue],
|
|
34
35
|
server_context: {client:}
|
|
35
36
|
)
|
|
36
37
|
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.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yevhenii Hurin
|
|
@@ -69,6 +69,7 @@ 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/get_project.rb
|
|
72
73
|
- lib/linear_toon_mcp/tools/list_cycles.rb
|
|
73
74
|
- lib/linear_toon_mcp/tools/list_issue_labels.rb
|
|
74
75
|
- lib/linear_toon_mcp/tools/list_issue_statuses.rb
|