<?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: Jonah Reed</title>
    <description>The latest articles on DEV Community by Jonah Reed (@jonahreed).</description>
    <link>https://dev.to/jonahreed</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3824438%2F8437f004-8d16-42bf-b315-2d0931c23365.png</url>
      <title>DEV Community: Jonah Reed</title>
      <link>https://dev.to/jonahreed</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jonahreed"/>
    <language>en</language>
    <item>
      <title>I Built an AI Agent That Thinks in Notion (And Can Give His Brain a Makeover)</title>
      <dc:creator>Jonah Reed</dc:creator>
      <pubDate>Mon, 30 Mar 2026 06:19:02 +0000</pubDate>
      <link>https://dev.to/jonahreed/i-built-an-ai-agent-that-thinks-in-notion-and-can-give-his-brain-a-makeover-48gf</link>
      <guid>https://dev.to/jonahreed/i-built-an-ai-agent-that-thinks-in-notion-and-can-give-his-brain-a-makeover-48gf</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/notion-2026-03-04"&gt;Notion MCP Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Fair warning: I found out about this challenge a week late. If I'm past the deadline, blame my feed algorithm ty ily &amp;lt;3)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Hey y'all! Quick context: I believe you've met OpenFiend in my previous post, so this one is all about the Notion MCP build.&lt;/p&gt;

&lt;p&gt;The idea: what if Notion wasn't where an AI agent just &lt;em&gt;stores&lt;/em&gt; things, but where it actually... &lt;em&gt;thinks&lt;/em&gt;? Decisions, tasks, memory, workspace customization - all living in Notion, all perfectly readable AND editable?&lt;/p&gt;

&lt;p&gt;This is an experiment in making AI agents transparent by design. If Bob's brain is a Notion workspace, you can open it up, read his reasoning, override his decisions, and shape how he operates with basically no code required (though you'll probably feel like you're writing some!).&lt;/p&gt;

&lt;h3&gt;
  
  
  What Bob can do today:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Decision gates (implemented + tested)&lt;/strong&gt; - Before Bob runs a risky action, he writes a proposal to Notion Decisions as &lt;code&gt;pending_approval&lt;/code&gt;. Approving directly in Notion (or in the UI - the choice is still yours) unblocks Bob.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Async task queue (implemented + tested)&lt;/strong&gt; - Drop a task in Tasks with &lt;code&gt;pending&lt;/code&gt;; Bob picks highest-priority work first, executes, writes result, and updates status.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scheduled tasks (implemented, lightly tested)&lt;/strong&gt; - Tasks with future &lt;code&gt;ScheduledFor&lt;/code&gt; are skipped until due time. Bob is quite punctual when circumstances allow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mind customization (implemented + tested)&lt;/strong&gt; - Bob can restyle his own Notion root page. He can preset themes, icons, covers, and block layouts (just so the workspace feels personal and less generic).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Persistent memory &amp;amp; will (implemented, not deeply tested in demo)&lt;/strong&gt; - Bob can read and write long-term memory and "will" statements to Notion, injecting them as runtime context. Deeper integration into the live session loop is coming in later updates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Autopsy reports (implemented, not demo-tested)&lt;/strong&gt; - Failure report write/read module exists.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Threat logging (implemented, not demo-tested)&lt;/strong&gt; - Bob can log and retrieve security events from Notion. The module works but isn't wired into active detection paths yet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Shadow logs (implemented, not demo-tested)&lt;/strong&gt; - Shadow observation read/write/enable exists but was not included in the final demo run.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The tech stack:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; React + Vite + Tailwind CSS (3-panel layout: chat, history, audit trail)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Node.js + Express + WebSockets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; SQLite (via Drizzle ORM) for local persistence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI:&lt;/strong&gt; Vercel AI SDK with Anthropic Claude&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notion:&lt;/strong&gt; Root page + structured databases for agent state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP:&lt;/strong&gt; &lt;code&gt;@notionhq/notion-mcp-server&lt;/code&gt; + &lt;code&gt;@modelcontextprotocol/sdk&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Video Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/ha6CnEln3tw"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the demo shows:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Boot + daemon init&lt;/strong&gt; - Backend starts, Notion brain is connected, pollers start.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Decision gate flow&lt;/strong&gt; - A sensitive action triggers a decision in Notion as &lt;code&gt;pending_approval&lt;/code&gt;. Approved directly in Notion. Bob continues only after approval.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Task queue flow&lt;/strong&gt; - A pending task is added to Tasks. Poller picks it up, transitions &lt;code&gt;pending&lt;/code&gt; -&amp;gt; &lt;code&gt;in_progress&lt;/code&gt; -&amp;gt; &lt;code&gt;completed&lt;/code&gt;, and writes result/timestamps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mind customization&lt;/strong&gt; - Bob applies a preset to his own workspace page. Icon/cover/blocks update in Notion.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Show us the code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/jreed18" rel="noopener noreferrer"&gt;
        jreed18
      &lt;/a&gt; / &lt;a href="https://github.com/jreed18/openfiend" rel="noopener noreferrer"&gt;
        openfiend
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Bob. A fiend on your side. Local-first AI agent that shows every move and asks before making them.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;&lt;code&gt;O P E N F I E N D&lt;/code&gt;&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;&lt;strong&gt;NO BLACK BOXES. NO TRUST REQUIRED.&lt;/strong&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/jreed18/openfiend" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0784cdc11e0389f48018571c2ff2a49e459dd1847ac561a0b764ee24f5f9b87b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f76657273696f6e2d302e312d6531316437653f7374796c653d666c61742d737175617265266c6162656c436f6c6f723d306130613061" alt="v0.1"&gt;&lt;/a&gt;
&lt;a href="https://github.com/jreed18/openfiend/." rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/64d0eaa86ddaaf87f6374173aaa399ed6b6db7bfac4a9c8f128a91a0e1505922/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d5442442d6639373331363f7374796c653d666c61742d737175617265266c6162656c436f6c6f723d306130613061" alt="License"&gt;&lt;/a&gt;
&lt;a href="https://github.com/jreed18/openfiend/." rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/4479875069b60695d539e471878e6a6b98520a5d349dd4f2f00cc475d5bb88c0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f547970655363726970742d7374726963742d6531316437653f7374796c653d666c61742d737175617265266c6162656c436f6c6f723d306130613061" alt="TypeScript"&gt;&lt;/a&gt;
&lt;a href="https://github.com/jreed18/openfiend/." rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/3c24c87d70991f3b16e8ca3c8c4a7a080de3cc252840e6305d61c8ff3a30a333/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6f70656e5f736f757263652d6461795f6f6e652d6639373331363f7374796c653d666c61742d737175617265266c6162656c436f6c6f723d306130613061" alt="Open Source"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security-first AI agent platform.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Every action visible. Every permission explicit. Everything auditable.&lt;/strong&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;br&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;&lt;code&gt;&amp;gt; WHAT IS THIS&lt;/code&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;OpenFiend is an AI agent platform where &lt;strong&gt;transparency isn't a feature — it's the architecture.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The first agent — &lt;strong&gt;Bob&lt;/strong&gt; — is a paranoid, audit-log-obsessed assistant powered by Claude. You talk to Bob through a real-time WebSocket chat interface. Everything he does is logged, visible, and controllable.&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;This is v0.1. It's early and messy, but we're (I'm) making it work!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Not production-ready:&lt;/strong&gt; OpenFiend is an early-stage project and has not been tested thoroughly enough for production use yet.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;&lt;code&gt;&amp;gt; QUICK START&lt;/code&gt;&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;git clone https://github.com/jreed18/openfiend.git
&lt;span class="pl-c1"&gt;cd&lt;/span&gt; openfiend
cp .env.example .env.local    &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; add your API keys (see .env.example)&lt;/span&gt;
pnpm install
pnpm dev&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;URL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Frontend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;localhost:5173&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;localhost:3737&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;WebSocket&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ws://localhost:3737/ws&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Requires &lt;strong&gt;Node.js 22+&lt;/strong&gt; and &lt;strong&gt;pnpm 9+&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;&lt;code&gt;&amp;gt; ARCHITECTURE&lt;/code&gt;&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;┌──────────────────────────────────────────────────────────────┐
│&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/jreed18/openfiend" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The Notion integration lives in &lt;code&gt;packages/backend/src/tools/notion/&lt;/code&gt;. Key files:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcpClient.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MCP connection + &lt;code&gt;API-*&lt;/code&gt; tool calls + data source lookup cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;client.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Original Notion SDK client (pre-MCP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setup.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First-run workspace/database creation + ID persistence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;poller.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Background polling for decisions + tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sections/decisions.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ethical review board (write/read/approve) via MCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sections/tasks.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Async task queue + scheduling via MCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sections/mind.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Root page customization via MCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sections/memory.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Long-term memory + will statements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sections/autopsies.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Post-mortem failure reports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sections/threats.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Security event feed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sections/shadow.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shadow mode observations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How I Used Notion MCP
&lt;/h2&gt;

&lt;p&gt;Notion isn't meant to be &lt;em&gt;just&lt;/em&gt; a storage layer for OpenFiend. I am working on further developing it as the &lt;strong&gt;identity and control layer&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The architecture
&lt;/h3&gt;

&lt;p&gt;Bob uses an MCP adapter (&lt;code&gt;mcpClient.ts&lt;/code&gt;) to call Notion tools exposed by &lt;code&gt;@notionhq/notion-mcp-server&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// packages/backend/src/tools/notion/mcpClient.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;callNotionTool&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getNotionMcpClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;T&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;T&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;T&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;Decisions/tasks/mind now use MCP tools like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;API-post-page&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;API-patch-page&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;API-query-data-source&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;API-patch-block-children&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;API-retrieve-a-database&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why MCP specifically
&lt;/h3&gt;

&lt;p&gt;MCP gave me a standardized interface for Notion operations and made it easier to keep Notion access in one adapter layer (plus, the challenge kinda required it).&lt;/p&gt;

&lt;h3&gt;
  
  
  Decision gates via MCP
&lt;/h3&gt;

&lt;p&gt;Before Bob takes a risky action, he writes a structured proposal to Notion as &lt;code&gt;pending_approval&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// packages/backend/src/tools/notion/sections/decisions.ts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;callNotionTool&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API-post-page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;database_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;database_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;decisionsDbId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&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="na"&gt;Reasoning&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rich_text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reasoning&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="na"&gt;Risks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rich_text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;risks&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="na"&gt;Risk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;riskLevel&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;ConversationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rich_text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conversationId&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="na"&gt;Annotation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rich_text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;annotation&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="na"&gt;Timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeEnd&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rich_text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tool&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;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user approves or rejects directly in Notion. The poller (below) detects the status change and unblocks Bob.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task queue via MCP
&lt;/h3&gt;

&lt;p&gt;Bob queries pending tasks sorted by priority:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// packages/backend/src/tools/notion/sections/tasks.ts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dataSourceId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDataSourceId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tasksDbId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notionResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;callNotionTool&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API-query-data-source&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;data_source_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dataSourceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;sorts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Priority&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;descending&lt;/span&gt;&lt;span class="dl"&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;h3&gt;
  
  
  The polling pattern (and why not webhooks)
&lt;/h3&gt;

&lt;p&gt;OpenFiend runs locally. Polling is simpler and reliable without public webhook infra.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decision poller (10s)&lt;/strong&gt; - Watches decision status changes and resolves pending permission promises.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task poller (60s, dynamic)&lt;/strong&gt; - Picks pending tasks, runs them, writes results back.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// packages/backend/src/tools/notion/poller.ts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decisions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;readPendingDecisions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decision&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;decisions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentStatus&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;pageId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Check if we've seen this decision before and if its status has changed&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;previousStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;statusCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;statusCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// First time seeing this decision - cache and skip&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;previousStatus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;previousStatus&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending_approval&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentStatus&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;approved&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;currentStatus&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rejected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentStatus&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;approved&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;decision_approved&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;decision_rejected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[Notion Poller] Detected decision &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currentStatus&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; for pageId &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pageId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nf"&gt;resolveDecision&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentStatus&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;approved&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;PermissionStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Approved&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PermissionStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Rejected&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;broadcastToClients&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;decision&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;h3&gt;
  
  
  Mind customization via MCP
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// packages/backend/src/tools/notion/sections/mind.ts - apply_preset action&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;callNotionTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API-patch-page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;page_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootPageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;external&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.notion.so/icons/brain_gray.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;external&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://images.unsplash.com/photo-1518770660439-4636190af475&lt;/span&gt;&lt;span class="dl"&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;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;callNotionTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API-patch-block-children&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;block_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootPageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;children&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="na"&gt;object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heading_2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;heading_2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rich_text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mind&lt;/span&gt;&lt;span class="dl"&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;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;paragraph&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;paragraph&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rich_text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Focused layout. Keep only what matters.&lt;/span&gt;&lt;span class="dl"&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;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;h3&gt;
  
  
  What this unlocks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Human-in-the-loop approvals directly in Notion&lt;/li&gt;
&lt;li&gt;Async Notion task execution without opening chat&lt;/li&gt;
&lt;li&gt;Agent workspace customization as a first-class operation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Crash recovery
&lt;/h3&gt;

&lt;p&gt;On restart:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;in_progress&lt;/code&gt; tasks are reset to &lt;code&gt;pending&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;orphaned &lt;code&gt;pending_approval&lt;/code&gt; decisions are auto-rejected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No manual cleanup required. Throughout development I've come to know how annoying it can get...&lt;/p&gt;

&lt;h3&gt;
  
  
  Upcoming Features
&lt;/h3&gt;

&lt;p&gt;Guided by my internal "Bob's Horcrux" plan (a little code name for this project), next features are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/config&lt;/code&gt; database&lt;/strong&gt; (not implemented yet)&lt;/li&gt;
&lt;li&gt;Notion-driven runtime config (model, thresholds, shadow toggle, tools)&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Poll config every 60s and apply without restart&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;/weekly-report&lt;/code&gt;&lt;/strong&gt; (not implemented yet)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sunday Notion report generated from SQLite audit logs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Structured sections: wins, struggles, unusual events, next-week plan&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Horcrux-level memory + will loop&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Read Will + recent memory on startup and inject into live system context every session&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Expand memory writes from simple storage to structured user/profile/project summaries&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Support session-linked memory trails for better long-horizon continuity&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deeper wiring for existing sections&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Threats/autopsies/shadow are implemented as modules, but need fuller production wiring + dedicated demo coverage&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Threats: wire directly into prompt-injection and suspicious-action detection paths&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Autopsies: auto-create on critical failures and tool-chain exceptions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shadow log: first-class review workflow where users can promote safe actions into enabled behavior. Will become rather crucial once the shadow / observer mode gets fully implemented&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Notion-native control surface&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make Notion the primary control panel for Bob behavior tuning, not just a log sink&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add "editable policy" pages so non-technical users can safely shape Bob's operating rules&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;More granular mind customization: let users define their own presets, per-section styling, and layout preferences beyond the built-in themes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Broader MCP adoption&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Continue moving remaining Notion sections to MCP transport for full consistency&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Toward full Horcrux mode&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Goal: if users opt in, Notion becomes Bob’s complete operational identity layer&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Decisions, tasks, memory, policies, reports, and configuration all become transparent and editable in one place&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If Notion is disconnected, Bob should degrade gracefully while clearly signaling that his "brain state" is unavailable&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Last-Minute Reality Check
&lt;/h3&gt;

&lt;p&gt;I initially built this integration with the Notion SDK, then realized late that the challenge specifically required &lt;strong&gt;Notion MCP&lt;/strong&gt; as a core part of the build.&lt;br&gt;
So I did a last-minute refactor to route the core paths (&lt;code&gt;decisions&lt;/code&gt;, &lt;code&gt;tasks&lt;/code&gt;, and &lt;code&gt;mind&lt;/code&gt; customization) through &lt;code&gt;@notionhq/notion-mcp-server&lt;/code&gt; + &lt;code&gt;@modelcontextprotocol/sdk&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It wasn't the most comfortable timing, but it pushed the architecture in a better direction: a single MCP adapter layer for Notion operations, with clearer boundaries for future expansion.&lt;/p&gt;



&lt;p&gt;I'm building OpenFiend in public because I think AI agents should be built in the open. If you've got thoughts on transparent AI, agent architecture, or just want to tell Bob he's doing great (he needs it), we'd both love to hear from you!&lt;/p&gt;

&lt;p&gt;Take care, polar bear!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://openfiend.com/" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Check out OpenFiend&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>notionmcpchallenge</category>
      <category>ai</category>
      <category>showdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>OpenFiend: Building an Honest AI Agent in Public (Starting From Zero)</title>
      <dc:creator>Jonah Reed</dc:creator>
      <pubDate>Wed, 18 Mar 2026 12:33:20 +0000</pubDate>
      <link>https://dev.to/jonahreed/openfiend-building-an-honest-ai-agent-in-public-starting-from-zero-473e</link>
      <guid>https://dev.to/jonahreed/openfiend-building-an-honest-ai-agent-in-public-starting-from-zero-473e</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What is OpenFiend?&lt;/li&gt;
&lt;li&gt;What OpenFiend Can Do Today (v0.1)&lt;/li&gt;
&lt;li&gt;Why Transparent AI Agents Matter&lt;/li&gt;
&lt;li&gt;What's Coming Next&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most AI assistants work like black boxes. You type something, wait, and hope for the best. &lt;strong&gt;Bob is different.&lt;/strong&gt; He's being built around a simple idea - transparency as the first principle.&lt;/p&gt;

&lt;p&gt;I’m Jonah Reed, a solo developer building OpenFiend in public as a learning project in AI agent development.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is OpenFiend? A Transparent AI Agent Platform
&lt;/h2&gt;

&lt;p&gt;OpenFiend is an open-source side project centered around a lil' guy named Bob. &lt;/p&gt;

&lt;p&gt;Unlike quite a few other assistants, Bob shows his reasoning in real-time. Right now, the UI displays logs, I'm expanding what gets tracked as the project grows.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  &lt;br&gt;
&lt;strong&gt;Bob's Personality Profile:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Anxious and passive-aggressive:&lt;/strong&gt; He has feelings (mostly stress).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Radically transparent:&lt;/strong&gt; No hidden prompts or silent failures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attention-seeking:&lt;/strong&gt; Not a fan of being ignored.

&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  What OpenFiend Can Do Today (v0.1)
&lt;/h2&gt;

&lt;p&gt;Right now, OpenFiend is in its early, messy era. It’s a functional learning project where Bob can engage in full conversations powered by a SQLite backend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Current Tech Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; React + Vite + Tailwind CSS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Node.js + Express + WebSockets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; SQLite (via Drizzle ORM)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Integration:&lt;/strong&gt; Vercel AI SDK&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Features currently live:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context switching:&lt;/strong&gt; Persistent conversation history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A clean 3-panel layout:&lt;/strong&gt; Chat in the middle, history on the left, and an audit trail on the right.
&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.amazonaws.com%2Fuploads%2Farticles%2Fukh1kolctgh2zg5owq4m.png" alt="A screenshot of the OpenFiend interface showing the 3-panel layout."&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Why Transparent AI Agents Matter (and why I'm building this)
&lt;/h2&gt;

&lt;p&gt;I'm curious whether transparency matters to people. OpenFiend is my experiment in building this openly - to keep myself honest, and because I genuinely want to learn in public instead of abandoning yet another side project.&lt;/p&gt;

&lt;p&gt;Check out the code here (if you're interested):&lt;br&gt;


&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/jreed18" rel="noopener noreferrer"&gt;
        jreed18
      &lt;/a&gt; / &lt;a href="https://github.com/jreed18/openfiend" rel="noopener noreferrer"&gt;
        openfiend
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Bob. A fiend on your side. Local-first AI agent that shows every move and asks before making them.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;&lt;code&gt;O P E N F I E N D&lt;/code&gt;&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;&lt;strong&gt;NO BLACK BOXES. NO TRUST REQUIRED.&lt;/strong&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/jreed18/openfiend" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0784cdc11e0389f48018571c2ff2a49e459dd1847ac561a0b764ee24f5f9b87b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f76657273696f6e2d302e312d6531316437653f7374796c653d666c61742d737175617265266c6162656c436f6c6f723d306130613061" alt="v0.1"&gt;&lt;/a&gt;
&lt;a href="https://github.com/jreed18/openfiend/." rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/64d0eaa86ddaaf87f6374173aaa399ed6b6db7bfac4a9c8f128a91a0e1505922/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d5442442d6639373331363f7374796c653d666c61742d737175617265266c6162656c436f6c6f723d306130613061" alt="License"&gt;&lt;/a&gt;
&lt;a href="https://github.com/jreed18/openfiend/." rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/4479875069b60695d539e471878e6a6b98520a5d349dd4f2f00cc475d5bb88c0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f547970655363726970742d7374726963742d6531316437653f7374796c653d666c61742d737175617265266c6162656c436f6c6f723d306130613061" alt="TypeScript"&gt;&lt;/a&gt;
&lt;a href="https://github.com/jreed18/openfiend/." rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/3c24c87d70991f3b16e8ca3c8c4a7a080de3cc252840e6305d61c8ff3a30a333/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6f70656e5f736f757263652d6461795f6f6e652d6639373331363f7374796c653d666c61742d737175617265266c6162656c436f6c6f723d306130613061" alt="Open Source"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security-first AI agent platform.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Every action visible. Every permission explicit. Everything auditable.&lt;/strong&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;br&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;&lt;code&gt;&amp;gt; WHAT IS THIS&lt;/code&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;OpenFiend is an AI agent platform where &lt;strong&gt;transparency isn't a feature — it's the architecture.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The first agent — &lt;strong&gt;Bob&lt;/strong&gt; — is a paranoid, audit-log-obsessed assistant powered by Claude. You talk to Bob through a real-time WebSocket chat interface. Everything he does is logged, visible, and controllable.&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;This is v0.1. It's early and messy, but we're (I'm) making it work!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;br&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;&lt;code&gt;&amp;gt; QUICK START&lt;/code&gt;&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;git clone https://github.com/jreed18/openfiend.git
&lt;span class="pl-c1"&gt;cd&lt;/span&gt; openfiend
cp .env.example .env.local    &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; add your API keys (see .env.example)&lt;/span&gt;
pnpm install
pnpm dev&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;URL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Frontend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;localhost:5173&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;localhost:3737&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;WebSocket&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ws://localhost:3737/ws&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Requires &lt;strong&gt;Node.js 22+&lt;/strong&gt; and &lt;strong&gt;pnpm 9+&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;&lt;code&gt;&amp;gt; ARCHITECTURE&lt;/code&gt;&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;┌──────────────────────────────────────────────────────────────┐
│                        OPENFIEND                             │
├──────────────┬────────────────────────┬──────────────────────┤
│  LEFT RAIL   │     CENTER PANEL       │    RIGHT PANEL       │
│              │                        │                       │
│  conversation│&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/jreed18/openfiend" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;




&lt;h2&gt;
  
  
  What's Coming Next: Streaming, Tools, and Live Audit Trails
&lt;/h2&gt;

&lt;p&gt;Building in public means showing the bugs alongside the features. To someone who cares about being "perfect," this is a nightmare. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good.&lt;/strong&gt; That’s the point.&lt;/p&gt;

&lt;p&gt;
  What's next on the roadmap?
  &lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Streaming Responses:&lt;/strong&gt; Making Bob feel more "alive."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool Use:&lt;/strong&gt; Giving Bob the power to perform web searches and file operations. And some other neat stuff (hope you'll wait and see &amp;lt;3)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live Audit Trail:&lt;/strong&gt; Filling up that right panel with real-time logs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personality Fine-tuning:&lt;/strong&gt; Finding the sweet spot for Bob's sass.
&lt;/li&gt;
&lt;/ul&gt;




&lt;/p&gt;
&lt;p&gt;If you’ve ever struggled with "shiny object syndrome" or just want to watch a solo dev try to keep an open-source project alive in the wild, I’d love for you to follow along.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I'm a solo developer learning AI agent development by building one from scratch in public. If you've used any similar tools in the past, what are they and what do you wish they handled better? Bob and I are taking notes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://openfiend.com/" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Check out OpenFiend&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>ai</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
