feat(linear): resolve API token via AgentCore Identity (Phase 2.0a)#2
Draft
isadeks wants to merge 1 commit into
Draft
feat(linear): resolve API token via AgentCore Identity (Phase 2.0a)#2isadeks wants to merge 1 commit into
isadeks wants to merge 1 commit into
Conversation
Migrates the agent runtime's Linear personal API token resolution from
AWS Secrets Manager to AWS Bedrock AgentCore Identity. This is the
"validate Identity SDK" step of the v2 plan; Phase 2.0b will swap the
API key for OAuth and converge Linear MCP onto AgentCore Gateway in
one cutover.
Per Alain's guidance: "start by using api key, if it works, switch to
oauth. you will setup an outbound auth for your server using agentcore
identity. that identity can be (AC identity is like a wrapper around
secrets manager) api key or oauth."
## Scope: agent runtime only
Lambdas (orchestrator + processor) intentionally keep using Secrets
Manager via the existing `LinearApiTokenSecret` for now. The Python
`bedrock_agentcore` SDK has no Node.js equivalent — Lambda migration
requires `@aws-sdk/client-bedrock-agentcore` raw API calls and folds
into 2.0b's bigger refactor. End-state of 2.0a: agent reads from
Identity, Lambdas read from Secrets Manager, both pointing at the same
underlying token value (admin populates both).
## What changed
`agent/src/config.py::resolve_linear_api_token`:
- Drops boto3 SecretsManager fetch + `LINEAR_API_TOKEN_SECRET_ARN` env.
- Reads new env `LINEAR_API_KEY_PROVIDER_NAME` (provider name in
Identity vault).
- Calls `IdentityClient.get_api_key()` with the workload access token
auto-injected into `BedrockAgentCoreContext` by AgentCore Runtime
(verified by reading the SDK's `auth.py` decorator implementation —
no manual workload-identity mint needed inside the runtime).
- Caches the resolved token in `LINEAR_API_TOKEN` so downstream
consumers stay unchanged: `channel_mcp.py`'s `${LINEAR_API_TOKEN}`
placeholder in `.mcp.json` and `linear_reactions.py`'s GraphQL
Authorization header.
Preserves PR aws-samples#87's nice-to-have improvements:
- `ImportError` graceful fallback (now for `bedrock_agentcore` instead
of `boto3`) — degrade with WARN, don't crash the agent.
- `AccessDeniedException` and `ResourceNotFoundException` logged at
ERROR severity (persistent IAM/config bugs that should page).
Other ClientErrors stay at WARN (transient throttle/network).
`agent/pyproject.toml`: adds `bedrock-agentcore==1.9.1` dep.
`cdk/src/stacks/agent.ts`:
- On the AgentCore runtime: drops `linearIntegration.apiTokenSecret.
grantRead(runtime)` and the `LINEAR_API_TOKEN_SECRET_ARN` env-var
override. Adds `LINEAR_API_KEY_PROVIDER_NAME` env (hardcoded
`'linear-api-key'` for now; can parametrize later via context if
multi-environment naming is needed) and IAM permissions for
`bedrock-agentcore:GetResourceApiKey` and
`bedrock-agentcore:GetWorkloadAccessToken`.
- Lambdas (orchestrator + processor) untouched — they still grant on
the Linear secret and read from Secrets Manager.
- Resource scope on the new IAM is `*` for now; AgentCore Identity ARN
format isn't fully standardized in public docs as of 2026-05-15.
Tighten in 2.0b when OAuth migration documents the canonical
resource shape.
`docs/guides/LINEAR_SETUP_GUIDE.md`: adds Step 4.5 documenting the
one-time `agentcore add credential --type api-key --name linear-api-key`
admin command users must run alongside the existing `bgagent linear
setup` wizard. Notes that Lambdas keep Secrets Manager temporarily and
2.0b will retire the dual-store setup. Starlight mirror synced.
## Tests
`agent/tests/test_config.py::TestResolveLinearApiToken` — 10 tests
covering: cached env var fast-path; missing provider name; missing
region; workload token absent (outside runtime); happy path with
env-var side-effect; botocore error swallowed with WARN; SDK returns
None defensively; ImportError fallback; AccessDeniedException → ERROR
severity; ResourceNotFoundException → ERROR severity.
542 agent / 1271 cdk / 196 cli, all green. Lint + typecheck clean.
CDK synth clean.
## Migration notes for reviewer
`bedrock_agentcore` SDK confirmed working in our runtime image (verified
in `node_modules` post-install). The `BedrockAgentCoreContext` workload
token auto-injection is documented behaviour for code running inside
AgentCore Runtime — verified by reading the SDK's `@requires_api_key`
decorator implementation, which uses the same context lookup we use
here.
Stacked on PR aws-samples#87 (`feat/linear-processor-feedback`). Will conflict on
`config.py` and `test_config.py` if aws-samples#87 needs further rework before
merge — happy to rebase.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 2.0a of the Linear v2 plan. Stacked on PR aws-samples#87 (
feat/linear-processor-feedback); this PR's base is set to that branch so the diff shows only the 2.0a changes. Once aws-samples#87 merges upstream, this will rebase ontomainand open againstaws-samples.Migrates the agent runtime's Linear personal API token resolution from AWS Secrets Manager to AWS Bedrock AgentCore Identity. This is the "validate the Identity SDK" step before the bigger OAuth + Gateway cutover in Phase 2.0b.
Per Alain's guidance from the v1.1 review thread:
Scope: agent runtime only
Lambdas (orchestrator + processor) intentionally keep using Secrets Manager. Reason: the Python
bedrock_agentcoreSDK has no Node.js equivalent — Lambda migration requires@aws-sdk/client-bedrock-agentcoreraw API calls and folds into 2.0b's bigger refactor (where OAuth tokens replace API keys for all consumers in one cutover).End-state of 2.0a:
agentcore add credentialonce and populates Linear API token in both stores)What changed
agent/src/config.py::resolve_linear_api_tokenLINEAR_API_TOKEN_SECRET_ARNenv varLINEAR_API_KEY_PROVIDER_NAME(provider name in the Identity vault, default:linear-api-key)IdentityClient.get_api_key()with the workload access token auto-injected intoBedrockAgentCoreContextby AgentCore RuntimeLINEAR_API_TOKENenv so downstream consumers stay unchanged:channel_mcp.py's\${LINEAR_API_TOKEN}placeholder in.mcp.jsonandlinear_reactions.py's GraphQL Authorization headerWhy imperative
IdentityClient.get_api_key()instead of the@requires_api_keydecorator: API keys don't need refresh. The decorator pattern shines for OAuth (refresh tokens, scopes, per-session binding) and is the right shape for 2.0b. For a static API key fetched once at agent startup, the imperative form keeps the MCP-config-with-placeholder model working unchanged.Verified by reading the SDK's
auth.py: the@requires_api_keydecorator does exactlyclient.get_api_key(provider_name=..., agent_identity_token=BedrockAgentCoreContext.get_workload_access_token()). Inside AgentCore Runtime the context returns the auto-injected token; outside (Lambda, local dev) it returns None. Our imperative version matches that behaviour without the decorator's call-site rewrite.Preserves PR aws-samples#87's nice-to-have improvements
ImportErrorgraceful fallback adapted fromboto3tobedrock_agentcore— degrade with WARN, don't crash the agentAccessDeniedException(likely missing IAM permission) andResourceNotFoundException(provider name typo / not yet created) logged at ERROR severity to page someone, not WARNcdk/src/stacks/agent.tsOn the AgentCore runtime:
linearIntegration.apiTokenSecret.grantRead(runtime)and theLINEAR_API_TOKEN_SECRET_ARNenv-var overrideLINEAR_API_KEY_PROVIDER_NAMEenv (hardcoded'linear-api-key'for now)bedrock-agentcore:GetResourceApiKey+bedrock-agentcore:GetWorkloadAccessTokenLambdas untouched. Verified at synth: only the AgentCore runtime's IAM policy and env vars changed.
Resource scope on the new IAM is
*for now; the canonical AgentCore Identity ARN format isn't fully documented in public AWS docs as of 2026-05-15. Tighten in 2.0b when OAuth migration documents the resource shape.docs/guides/LINEAR_SETUP_GUIDE.mdAdds Step 4.5 documenting the one-time admin command users run alongside the existing
bgagent linear setupwizard:agentcore add credential --type api-key --name linear-api-key # (paste the same lin_api_… token when prompted)Explains the dual-store setup and notes that 2.0b will retire the duplicate. Starlight mirror synced.
Tests
agent/tests/test_config.py::TestResolveLinearApiToken— 10 tests covering: cached env-var fast path; missing provider name; missing region; workload token absent (outside runtime); happy path with env-var side-effect; botocore errors swallowed; SDK returns None defensively; ImportError fallback; AccessDeniedException → ERROR severity; ResourceNotFoundException → ERROR severity.542 agent / 1271 cdk / 196 cli — all green. Lint clean. Typecheck clean. CDK synth clean.
Phase plan context
Reviewer notes
LINEAR_API_TOKENchanged. Unit tests cover the new code path with realistic SDK mocks. Will deploy + smoke before requesting upstream review.'linear-api-key'. Happy to parametrize via CDK context if reviewer prefers — opted for the simple shape since there's one deployment.aws-samples/mainand open the cross-fork PR againstmain.