MemoFSMemoFS
Connectors

Built-in Connectors

Technical reference guide for built-in GitHub and Notion connectors in MemoFS.

@memofs/connectors ships with native, zero-dependency connectors for GitHub and Notion. Both connectors are registered by default in ConnectorRegistry and createConnectorRegistry().

Connectortype ClassProtocol / APIIngestion Source
GitHub"github" (GitHubConnector)GraphQL API (api.github.com/graphql)Issues, Pull Requests, Discussions
Notion"notion" (NotionConnector)REST API v1 (api.notion.com/v1)Database Rows, Workspace Search Pages

Execution & Batch Atomicity

Both built-in connectors follow an atomic pass lifecycle:

  1. The connector fetches and paginates through its remote API, buffering all normalized ConnectorRecord objects in memory.
  2. If an error occurs during pagination (e.g. rate limit, invalid credentials, network failure, or timeout), the connector throws immediately.
  3. The runner catches the error, records it in RunConnectorsResult.errors, and moves to the next connector without writing any partial records from the failed pass.
  4. If all pages succeed, the runner deduplicates records and commits new notes to .memofs/memory/notes.md.

This all-or-nothing pass behavior combined with the Connector-Write Discipline ensures that retrying an aborted sync is completely safe: unchanged items are never double-written, and failed batches leave no orphaned or half-written memory files.

GitHub Connector

The GitHubConnector ingests repository issues, pull requests, and discussions via GitHub's GraphQL API.

  • Class: GitHubConnector
  • Type Identifier: "github"
  • Endpoint: POST https://api.github.com/graphql
  • Authentication: GitHub Personal Access Token (classic or fine-grained) or OAuth App token with repository read permissions.
  • Request Headers:
    • Authorization: Bearer <token>
    • Accept: application/vnd.github+json
    • Content-Type: application/json
    • GraphQL-Features: discussions_api
  • Request Timeout: 30,000 ms (REQUEST_TIMEOUT_MS) per page request.
  • Page Size: 25 items per request (PAGE_SIZE).

sourceMapping Configuration

PropertyTypeDefaultDescription
repositorystring— (Required)The target repository in "owner/repo" format (e.g. "facebook/react"). Validated against /^[^/]+\/[^/]+$/.
kindsstring[]["issues", "prs", "discussions"]Which item types to ingest. Supported values: "issues", "prs", "discussions". Throws if unknown kinds are passed.
limitnumber50Maximum number of items to fetch per kind (cost and memory control).

Normalization & Data Mapping

For each retrieved item, GitHubConnector generates a normalized ConnectorRecord:

  • externalId: Formatted as ${kind}:${number} (e.g. "issues:102", "prs:45", "discussions:12").
  • title: The issue, PR, or discussion title (falls back to "(untitled)").
  • content: Formatted markdown structure:
    # <title>
    
    <truncated_body>
    
    Source: <url>
    The markdown body is truncated at 4,000 characters (MAX_BODY_CHARS = 4000) with an appended ellipsis () if truncated.
  • url: GitHub HTML web URL (e.g. https://github.com/owner/repo/issues/102).
  • occurredAt: ISO 8601 creation timestamp (createdAt).
  • metadata:
    {
      "repository": "owner/repo",
      "number": 102,
      "state": "OPEN",
      "author": "octocat",
      "labels": ["bug", "v2"],
      "kind": "issues"
    }

GitHub Discussions do not have state or labels properties in the GraphQL API. Instead, discussion category names are extracted and mapped to metadata.labels = [category.name]. If discussions are disabled for the target repository, GitHub returns a field warning which the connector treats as empty rather than failing the run.

Error Handling & Rate Limits

  • Rate Limits (HTTP 403 / 429): Throws GitHubRateLimitError. If the response contains an x-ratelimit-reset header, the UNIX epoch is extracted into error.resetEpoch.
  • Authentication Failure (HTTP 401): Throws an error indicating the token is missing or invalid. Tokens are never included in error messages.
  • GraphQL Errors: Fatal GraphQL errors are aggregated and thrown as a joined error message.

Configuration Example

In .memofs/connectors.json:

.memofs/connectors.json
{
  "connectors": [
    {
      "id": "gh-main",
      "type": "github",
      "enabled": true,
      "schedule": "@hourly",
      "sourceMapping": {
        "repository": "facebook/react",
        "kinds": ["issues", "prs"],
        "limit": 50
      },
      "secretRef": "gh_pat"
    }
  ]
}

CLI setup:

memofs connectors add \
  --id gh-main \
  --type github \
  --secret-ref gh_pat \
  --source-mapping facebook/react

Notion Connector

The NotionConnector ingests pages from a Notion database query or workspace-wide search via the Notion v1 REST API.

  • Class: NotionConnector
  • Type Identifier: "notion"
  • Endpoints:
    • Database Query: POST https://api.notion.com/v1/databases/:id/query
    • Workspace Search: POST https://api.notion.com/v1/search
  • Authentication: Notion Internal Integration Secret (secret_... or ntn_...) in Authorization: Bearer <token>. The target database or page must be explicitly shared with the integration.
  • Request Headers:
    • Authorization: Bearer <token>
    • Notion-Version: 2022-06-28
    • Content-Type: application/json
  • Request Timeout: 30,000 ms (REQUEST_TIMEOUT_MS) per page request.
  • Page Size: 25 items per request (PAGE_SIZE), paginated via start_cursor.

sourceMapping Configuration

PropertyTypeDefaultDescription
databaseIdstring32-character hexadecimal database ID (hyphenated or plain). Either databaseId or searchQuery is required.
searchQuerystringFree-text keyword search query across the workspace (filtered by filter: { value: "page", property: "object" }).
limitnumber50Maximum total pages to ingest (cost control).

Normalization & Data Mapping

For each retrieved Notion page, NotionConnector generates a normalized ConnectorRecord:

  • externalId: Formatted as notion:<page_id> (e.g. "notion:12345678-1234-1234-1234-123456789abc").
  • title: Extracted from the page's title property (falls back to "Untitled").
  • content: Formatted markdown:
    # <title>
    
    Source: <url>
  • url: Web URL (page.url or canonical https://www.notion.so/<id_without_hyphens>).
  • occurredAt: Page creation timestamp (created_time).
  • metadata:
    {
      "kind": "page",
      "createdAt": "2026-08-01T12:00:00.000Z",
      "lastEditedAt": "2026-08-05T15:30:00.000Z",
      "createdBy": "user_123456",
      "properties": {
        "Status": "In Progress",
        "Priority": "High",
        "Owner": "Jane Doe"
      }
    }

Property Flattening

Notion property objects are automatically flattened into a key-value string dictionary under metadata.properties:

  • select / status: Value name string.
  • multi_select / people: Comma-separated names.
  • rich_text / title: Plain-text concatenated string.
  • url / email / phone_number: Raw string.
  • checkbox: "true" or "false".
  • number: String representation of the number.

Error Handling & Rate Limits

  • Rate Limits (HTTP 429): Throws NotionRateLimitError. If a retry-after header is returned, the delay in seconds is parsed into error.retryAfterSeconds.
  • Authorization & Permissions (HTTP 401 / 403): Throws NotionAuthError ("Notion authorization failed (401/403)..."). This distinct error indicates the integration token is invalid or the target database/page was never shared with the integration.
  • Request Failures: General HTTP failures throw descriptive errors without echoing the response body to prevent credential leaks.

Configuration Example

In .memofs/connectors.json:

{
  "connectors": [
    {
      "id": "team-notion-specs",
      "type": "notion",
      "enabled": true,
      "sourceMapping": {
        "databaseId": "12345678123412341234123456789abc",
        "limit": 25
      },
      "secretRef": "notion_key"
    }
  ]
}

CLI setup using database ID shorthand:

memofs connectors add \
  --id team-notion-specs \
  --type notion \
  --secret-ref notion_key \
  --source-mapping 12345678123412341234123456789abc

Shared Framework Constants

The built-in connectors use shared constants defined in @memofs/connectors:

ConstantValuePurpose
REQUEST_TIMEOUT_MS30000 (30s)Prevents hanging requests from blocking ingestion.
PAGE_SIZE25Default chunk size for paginated upstream API calls.
DEFAULT_LIMIT50Default maximum items ingested per kind or query.
MAX_BODY_CHARS4000Maximum character length for markdown note bodies before truncation.

See Also

On this page