<?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: Murthy G</title>
    <description>The latest articles on DEV Community by Murthy G (@murthyg).</description>
    <link>https://dev.to/murthyg</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4187695%2F6788943b-726c-41e3-bdc7-ccb1b47fc3d6.png</url>
      <title>DEV Community: Murthy G</title>
      <link>https://dev.to/murthyg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/murthyg"/>
    <language>en</language>
    <item>
      <title>My AI agent read a web page on Monday and got tricked on Tuesday. So I built a ledger.</title>
      <dc:creator>Murthy G</dc:creator>
      <pubDate>Sat, 17 Oct 2026 00:11:42 +0000</pubDate>
      <link>https://dev.to/murthyg/my-ai-agent-read-a-web-page-on-monday-and-got-tricked-on-tuesday-so-i-built-a-ledger-33ck</link>
      <guid>https://dev.to/murthyg/my-ai-agent-read-a-web-page-on-monday-and-got-tricked-on-tuesday-so-i-built-a-ledger-33ck</guid>
      <description>&lt;p&gt;I've been working on a side project called ADTL (Agent Data Trust Labels) for a while now. It just hit v0.4, and the new part is something I didn't expect to need when I started, so I wanted to write up how I got there.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/probita-ai/adtl" rel="noopener noreferrer"&gt;&lt;/a&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F31duub9ecdvcbyi0645n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F31duub9ecdvcbyi0645n.png" alt=" " width="800" height="793"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Quick disclaimer up front: it's a personal open-source project, it's experimental, and it's built on MCP proposals that are still drafts. Please don't put it in front of anything important yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;AI agents read a lot of stuff they shouldn't trust. GitHub issues written by strangers, web pages, emails. And the same agent usually has tools that can send data out: post a comment, send a mail, fetch a URL.&lt;/p&gt;

&lt;p&gt;That combination is the whole problem. In 2025 Invariant Labs showed it against GitHub's official MCP server: a public issue contained a hidden instruction telling the agent to read a private repo and post its contents as a comment. The agent did it.&lt;/p&gt;

&lt;p&gt;You can tell the model "ignore instructions in tool results", and good models often do. But "often" isn't a security property. I wanted something that doesn't depend on the model noticing the attack.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea: label everything, check before acting
&lt;/h2&gt;

&lt;p&gt;ADTL is an MCP gateway. Your agent (Claude Code, Cursor, anything that speaks MCP) talks to ADTL, and ADTL talks to your real MCP servers.&lt;/p&gt;

&lt;p&gt;Every result that comes back gets a label with two parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;integrity&lt;/strong&gt;: can this be trusted to influence what the agent does? (&lt;code&gt;trusted&lt;/code&gt;, &lt;code&gt;unknown&lt;/code&gt;, &lt;code&gt;untrusted&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;confidentiality&lt;/strong&gt;: who may see it? (&lt;code&gt;public&lt;/code&gt;, &lt;code&gt;private&lt;/code&gt;, &lt;code&gt;secret&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The session keeps a running label that only gets stricter. Read one untrusted issue and the session is untrusted until it ends. Read one private file and it's private.&lt;/p&gt;

&lt;p&gt;Then before every tool call, a policy checks the session label against where the tool can send data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no-public-write-after-untrusted&lt;/span&gt;
      &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;session.integrity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;untrusted&lt;/span&gt;
        &lt;span class="na"&gt;tool.egress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;public&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;unknown&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;then&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ask&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;private-stays-private&lt;/span&gt;
      &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;session.confidentiality&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;private&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;tool.egress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;public&lt;/span&gt;
      &lt;span class="na"&gt;then&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;block&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So in the GitHub attack: the agent reads the issue (untrusted), reads the private file (private), then tries &lt;code&gt;post_comment&lt;/code&gt; (public). That's blocked, no matter what the model thinks it's doing.&lt;/p&gt;

&lt;p&gt;"Ask" can send a push notification to your phone with Allow / Deny buttons (through &lt;a href="https://ntfy.sh" rel="noopener noreferrer"&gt;ntfy&lt;/a&gt;). For Claude Code's built-in tools (more on that below) it shows Claude Code's normal permission prompt instead.&lt;/p&gt;

&lt;p&gt;The labels use MCP's draft &lt;a href="https://github.com/modelcontextprotocol/experimental-ext-tool-annotations" rel="noopener noreferrer"&gt;trust-annotations&lt;/a&gt; format, and the checks run as interceptors from the &lt;a href="https://github.com/modelcontextprotocol/experimental-ext-interceptors" rel="noopener noreferrer"&gt;SEP-2624 draft&lt;/a&gt;. I didn't want to invent a new format if one was already being worked on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying it on your own setup
&lt;/h2&gt;

&lt;p&gt;If you already use MCP servers in Claude Code, this imports them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;-y&lt;/span&gt; @adtl/gateway init &lt;span class="nt"&gt;-c&lt;/span&gt; adtl.yaml &lt;span class="nt"&gt;--from&lt;/span&gt; .mcp.json           &lt;span class="c"&gt;# shows what it would do&lt;/span&gt;
npx &lt;span class="nt"&gt;-y&lt;/span&gt; @adtl/gateway init &lt;span class="nt"&gt;-c&lt;/span&gt; adtl.yaml &lt;span class="nt"&gt;--from&lt;/span&gt; .mcp.json &lt;span class="nt"&gt;--write&lt;/span&gt;   &lt;span class="c"&gt;# does it (keeps .bak copies)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It moves your servers into &lt;code&gt;adtl.yaml&lt;/code&gt;, writes a starting rule for every tool (there are presets for GitHub, filesystem and fetch, and strict defaults for everything else), and points &lt;code&gt;.mcp.json&lt;/code&gt; at the gateway. Then you go through the rules marked &lt;code&gt;review&lt;/code&gt; and loosen what makes sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap I had to close first: built-in tools
&lt;/h2&gt;

&lt;p&gt;The gateway only sees MCP traffic. Claude Code also has its own tools: &lt;code&gt;WebFetch&lt;/code&gt;, &lt;code&gt;Bash&lt;/code&gt;, &lt;code&gt;Read&lt;/code&gt;. A web page fetched with &lt;code&gt;WebFetch&lt;/code&gt; never went through ADTL, so it never tainted the session.&lt;/p&gt;

&lt;p&gt;So v0.3.1 added a Claude Code hook. It runs before and after every tool call, labels the output, and checks the same policy:&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;"hooks"&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;"PreToolUse"&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;"matcher"&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="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&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;"npx -y @adtl/gateway hook --config /path/to/adtl.yaml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"onFailure"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"block"&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;"PostToolUse"&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;"matcher"&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="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&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;"npx -y @adtl/gateway hook --config /path/to/adtl.yaml"&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;The gateway and the hook share one session label (Claude Code passes the same session id to both), so a &lt;code&gt;WebFetch&lt;/code&gt; taints the session for a GitHub post through the gateway, and the other way around. If ADTL would allow a call, the hook just stays quiet, so your normal Claude Code permissions still apply. ADTL never approves anything on its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then I thought about Tuesday
&lt;/h2&gt;

&lt;p&gt;With all that working, I tried this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monday&lt;/strong&gt;: the agent fetches a web page. Hidden in it is an instruction for AI agents. The agent saves the page into &lt;code&gt;notes.md&lt;/code&gt;. The session is untrusted, but it doesn't do anything bad, so nothing gets blocked. The session ends.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tuesday&lt;/strong&gt;: you start a fresh session and ask it to look at yesterday's notes. It reads &lt;code&gt;notes.md&lt;/code&gt;, finds the instruction, and posts the attacker's link as a public comment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ADTL let it through. Tuesday's session never saw the web page, and reading a file from your own project is trusted. Every individual step looked fine.&lt;/p&gt;

&lt;p&gt;Session labels end when the session ends. The data doesn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ledger
&lt;/h2&gt;

&lt;p&gt;v0.4 adds a label ledger. The idea is simple: labels should follow the text, not the process.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When a session that has read untrusted or private data &lt;strong&gt;sends text anywhere&lt;/strong&gt; (writes a file, stores something in a memory server, hands a prompt to a sub-agent, even lines a shell command appended to a file), ADTL hashes that text and stores the hashes with the session's label.&lt;/li&gt;
&lt;li&gt;When &lt;strong&gt;any result comes back&lt;/strong&gt;, in any session, ADTL hashes it the same way and looks it up. If it matches, the old label comes back too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It hashes the whole text, each paragraph and each line (normalized for case and whitespace, and skipping short lines like &lt;code&gt;}&lt;/code&gt;), so a paragraph copied into a bigger file still matches. If the agent rewrote the text in its own words, that's fine too, because whatever a tainted session writes is recorded with its label.&lt;/p&gt;

&lt;p&gt;Clean sessions record nothing, so the ledger stays small and you don't get your whole codebase flagged.&lt;/p&gt;

&lt;p&gt;It's one SQLite file (&lt;code&gt;~/.adtl/ledger.db&lt;/code&gt;), using Node's built-in &lt;code&gt;node:sqlite&lt;/code&gt;. Entries are hash-chained, so &lt;code&gt;adtl-gateway ledger verify&lt;/code&gt; can tell if someone edited or deleted entries. If you've checked a file and want to trust it again, you can clear its label:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adtl-gateway ledger declassify notes.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hook asks you before the agent can run that itself, and it won't let the agent edit the ledger file directly.&lt;/p&gt;

&lt;p&gt;There's a demo that runs the real processes twice, once without the ledger and once with it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/probita-ai/adtl &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;adtl
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run demo:ledger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without the ledger, Tuesday's read of &lt;code&gt;notes.md&lt;/code&gt; comes back trusted and the link gets posted. With it, the read comes back as &lt;code&gt;untrusted, written by session mon&lt;/code&gt;, and the post is stopped.&lt;/p&gt;

&lt;p&gt;It works the same with real Claude Code across two separate sessions, which was a relief.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it doesn't do
&lt;/h2&gt;

&lt;p&gt;I'd rather be clear about this than have someone find out the hard way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Matching is by content hash. If a &lt;strong&gt;person&lt;/strong&gt; rewrites the text by hand, or it's binary, or an MCP server returns it base64-encoded, it won't match.&lt;/li&gt;
&lt;li&gt;The ledger is hash-chained but &lt;strong&gt;not signed&lt;/strong&gt; yet. An agent with shell access could rebuild the whole file, and &lt;code&gt;verify&lt;/code&gt; wouldn't catch that. The hook makes it harder, not impossible.&lt;/li&gt;
&lt;li&gt;The hook checks shell commands against the policy, but once a command is allowed, the shell can do anything. Keep a sandbox. ADTL is one layer, not the only one.&lt;/li&gt;
&lt;li&gt;It only knows about tools you've described. Unknown tools get strict defaults, which is safe but means more "ask" prompts until you write rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Signed session receipts: a tamper-evident summary of every call in a session, so you can check what an agent claims it did against what actually happened.&lt;/li&gt;
&lt;li&gt;Running the GitHub demo against the real &lt;code&gt;github-mcp-server&lt;/code&gt;, not just my fake one.&lt;/li&gt;
&lt;li&gt;Carrying labels across agents (A2A), with signed ledger entries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've also proposed the "ask a human" idea to the MCP Interceptors working group as a spec PR. It's under review, so we'll see where it goes.&lt;/p&gt;

&lt;p&gt;If you try it, I'd really like to hear what breaks. Issues are open on &lt;a href="https://github.com/probita-ai/adtl" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, and some are marked &lt;code&gt;help wanted&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>mcp</category>
      <category>security</category>
    </item>
    <item>
      <title>Structured Workflows for AI Agents: TypeScript, Python, and MCP</title>
      <dc:creator>Murthy G</dc:creator>
      <pubDate>Fri, 16 Oct 2026 02:10:53 +0000</pubDate>
      <link>https://dev.to/murthyg/how-to-build-reliable-work-handoffs-between-ai-agents-in-typescript-1neh</link>
      <guid>https://dev.to/murthyg/how-to-build-reliable-work-handoffs-between-ai-agents-in-typescript-1neh</guid>
      <description>&lt;h1&gt;
  
  
  Structured Workflows for AI Agents: TypeScript, Python, and MCP
&lt;/h1&gt;

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

&lt;p&gt;AI agents can complete tasks, generate code, and collaborate with other agents. But as workflows become more complex, a question becomes increasingly important:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do we make work between AI agents structured, traceable, and easier to hand off?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Passing messages between agents is straightforward. Keeping track of requirements, decisions, results, validation, and unfinished work is much harder.&lt;/p&gt;

&lt;p&gt;I've been working on an open-source project to explore a structured approach to this problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea: Treat agent work as something structured
&lt;/h2&gt;

&lt;p&gt;Imagine a research agent handing its findings to a coding agent.&lt;/p&gt;

&lt;p&gt;The coding agent needs more than a summary. It may need the original requirements, constraints, decisions already made, results, unresolved questions, and the next steps.&lt;/p&gt;

&lt;p&gt;Without a consistent format, every application ends up inventing its own conventions.&lt;/p&gt;

&lt;p&gt;My project explores using &lt;strong&gt;Work Orders and Work Reports&lt;/strong&gt; to represent work and its outcomes in a structured, reusable way.&lt;/p&gt;

&lt;h2&gt;
  
  
  One project, multiple integrations
&lt;/h2&gt;

&lt;p&gt;I've published three entry points for developers working with different environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. TypeScript / JavaScript
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/agent-work-layer" rel="noopener noreferrer"&gt;&lt;code&gt;agent-work-layer&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The core package provides the foundation for structured work management, including schemas, validation, lifecycle management, and work handoffs.&lt;/p&gt;

&lt;p&gt;Install it:&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;agent-work-layer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. MCP integration
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/agent-work-layer-mcp" rel="noopener noreferrer"&gt;&lt;code&gt;agent-work-layer-mcp&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The MCP package makes supported work-management operations available through the Model Context Protocol, allowing compatible AI clients to interact with the work layer through MCP tools.&lt;/p&gt;

&lt;p&gt;Install it:&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;agent-work-layer-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project also explores an MCP gateway that can enforce work-order-specific tool permissions and record tool-call evidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Python implementation
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/probita-ai/agent-work-layer/tree/main/python" rel="noopener noreferrer"&gt;Agent Work Layer — Python implementation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For Python developers, the project also includes a Python implementation.&lt;/p&gt;

&lt;p&gt;This gives Python-based AI-agent applications a way to explore the same broader approach to structured work, without requiring the entire application to be written in TypeScript.&lt;/p&gt;

&lt;p&gt;Check the Python directory's README for its current installation instructions and supported features.&lt;/p&gt;

&lt;h2&gt;
  
  
  What could this look like in practice?
&lt;/h2&gt;

&lt;p&gt;Consider a multi-agent workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A coordinator defines a task and its constraints.&lt;/li&gt;
&lt;li&gt;A research agent investigates the problem.&lt;/li&gt;
&lt;li&gt;The agent records its results and unresolved questions.&lt;/li&gt;
&lt;li&gt;An implementation agent continues from the recorded work.&lt;/li&gt;
&lt;li&gt;Validation and lifecycle rules help track the outcome.&lt;/li&gt;
&lt;li&gt;MCP provides a standardized way for compatible clients to access supported operations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The objective is to make agent handoffs less dependent on ad hoc prompts and more dependent on explicit, inspectable work structures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I'm building this
&lt;/h2&gt;

&lt;p&gt;I want to explore how structured work definitions, validated reports, and controlled tool access can improve the reliability of AI-agent workflows.&lt;/p&gt;

&lt;p&gt;Rather than every project independently inventing its own work-order format, lifecycle rules, and handoff conventions, could these become reusable infrastructure?&lt;/p&gt;

&lt;p&gt;That's the problem I'm working on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explore the project
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;npm — TypeScript/JavaScript:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/agent-work-layer" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/agent-work-layer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;npm — MCP integration:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/agent-work-layer-mcp" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/agent-work-layer-mcp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub — Python implementation:&lt;/strong&gt; &lt;a href="https://github.com/probita-ai/agent-work-layer/tree/main/python" rel="noopener noreferrer"&gt;https://github.com/probita-ai/agent-work-layer/tree/main/python&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub — Main repository:&lt;/strong&gt; &lt;a href="https://github.com/probita-ai/agent-work-layer" rel="noopener noreferrer"&gt;https://github.com/probita-ai/agent-work-layer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;**pypi - &lt;a href="https://pypi.org/project/agent-work-layer/" rel="noopener noreferrer"&gt;https://pypi.org/project/agent-work-layer/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  I'd love your feedback
&lt;/h2&gt;

&lt;p&gt;If you're building AI agents, agent frameworks, or MCP integrations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do you structure work passed between agents?&lt;/li&gt;
&lt;li&gt;Do you use explicit task and result schemas?&lt;/li&gt;
&lt;li&gt;How do you control tool access for individual tasks?&lt;/li&gt;
&lt;li&gt;Would a common work-order and work-report model be useful across TypeScript and Python projects?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm interested in feedback, contributions, and real-world use cases from developers working on these problems.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>architecture</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
