<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Local-MCP</title>
    <description>The latest articles on DEV Community by Local-MCP (@local-mcp).</description>
    <link>https://dev.to/local-mcp</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4152404%2F0aba9e8c-1538-4f40-85da-c50eb53cc23a.png</url>
      <title>DEV Community: Local-MCP</title>
      <link>https://dev.to/local-mcp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/local-mcp"/>
    <language>en</language>
    <item>
      <title>How we read Microsoft Teams messages without touching the Graph API</title>
      <dc:creator>Local-MCP</dc:creator>
      <pubDate>Wed, 30 Sep 2026 13:41:15 +0000</pubDate>
      <link>https://dev.to/local-mcp/how-we-read-microsoft-teams-messages-without-touching-the-graph-api-2ff9</link>
      <guid>https://dev.to/local-mcp/how-we-read-microsoft-teams-messages-without-touching-the-graph-api-2ff9</guid>
      <description>&lt;p&gt;Quick context if you haven't played with MCP yet: MCP (Model Context Protocol) is the standard that lets AI assistants like Claude use tools, things like "search my email" or "read this file". An MCP server is the thing that exposes those tools. I work on LMCP, a free MCP server for Mac that connects Claude, Cursor, Codex and friends to the apps you already use.&lt;/p&gt;

&lt;p&gt;Teams was the integration that gave us the most headaches, and the way we ended up solving it is kind of fun, so here it goes.&lt;/p&gt;

&lt;p&gt;The annoying part about Graph&lt;/p&gt;

&lt;p&gt;If you want Teams data in an AI assistant, the "official" way is Microsoft Graph. Which means: register an app in Entra ID, get admin consent, deal with OAuth tokens, respect rate limits.&lt;/p&gt;

&lt;p&gt;That's fine if you run the tenant. But if you're just a regular person at a company where IT owns everything? You're stuck at "please ask your administrator" and that's the end of it.&lt;/p&gt;

&lt;p&gt;The trick: Teams is secretly a website&lt;/p&gt;

&lt;p&gt;New Teams isn't really a native app. It's a web app running inside WebView2, which is basically Microsoft's embedded Edge. And like any website running in a Chromium browser, it saves its data in IndexedDB. Chromium stores IndexedDB on disk as LevelDB.&lt;/p&gt;

&lt;p&gt;So all your synced chats are already sitting on your Mac, here:&lt;/p&gt;

&lt;p&gt;~/Library/Containers/com.microsoft.teams2/Data/Library/Application Support/Microsoft/MSTeams/EBWebView/WV2Profile_tfw*/IndexedDB/https_teams.microsoft.com_0.indexeddb.leveldb&lt;/p&gt;

&lt;p&gt;Heads up if you go digging: lots of older posts point to &lt;code&gt;EBWebView/Default/&lt;/code&gt;. That's outdated. Current builds create one folder per profile. The &lt;code&gt;tfw&lt;/code&gt; ones are work/school accounts, and there's a &lt;code&gt;tfl&lt;/code&gt; one for personal Teams that we just skip.&lt;/p&gt;

&lt;p&gt;Actually reading the thing&lt;/p&gt;

&lt;p&gt;LevelDB full of V8-serialized JavaScript objects is not something you want to parse by hand. Luckily there's &lt;a href="https://github.com/cclgroupltd/ccl_chromium_reader" rel="noopener noreferrer"&gt;ccl_chromium_reader&lt;/a&gt;, a Python library that comes from the digital forensics world (yes, the people who pull evidence out of browsers). It walks the LevelDB files, decodes the V8 objects and rebuilds the IndexedDB records. From there we grab chats, channels, messages and people.&lt;/p&gt;

&lt;p&gt;Some stuff we learned the hard way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Teams locks the database while it's running.&lt;/strong&gt; We read the table files directly without grabbing the lock, so you don't have to close Teams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Some records just refuse to decode.&lt;/strong&gt; We skip them and keep a count, instead of blowing up the whole read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Big accounts, big databases.&lt;/strong&gt; The first read can take a bit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple work profiles?&lt;/strong&gt; We read all of them and merge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The schema moves around between Teams versions.&lt;/strong&gt; Honestly this is the real pain. Every update might shuffle something and we have to go chase it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What it looks like in practice&lt;/p&gt;

&lt;p&gt;You ask Claude "what happened in #engineering today?" or "find the message where someone shared the deploy checklist", and it answers from what's already on your machine. No Graph permissions, no Microsoft login dance.&lt;/p&gt;

&lt;p&gt;One thing people always ask: sending messages is a different story. That part does go through Graph, so you need to connect your Microsoft 365 account for it.&lt;/p&gt;

&lt;p&gt;The honest limitations&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It only sees what your Teams client has already synced. If Teams didn't pull a chat down, we can't see it either.&lt;/li&gt;
&lt;li&gt;It depends on an undocumented format, so a Teams update can break it until we catch up.&lt;/li&gt;
&lt;li&gt;You need the Teams desktop app installed and signed in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wanna try it?&lt;/p&gt;

&lt;p&gt;It's free:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; &lt;span class="s1"&gt;'https://local-mcp.com/install?ref=devto'&lt;/span&gt; | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or &lt;code&gt;npx -y local-mcp@latest setup&lt;/code&gt; if you prefer npm. There's a longer walkthrough &lt;a href="https://local-mcp.com/guides/claude-teams-no-api?ref=devto" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you've tried reading other Electron or WebView apps this way, I'd love to hear how it went. Happy to answer questions in the comments.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>claude</category>
      <category>ai</category>
      <category>swift</category>
    </item>
  </channel>
</rss>
