AI & Automation

Connecting an AI Agent to Slack, GitHub, Wikis, and Tickets

Jabane Mohamed Ayoub4 min read

Connecting an AI Agent to Slack, GitHub, Wikis, and Tickets title card on a pink-to-purple gradient, reading: One connector interface, four very different sources
Table of contents
  1. One interface, four sources
  2. What’s actually different per source
  3. Sync has to be incremental and idempotent
  4. Step by step
  5. Pitfalls
  6. FAQ
  7. Related resources

Connecting an agent to Slack, GitHub, a wiki, and a ticket system means writing four different connectors, one per source’s API and data shape. The mistake is letting those four connectors produce four different shapes of output. Do that, and every downstream piece, search, citations, permissions, has to special-case each source forever. Force all four into one shared shape instead, and the rest of the system never needs to know where a chunk came from to use it correctly.

One interface, four sources

Each connector needs exactly three operations: configure() to say how to reach the source, fetch(watermark) to get what changed since the last run, and transform() to shape that into the same row format every other connector produces. Nexus runs this exact interface across Slack, GitHub, Confluence, Notion, Google Drive, Jira, Linear and the open web, and the query side never has to know which one answered a given question.

flowchart TB
  accTitle: One connector interface across different sources
  accDescr: Slack, GitHub, wikis and tickets each have their own connector implementing configure, fetch, and transform. All four produce rows in the same shape, which land in one unified table that search queries without needing to know which source a row came from.

  subgraph SOURCES["Sources, each with configure/fetch/transform"]
    direction LR
    SL[Slack]
    GH[GitHub]
    WK[Wiki]
    TK[Tickets]
  end

  SOURCES --> SHAPE[Same row shape:<br/>content, embedding, source, timestamp]
  SHAPE --> UNIFIED[(Unified table)]
  UNIFIED --> SEARCH[Search, with no<br/>per-source special-casing]

What’s actually different per source

The shared shape doesn’t mean the sources are handled identically before they reach it. Slack needs thread-level handling: a reply to an old thread should re-fetch the whole conversation, not just the new message, so the stored row always reflects the full discussion. GitHub is mostly code, which chunks better along language-aware boundaries (class, then method, then block) than along a fixed token window. A wiki is prose, which chunks better with overlapping windows that prefer paragraph and line breaks. A ticket system contributes structured fields plus a comment thread, closer to Slack’s shape than to a wiki’s.

The work is deciding, per source, what counts as one “chunk” worth citing. The payoff for getting the shared shape right afterward is that search, reranking and citation code gets written exactly once.

Sync has to be incremental and idempotent

Re-fetching an entire Slack workspace or GitHub org on every sync is slow and burns embedding calls on content that hasn’t changed. A fetch(watermark) call should only pull what changed since the last successful sync, and each resulting chunk should be hashed so an unchanged chunk skips re-embedding entirely, a changed one gets replaced in place, and anything deleted at the source gets cleaned up on the next pass.

That combination, incremental plus idempotent, is what makes it safe to run a sync on a schedule without worrying about cost scaling with how often it runs rather than with how much actually changed.

Step by step

  1. Define the shared row shape before writing the first connector. Content, embedding, source id, timestamp, and whatever permission metadata you need travel with every row regardless of source.
  2. Build one connector at a time against that shape, starting with whichever source has the simplest API. Getting the pattern right once makes every connector after it faster to write.
  3. Get incremental sync working before testing search. A connector that works but re-fetches everything every time will hide cost and latency problems until they’re expensive to fix.
  4. Carry permissions from the connector, not from the content. Decide at ingest time who can see this chunk, and store that alongside it. See MCP security: permissions, secrets, and tool boundaries for why this can’t be reconstructed later from the text alone.
  5. Test with a real, messy source, not a clean sample. The connector that handles a disorganized Slack channel with inconsistent threading is the one that’ll survive contact with your actual data.

Pitfalls

  • Treating all four sources as “just text” and chunking them identically. Code, prose and chat threads break in different places, and a fixed-size window ignores that.
  • Forgetting that a deleted source item needs to be removed from the index too. A connector that only adds and updates will quietly accumulate stale, deleted content forever.
  • Skipping the watermark and re-syncing everything “to be safe.” This looks cautious and is actually the thing most likely to make the whole sync too slow and expensive to run often enough to stay current.

FAQ

Should every source use the same chunk size?

No. Chunk by what makes a complete, citable thought for that source: a class or method for code, a paragraph or section for a wiki, a full thread for chat. Forcing one fixed size across all of them produces chunks that are either too fragmented to cite or too broad to be useful.

How do you handle a source that doesn't expose a "what changed since X" API?

Fall back to comparing content hashes against the last full fetch: pull everything, hash each item, and only treat items whose hash changed as needing re-embedding. It costs more in API calls than a real incremental endpoint, but it still avoids the expensive part, which is re-embedding content that hasn’t actually changed.

Can one connector serve multiple projects or teams?

Yes, and it usually should. Scope access at the project or permission layer, not by duplicating the connector per team. Nexus lets the same Slack or GitHub source belong to multiple projects so a shared channel isn’t indexed twice.