Connector authoring

Connectors are an in-app abstraction today: every integration implements one Swift protocol so the sync engine treats them uniformly. Third-party connectors aren't a shipped extension point yet — if you're building an external integration, the surface you want is the MCP tools. This page documents the connector model so you understand how sync behaves and where it's headed.

The model

A connector links a page to an external system in up to three directions: push the page out, pull items in (as seeds), and reconcile task status both ways. Each connector declares which of these it supports via a capability set, and the sync engine only calls what's declared.

The ConnectorService protocol

protocol ConnectorService: Sendable {
    var connectorType: ConnectorType { get }
    var capabilities: ConnectorCapabilities { get }
    var displayName: String { get }
    var requiresEntitlement: Bool { get }   // Pro-gated?

    func connectionStatus() async throws -> ConnectorStatus
    func disconnect() async throws
    func oauthURL() async throws -> URL
    func triggerSync(pageId: UUID, journalId: UUID) async throws -> ConnectorSyncResult
    func syncStats() async throws -> ConnectorSyncStats
    func pullSeeds(pageId: UUID, pageDate: Date) async throws -> TaskPullResult
    func updateTaskStatus(taskLinkId: String, status: String?, taskText: String?) async throws -> TaskStatusResult
}

pullSeeds and updateTaskStatus have default no-op implementations, so a connector only implements the directions it advertises.

Capabilities

CapabilityMeaning
.pageSync Render and push the page (e.g. PDF/PNG/structure) to the external system.
.taskPull Pull external items in as seeds materialized onto the page.
.taskUpdate Push task status/text changes back out to the external system.
.oauth Uses an OAuth connection (vs. a system permission like EventKit).

Example: Notion

Capabilities [.pageSync, .taskPull, .taskUpdate, .oauth], and requiresEntitlement = true (Pro). Notion is the full-fat connector: it pushes page content, pulls tasks from the Notion Tasks DB in as seeds, and reconciles task status in both directions over an OAuth connection. Task identity is matched by line_id, and pushes stamp a last-write-wins checkpoint so a pull doesn't fight a recent push.

Example: Calendar

Capabilities [.taskPull], free tier (no entitlement). The Calendar connector is deliberately one-way and read-only: it uses EventKit (a system permission, not OAuth) to display the day's events on the page — the "shape of day." It never pushes, never writes to your calendar, and there's no two-way reconciliation (the contrast with Notion is the point: events are observed facts, not editable tasks). Event identity is the composite (calendar, external id, occurrence start), so a reschedule produces a new seed rather than mutating the old one.

What a new connector would implement

  • Declare connectorType, capabilities, and whether it requiresEntitlement.
  • Implement connection lifecycle (connectionStatus, oauthURL, disconnect).
  • Implement only the sync directions its capabilities advertise.
  • Respect the shared invariants: line_id identity, last-write-wins checkpoints, and the pending-arrival rule (seeds onto an inked page land hidden).