<?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: Yukihiro Terakado</title>
    <description>The latest articles on DEV Community by Yukihiro Terakado (@yutsuki3).</description>
    <link>https://dev.to/yutsuki3</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%2F4003942%2Fa25909a3-b6e4-461e-9538-6ad297639242.jpg</url>
      <title>DEV Community: Yukihiro Terakado</title>
      <link>https://dev.to/yutsuki3</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yutsuki3"/>
    <language>en</language>
    <item>
      <title>Riverpod DevTools — visualize your provider graph, state, and events (now with an MCP server for AI coding tools)</title>
      <dc:creator>Yukihiro Terakado</dc:creator>
      <pubDate>Fri, 26 Jun 2026 12:17:09 +0000</pubDate>
      <link>https://dev.to/yutsuki3/riverpod-devtools-visualize-your-provider-graph-state-and-events-now-with-an-mcp-server-for-ai-3jen</link>
      <guid>https://dev.to/yutsuki3/riverpod-devtools-visualize-your-provider-graph-state-and-events-now-with-an-mcp-server-for-ai-3jen</guid>
      <description>&lt;p&gt;If you've used &lt;a href="https://riverpod.dev" rel="noopener noreferrer"&gt;Riverpod&lt;/a&gt; for any non-trivial Flutter app, you've probably hit this moment: a provider's state looks wrong, and you have no idea which other provider triggered the update, or why it rebuilt at all. &lt;code&gt;print&lt;/code&gt; statements and breakpoints only get you so far when the bug involves a chain of &lt;code&gt;watch&lt;/code&gt;/&lt;code&gt;read&lt;/code&gt;/&lt;code&gt;listen&lt;/code&gt; relationships.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://pub.dev/packages/riverpod_devtools" rel="noopener noreferrer"&gt;&lt;code&gt;riverpod_devtools&lt;/code&gt;&lt;/a&gt; to fix that — a &lt;a href="https://flutter.dev/devtools" rel="noopener noreferrer"&gt;Flutter DevTools&lt;/a&gt; extension that gives you a live, visual view of your providers. Plus, as of v0.6.0, it bundles an optional MCP server so AI coding tools like Claude Code can read that exact same data.&lt;/p&gt;

&lt;p&gt;I've been building and iterating on it on pub.dev for about five months now (it's already at v0.6.0 across nine releases), but I haven't actually written about it or shared it anywhere until today — this is its first real introduction. Feedback, issues, and stars are very welcome.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 What it does
&lt;/h2&gt;

&lt;p&gt;Once it's wired up, a &lt;code&gt;riverpod_devtools&lt;/code&gt; tab shows up in Flutter DevTools with three panels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Provider Graph&lt;/strong&gt; — visualizes dependencies between providers (who watches/reads/listens to whom)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State Inspector&lt;/strong&gt; — the current state of any selected provider, with type labels and a collapsible JSON tree for complex objects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event Log&lt;/strong&gt; — a hierarchical, real-time log of provider lifecycle events (&lt;code&gt;added&lt;/code&gt;, &lt;code&gt;updated&lt;/code&gt;, &lt;code&gt;disposed&lt;/code&gt;), grouped so related sub-events don't get lost in the noise&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvpjsnga7yylta2s343wi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvpjsnga7yylta2s343wi.png" alt="Riverpod DevTools screenshot" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It supports both light and dark themes, and works with both Riverpod 2.x and 3.x.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 Why static analysis instead of runtime detection
&lt;/h2&gt;

&lt;p&gt;Earlier versions tried to infer provider dependencies by watching update timing at runtime — which works, until two providers update close together for unrelated reasons and you get a false edge in the graph.&lt;/p&gt;

&lt;p&gt;0.5.0 replaced that with a CLI tool that does &lt;strong&gt;AST-based static analysis&lt;/strong&gt; of your actual source code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# one-time&lt;/span&gt;
dart run riverpod_devtools:analyze

&lt;span class="c"&gt;# or watch mode while you work&lt;/span&gt;
dart run riverpod_devtools:analyze &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This walks your code with the &lt;code&gt;analyzer&lt;/code&gt; package, finds every &lt;code&gt;ref.watch&lt;/code&gt; / &lt;code&gt;ref.read&lt;/code&gt; / &lt;code&gt;ref.listen&lt;/code&gt; call, and emits a &lt;code&gt;lib/riverpod_dependencies.json&lt;/code&gt; with exact dependency types and source locations (file/line/column). No heuristics, no false positives — the graph reflects what your code actually does, not what it looked like it did at runtime.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Quick start
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Add the dependency&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;riverpod_devtools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^0.6.0&lt;/span&gt;
  &lt;span class="na"&gt;flutter_riverpod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;=2.3.0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;4.0.0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Wire it up in &lt;code&gt;main.dart&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;WidgetsFlutterBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ensureInitialized&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;jsonString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;rootBundle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;loadString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s"&gt;'lib/riverpod_dependencies.json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;RiverpodDevToolsRegistry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;loadFromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// DevTools shows setup instructions if this hasn't been generated yet&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ProviderScope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;observers:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RiverpodDevToolsObserver&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
      &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the app, open Flutter DevTools, and the &lt;code&gt;riverpod_devtools&lt;/code&gt; tab is there automatically. Full setup (including the &lt;code&gt;pubspec.yaml&lt;/code&gt; asset entry) is in the &lt;a href="https://github.com/yutsuki3/riverpod_devtools/blob/main/packages/riverpod_devtools/README.md" rel="noopener noreferrer"&gt;README&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤖 The part I'm most excited about: an MCP server for your provider logs
&lt;/h2&gt;

&lt;p&gt;This is new in 0.6.0, and it's the reason I wanted to write this up rather than just quietly ship it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;RiverpodDevToolsObserver&lt;/code&gt; already starts a local, debug-only HTTP server (&lt;code&gt;localhost:8788&lt;/code&gt;) to feed the DevTools UI. 0.6.0 adds a bundled MCP server executable that relays that same data over &lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;MCP&lt;/a&gt; — so tools like Claude Code can read your app's live provider events directly.&lt;/p&gt;

&lt;p&gt;Setup is one &lt;code&gt;.mcp.json&lt;/code&gt; entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"riverpod_devtools_mcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stdio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"riverpod_devtools:riverpod_devtools_mcp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(or &lt;code&gt;claude mcp add --scope project riverpod_devtools_mcp -- dart run riverpod_devtools:riverpod_devtools_mcp&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Run your app in debug mode, and Claude Code can now call &lt;code&gt;get_riverpod_logs&lt;/code&gt; to pull buffered &lt;code&gt;provider_added&lt;/code&gt; / &lt;code&gt;provider_updated&lt;/code&gt; / &lt;code&gt;provider_disposed&lt;/code&gt; events, and &lt;code&gt;clear_riverpod_logs&lt;/code&gt; to reset the buffer before reproducing a specific flow. In practice that means you can say something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Reproduce the checkout flow, then look at the current provider logs and tell me why &lt;code&gt;cartTotalProvider&lt;/code&gt; isn't updating after &lt;code&gt;removeItem&lt;/code&gt;."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;...and the agent actually has runtime ground truth instead of guessing from source code alone.&lt;/p&gt;

&lt;p&gt;Current limitations: HTTP-only on &lt;code&gt;localhost&lt;/code&gt;, native platforms only (no web), and real devices/emulators need manual port forwarding (&lt;code&gt;adb forward&lt;/code&gt; / &lt;code&gt;iproxy&lt;/code&gt;) since they don't share the host's loopback. Full details in &lt;a href="https://github.com/yutsuki3/riverpod_devtools/blob/main/packages/riverpod_devtools/MCP.md" rel="noopener noreferrer"&gt;MCP.md&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter pub add riverpod_devtools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;pub.dev: &lt;a href="https://pub.dev/packages/riverpod_devtools" rel="noopener noreferrer"&gt;pub.dev/packages/riverpod_devtools&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/yutsuki3/riverpod_devtools" rel="noopener noreferrer"&gt;github.com/yutsuki3/riverpod_devtools&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Issues / feedback: &lt;a href="https://github.com/yutsuki3/riverpod_devtools/issues" rel="noopener noreferrer"&gt;github.com/yutsuki3/riverpod_devtools/issues&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's MIT-licensed and still early — the Provider Graph and MCP integration in particular could use real-world testing across different app sizes. If you use Riverpod, I'd genuinely appreciate you trying it on an existing project and telling me what breaks or what's missing.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>mcp</category>
      <category>tooling</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
