Repository navigation
Description
Summary
V2 does not import remote MCP OAuth credentials that V1 stored in <data>/mcp-auth.json. After upgrading, every OAuth-protected remote MCP server goes to needs_auth, even when the V1 file still holds a valid refresh token. V2 gives no warning or hint that a V1 login exists.
Environment
- opencode version: 0.0.0-dev-202610031828 (local build of
dev, comparable to v2.0.18+) - OS: Linux 6.12.107+deb13-amd64 x86_64
- Terminal: Unavailable: terminal environment variables are not set (server/web client)
- Shell: /bin/bash
- Install/channel: dev
- Active plugins: none found in config
Reproduction
- On V1, configure a remote MCP server with OAuth (
type: "remote",oauth: {...}) and authorize it. V1 writes~/.local/share/opencode/mcp-auth.jsonwithtokens.accessToken,tokens.refreshToken,tokens.expiresAtandserverUrlfor the server. - Upgrade to V2 and start it.
- Look at the server's MCP status, or the
credentialtable inopencode.db.
Expected Behavior
V2 imports the V1 MCP credential into the credential table, as it already does for provider credentials from auth.json (migration 20260805200742_import_legacy_credentials). The server should then connect, refreshing with the imported refresh token if the access token has expired.
If an entry can't be imported, V2 should at least say that a V1 credential exists and that you need to sign in again.
Actual Behavior
- The
credentialtable has no row for the MCP server (0 rows in total here). - The server sends a 401 and OpenCode reports
needs_auth:level=WARN message="mcp http authentication rejected" status=401 server=<name> level=WARN message="mcp connect failed" server=<name> status.status=needs_auth status.error="MCP server \"https://…/mcp\" requires user authorization" - No token-endpoint call is ever made.
mcp-auth.jsonis never read again. Here its last write is the final V1 login on 2026-09-28. - On the authorization server (Authentik), the V1 refresh token is still active and unrevoked, valid until 2026-10-28. It could have been used.
Additional Context
Cause: packages/core/src/database/migration/20260805200742_import_legacy_credentials.ts imports <data>/auth.json only. No non-test code in V2 reads mcp-auth.json. V2 MCP OAuth (packages/core/src/mcp/oauth.ts, connectProvider) reads only Credential.Service, keyed by an integration id derived in packages/core/src/mcp/index.ts as "mcp_" + sha1(name + "\0" + url).slice(0, 16).
Suggested fix: add a migration next to import_legacy_credentials that, for each mcp-auth.json entry with tokens and serverUrl:
- derives the same integration id from
(name, serverUrl), using a helper shared withmcp/index.tsrather than a copy; - writes a
Credential.OAuthshaped likeMcpOAuth.toCredential:access;refresh ?? "";expires = expiresAt * 1000(V1 stores seconds). IfexpiresAtis absent, use a time in the past such as1, not0: in V20means non-expiring, sotoTokenswould omitexpires_inand the SDK would never refresh;metadatawithserverUrl,tokenType: "Bearer",scope, andclient, whereclientis V1clientInfomapped to the SDK client-information shape.clientInfois optional. When the config setsoauth.client_id,provider()takes the client from config and refreshes normally. When neither is present (a server that used dynamic client registration),clientInformation()throws before any token request, so the server reportsneeds_authas it does today. The imported row is not invalidated; it simply stays until the user signs in again and replaces it;
- skips entries whose integration id already has a credential, entries without tokens, and malformed input, without failing the migration;
- leaves
mcp-auth.jsonin place.
An expired imported access token is fine: toTokens yields expires_in: 0, so the SDK refreshes on first use.
Related, but a separate bug: #49773 / #51979 cover V2 not refreshing MCP OAuth tokens once a credential exists. This issue is about the credential never reaching V2 in the first place. Fixing either one alone doesn't restore upgraded users who have long-lived refresh tokens.
Workaround: sign in to each OAuth MCP server again (/mcps → server, or opencode mcp auth <name>).
Testing note: a fresh install records every migration as applied without running its up step, which is correct. So only an upgrade, meaning an existing database with this migration still pending and an old mcp-auth.json present, exercises the import. Test an end-to-end fix on that path, not on a fresh data directory.
Note on the base branch: the relevant code is on the v2 branch, not dev.
Activity
github-actions commentedon Oct 6, 2026
This issue doesn't fully meet our contributing guidelines.
What needs to be fixed:
Descriptionfield; this issue usesSummaryand custom sections instead. Please edit it to use the Bug Report template format and put the bug description in the required field. The reproduction steps and system details already provided are useful.Please edit this issue to address the above within 72 hours, or it will be automatically closed.
This issue might be a duplicate of existing issues. Please check:
needs_authand restart symptoms; that report concerns token refresh after a credential exists.mcp-auth.json; that report concerns tokens being lost from the V1 file rather than V2 failing to import them.If you believe this was flagged incorrectly, please let a maintainer know.
opencode-agent commentedon Oct 6, 2026
Thanks for the detailed report. I reproduced this on v2.0.24.
Steps:
mcp-auth.jsonentry for a remote OAuth MCP server (tokens.accessToken,refreshToken, a futureexpiresAt,clientInfoandserverUrl).opencode.jsonwithtype: "remote"andoauth: {}.opencode2 mcp list, then opened/mcpsin the TUI.The startup migrations ran, but the
credentialtable stayed empty. The server reportedneeds authentication(Sign in requiredin/mcps). The test MCP server only received requests without anAuthorizationheader, and its token endpoint was never called, so v2 never read the v1 credentials. As you said, signing in to the server again in v2 is the workaround for now.github-actions commentedon Oct 6, 2026
Thanks for the update. This still needs one required template field: the report uses a Summary section, but the Bug Report template requires a Description field with real content. Please update the issue to use the Bug Report template and put the existing bug description in its required Description field. The reproduction steps and environment details are already useful.
github-actions commentedon Oct 6, 2026
This is still missing the required Description field from the Bug Report template. The current report has a Summary section instead. Please update the issue to use the Bug Report template and place the bug description under its required Description field; the reproduction steps and system details are already helpful.
github-actions commentedon Oct 9, 2026
This issue has been automatically closed because it was not updated to meet our contributing guidelines within the 72-hour window.
Feel free to open a new issue that follows our issue templates.