Source Integrations

Sync & Updates

What happens after a successful first import. The current model is "one-shot import on connect, plus a manual refresh you trigger from the UI". Webhook-driven auto-sync is on the roadmap but not yet shipped — this page is honest about that.

The sync model at a glance

On connect, the integration runs a full import of the selected branch (with optional path filter). After that, the integration is idle — it does nothing until you trigger a manual refresh, rotate the token, or delete the integration. There is no scheduler, no webhook listener, and no background drift detector.

connect  ->  initial import  ->  idle  ->  manual refresh  ->  idle  ->  …

Manual refresh

Manual refresh is shipped. From the Source Integrations table, the Refresh row action re-runs the read pipeline against the current HEAD of the configured branch. Small repositories run inline; larger ones queue a background job — the same sync-vs-queued split the initial import uses, with a 20-file pre-flight threshold.

Refresh runs at MCP-server scope and supports two modes:

  • Prune deleted (default) — content rows whose paths no longer appear in the remote listing are removed. Requires the contents.can_delete entitlement on top of the import permission.
  • No prune — removed files linger as stale content rows. Useful if you want to keep historical versions around for review before deleting them by hand.

On the MCP server edit page, a "Refresh all integrations" header action queues one refresh job per integration that has a stored access token. Empty-token rows are skipped, and the prune choice threads through to every dispatched job.

Re-connecting vs refreshing

Re-completing the OAuth flow against the same MCP server, provider, and repository upserts the integration row. That rotates the access token (and on GitLab, also the branch + path filter) rather than creating a duplicate row. A database-level unique constraint to lock this in is on the roadmap but not yet in place.

Token rotation on edit

On the Edit form, the Access Token field is blank by default. Leaving it blank preserves the existing encrypted token; typing a new value replaces it. The new value is validated against the provider before the row is updated, identical to the create-time flow — a bad token surfaces as a field-level error and nothing is written.

Access-token expiry by provider

Provider PAT / App Password OAuth access token
GitHub No auto-expiry No auto-expiry
Bitbucket App Password — no auto-expiry Expires after 2 hours; refresh-token handling not yet wired
GitLab (gitlab.com) No auto-expiry Expires after 2 hours; refresh-token handling not yet wired

Until refresh-token handling lands for Bitbucket and GitLab OAuth, prefer the App Password / PAT flow for any production integration that runs more than a one-shot import.

Deleting an integration

Removing the integration row stops future refreshes from that repository immediately. Content rows that were already imported into the MCP server are NOT automatically removed — that is a deliberate choice so a bad disconnect cannot nuke production content. If you want a clean slate, delete the corresponding content entries by hand after disconnecting.

Partial failures

If a refresh trips a cap — GitHub tree truncation, the Bitbucket BFS caps, or the GitLab page or wall-clock budget — a warning is logged and the partial result is imported. Per-file skips (binary, non-UTF-8, blob too large) are recorded against each file and counted in the result; they do not abort the import. The integration itself is not marked failed for these cases.

There is one safety gate: if a refresh returns zero blobs, you have more than ten existing content rows for the server, and prune-deleted is on, the refresh aborts with an explicit warning instead of wiping the content. This protects against a mis-edited path filter zeroing out a server.

What is deliberately not yet supported

  • Webhook-driven auto-sync on push.
  • Scheduled periodic sync.
  • Refresh-token rotation for Bitbucket and GitLab OAuth.
  • Multi-branch tracking on a single integration.
  • Per-content-row attribution back to the integration that imported it (currently refresh works at MCP-server scope, so two integrations pointing at the same server with prune-deleted on can delete each other's rows — keep one integration per server until that lands).

Stale content?: If you cut a new MCP version after a refresh, the version snapshots the freshly upserted content. If you forget to cut a new version, the active version keeps serving the previous snapshot — refresh updates the live content table, not the active version. See Versioning & Releases for the snapshot-vs-live distinction.