Skip to content

fix: fall back to project credential for user-scoped tools without per-user credential#2904

Closed
amikofalvy wants to merge 1 commit intomainfrom
fix/user-scoped-tool-credential-fallback
Closed

fix: fall back to project credential for user-scoped tools without per-user credential#2904
amikofalvy wants to merge 1 commit intomainfrom
fix/user-scoped-tool-credential-fallback

Conversation

@amikofalvy
Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #2899. After that PR correctly resolved userId for playground requests, user-scoped tools now properly enter the user-credential lookup branch in AgentMcpManager. However, if no per-user credential exists for the current user, the code gave up without checking if a project-level credentialReferenceId was available as a fallback.

Before #2899, userId was undefined for playground requests, so the isUserScoped && userId condition was false and the code fell through to the else if (credentialReferenceId) branch — accidentally using the project credential. #2899 fixed the userId but removed this accidental fallback.

This PR adds an explicit fallback: when a user-scoped tool has no per-user credential, check for a project-level credentialReferenceId before giving up.

Test plan

  • Use "Try it" playground on an agent with a user-scoped MCP tool that has a project-level credentialReferenceId but no per-user credential — verify it falls back to the project credential
  • User-scoped tools WITH a per-user credential should still use the per-user credential (no change)
  • Project-scoped tools are unaffected (no change to the else if branch)

Made with Cursor

…r-user credential

After #2899 correctly resolved userId for playground requests, user-scoped
tools now enter the user-credential lookup path. If no per-user credential
exists, the code previously gave up. This adds an explicit fallback to the
project-level credentialReferenceId, restoring the pre-existing behavior
where tools configured with a shared project credential continued to work.

Made-with: Cursor
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Mar 30, 2026

⚠️ No Changeset found

Latest commit: 092bed0

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agents-api Ready Ready Preview, Comment Mar 30, 2026 7:49pm
agents-manage-ui Ready Ready Preview, Comment Mar 30, 2026 7:49pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
agents-docs Skipped Skipped Mar 30, 2026 7:49pm

Request Review

@pullfrog
Copy link
Copy Markdown
Contributor

pullfrog Bot commented Mar 30, 2026

TL;DR — When a user-scoped MCP tool has no per-user credential, the code now falls back to the project-level credentialReferenceId instead of giving up. This restores behavior that was accidentally removed in #2899 when userId resolution was fixed for playground requests.

Key changes

  • Add project-credential fallback for user-scoped tools in AgentMcpManager — when no per-user credential exists, check for a project-level credentialReferenceId before logging a warning and bailing out.

Summary | 1 file | 1 commit | base: mainfix/user-scoped-tool-credential-fallback

Before: A user-scoped tool with no per-user credential would immediately warn and skip credential resolution — even if a project-level credential was configured. This worked before #2899 only by accident (playground userId was undefined, so the user-scoped branch was never entered).
After: An explicit else if (credentialReferenceId) branch looks up the project credential as a fallback, logs an info-level message when used, and warns only when neither user nor project credentials are available.

Why did #2899 break this?

Before #2899, playground requests had userId as undefined, so the isUserScoped && userId guard was false and execution fell through to the project-credential else if branch. #2899 correctly resolved userId for playground requests, which meant user-scoped tools now entered the user-credential branch — but there was no fallback when no per-user credential existed.

AgentMcpManager.ts

Pullfrog  | View workflow run | Triggered by Pullfrog | Using Claude Opus𝕏

Copy link
Copy Markdown
Contributor

@pullfrog pullfrog Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, well-scoped fix. The fallback logic is correct and the soft error handling (warn instead of throw) for the user-scoped case is the right call — a missing project credential for a user-scoped tool should degrade gracefully. The storeReference flows correctly into downstream Composio pinning. LGTM.

Pullfrog  | View workflow run | Using Claude Opus𝕏

Copy link
Copy Markdown
Contributor

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

(3) Total Issues | Risk: Medium

🟠⚠️ Major (2) 🟠⚠️

🟠 1) AgentMcpManager.ts:99-103 Inconsistent error handling for missing credential reference

Issue: When a user-scoped tool falls back to a project credential but the credentialReferenceId lookup fails, the code logs a warning and continues with storeReference = undefined. This allows the MCP connection to proceed without credentials.

Why: The project-scoped branch (line 113) throws an error for the identical scenario. This creates unpredictable failure modes: user-scoped tools fail silently with cryptic 401/403 errors at runtime, while project-scoped tools fail fast with clear configuration errors. Engineers debugging "why is my playground tool unauthorized?" will waste time tracing through MCP connection logs when the root cause is a missing credential reference.

Fix: See inline comment — throw an error matching the project-scoped branch behavior.

Refs:

Inline Comments:

  • 🟠 Major: AgentMcpManager.ts:99-103 Inconsistent error handling — logs warning instead of throwing error

🟠 2) AgentMcpManager.test.ts Missing test coverage for user-scoped credential fallback

Issue: The new credential fallback logic (lines 88-104) has zero automated test coverage. The existing test suite covers project-scoped credentials, Composio integration, and header forwarding, but no tests exist for user-scoped credential resolution.

Why: This is critical authentication logic that determines whether playground tools work. If this code path breaks in future refactoring, there's no automated detection. The manual test plan in the PR description confirms manual testing, but the logic is complex enough (user credential → project credential fallback → no credential) that unit tests would catch regressions that manual testing might miss.

Fix: Add test cases covering:

  1. User-scoped tool uses per-user credential when available
  2. User-scoped tool falls back to project credential when no per-user credential exists ← the new code path
  3. User-scoped tool with no credentials at all (warning path)

Refs:

🟡 Minor (1) 🟡

🟡 1) scope Missing changeset for bug fix

Issue: The changeset-bot flagged this PR has no changeset. Per AGENTS.md, bug fixes to published packages require a changeset.

Why: This is a bug fix to agents-api that restores expected behavior for playground users. Without a changeset, the fix won't be documented in release notes.

Fix: Run pnpm bump patch --pkg agents-api "Fix fallback to project credential for user-scoped tools without per-user credential"

Refs:

💭 Consider (2) 💭

💭 1) AgentMcpManager.ts:88-104 Code duplication with project-credential branch

Issue: The fallback credential lookup (lines 88-104) duplicates the exact same pattern from the project-scoped branch (lines 111-117): lookup credential by ID, check if exists, build storeReference object.

Why: Any future changes to credential resolution must be made in two places. Consider extracting to a helper or restructuring control flow so both paths share the lookup.

Fix: Optional refactor — extract credential lookup or restructure so user-scoped fallback falls through to the shared project-credential branch.

💭 2) AgentMcpManager.ts:88 IAM policy consideration: user-scoped tool using project credential

Issue: When a tool is marked credentialScope: 'user', the intent is typically per-user credential isolation. The fallback allows any authenticated project member to use shared project credentials even when the tool was configured for per-user access.

Why: This is likely intentional (graceful degradation when users haven't connected their account), and the info log at lines 95-98 provides an audit trail. Worth documenting this behavior explicitly if it's the intended policy.

Fix: No code change needed if this is intentional — consider adding a code comment explaining the policy rationale.


🚫 REQUEST CHANGES

Summary: The fix correctly restores the fallback behavior, but the error handling inconsistency (warning vs. throwing) creates a silent failure path that will confuse users and complicate debugging. The missing test coverage for a critical auth path is also concerning — this exact scenario (user-scoped credential resolution) caused the regression that this PR fixes. Adding tests would prevent future regressions.

Blocking: Fix the error handling inconsistency (Major #1). The changeset can be added before merge.

Discarded (2)
Location Issue Reason Discarded
AgentMcpManager.ts:106-109 Inadequate error message lacks actionable guidance Pre-existing issue not introduced by this PR — the warning message existed before
system Integration test for playground flow Nice-to-have but unit tests are sufficient to catch regressions in credential resolution logic
Reviewers (5)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-errors 3 1 0 0 1 0 1
pr-review-tests 2 1 0 0 0 0 1
pr-review-precision 2 0 1 0 0 0 1
pr-review-security-iam 1 0 1 0 0 0 0
pr-review-standards 1 0 0 0 0 0 1
Total 9 2 2 0 1 0 4

Comment on lines +99 to +103
} else {
logger.warn(
{ toolId: tool.id, userId, credentialReferenceId },
'User-scoped tool has no user or project credential'
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 MAJOR Inconsistent error handling when credential reference not found

Issue: When credentialReferenceId exists but the lookup fails (line 90 returns undefined), this fallback branch logs a warning (lines 100-103) and continues with storeReference = undefined. However, the project-scoped branch below (line 113) throws an error for the identical scenario.

Why: This inconsistency creates unpredictable failure modes:

  • User-scoped tool with missing project credential → soft fail (warning logged, MCP connection proceeds without auth, tool fails later with cryptic 401/403)
  • Project-scoped tool with missing credential → hard fail (immediate error with clear message)

Users debugging "why is my playground tool returning 401?" will have to trace through MCP connection logs and remote service errors to discover the root cause is a missing credential reference.

Fix: Match the project-scoped behavior — throw an error when the credential reference is configured but not found:

Suggested change
} else {
logger.warn(
{ toolId: tool.id, userId, credentialReferenceId },
'User-scoped tool has no user or project credential'
);
} else {
throw new Error(
`User-scoped tool ${tool.id} has no user credential for userId ${userId} and project credential reference not found: ${credentialReferenceId}`
);

Refs:

@github-actions github-actions Bot deleted a comment from claude Bot Mar 30, 2026
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.

1 participant