<?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>Forem: Enmanuel Magallanes Pinargote</title>
    <description>The latest articles on Forem by Enmanuel Magallanes Pinargote (@enmanuelmag).</description>
    <link>https://forem.com/enmanuelmag</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%2F1258109%2F1758118e-5a79-4085-9ce3-836a0cfedef0.jpeg</url>
      <title>Forem: Enmanuel Magallanes Pinargote</title>
      <link>https://forem.com/enmanuelmag</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/enmanuelmag"/>
    <language>en</language>
    <item>
      <title>Stop flying blind with MCP calls</title>
      <dc:creator>Enmanuel Magallanes Pinargote</dc:creator>
      <pubDate>Thu, 14 May 2026 18:08:51 +0000</pubDate>
      <link>https://forem.com/enmanuelmag/stop-flying-blind-with-mcp-calls-1po</link>
      <guid>https://forem.com/enmanuelmag/stop-flying-blind-with-mcp-calls-1po</guid>
      <description>&lt;p&gt;After getting frustrated not knowing what was actually happening inside MCP servers — which tools were slow, which failed silently, what inputs Claude was sending.&lt;/p&gt;

&lt;p&gt;The idea: a transparent proxy that sits between your MCP client (Claude Desktop, OpenCode, Cursor) and any MCP server, captures every JSON-RPC message as an OpenTelemetry span, and persists it to a storage backend you configure.&lt;/p&gt;

&lt;p&gt;No modifications to the target server required. You just wrap it in mcp.json:&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="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;"heimdall-mcp"&lt;/span&gt;&lt;span class="err"&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;"--store"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sqlite://~/.heimdall/traces.db"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-server.js"&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;For remote servers (HTTP/SSE):&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="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;"heimdall-mcp"&lt;/span&gt;&lt;span class="err"&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;"--store"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"postgres://..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--target"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://remote-server/sse"&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;Storage options: SQLite (local WASM, no native deps), Postgres, MySQL, or any OTLP-compatible backend.&lt;/p&gt;

&lt;p&gt;Also ships as a TypeScript library with a fluent builder API if you want to embed it directly.&lt;/p&gt;

&lt;p&gt;Why I think this is useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LLM agents are increasingly orchestrating MCP tools, but observability tooling hasn't caught up&lt;/li&gt;
&lt;li&gt;IBM's ContextForge does something similar but it's a heavy Python gateway — this is meant to be a lightweight npm package&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still early (v0.1), but the core proxy + SQLite store is working. Looking for feedback on the interceptor API design and whether the OTel semantic conventions mapping makes sense.&lt;/p&gt;

&lt;p&gt;Website: &lt;a href="https://stack.cardor.dev/heimdall" rel="noopener noreferrer"&gt;https://stack.cardor.dev/heimdall&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/enmanuelmag/heimdall-mcp" rel="noopener noreferrer"&gt;https://github.com/enmanuelmag/heimdall-mcp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check the repo for more feature planned on roadmap 🚀&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>agents</category>
      <category>claude</category>
      <category>ai</category>
    </item>
    <item>
      <title>Heimdall MCP: Add OpenTelemetry tracing to any MCP server without touching its code</title>
      <dc:creator>Enmanuel Magallanes Pinargote</dc:creator>
      <pubDate>Sat, 09 May 2026 14:03:48 +0000</pubDate>
      <link>https://forem.com/enmanuelmag/heimdall-mcp-add-opentelemetry-tracing-to-any-mcp-server-without-touching-its-code-4ae6</link>
      <guid>https://forem.com/enmanuelmag/heimdall-mcp-add-opentelemetry-tracing-to-any-mcp-server-without-touching-its-code-4ae6</guid>
      <description>&lt;p&gt;If you've been building with MCP servers lately, you've probably hit this wall: something goes wrong (or just feels slow) and you have zero visibility into what actually happened.&lt;/p&gt;

&lt;p&gt;Which tool did Claude call? What input did it send? Did the server error silently? How long did it take?&lt;/p&gt;

&lt;p&gt;That's the gap Heimdall fills.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Heimdall?
&lt;/h2&gt;

&lt;p&gt;Heimdall is a transparent proxy for MCP servers. It sits between your MCP client (Claude Desktop, OpenCode, Cursor, or any other) and your MCP server — local or remote — and records every interaction as an OpenTelemetry span.&lt;/p&gt;

&lt;p&gt;No modifications to your server. No SDK to integrate. No infra to run. Just wrap your existing server and start seeing what's happening inside.&lt;/p&gt;

&lt;p&gt;The name comes from Norse mythology: Heimdall is the guardian of the Bifrost bridge, with the ability to see and hear everything that crosses between worlds. That's exactly the role this proxy plays.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem in one screenshot
&lt;/h2&gt;

&lt;p&gt;You're running a Claude agent that orchestrates 5+ MCP tools. One of them is consistently slow. Another occasionally returns empty results. You have no way to know which one, when, or why — unless you dig into logs manually.&lt;/p&gt;

&lt;p&gt;With Heimdall, every tool call becomes a structured span:&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp.tool.call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"attributes"&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;"gen_ai.tool.name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"search_documents"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mcp.duration_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;843&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mcp.status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ok"&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;"events"&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="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"body"&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;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"quarterly report"&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;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"response"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"body"&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;"results"&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="err"&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;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;Stored. Queryable. Yours.&lt;/p&gt;




&lt;h2&gt;
  
  
  Zero config wrapping via mcp.json
&lt;/h2&gt;

&lt;p&gt;The easiest way to use Heimdall requires zero access to the server's source code. Just install it globally and update your &lt;code&gt;mcp.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @cardor/heimdall-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt; — your current config:&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;"my-server"&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;"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;"node"&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;"my-server.js"&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;&lt;strong&gt;After&lt;/strong&gt; — wrapped with Heimdall:&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;"my-server"&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;"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;"heimdall"&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="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--store"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sqlite://~/.heimdall/traces.db"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-server.js"&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;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;That's it. Restart your client and every tool call starts being recorded. The &lt;code&gt;--&lt;/code&gt; separator tells Heimdall where its args end and the real server command begins.&lt;/p&gt;

&lt;p&gt;For remote servers (HTTP or SSE), it's just as simple:&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;"remote-server"&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;"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;"heimdall"&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="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--store"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"postgres://user:pass@localhost/mydb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--target"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://my-remote-mcp.com/sse"&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;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;Heimdall exposes stdio to your client, talks HTTP/SSE to the real server, and intercepts everything in between.&lt;/p&gt;




&lt;h2&gt;
  
  
  What gets recorded
&lt;/h2&gt;

&lt;p&gt;Heimdall captures all standard MCP events:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;What's recorded&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tools/call&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tool name, input args, response, duration, status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tools/list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Available tools and their schemas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;resources/read&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URI, MIME type, response size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;prompts/get&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt name, arguments, rendered output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;initialize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Client/server versions, negotiated capabilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;shutdown&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Total session duration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every span follows the &lt;a href="https://opentelemetry.io/docs/specs/semconv/gen-ai/mcp/" rel="noopener noreferrer"&gt;OpenTelemetry gen_ai.* semantic conventions&lt;/a&gt;,&lt;br&gt;
so if you later want to send traces to Jaeger, Honeycomb, or Grafana Tempo, the data is already well-formed.&lt;/p&gt;


&lt;h2&gt;
  
  
  Storage options
&lt;/h2&gt;

&lt;p&gt;Pick the backend that fits your setup:&lt;/p&gt;
&lt;h3&gt;
  
  
  SQLite — local file, zero infra
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--store&lt;/span&gt; sqlite://./traces.db
&lt;span class="c"&gt;# or with absolute path&lt;/span&gt;
&lt;span class="nt"&gt;--store&lt;/span&gt; sqlite:///Users/you/.heimdall/traces.db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Best for: local development, single-machine setups, quick debugging sessions.&lt;/p&gt;

&lt;p&gt;Uses &lt;code&gt;@libsql/client&lt;/code&gt; under the hood — pure WASM, no native bindings, no node-gyp.&lt;/p&gt;
&lt;h3&gt;
  
  
  Postgres — your existing database
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--store&lt;/span&gt; postgres://user:password@localhost:5432/mydb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Best for: production environments, teams sharing observability data,&lt;br&gt;
long-term storage with SQL queries across multiple servers.&lt;/p&gt;
&lt;h3&gt;
  
  
  MySQL
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--store&lt;/span&gt; mysql://user:password@localhost:3306/mydb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Same use case as Postgres. Pick whichever you already run.&lt;/p&gt;

&lt;p&gt;All three use &lt;a href="https://orm.drizzle.team/" rel="noopener noreferrer"&gt;drizzle-orm&lt;/a&gt; with a shared schema, so the query experience is consistent regardless of which backend you choose.&lt;/p&gt;


&lt;h2&gt;
  
  
  Using it as a TypeScript library
&lt;/h2&gt;

&lt;p&gt;If you have access to your server's code and want tighter integration, Heimdall ships as a fully-typed TypeScript library with a fluent builder API:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;McpProxy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heimdall-mcp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;proxy&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;McpProxy&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inbound&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stdio&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="nf"&gt;outbound&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&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;http://localhost:3001&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="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sqlite://./traces.db&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="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also plug in custom interceptors — for example, to redact sensitive fields before they're persisted, or to add your own business logic on top of the tracing:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;McpProxy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Interceptor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heimdall-mcp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Custom interceptor: redact API keys from tool inputs before storing&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redactSecrets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Interceptor&lt;/span&gt; &lt;span class="o"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;redact-secrets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;intercept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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;sanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;redactSensitiveFields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sanitized&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;proxy&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;McpProxy&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inbound&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stdio&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="nf"&gt;outbound&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&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;http://localhost:3001&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="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgres://user:pass@host/db&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="nf"&gt;intercept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;redactSecrets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Design goals
&lt;/h2&gt;

&lt;p&gt;A few decisions worth calling out:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No native dependencies.&lt;/strong&gt; SQLite runs via WASM (&lt;code&gt;@libsql/client&lt;/code&gt;), Postgres via the pure-JS &lt;code&gt;postgres&lt;/code&gt; package, MySQL via &lt;code&gt;mysql2&lt;/code&gt;. You can install and run Heimdall in any Node 22+ environment without compilation steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No policy opinions.&lt;/strong&gt; Heimdall doesn't block, approve, or modify tool calls. It only observes and records. If you need access control or rate limiting, pair it with a tool like &lt;a href="https://github.com/behrensd/mcpwall" rel="noopener noreferrer"&gt;mcpwall&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transport mixing.&lt;/strong&gt; Your client connects via stdio. Your server can be stdio, HTTP, or SSE. Heimdall bridges them transparently — useful for wrapping local CLI servers behind an HTTP interface without touching their code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @cardor/heimdall-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: &lt;a href="https://github.com/enmanuelmag/heimdall-mcp" rel="noopener noreferrer"&gt;github.com/enmanuelmag/heimdall-mcp&lt;/a&gt;&lt;br&gt;
Website: &lt;a href="https://heimdall-mcp.cardor.dev" rel="noopener noreferrer"&gt;https://heimdall-mcp.cardor.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try it out, I'd love to hear what you think — especially feedback on the interceptor API design and the OTel schema. Open an issue or find me on X [&lt;a class="mentioned-user" href="https://dev.to/enmanuelmag"&gt;@enmanuelmag&lt;/a&gt;].&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>opentelemetry</category>
      <category>devtools</category>
      <category>agents</category>
    </item>
    <item>
      <title>I got tired of AI agents roaming my codebase — so I built a harness layer</title>
      <dc:creator>Enmanuel Magallanes Pinargote</dc:creator>
      <pubDate>Wed, 06 May 2026 00:55:08 +0000</pubDate>
      <link>https://forem.com/enmanuelmag/i-got-tired-of-ai-agents-roaming-my-codebase-so-i-built-a-harness-layer-1o88</link>
      <guid>https://forem.com/enmanuelmag/i-got-tired-of-ai-agents-roaming-my-codebase-so-i-built-a-harness-layer-1o88</guid>
      <description>&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Every time I open Claude Code or any MCP-compatible AI tool, the same thing happens: the agent starts working, does &lt;em&gt;something&lt;/em&gt;, and I have no idea what it changed, what it tried, or why it got stuck. Across sessions, it's even worse — the agent has no memory of what was already attempted.&lt;/p&gt;

&lt;p&gt;If you want multiple agents working in parallel or in sequence, good luck coordinating them without race conditions, double work, or conflicting changes.&lt;/p&gt;

&lt;p&gt;I wanted something I could actually trust to run on my codebase.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@cardor/agent-harness-kit" rel="noopener noreferrer"&gt;&lt;strong&gt;agent-harness-kit&lt;/strong&gt;&lt;/a&gt; (&lt;code&gt;ahk&lt;/code&gt;) is a scaffolding layer for structured multi-agent workflows. One command drops it into any project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ahk init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;t creates a local MCP server (runs on stdio, no ports needed), a SQLite database, a task backlog, a health gate, and four agent definition files.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The 4-agent workflow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Lead → Explorer → Builder → Reviewer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lead&lt;/strong&gt; decomposes the task into a plan. Never reads source files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explorer&lt;/strong&gt; maps the codebase. Never writes files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Builder&lt;/strong&gt; implements. Only writes to &lt;code&gt;writablePaths&lt;/code&gt; you define.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewer&lt;/strong&gt; checks acceptance criteria. Runs health check before approving.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each role has its own Markdown file you can customize. They're created once and never overwritten.&lt;/p&gt;

&lt;h3&gt;
  
  
  Atomic task claiming
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;claim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// SQLite transaction — no double-work possible&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two agents can't grab the same task. The second one gets &lt;code&gt;task_already_claimed&lt;/code&gt; and moves on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Health gate
&lt;/h3&gt;

&lt;p&gt;Before any agent starts or closes a task, it runs your &lt;code&gt;health.sh&lt;/code&gt;:&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;#!/usr/bin/env bash&lt;/span&gt;
npm &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
curl &lt;span class="nt"&gt;-sf&lt;/span&gt; http://localhost:3000/health &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"All checks passed."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it exits with anything other than 0, the task stays open. You define what "healthy" means for your project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full audit trail
&lt;/h3&gt;

&lt;p&gt;Every action is recorded:&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="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;             &lt;span class="c1"&gt;// start&lt;/span&gt;
&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;files_modified&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;src/auth.ts, src/routes/login.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;result&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;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;summary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;// close&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ahk export --json&lt;/code&gt; dumps the full history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provider-agnostic
&lt;/h2&gt;

&lt;p&gt;Works with Claude Code today. Moving to OpenCode? One command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ahk migrate &lt;span class="nt"&gt;--to&lt;/span&gt; opencode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your task history, agent definitions, and config stay intact.&lt;/p&gt;

&lt;h2&gt;
  
  
  No cloud, no native deps
&lt;/h2&gt;

&lt;p&gt;Everything lives in &lt;code&gt;.harness/harness.db&lt;/code&gt; (SQLite, gitignored). Uses &lt;code&gt;node:sqlite&lt;/code&gt; built-in — no &lt;code&gt;node-gyp&lt;/code&gt;, no native compilation.&lt;/p&gt;

&lt;p&gt;Requirements: Node ≥ 22 or Bun.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ahk init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interactive setup. Asks for your project name, AI provider, docs path, and an optional first task. Creates everything in under a minute.&lt;/p&gt;




&lt;p&gt;GitHub: &lt;a href="https://github.com/enmanuelmag/agent-harness-kit" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;br&gt;
npm: &lt;code&gt;@cardor/agent-harness-kit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I'd love feedback on the health gate design and the atomic claiming approach — those were the trickiest parts to get right.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>tooling</category>
      <category>agents</category>
    </item>
    <item>
      <title>Security Logger</title>
      <dc:creator>Enmanuel Magallanes Pinargote</dc:creator>
      <pubDate>Tue, 16 Jan 2024 13:56:10 +0000</pubDate>
      <link>https://forem.com/enmanuelmag/security-logger-kba</link>
      <guid>https://forem.com/enmanuelmag/security-logger-kba</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Disclaimer: The only real data in this site was hidden to protect the privacy of the user. The rest of the data is fake.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This project is build with ViteJs and ReactJs, the main idea is to create a web app and also a native desktop app to register new visitors on urbanization, the web app is for the directive (i.e urbanization president) and the desktop app is for the security guard.&lt;/p&gt;

&lt;p&gt;The app allow to create user three types of user:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Admin: Can create new users and also can see all the logs&lt;/li&gt;
  &lt;li&gt;Security guard: Can create, read and update new logs&lt;/li&gt;
  &lt;li&gt;Guest: Can only read the logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The records are stored first in the &lt;strong&gt;IndexDB&lt;/strong&gt; and then are synced with the Firestore database, the app also has a &lt;strong&gt;offline mode&lt;/strong&gt;, so the user can use the app without internet connection and when the connection is available the app will &lt;strong&gt;sync the data with the database&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The records could be filtered by visitor name, visitor ID, date, and also by the house number, the app also has a &lt;strong&gt;search bar&lt;/strong&gt; that allow to search by name quickly. Also, you can exported the entire records of filtered records to a XLSX file or a PDF file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep learning integration
&lt;/h2&gt;

&lt;p&gt;This app also has a integration with a other personal project, that consist in a &lt;strong&gt;Deep Learning model&lt;/strong&gt; that can detect and extract the text of visitor name and ID from a image. So the app can extract the image from the security camera that focus the visitor ID and the model will extract the text and fill the form with the data. The information can be edited by the security guard too. The model is build with &lt;strong&gt;Tensorflow&lt;/strong&gt; based on a little version of YOLOv5.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fyv5w1e7jbyy6t8moct0c.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.amazonaws.com%2Fuploads%2Farticles%2Fyv5w1e7jbyy6t8moct0c.png" alt="Web app view" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Currently the app is only available in Spanish, and deploy or installation is only available previos contact and agreement. The app will have more tools powered by AI to detect and extract more information as vehicle plate, visitor face, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech used
&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Firebase (Auth, Firestore)&lt;/li&gt;
  &lt;li&gt;ReactJs, React Router, React Context&lt;/li&gt;
  &lt;li&gt;React-Query&lt;/li&gt;
  &lt;li&gt;Zod (Validation)&lt;/li&gt;
  &lt;li&gt;Tensorflow&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ia</category>
      <category>webdev</category>
      <category>security</category>
    </item>
    <item>
      <title>Budgetfy</title>
      <dc:creator>Enmanuel Magallanes Pinargote</dc:creator>
      <pubDate>Tue, 16 Jan 2024 13:50:59 +0000</pubDate>
      <link>https://forem.com/enmanuelmag/budgetfy-ii7</link>
      <guid>https://forem.com/enmanuelmag/budgetfy-ii7</guid>
      <description>&lt;p&gt;In this web app you can create budget to define your expenses and incomes as unique events or recurrent events as you usually create events for a calendar, in order to plan your &lt;strong&gt;budget through slice of time that you can define&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Budget view has show you one card per moth with three sections. The first section is Category summary, it show the total amount of money for each category that you have defined.&lt;/p&gt;

&lt;p&gt;The second section is Timeline, that show the following information:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Timeline: show all the events for that month (expenses and incomes) and the estimated balance for that month. Also if you clic on the event you can mark it as paid (for expenses) or received (for incomes). This will update the balance.&lt;/li&gt;
  &lt;li&gt;Balance: section you will see all the money available without expenses for that month and the monthly balance, it mean the difference between the incomes and the expenses for that specific month.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally the third section is the &lt;strong&gt;Timeline plot&lt;/strong&gt;, that show the evolution of the incomes, expenses and balance for the the defined range of time on the budget creation.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fe575gwljmpr8up8y713y.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.amazonaws.com%2Fuploads%2Farticles%2Fe575gwljmpr8up8y713y.png" alt="Web app view" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can create a account for free using a email and password or &lt;strong&gt;Google account&lt;/strong&gt;, then you can create your budget and start to add your incomes and expenses. Visit the &lt;a rel="noopener noreferrer" href="https://budgetfy.cardor.dev"&gt;website&lt;/a&gt; to see more details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech used
&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Firebase (Auth, Firestore)&lt;/li&gt;
  &lt;li&gt;ReactJs, React Router, React Context&lt;/li&gt;
  &lt;li&gt;React-Query&lt;/li&gt;
  &lt;li&gt;Highcharts&lt;/li&gt;
  &lt;li&gt;Zod (Validation)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>finances</category>
      <category>firebase</category>
      <category>webdev</category>
    </item>
    <item>
      <title>IAflow</title>
      <dc:creator>Enmanuel Magallanes Pinargote</dc:creator>
      <pubDate>Tue, 16 Jan 2024 13:42:19 +0000</pubDate>
      <link>https://forem.com/enmanuelmag/iaflow-j3m</link>
      <guid>https://forem.com/enmanuelmag/iaflow-j3m</guid>
      <description>&lt;p&gt;This library help to create models with identifiers, checkpoints, logs and metadata automatically, in order to make the training process more efficient and traceable.&lt;/p&gt;

&lt;p&gt;For install the library, you can use pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;iaflow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can create the &lt;code&gt;ia_make&lt;/code&gt; with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ia_maker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;IAFlow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;models_folder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./models&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;checkpoint_params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;monitor&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;val_loss&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;save_best_only&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;save_weights_only&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;tensorboard_params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;histogram_freq&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;write_graph&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;write_images&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&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;And then you can add the model with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;model_1_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ia_maker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;model_1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;model_params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input_shape&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;load_model_params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="n"&gt;compile_params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;metrics&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;accuracy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;optimizer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;adam&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;loss&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mse&lt;/span&gt;&lt;span class="sh"&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;Finally, you can train the model with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ia_maker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;model_1_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;epochs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;dataset_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dataset_1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This library has integration with &lt;a href="https://github.com/enmanuelmag/notify_function" rel="noopener noreferrer"&gt;Notifier Status Function&lt;/a&gt;, so you can send notifications to Telegram when the training process is finished. To check how to use it and more feature as managing Dataset and Models with the library, you can check the &lt;a rel="noopener noreferrer" href="https://iaflow.cardor.dev"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech used
&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Python&lt;/li&gt;
  &lt;li&gt;Tensorflow&lt;/li&gt;
  &lt;li&gt;Telegram API&lt;/li&gt;
  &lt;li&gt;Notifier Status Function (personal lib)&lt;/li&gt;
  &lt;li&gt;Webhooks&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>deeplearning</category>
      <category>tensorflow</category>
      <category>python</category>
    </item>
    <item>
      <title>Mi Horario Web (MHW)</title>
      <dc:creator>Enmanuel Magallanes Pinargote</dc:creator>
      <pubDate>Tue, 16 Jan 2024 13:39:51 +0000</pubDate>
      <link>https://forem.com/enmanuelmag/mi-horario-web-mhw-epp</link>
      <guid>https://forem.com/enmanuelmag/mi-horario-web-mhw-epp</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Disclaimer: Currently we are working on improve and update of NodeJS version of the Lambda functions, so the website is not working properly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is an online timetable generator for students of the ESPOL University. It allows to obtain information of the planned courses, statistics of the teacher in charge and to generate options of schedules according to the courses and selected parallels.&lt;/p&gt;

&lt;p&gt;The project is developed in React and Material-UI, MongoDB as main database for all courses information and FireStore as realtime database for update de progress of schedule generation on the client side.&lt;/p&gt;

&lt;p&gt;The steps to create schedules are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Select the subjects that you will take.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Selected the course numbers that you would like to choose. In this section you can also see the course's teacher, his/her qualification and student's opinios about him/her.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The you can execute the schedules generations, the process can take a while, so a progress bar is show up with the progress update in real time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally you can see all the options of schedules with out conflict on hours class or exams. You can also download the information as a table (just course numbers and subject) or download the calendar view.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&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.amazonaws.com%2Fuploads%2Farticles%2Fj0xknwh4d4ddq3f05loe.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.amazonaws.com%2Fuploads%2Farticles%2Fj0xknwh4d4ddq3f05loe.png" alt="View of MHW web app" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech used
&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;React&lt;/li&gt;
  &lt;li&gt;Material-UI&lt;/li&gt;
  &lt;li&gt;MongoDB&lt;/li&gt;
  &lt;li&gt;Highcharts&lt;/li&gt;
  &lt;li&gt;AWS State Machine&lt;/li&gt;
  &lt;li&gt;AWS Lambda Functions&lt;/li&gt;
  &lt;li&gt;Firebase (Auth, Firestore)&lt;/li&gt;
  &lt;li&gt;Github Actions&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>firebase</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Notifier Function Status</title>
      <dc:creator>Enmanuel Magallanes Pinargote</dc:creator>
      <pubDate>Tue, 16 Jan 2024 13:34:38 +0000</pubDate>
      <link>https://forem.com/enmanuelmag/notifier-function-status-4bd6</link>
      <guid>https://forem.com/enmanuelmag/notifier-function-status-4bd6</guid>
      <description>&lt;p&gt;This library provides a decorator to show a toast in your screen and if you setup, send a email when your function end. All that you need to do is use a decorator and some specific parameters, like in the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;notifier&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;notify&lt;/span&gt;

&lt;span class="c1"&gt;# Send a message to telegram
&lt;/span&gt;&lt;span class="nd"&gt;@notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;your_api_token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chat_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;your_chat_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;your_function&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hello World!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another way is by manually calling the function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;notifier&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Notifier&lt;/span&gt;

&lt;span class="n"&gt;notifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Notifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Execution for&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;api_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;your_api_token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
  &lt;span class="n"&gt;chat_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;your_chat_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hello World &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# You can also override all the previous parameters,
# on the constructor, by passing them to the function
&lt;/span&gt;&lt;span class="nf"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Execution done&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Finished!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check these features a more detailed on the &lt;a rel="noopener noreferrer" href="https://github.com/enmanuelmag/notify_function"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech used
&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Python&lt;/li&gt;
  &lt;li&gt;Telegram API&lt;/li&gt;
  &lt;li&gt;Discord webhooks&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
