Repository navigation
Sign in to the Instagram connectors with Meta (Unified Login) - #1630
Merged
Merged
Conversation
instagram-login covers the Instagram API with Instagram Login on graph.instagram.com: no Facebook Page needed. Profile, media, stories, tagged media, mention replies, account and media insights, comment moderation, Direct Messages (conversations, messages, user profile) and publishing. Sending, publishing and deleting install switched off. The token is refreshed by the existing instagram_login renewal. The Facebook Login adapter gains mentioned media and comments, mention replies, top-level comments, comments on/off, recently searched hashtags, and comment and media deletion (switched off).
Meta's logins issue no refresh token, so the Instagram connectors took a pasted token. "Authorize with Facebook" (instagram) and "Authorize with Instagram" (instagram-login) now get it by signing in: the code is exchanged for a long-lived token, written to INSTAGRAM_ACCESS_TOKEN with the account id, and handed to the bearer token renewal. A Facebook user with several Instagram accounts picks one. The app is the connector's own, else the server's when META_PLATFORM_APP_* or INSTAGRAM_PLATFORM_APP_* are set, the same on Cloud and self-hosted; the redirect URI is <SERVER_URL>/api/meta/callback. Tokens from the server's Facebook app are renewed with that app. Pasting a token keeps working. A pasted Instagram Login token whose first refresh came too early was never refreshed afterwards; it is now retried once the wait is over.
New Meta apps no longer get Instagram Login: they sign in through Facebook Login for Business with a configuration id. The instagram connector now uses that flow when its app (or the server app with META_PLATFORM_CONFIG_ID) has one, stores the non-expiring system user token without renewal, finds the shared accounts, and gains Direct Message tools. Instagram Login stays for apps that still have it, with no server app. Deleting an Instagram connector deletes its tool-call logs in the same transaction.
A Unified Login that returns no Instagram account kept nothing. Now the token is stored, the accounts are looked up through the client business portfolio and the granted scopes as well, and every outcome of the sign-in returns to the connector page with a banner and its next step: test, pick an account, enter its id, or authorize again.
debug_token shows the account ids a Unified Login token was granted. None at all means the client chose a business portfolio that does not own the account, so say that on the connector page instead of guessing. The portfolio lists are asked only with business_management, and the account id hint no longer insists on a 1784 prefix.
helpcodeai
marked this pull request as ready for review
November 11, 2026 10:30
helpcodeai
enabled auto-merge (squash)
November 11, 2026 11:41
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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
Connecting Instagram today means pasting a Graph API token and looking up an account id by hand. This PR adds Authorize with Provider for the Instagram connectors, so a user signs in with Instagram or Facebook and AnythingMCP fills in the token and the account id.
Meta apps created since autumn 2026 no longer get "Instagram Login": their dashboard only offers Instagram API with Unified Login, which is Facebook Login for Business with a configuration id. The
instagramconnector therefore signs in with Unified Login when its app has a configuration id, and with classic Facebook Login otherwise. A newinstagram-loginadapter covers apps that still have Instagram Login.Draft until the sign-in has been completed end to end against a real account (see Testing).
Changes
instagramconnector (meta-login.util.ts,meta-login.service.ts,meta-auth.controller.ts):config_idinstead ofscope, withoverride_default_response_type=trueandprompt=loginsystemUserflag in the bearer renewal)/me/assigned_instagram_accounts; when that is empty, from the client business portfolio (owned_instagram_accounts,instagram_accounts) and from the account ids in the token's granular scopes (debug_token)scopelist for older apps (no configuration id); Instagram Login (instagram-login, graph.instagram.com) for apps that still have it.META_APP_ID/META_APP_SECRET, plusMETA_CONFIG_IDfor Unified Login; orINSTAGRAM_APP_ID/INSTAGRAM_APP_SECRET), else forinstagramonly a server app (META_PLATFORM_APP_ID/_SECRET/META_PLATFORM_CONFIG_ID). There is no server app for Instagram Login, because new Meta apps cannot get it.instagram: newinstagram_list_assigned_accounts, six Direct Message tools (send message and private reply installed switched off), mention and comment tools, Unified Login instructions and variables, and the "Instagram DM" / "Instagram Direct" aliases moved hereinstagram-loginadapterinstagramorinstagram-loginconnector deletes itstool_invocationsrows (scoped by organization and connector) in the same transaction (Meta Platform Terms §3.d). They already cascaded through the tools; the explicit delete keeps it so if the relation changes.GET /api/meta/platform,GET /api/meta/connectors/:id/accounts,POST /api/meta/connectors/:id/account; OpenAPI tagMeta.docker-compose.cloud.ymlanddocker-compose.ymlpassMETA_PLATFORM_APP_ID,META_PLATFORM_APP_SECRETandMETA_PLATFORM_CONFIG_IDto the backend, next to the TikTok Shop platform variables. The Coolify and quickstart files list none of the platform variables, so they are unchanged.docs/deployment.md,docs/tool-definition.md),.env.example, quoted adapter count 732, regeneratedllms.txt/llms-full.txt; the validator acceptstokenRenewal.configIdVar.Cloud vs self-hosted
META_PLATFORM_APP_ID,META_PLATFORM_APP_SECRETandMETA_PLATFORM_CONFIG_IDset (app 1785424892691435, configuration 2539939149839675, redirecthttps://cloud.anythingmcp.com/api/meta/callback), the setup page asks for no app: the user only signs in. Users without a role on the app need Meta App Review (Advanced Access) and Live mode. That review is in preparation and is not part of this PR.<SERVER_URL>/api/meta/callback. Pasting a token keeps working in both.Type
Testing
Commands (worktree root unless noted):
node scripts/validate-adapters.mjs --warn: 748 passed, 0 failed, 1434 warnings, the same total as before the rebase.node scripts/regenerate-catalog.mjs,node scripts/adapter-count.mjs --check(OK, 732 adapters),node scripts/generate-llms-txt.mjs,node --test scripts/validate-adapters.test.mjs(32/32).packages/backend:npm run lint: 0 errors.npx tsc --noEmit -p tsconfig.json: clean.npx jest --maxWorkers=4withDATABASE_URLset to a local test database: 1075 suites passed, 1 failed, 10 skipped. The failure isee/cloud/onboarding-cron.service.spec.ts, and only because my local.envexportsFRONTEND_URL=http://localhost:3000; run without that env it passes 89/89.auth/jwt-or-management-key.guard.spec.tscannot run; it passes 14/14 against the test database.packages/frontend:npm run lint(0 errors),npx tsc --noEmit -p tsconfig.json(clean).Manual, local stack (backend and frontend dev servers, a Cloudflare quick tunnel as
SERVER_URL, the Cloud Meta app in development mode, a real Facebook profile):GET /api/meta/platformreturnsunified: true. Authorize returnshttps://www.facebook.com/dialog/oauthwithclient_id,config_id,override_default_response_type=true,prompt=loginand noscope.First sign-in: the code exchange worked, but no account came back, and the old code discarded the token. That led to keeping the token, the banner on the connector page, and the logging.
Second attempt: Meta never redirected to the callback (the attempt stayed pending). Logging for every callback and completion outcome was added.
Third sign-in (portfolio helpcode.ai GmbH, Instagram account treppeo.de, which is owned by another portfolio and only shared into helpcode.ai): callback, exchange and storage all worked, and the token is kept. With the stored token:
debug_token:type: SYSTEM_USER, valid,expires_at: 0(never), all five Instagram scopes granted, but the granular scopes carry notarget_ids/me?fields=client_business_idreturns the helpcode.ai portfolio/me/assigned_instagram_accountsis empty at v25.0 and at v27.0(#10) Application does not have permissionbusiness_management, which the configuration does not requestSo Meta granted the permissions but no Instagram account, because the chosen portfolio does not own it. The connector page now says exactly that (
reason=no_assets), and the instructions tell users to pick the portfolio that owns the account.Catalog update on the installed connector applied through
POST /api/connectors/:id/resync-catalog: the stored instructions now start with Unified Login, and the catalog diff is up to date.Fourth sign-in with the portfolio that owns the account (Treppeo, account treppeo.de):
Unified Login: 1 Instagram account(s) (assigned_instagram_accounts=1)INSTAGRAM_ACCOUNT_ID(17841448449522133) were stored automatically, and the user landed on the connector page with the "connected" bannerRead-only tool checks against that account (nothing published, nothing sent):
instagram_get_account: OK (username, followers, media count)instagram_list_assigned_accounts: OK (1 account)instagram_list_media: OK (5 of 11 posts)instagram_get_media_insightson a video post: OK (reach, views, likes)instagram_get_account_insights(reach, day): OKinstagram_list_commentson a post with a comment: OK (1 comment)instagram_list_conversations: 403, Meta code 200, "The account owner has disabled access to Instagram Direct Messaging Outside of Instagram App". The path and token are correct (the same answer at v25.0 and v27.0, with and withoutplatform=instagram); the owner has to turn on Allow access to messages in the Instagram app. The instructions now quote this message so the assistant explains it instead of blaming the token.The banner title names the account ("Instagram is connected as @treppeo.de").
Checklist
npm test) (see Testing for the two environment-only cases)Related Issues
Instagram connector sign-in; Meta App Review for the Cloud app is tracked separately.