LEG-126: fix label updates in Linear MCP update and create actions#3
LEG-126: fix label updates in Linear MCP update and create actions#3sjawhar wants to merge 1 commit into
Conversation
📝 WalkthroughWalkthroughAdds workspace label support to the Linear integration: new label fetch/cache and resolution functions, label integration into create/update/get flows, and updated help text and examples. Public API exports include Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant LinearCore as Linear Core
participant LabelCache as WorkspaceLabelCache
participant LinearAPI as Linear GraphQL API
Client->>LinearCore: create/update/get with label names
LinearCore->>LabelCache: getWorkspaceLabels()?
alt cache miss
LabelCache->>LinearAPI: GraphQL query for workspace labels
LinearAPI-->>LabelCache: labels list (id, name)
LabelCache-->>LinearCore: cached labels
else cache hit
LabelCache-->>LinearCore: cached labels
end
LinearCore->>LinearCore: resolveLabels(names) -> ids or missing
alt creating/updating with resolved ids
LinearCore->>LinearAPI: GraphQL mutation (include label IDs)
LinearAPI-->>LinearCore: mutation result
LinearCore-->>Client: created/updated issue (includes labels)
else missing label names
LinearCore-->>Client: error/report of missing label names
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Review SummaryCRITICAL (P1): 0 issues Verdict: Approved to merge — no blocking issues. What This DoesFixes the silent failure when Key FindingsArchitecture (✅ Good)
Correctness (✅ Verified against acceptance criteria)
P2: 250 label pagination limit — P2: Formatter noise in diff — ~40% of the diff is cosmetic reformatting (parenthesized P3: Unrelated lockfile changes — |
| export async function getWorkspaceLabels(): Promise<Array<{ id: string; name: string }>> { | ||
| if (cachedLabels) return cachedLabels; | ||
| const data = (await graphql(` | ||
| query { issueLabels(first: 250) { nodes { id name } } } |
There was a problem hiding this comment.
[P2 — Observation]: first: 250 without pagination. If a workspace accumulates 250+ labels, resolveLabels will fail to find labels beyond that limit and return a "not found" error even for valid names. Matches existing patterns (teams fetch is also unpaginated), so not blocking — but worth a // TODO: paginate if needed comment for future awareness.
There was a problem hiding this comment.
Added // TODO: paginate if >250 labels comment. Also applied CodeRabbit's dedup suggestion ([...new Set(ids)]) in resolveLabels return.
| "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", | ||
| "license": "MIT", | ||
| "peer": true, | ||
| "dependencies": { |
There was a problem hiding this comment.
[P3 — Nit]: Unrelated lockfile changes — peer: true removed from express and zod entries. Probably an npm install side effect. Not harmful, just noise in the diff.
There was a problem hiding this comment.
Acknowledged. The lockfile changed again during conflict resolution rebuild — same npm install side effect. No functional impact.
Resolve label names to IDs via new getWorkspaceLabels()/resolveLabels() functions. handleUpdate() and handleCreate() now process the labels parameter instead of silently dropping it. Empty arrays clear labels, unknown labels return actionable errors. Label cache refreshes on miss to handle recently created labels.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@mcp/src/linear-core.ts`:
- Around line 130-132: The GraphQL query string uses JavaScript-style "//"
comments which Linear's parser will reject; in the getWorkspaceLabels() call
where graphql(`query { issueLabels(first: 250) { nodes { id name } } } // TODO:
paginate if >250 labels`), replace the inline "//" comment with a GraphQL "#"
comment (or remove it) so the query becomes valid GraphQL (e.g., append " #
TODO: paginate if >250 labels" or just remove the comment) and ensure the
graphql(...) invocation and the data typing remain unchanged.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
mcp/dist/cli.jsis excluded by!**/dist/**mcp/dist/index.jsis excluded by!**/dist/**mcp/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (1)
mcp/src/linear-core.ts
| const data = (await graphql(` | ||
| query { issueLabels(first: 250) { nodes { id name } } } // TODO: paginate if >250 labels | ||
| `)) as { issueLabels: { nodes: Array<{ id: string; name: string }> } }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cd mcp/src && head -n 140 linear-core.ts | tail -n 15 && echo "---" && sed -n '125,135p' linear-core.tsRepository: obra/streamlinear
Length of output: 1176
🌐 Web query:
GraphQL comment syntax # vs // valid comments
💡 Result:
In standard GraphQL syntax (queries and SDL), the only valid comment form is single-line comments starting with #:
# this is a comment
query { me { id } } # trailing commentThe GraphQL spec defines comments as starting with # and continuing to the end of the line; comments are otherwise ignored like whitespace. [1]
// is not a valid GraphQL comment delimiter per the spec, so using it will generally produce a parse error unless a particular tool adds a non-standard extension. [1]
Also note: """ ... """ is not a “comment”; in SDL it’s a description string (often shown in schema docs/introspection), not ignored like # comments. [2]
Sources:
[1] GraphQL Specification (Working Draft) — Comment grammar uses # only https://spec.graphql.org/draft/
[2] GraphQL.com Learn — notes # comments vs documentation/descriptions https://graphql.com/learn/schema/
🏁 Script executed:
rg -n "export.*function graphql|const graphql.*=" mcp/src/linear-core.tsRepository: obra/streamlinear
Length of output: 94
🏁 Script executed:
# Search for graphql helper definition in the codebase
rg -n "function graphql\|const graphql" --type ts -A 10 | head -50Repository: obra/streamlinear
Length of output: 43
🏁 Script executed:
sed -n '19,50p' mcp/src/linear-core.tsRepository: obra/streamlinear
Length of output: 964
Replace // with # for GraphQL comment syntax.
Line 131 uses JavaScript-style // comment syntax inside the GraphQL query string. GraphQL only supports # comments, so this will cause a parse error when sent to the Linear API, breaking getWorkspaceLabels() at runtime.
Suggested fix
export async function getWorkspaceLabels(): Promise<Array<{ id: string; name: string }>> {
if (cachedLabels) return cachedLabels;
const data = (await graphql(`
+ # TODO: paginate if >250 labels
query { issueLabels(first: 250) { nodes { id name } } }
`)) as { issueLabels: { nodes: Array<{ id: string; name: string }> } };📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const data = (await graphql(` | |
| query { issueLabels(first: 250) { nodes { id name } } } // TODO: paginate if >250 labels | |
| `)) as { issueLabels: { nodes: Array<{ id: string; name: string }> } }; | |
| export async function getWorkspaceLabels(): Promise<Array<{ id: string; name: string }>> { | |
| if (cachedLabels) return cachedLabels; | |
| const data = (await graphql(` | |
| # TODO: paginate if >250 labels | |
| query { issueLabels(first: 250) { nodes { id name } } } | |
| `)) as { issueLabels: { nodes: Array<{ id: string; name: string }> } }; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@mcp/src/linear-core.ts` around lines 130 - 132, The GraphQL query string uses
JavaScript-style "//" comments which Linear's parser will reject; in the
getWorkspaceLabels() call where graphql(`query { issueLabels(first: 250) { nodes
{ id name } } } // TODO: paginate if >250 labels`), replace the inline "//"
comment with a GraphQL "#" comment (or remove it) so the query becomes valid
GraphQL (e.g., append " # TODO: paginate if >250 labels" or just remove the
comment) and ensure the graphql(...) invocation and the data typing remain
unchanged.
Summary
Fixes silent failure when using
labelsparameter inupdateandcreateactions. The MCP accepted alabelsarray but never processed it, always returning "No updates provided" when labels were the only update.Changes
getWorkspaceLabels()cached fetcher andresolveLabels()name-to-ID resolver (follows existinggetTeams()/resolveState()pattern)handleUpdate()to resolve label names to IDs and includelabelIdsin the mutationhandleCreate()similarlyVerification
labels=["worker-active"]→ sets label (instead of "No updates provided")labels=[]→ clears all labelslabels=["nonexistent"]→ error with missing name and available labelsstate="Done"+labels=[...]→ both updatenpm run build✅Implements LEG-126
Summary by CodeRabbit
New Features
Documentation