teams-extractor
Skill0 starsChanged 33 days agoMITView on GitHub
Read Microsoft Teams messages from the local Chromium IndexedDB cache or native Teams app cache — no API auth, no Entra app, no admin permissions required. Works with the Chromium web app (teams.cloud.microsoft) on macOS or Linux, and with the native Teams desktop app (WebView2-based, teams.microsoft.com) on macOS — Microsoft discontinued the official native Linux client in 2022. Use this skill whenever the user wants to retrieve Teams messages, check what someone said in Teams, search Teams chat history, or capture a Teams message as a note. Trigger on: "check my Teams", "what did [person] say in Teams", "find [person]'s message about [topic] in Teams", "Teams DM from [person]", "capture that Teams message", or any request to read or search Teams chat history. Do NOT use for sending messages — read-only.
In this repository13 other files it gives its agents.
- AGENTS.mdAGENTS.md
- 1on1Skill
- context-reviewSkill
- gmail-inboxSkill
- lamport-problem-statementSkill
- learning-labSkill
- logseq-db-querySkill
- nix-sandboxSkill
- obsidian-zettelkastenSkill
- peer-reviewSkill
- portkey-statusSkill
- proton-inboxSkill
- theory-of-constraintsSkill
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 3 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 50
If a very recent message is still missing after a fresh copy, wait a minute with Teams open and re-copy.
- Very specific: line 210
Use the exact name as it appears in Teams (e.g.
- Distinctive: line 216
Read-only — no send, reply, or organize capability
- Distinctive: line 230
Only repin it if something breaks (e.g.
- Very specific: line 237
Update the ccl_chromium_reader @ git+...@<commit> line in scripts/teams_extractor.py's dependencies list to the new commit hash.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Teams Extractor
Reads Microsoft Teams messages directly from the local IndexedDB LevelDB
cache. No auth setup. No API calls. Works offline. Zero IT footprint. The
script is scripts/teams_extractor.py, self-contained via PEP 723 inline
deps — run with uv run.
Parsing is handled by
ccl_chromium_reader, a
LevelDB and V8 deserialization library. Messages are read as structured data
(correct field names, nested objects) from both compacted .ldb files and the
write-ahead log (.log) with equal reliability.
Freshness lag
This reads a snapshot of the local cache, not a live feed — messages sent seconds ago may not show up immediately. Two delays stack:
- App → cache sync — Teams must write the message into its local IndexedDB before it's readable at all. Typically seconds to a couple of minutes while Teams is open; doesn't happen while Teams is closed.
- Snapshot age — the extractor only sees what was in the cache at the
moment
cp -rran. Re-copy the cache (Step 1) immediately before re-running the extractor to pick up anything recent.
If a very recent message is still missing after a fresh copy, wait a minute with Teams open and re-copy.
Works with two Teams deployment types:
- Chromium web app — Teams opened in a Chromium browser at
teams.cloud.microsoft(macOS or Linux) - Native desktop app — The Microsoft Teams 2.x app (WebView2-based), available on macOS only. Microsoft discontinued the official native Linux client in 2022, replacing it with the web/PWA app. On Linux, use the Chromium web app path instead.
Prerequisites
- Teams must have been opened recently (cache must be populated)
uvmust be available (which uv)gitmust be available (which git) —uvuses it to fetch the pinnedccl_chromium_readerengine, which isn't published on PyPI. First run needs network access to fetch it and its PyPI dependencies; after that,uv's cache makes subsequent runs fast and offline.
Step 1 — Locate and copy the cache
Always probe first — don't assume which app type is in use. Run the discovery snippet below, then copy whichever path resolves.
Cache path discovery (run this first)
# --- macOS ---
# Chromium web app (teams.cloud.microsoft)
CHROMIUM_MAC=~/.config/chromium/Default/IndexedDB/https_teams.cloud.microsoft_0.indexeddb.leveldb
# Native Teams desktop app (WebView2, teams.microsoft.com)
NATIVE_MAC="$HOME/Library/Containers/com.microsoft.teams2/Data/Library/Application Support/Microsoft/MSTeams/EBWebView/WV2Profile_tfw/IndexedDB/https_teams.microsoft.com_0.indexeddb.leveldb"
# --- Linux ---
# Chromium web app (the only supported path on Linux — Microsoft discontinued
# the native Linux client in 2022; the WebView2 native app is macOS-only)
CHROMIUM_LINUX=~/.config/chromium/Default/IndexedDB/https_teams.cloud.microsoft_0.indexeddb.leveldb
# Auto-detect: try all known paths, use first that exists
for p in "$NATIVE_MAC" "$CHROMIUM_MAC" "$CHROMIUM_LINUX"; do
if [ -d "$p" ]; then
echo "Found: $p"
TEAMS_CACHE="$p"
break
fi
done
echo "Using: $TEAMS_CACHE"Quick reference table
| OS | App type | Cache path |
| macOS | Native desktop app | ~/Library/Containers/com.microsoft.teams2/Data/Library/Application Support/Microsoft/MSTeams/EBWebView/WV2Profile_tfw/IndexedDB/https_teams.microsoft.com_0.indexeddb.leveldb |
| macOS | Chromium web app | ~/.config/chromium/Default/IndexedDB/https_teams.cloud.microsoft_0.indexeddb.leveldb |
| Linux | Chromium web app (only supported path) | ~/.config/chromium/Default/IndexedDB/https_teams.cloud.microsoft_0.indexeddb.leveldb |
Linux note: Microsoft discontinued the official native Teams Linux client in 2022 in favor of the web app / PWA. There is no WebView2-based native app cache to read on Linux. If the user runs an unofficial third-party Electron wrapper (e.g.
teams-for-linux), its cache is not guaranteed to share the same IndexedDB structure — treat it as unverified and fall back to the Chromium web app path if extraction fails.
Key distinction: Chromium web app uses
https_teams.cloud.microsoftin the path. Native desktop app useshttps_teams.microsoft.com. Don't mix them up.
Copy the cache
Teams holds a write lock on the LevelDB directory. Always copy first:
cp -r "$TEAMS_CACHE" /tmp/teams_copyThe native macOS app also has a sibling .blob directory (same name, ending
in .blob instead of .leveldb) holding externally-stored large message
values. Copy it too if present, alongside the .leveldb copy:
BLOB_DIR="${TEAMS_CACHE%.leveldb}.blob"
[ -d "$BLOB_DIR" ] && cp -r "$BLOB_DIR" /tmp/teams_blob_copyThe script auto-detects /tmp/teams_blob_copy if it sits next to
/tmp/teams_copy with the expected naming; pass --blob-path explicitly if
you copy it elsewhere.
Step 2 — Run the extractor
Get messages from a specific person
uv run scripts/teams_extractor.py /tmp/teams_copy \
--from-user 'Display Name Here' \
--recent 20Search by keyword (all senders)
uv run scripts/teams_extractor.py /tmp/teams_copy \
--search 'keyword or phrase'Combine: messages from a person about a topic
uv run scripts/teams_extractor.py /tmp/teams_copy \
--from-user 'Jane Doe' \
--search 'partnership' \
--recent 10Extract your own sent messages (default mode)
uv run scripts/teams_extractor.py /tmp/teams_copy \
--recent 50Save to file
uv run scripts/teams_extractor.py /tmp/teams_copy \
--from-user 'Person Name' \
--recent 30 \
--output /tmp/teams_results.txt
cat /tmp/teams_results.txtFor large result sets, always use
--output FILE— output may be truncated if piped directly to the agent. Use--recent Nto bound output size.
Flags
uv run scripts/teams_extractor.py --help| Flag | Description |
--from-user NAME | Only messages from this display name. Without this, only your own sent messages are returned. |
--search WORD | Only messages containing this text (case-insensitive). |
--recent N | Only the N most recent matching messages. |
--blob-path PATH | Sibling .blob directory for externally-stored values (auto-detected by default). |
--output FILE | Write results to a file instead of stdout. |
Output format
One line per message:
[YYYY-MM-DDTHH:MM:SS] message textTimestamps are UTC. Messages with unresolvable timestamps show [unknown].
Display name lookup
--from-user matches on the display name as stored in the message record.
Use the exact name as it appears in Teams (e.g. 'Jane Doe', not 'Doe'
or 'jane.doe@example.com'). If unsure, omit --from-user and use --search
with a unique phrase from the person to locate them.
Limitations
- Read-only — no send, reply, or organize capability
- Local cache only — history limited to what the app has cached (typically weeks)
- Snapshot-based, not live — see "Freshness lag" note near the top; re-copy the cache before re-running if you need something that just happened
- Received messages rely on display name matching; partial names may miss results
- Cache must exist on disk — Teams must have been opened at least once on this machine
- First run needs network access (to fetch the pinned
ccl_chromium_readerengine and its PyPI dependencies); offline after that viauv's cache
Maintenance: updating the pinned engine version
The parsing engine (ccl_chromium_reader) is pinned to a specific commit in
scripts/teams_extractor.py's PEP 723 header, not a version range — this
repo doesn't publish releases, so a commit hash is the only stable reference.
Only repin it if something breaks (e.g. Teams changes its IndexedDB schema in
a way the current pin can't parse) or you deliberately want a newer commit's
fixes. There's no routine update cadence.
To repin:
- Pick the new commit from https://github.com/cclgroupltd/ccl_chromium_reader/commits/master
- Update the
ccl_chromium_reader @ git+...@<commit>line inscripts/teams_extractor.py'sdependencieslist to the new commit hash. - Read
scripts/vendor/VENDORED.mdand follow its "Maintenance" section — the new commit'spyproject.tomlmay pin a differentccl_simplesnappycommit than the one vendored here, and the override in the PEP 723 header won't warn you if it's now out of sync. - Re-run the extractor against a real (copied) cache and confirm message counts and content look sane before trusting the new pin — see Step 2 above for example invocations.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

