Skip to content

Integrate with Linear MCP server#41

Merged
yosrixp merged 3 commits into
mainfrom
linear
May 21, 2026
Merged

Integrate with Linear MCP server#41
yosrixp merged 3 commits into
mainfrom
linear

Conversation

@yosrixp

@yosrixp yosrixp commented May 21, 2026

Copy link
Copy Markdown
Collaborator

Add Linear MCP provider integration

Adds linear as an upstream MCP provider in MoP, mirroring the Datadog rollout. Linear is exposed at https://(dev-)mcp-gateway.ouryahoo.com/v1/linear/mcp.

Linear-specific deltas vs Datadog

  • Public PKCE client (no client_secret on the wire). client-secret-key placeholder is configured but ignored by LinearUpstreamRefreshClient and TokenExchangeServiceLinearImpl; tests assert no client_secret in body and no Authorization header.
  • Comma-separated scopes — Linear requires comments:create,issues:create,read,timeSchedule:write,write joined with commas. Forced via extra-params.scope (Quarkus' authentication.scopes list space-joins).
  • actor=user extra-param on /authorize (deliberately no prompt=consent).
  • Rotating refresh tokens with a documented 30-min replay-grace window. We persist response RT verbatim; defensive carry-forward retained as a safety net.
  • L2 cap at 6 months (15 552 000 s) — parity with Google/Datadog. Linear's RT lifetime is otherwise unbounded.
  • 24h L1 row TTL via the 8-arg storeTokens overload (Linear AT exceeds the global ~8h cap).
  • Synthetic userinfo at /internal/linear/oauth-userinfo POSTs query { viewer { id name email } } to https://api.linear.app/graphql and flattens to top-level email/id/name/sub.

Workflow

flowchart TB
  user[User browser]
  authorize["AuthorizeResource /authorize"]
  liCb["LinearResource /linear/authorize @Authenticated"]
  liSynth["/internal/linear/oauth-userinfo flattens GraphQL viewer"]
  tokenEp["TokenResource /token"]
  refreshSvc[UpstreamRefreshService.refreshUpstream]
  liClient[LinearUpstreamRefreshClient public PKCE]
  authorizerSvc[AuthorizerService.storeTokens L2 seed]
  L2[("mcp-oauth-proxy-upstream-tokens row linear#sub")]
  L1[("mcp-oauth-proxy-tokens row client#linear#sub")]
  liAuthz[https://linear.app/oauth/authorize]
  liToken[https://api.linear.app/oauth/token]
  liGraph[https://api.linear.app/graphql]
  user -->|"/authorize?resource=.../v1/linear/mcp"| authorize
  authorize -->|"redirect /linear/authorize"| liCb
  liCb -->|"PKCE + actor=user + comma scopes"| liAuthz
  liAuthz -->|"code"| liCb
  liCb -->|"POST code+verifier (no client_secret)"| liToken
  liToken -->|"AT 24h + RT rotating"| liCb
  liCb -->|"Quarkus auto userinfo"| liSynth
  liSynth -->|"POST viewer query, Bearer AT"| liGraph
  liSynth -->|"flat email, id, name, sub"| liCb
  liCb -->|"storeTokens linear lifetime=86399"| authorizerSvc
  authorizerSvc -->|"seed canonical RT"| L2
  authorizerSvc -->|"per-client AT"| L1
  tokenEp -->|"refresh_token grant"| refreshSvc
  refreshSvc -->|"resolveRefreshTokenClient(linear)"| liClient
  liClient -->|"POST grant_type=refresh_token client_id only"| liToken
Loading

Files

New (production)

  • LinearResource.java/linear/authorize callback, 8-arg storeTokens.
  • LinearUserInfoResource.java — synthetic GraphQL viewer flattener.
  • LinearUpstreamRefreshClient.java — canonical L2 refresh client (public PKCE, RT rotation, carry-forward fallback, TODO(linear-confidential) hook).
  • TokenExchangeServiceLinearImpl.java — legacy passthrough impl.

Modified (production)

  • UpstreamProviderClassifier.java — Linear added to PROMOTED_PROVIDERS + dispatch.
  • TokenExchangeServiceProducer.javacase "linear".
  • OauthProviderLabel.java — added LINEAR.

Config

  • application.yaml, application.dev.yaml, configmap.yaml, chart values.yaml, stage + both prod values files — quarkus.oidc.linear tenant, /linear/* and /internal/linear/* permissions, server.token-exchange.linear, upstream-token.expiry-seconds-by-provider.linear=15552000, resource-mapping for /v1/linear/mcp.
  • application.dev.yaml — also renamed embrace-local-insecuredev-local-insecure (generic) and reused for Datadog/Linear/Embrace tenants whose synthetic userinfo runs on https://localhost.

Tests (195 pass)

  • New: LinearUpstreamRefreshClientTest, TokenExchangeServiceLinearImplTest, LinearResourceTest, LinearUserInfoResourceTest.
  • Extended: UpstreamProviderClassifierTest, UpstreamTokenConfigTest, TokenExchangeServiceProducerTest.
  • Public-client wire shape pinned: assert no client_secret in body, no Authorization header on refresh.
cd mcp-oauth-proxy && ~/.cursor/bin/mvnw-jdk25 test \
  -Dtest='LinearUpstreamRefreshClientTest,TokenExchangeServiceLinearImplTest,LinearResourceTest,LinearUserInfoResourceTest,UpstreamProviderClassifierTest,UpstreamTokenConfigTest,TokenExchangeServiceProducerTest' \
  -Djacoco.skip=true

TTL summary

Tier TTL Notes
L0 IdpSessionCache 1h unchanged global
L1 mcp-oauth-proxy-tokens (bare row) ~24h pinned via 8-arg storeTokens (matches Linear AT)
L2 mcp-oauth-proxy-upstream-tokens 6 months + 3d buffer RT rotates each refresh; row updated in place
Bearer-index ~24h derived from L1

Risks / assumptions

  • No client_secret todayLinearUpstreamRefreshClient uses the public-client Nimbus overload. TODO(linear-confidential) hook documents the future flip to ClientSecretBasic. Tests assert no secret leaks onto the wire.
  • Comma scopes — bypasses Quarkus' space-joining via extra-params.scope. authentication.scopes: [] + add-openid-scope: false ensures Quarkus emits no scope of its own. Verify the assembled /authorize URL in stage.
  • Replay grace — Linear's 30-min replay window is server-side; MoP just retries the same request and converges on the same new RT. No code change needed.

yosrixp added 3 commits May 21, 2026 02:08
Signed-off-by: yosrixp <yosrixp@yahoo.com>
Signed-off-by: yosrixp <yosrixp@yahoo.com>
Signed-off-by: yosrixp <yosrixp@yahoo.com>
@yosrixp
yosrixp merged commit ebadeec into main May 21, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants