Teaching an AI agent what’s blocking the sprint: a ClickUp connector for cognee
Most teams have the same problem: the answer to "why is this blocked?" exists, but it's spread across a task status, a custom field, three comments and a Doc someone wrote last month. Nobody reads all of that before stand-up. I wanted an agent that could, so I built a ClickUp connector for cognee ,

Most teams have the same problem: the answer to "why is this blocked?" exists, but it's spread across a task status, a custom field, three comments and a Doc someone wrote last month. Nobody reads all of that before stand-up.
I wanted an agent that could, so I built a ClickUp connector for cognee, the open-source memory layer for AI agents, as part of Mergetober. It turns tasks, comments and Docs into memory an agent can query, and keeps that memory in sync as the workspace changes.
This post is about what it took to make that reliable. The ClickUp API had a few surprises, and two of them only showed up when I tested against a real workspace.
import asyncio import cognee from cognee_community_connector_clickup import clickup_source async def main (): await cognee. remember ( clickup_source ( space_ids = [ " 90120001 " ]), # token from CLICKUP_API_TOKEN dataset_name = " sprint_memory ", primary_key = " id ", write_disposition = " merge ", max_rows_per_table = 0, ) answer = await cognee. search ( query_text = " Which tasks are blocked, and what are they waiting on? ", query_type = cognee. SearchType.
GRAPH_COMPLETION, datasets = [ " sprint_memory " ], ) print ( answer ) asyncio. run ( main ()) Enter fullscreen mode Exit fullscreen mode Authentication is a ClickUp personal API token (avatar → Settings → Apps → API Token → Generate). You choose what to ingest with space_ids, folder_ids or list_ids. Run it again tomorrow and only what changed gets fetched.
ClickUp nests everything: Workspace → Space → Folder → List → Task, and each level is its own API call. The obvious implementation walks the whole tree before it sees a single task, and does it again on every sync. ClickUp allows 100 requests per minute, so on a large workspace that walk is most of your budget.
The way around it is the filtered team-tasks endpoint, GET /team/{team_id}/task. It returns tasks from every list in the workspace, 100 per page, and accepts a date_updated_gt filter. Each task already carries its list and folder names. The only thing it doesn't name is the Space, so the connector fetches Space names once, caches them in the dlt state, and refreshes them only when a task points at a Space it hasn't seen.
On my test workspace that meant one Space lookup on the first sync and zero on every sync after.
Custom fields are where teams keep the important stuff: severity, components, the reviewer. But the API returns them in a form only ClickUp understands. A dropdown comes back as 2, labels as a list of option IDs, and a people field as a list of user objects.
Feed that straight into a knowledge graph and the agent learns that "Severity is 2". The connector resolves each value through the field's type_config, so the document says "Severity: High". It also handles dates, checkboxes, currency, ratings, locations and progress. Here's the connector's real output for an example task:
Each task document also carries status, priority, assignees, tags, its Space / Folder / List location, checklists and every comment. The comment endpoint returns only the 25 newest, so the connector pages back through the rest. ClickUp Docs are synced too, through the v3 Docs API, with nested pages rendered as sections.
My test suite passed. Then I pointed the connector at a real ClickUp workspace, and two assumptions turned out to be wrong.
1. date_updated_gt isn't strictly "greater than". I passed the newest task's own timestamp, and ClickUp still returned that task (newest − 1 ms returned it, newest returned it, newest + 1 ms didn't). So every "nothing changed" rerun was re-ingesting the latest task and re-fetching its comments. The fix: skip a task whose timestamp equals the cursor if it's already been ingested, while still picking up a genuinely new task created in that same millisecond.
2. Deleting a comment is invisible. I posted a test comment on a real task and watched the task's date_updated. It moved, so new comments are picked up. Then I deleted the comment, and date_updated stayed exactly the same. A cursor-based sync would have kept that deleted comment in the agent's memory forever.
The fix is a comment re-check, once a day by default. The connector re-reads comments on the tasks in scope, compares them with a fingerprint it stored last time, and re-renders only the tasks whose comments changed. I verified it against the live workspace too: the deleted comment was gone from memory after the next check.
These numbers are from my test workspace (12 tasks, 1 Doc). The first sync made 17 requests, most of them comments, one per task. A rerun with nothing changed made 4: the workspace lookup, the incremental listing, the deletion check and the Docs listing. The daily comment re-check costs about one request per task, which is why it runs daily rather than on every sync.
ClickUp has no deletion feed, so every run re-lists the IDs in scope. Tasks and Docs that were deleted, archived or moved out of the selection become tombstone rows ( {"id":..., "_deleted": true} ). dlt hard-deletes them, and cognee's orphan cleanup removes them from the graph and vector store.
Two guards stop a bad day from wiping memory. If a listing comes back empty while items are known, the connector assumes an outage and skips the cleanup. And it only saves its progress after a run fully succeeds, so a crash halfway through just means the next run repeats the work.
Mocked tests told me the code did what I expected. Only the real API told me whether my expectations were right. Both of the bugs above passed every unit test, because the fake API behaved the way I assumed ClickUp did. Now the fake behaves the way ClickUp actually does, so the tests guard against those bugs coming back.
The connector is in review as topoteretes/cognee-community#278, for issue #4701. Until it's merged, you can install it from my branch:
pip install "cognee-community-connector-clickup @ git+https://github.com/priyansh-narang2308/cognee-community@feature/clickup-connector#subdirectory=packages/connector/clickup" Enter fullscreen mode Exit fullscreen mode If your team lives in ClickUp, I'd like to hear what you'd ask a sprint agent first.