---
title: AI coworkers in Slack need their own identity
description: MintMCP runs 40 to 50 AI agents in Slack, and none of them borrows a human's login. Notes on Jiquan Ngiam's talk about multiplayer threads, per-agent identity, permissions decided at runtime and memory silos.
date: 2026-10-07
tags: AI agents, Slack, MCP, agent identity, access control
---

MintMCP runs 40 to 50 AI agents in Slack. They have names (Max does sales, Leo marketing, Peter procurement), people message them like coworkers, and none of them borrows a human's login. Each agent is its own principal, with its own identity, its own allow list of tools and an audit trail at the gateway.

That's from [The Context ft MintMCP](https://www.youtube.com/watch?v=xLd-mLCN_t0), the Agentic AI Foundation's livestream, where Jiquan Ngiam, MintMCP's co-founder and CEO, showed how his team works with agents. My own [agent fleet talk](/talks/openclaw/) ends on the part a home setup gets to skip: monitoring, traceability, identity management. This talk is that part.

## Identity is the step from tool to teammate

JQ (that's what he goes by) maps AI adoption in four levels. The steps between them are the interesting part:

<div class="levels" role="group" aria-label="Four levels of AI adoption: chat, co-work, co-worker and the machine, with MCP data access, agent identity and software factories as the steps between them">
  <div class="level">
    <p class="level-num">L1</p>
    <h3>Chat</h3>
    <p>It advises.</p>
  </div>
  <div class="level-step"><span>MCP data access</span></div>
  <div class="level">
    <p class="level-num">L2</p>
    <h3>Co-work</h3>
    <p>It finishes tasks.</p>
  </div>
  <div class="level-step"><span>Agent identity</span></div>
  <div class="level level--focus">
    <p class="level-num">L3</p>
    <h3>Co-worker</h3>
    <p>It joins the team.</p>
  </div>
  <div class="level-step"><span>Software factories</span></div>
  <div class="level">
    <p class="level-num">L4</p>
    <h3>The machine</h3>
    <p>It builds the software.</p>
  </div>
</div>

Level 2 is where most of us are. You connect Claude Cowork or a similar tool to the CRM and ask for a forecast deck. It finishes hours of work, but it's single player.

Level 3 is an agent you don't open like an app. It sits in Slack or Teams, maybe with its own account and email address, and you message it the way you'd message a colleague. That's where agent identity, access control and agent-to-agent messages start to matter. Level 4 is the [software factory](/notes/software-factory-2022-to-lights-off.html) I wrote about last month.

## From single player to multiplayer

When I run Claude Code or Codex in a terminal, nobody else sees the session. Nobody can nudge it or pick up where it stopped. JQ calls that single-player AI.

In a Slack thread, several humans and several agents work in the same place, everyone sees what's happening, and anyone can step in. People adopt agents faster when they watch them work, and agents get a place to work with each other.

One number surprised me. On MintMCP's gateway, which handles over two million MCP and agent calls a day, the 20 most-installed connectors are everyday work tools: Google, Slack, Notion, Figma, HubSpot. Engineers came first, but non-engineers now save the most time. At Modern Treasury, agents save technical account managers hours per case.

<figure class="fd-fig ac-play" aria-labelledby="ac1-cap" data-desc="Single player and multiplayer. Single player is Claude Code or Codex in a terminal: nobody else sees the session, and nobody can nudge it or pick up where it stopped. Multiplayer is a Slack thread: several humans and several agents work in the same place, everyone sees what's happening, and anyone can step in. Below, three facts from MintMCP's gateway: it handles over two million MCP and agent calls a day; its 20 most-installed connectors are everyday work tools such as Google, Slack, Notion, Figma and HubSpot; and at Modern Treasury, agents save technical account managers hours per case.">
<div class="mp-two-row">
<div class="mp-two-card">
<p class="mp-two-who">Claude Code or Codex in a terminal</p>
<p class="mp-two-name">Single player</p>
<svg class="mp-two-art" viewBox="0 0 300 96" aria-hidden="true">
<circle class="mp-you-c" cx="34" cy="48" r="14"/>
<path class="mp-thin" d="M48,48 H84"/>
<rect class="mp-w" x="84" y="14" width="120" height="68"/><rect class="mp-wbar" x="84" y="14" width="120" height="8"/>
<rect class="mp-ln" x="94" y="32" width="70" height="4"/><rect class="mp-ln" x="94" y="42" width="50" height="4"/><rect class="mp-ln" x="94" y="52" width="62" height="4"/>
<rect class="mp-cur" x="94" y="62" width="6" height="10"/>
<path class="ac-wall" d="M230,8 V88"/>
<g class="ac-out"><circle cx="264" cy="28" r="11"/><circle cx="264" cy="68" r="11"/></g>
</svg>
<p class="mp-two-line">Nobody else sees the session</p>
<p class="mp-two-line">Nobody can nudge it or pick up where it stopped</p>
</div>
<p class="mp-two-vs" aria-hidden="true">vs</p>
<div class="mp-two-card mp-two-peter">
<p class="mp-two-who">A Slack thread</p>
<p class="mp-two-name">Multiplayer</p>
<svg class="mp-two-art" viewBox="0 0 300 96" aria-hidden="true">
<g class="mp-mates"><circle cx="26" cy="26" r="11"/><circle cx="26" cy="70" r="11"/></g>
<rect class="ac-sq" x="263" y="15" width="22" height="22"/><rect class="ac-sq" x="263" y="59" width="22" height="22"/>
<path class="mp-thin" d="M37,28 L78,36 M37,68 L78,60"/>
<path class="mp-thin mp-thin-agent" d="M263,28 L222,36 M263,68 L222,60"/>
<rect class="mp-w" x="78" y="6" width="144" height="84"/><rect class="mp-wbar" x="78" y="6" width="144" height="8"/>
<g class="ac-row-1"><circle class="ac-av" cx="92" cy="27" r="5"/><rect class="mp-ln" x="102" y="25" width="80" height="4"/></g>
<g class="ac-row-2"><rect class="ac-sq" x="87" y="40" width="10" height="10"/><rect class="mp-ln" x="102" y="43" width="104" height="4"/></g>
<g class="ac-row-3"><circle class="ac-av" cx="92" cy="63" r="5"/><rect class="mp-ln" x="102" y="61" width="60" height="4"/></g>
<g class="ac-row-4"><rect class="ac-sq" x="87" y="74" width="10" height="10"/><rect class="mp-ln" x="102" y="77" width="92" height="4"/></g>
</svg>
<p class="mp-two-line">Several humans and several agents, one place</p>
<p class="mp-two-line">Everyone sees it, anyone can step in</p>
</div>
</div>
<div class="mp-stats">
<p><b>2M+</b><span>MCP and agent calls a day through MintMCP's gateway</span></p>
<p><b>Top 20</b><span>connectors are everyday work tools: Google, Slack, Notion, Figma, HubSpot</span></p>
<p><b>Hours</b><span>saved per case for Modern Treasury's technical account managers</span></p>
</div>
<figcaption id="ac1-cap">JQ's single player and multiplayer, with the numbers he gave from MintMCP's gateway. Engineers came first; non-engineers now save the most time.</figcaption>
</figure>

## A Slack thread is a sandbox

The live demo was simple. He asked Leo, the marketing agent, which AAIF events they had been to and which were coming up. Leo opened a thread, ran bash and search, read Slack through the Slack MCP server, and streamed every step into the thread. Its answer included the livestream we were watching.

Here's the setup as he described it:

<div class="flow" role="group" aria-label="MintMCP's setup: a Slack thread starts a session in an ephemeral sandbox running a coding harness, which reaches every tool only through the MCP gateway">
  <div class="flow-node">
    <p class="flow-label">Front door</p>
    <h3>Slack thread</h3>
    <ul>
      <li>Humans and agents together</li>
      <li>One thread, one session</li>
      <li>Schedules wake agents too</li>
    </ul>
  </div>
  <div class="flow-link" aria-hidden="true"><span>→</span></div>
  <div class="flow-node flow-node--core">
    <p class="flow-label">Ephemeral sandbox</p>
    <h3>Coding harness</h3>
    <ul>
      <li>Claude, Codex or open harnesses</li>
      <li>Every step streamed to Slack</li>
      <li>Monitoring hooks · memory</li>
    </ul>
  </div>
  <div class="flow-link" aria-hidden="true"><span>⇄</span></div>
  <div class="flow-node">
    <p class="flow-label">The only way out</p>
    <h3>MCP gateway</h3>
    <ul>
      <li>The agent's own identity</li>
      <li>Tool and data allow lists</li>
      <li>Every call logged</li>
    </ul>
  </div>
</div>

My favourite example is from engineering. Dan is a coding agent on Claude, Sam is one on Codex. A human asks Dan for a change to their LLM gateway. Dan reads the codebase, proposes a plan and waits for an OK. He builds a first version and messages Sam, and the two review each other in the channel until they agree. Then Dan pings the human, the human reviews and merges, and the bot wakes up once more to ask whether the docs need an update.

<figure class="fd-fig ac-thread-fig" aria-labelledby="ac2-cap" data-desc="Dan and Sam, two coding agents, working in one Slack channel. A human asks Dan, a coding agent on Claude, for a change to their LLM gateway. Dan reads the codebase, proposes a plan and waits for an OK. Dan builds a first version and messages Sam, a coding agent on Codex. The two review each other in the channel until they agree. Dan pings the human, who reviews and merges. Then the bot wakes up once more to ask whether the docs need an update. The whole back-and-forth happens in a channel where people can watch and step in.">
<div class="ac-thread-in">
<ol class="ac-thread">
<li><b class="ac-who ac-who-h">human</b><span>Asks Dan for a change to their LLM gateway</span></li>
<li><b class="ac-who">Dan</b><span>Reads the codebase, proposes a plan, waits for an OK</span><i class="ac-tag">Claude</i></li>
<li><b class="ac-who">Dan</b><span>Builds a first version and messages Sam</span></li>
<li class="ac-pair"><b class="ac-who">Dan ⇄ Sam</b><span>Review each other until they agree</span><i class="ac-tag">Claude ⇄ Codex</i></li>
<li><b class="ac-who">Dan</b><span>Pings the human</span></li>
<li><b class="ac-who ac-who-h">human</b><span>Reviews and merges</span></li>
<li><b class="ac-who">bot</b><span>Wakes up once more: do the docs need an update?</span></li>
</ol>
</div>
<p class="mp-strip"><b>In the open</b><span>Every step sits in a channel where people can watch and step in.</span></p>
<figcaption id="ac2-cap">The engineering example, one message per row. Dan runs on Claude and Sam on Codex, so two model families review each other.</figcaption>
</figure>

My coding agent, Spark, already gets a model from another family to review its pull requests. The difference is where it happens: their whole back-and-forth sits in a channel where people can watch and step in.

The others he showed: Peter chases security reviews, missing documents and trial tenants across deals in flight, and pings people about what's still open. JQ said he's no longer sure whether he pings the bots or they ping him. Minty answers customers in their shared channels; after a long thread about SSO, one customer wrote "you were perfect, no human needed, I'm all set."

Their first agent, Max, went live in February. They went from one agent to five fast, then to 20. Now every new hire creates an agent in their first week.

## Every agent is its own principal

Without an identity layer, an agent holds a human's credentials. A sales agent acts as JQ in the CRM, on LinkedIn and in Gmail, and you lose control, the audit log and any record of who did what. His example: you ask it to clean up the CRM, and it reads "clean up" as "delete everything".

So each agent at MintMCP gets:

- its own identity and its own scope, with an allow list of what it can do
- explicit credentials on every tool call: a service account, or a delegated credential acting for a user, with the delegation chain recorded
- OAuth handled at the gateway, refresh included, because a background agent can't stop to click through a login screen

<figure class="fd-fig ac-badge-fig" aria-labelledby="ac3-cap" data-desc="Two ways to give a sales agent access. Borrowed login: without an identity layer, the agent holds a human's credentials and acts as JQ in the CRM, on LinkedIn and in Gmail. You lose control, the audit log and any record of who did what, and a request to clean up the CRM can be read as delete everything. Own principal, the way MintMCP does it: the agent has its own identity and its own scope, with an allow list of what it can do; every tool call carries explicit credentials, either a service account or a delegated credential acting for a user, with the delegation chain recorded; and OAuth is handled at the gateway, refresh included, because a background agent can't stop to click through a login screen.">
<div class="mp-two-row">
<div class="mp-two-card">
<p class="mp-two-who">No identity layer</p>
<p class="mp-two-name">Borrowed login</p>
<svg class="mp-two-art" viewBox="0 0 300 96" aria-hidden="true">
<g class="mp-bot"><rect x="6" y="30" width="36" height="36"/><rect class="mp-bot-eye" x="14" y="40" width="6" height="8"/><rect class="mp-bot-eye" x="28" y="40" width="6" height="8"/><path class="mp-bot-mouth" d="M16,57 H32"/></g>
<g class="mp-id"><rect x="52" y="18" width="92" height="60"/></g>
<circle class="ac-face" cx="72" cy="42" r="10"/>
<text class="mp-id-k" x="90" y="36">HUMAN</text>
<text class="mp-id-t" x="90" y="54">JQ</text>
<text class="mp-id-s" x="62" y="70">his login</text>
<path class="mp-thin" d="M144,48 L202,18 M144,48 L202,48 M144,48 L202,78"/>
<g class="ac-sys"><rect x="202" y="6" width="90" height="24"/><text x="247" y="22" text-anchor="middle">CRM</text></g>
<g class="ac-sys"><rect x="202" y="36" width="90" height="24"/><text x="247" y="52" text-anchor="middle">LinkedIn</text></g>
<g class="ac-sys"><rect x="202" y="66" width="90" height="24"/><text x="247" y="82" text-anchor="middle">Gmail</text></g>
</svg>
<p class="mp-two-line">Acts as JQ in the CRM, LinkedIn and Gmail</p>
<p class="mp-two-line">No record of who did what</p>
<p class="mp-two-line ac-bad">“Clean up the CRM” read as “delete everything”</p>
</div>
<p class="mp-two-vs" aria-hidden="true">vs</p>
<div class="mp-two-card mp-two-peter">
<p class="mp-two-who">At MintMCP</p>
<p class="mp-two-name">Own principal</p>
<svg class="mp-two-art" viewBox="0 0 300 96" aria-hidden="true">
<g class="mp-bot"><rect x="6" y="30" width="36" height="36"/><rect class="mp-bot-eye" x="14" y="40" width="6" height="8"/><rect class="mp-bot-eye" x="28" y="40" width="6" height="8"/><path class="mp-bot-mouth" d="M16,57 H32"/></g>
<g class="mp-id"><rect x="52" y="18" width="92" height="60"/><rect class="mp-id-photo" x="62" y="30" width="20" height="22"/></g>
<text class="mp-id-k" x="90" y="36">AGENT</text>
<text class="mp-id-t" x="90" y="54">Max</text>
<text class="mp-id-s" x="62" y="70">sales agent</text>
<path class="mp-thin mp-thin-agent" d="M144,48 H180"/>
<g class="ac-allow"><rect x="180" y="8" width="112" height="80"/></g>
<text class="mp-art-t" x="190" y="25">allow list</text>
<path class="ac-ok" d="M190,40 l4,4 l8,-8"/><rect class="mp-ln" x="208" y="38" width="70" height="4"/>
<path class="ac-ok" d="M190,56 l4,4 l8,-8"/><rect class="mp-ln" x="208" y="54" width="56" height="4"/>
<path class="ac-ok" d="M190,72 l4,4 l8,-8"/><rect class="mp-ln" x="208" y="70" width="64" height="4"/>
</svg>
<p class="mp-two-line">Its own identity and scope, with an allow list</p>
<p class="mp-two-line">Explicit credentials on every call, delegation chain recorded</p>
<p class="mp-two-line">OAuth at the gateway, refresh included</p>
</div>
</div>
<figcaption id="ac3-cap">The same sales agent, two ways in. With its own principal, every call says who's acting and for whom.</figcaption>
</figure>

## Permissions get decided at runtime

This is the part I hadn't thought through. Should the agent read the data as itself, or as the person who asked? It depends on the system. An assistant reading calendars should use the asker's credentials. An agent watching DevOps monitoring should use a service account.

An OAuth grant is a static token, and that's easy to reason about. In a channel, the right permissions depend on the grant, who sent the message, who else will see the reply, and what the task is. JQ calls this the shift from static to dynamic permissions.

<figure class="fd-fig ac-rt" aria-labelledby="ac4-cap" data-desc="Static and dynamic permissions. Static: an OAuth grant is one token, and that's easy to reason about. Dynamic: in a channel, the right permissions for a message depend on four things together: the grant, who sent the message, who else will see the reply, and what the task is. Whose credentials to use depends on the system: an assistant reading calendars should use the asker's credentials, and an agent watching DevOps monitoring should use a service account.">
<div class="ac-rt-in">
<div class="ac-static">
<p class="ac-k">Static</p>
<p class="ac-token"><b>OAuth grant</b><span>One token, easy to reason about</span></p>
</div>
<div class="ac-dyn">
<p class="ac-k">Dynamic · in a channel</p>
<ul class="ac-inputs">
<li>the grant</li>
<li>who sent the message</li>
<li>who else will see the reply</li>
<li>what the task is</li>
</ul>
<p class="ac-res">the right permissions, for this message</p>
</div>
</div>
<div class="ac-as">
<p class="ac-as-row"><span>An assistant reading calendars</span><i aria-hidden="true">→</i><b>the asker's credentials</b></p>
<p class="ac-as-row"><span>An agent watching DevOps monitoring</span><i aria-hidden="true">→</i><b>a service account</b></p>
</div>
<figcaption id="ac4-cap">JQ's shift from static to dynamic permissions. The two rows at the bottom answer “as itself, or as the person who asked?” It depends on the system.</figcaption>
</figure>

About 99% of MintMCP's controls are still deterministic. His rule: what you can make deterministic, make deterministic. The hard case is a thread with person A, who can see some data, and person B, who can't. If A says "go get it", that's probably fine, the same as if both were in a meeting room. They're trying a bit of AI for that call, with two conditions: everyone in the thread sees that access was granted, and A gets an undo button.

<figure class="fd-fig ac-ab" aria-labelledby="ac5-cap" data-desc="The hard case, and how small a share of MintMCP's controls it is. About 99% of the controls are still deterministic, and the rule is to make deterministic whatever can be. The rest is cases like this one: a thread with person A, who can see some data, and person B, who can't. A says go get it. That's probably fine, the same as if both were in a meeting room, so they're trying a bit of AI for that call, with two conditions: everyone in the thread sees that access was granted, and A gets an undo button.">
<div class="ac-split">
<p class="ac-split-bar" aria-hidden="true"><i></i></p>
<p class="ac-split-k"><span><b>About 99%</b> deterministic</span><span>the rest</span></p>
</div>
<div class="ac-ab-in">
<p class="ac-ab-h"><span class="ac-ppl"><b class="ac-pa">A</b>can see the data</span><span class="ac-ppl"><b class="ac-pb">B</b>can't</span></p>
<p class="ac-ab-msg"><b class="ac-pa">A</b><span>go get it</span></p>
<p class="ac-ab-grant"><span>Access granted in this thread. Everyone here sees this.</span><em>Undo · A only</em></p>
</div>
<p class="mp-strip"><b>A bit of AI</b><span>Probably fine, the same as if both were in a meeting room.</span></p>
<figcaption id="ac5-cap">The hard case from the talk, drawn as a thread. The notice and the undo button are his two conditions; the wording on them is mine.</figcaption>
</figure>

## Lock the tools down at the gateway

OAuth scopes are often too wide, so an agent inherits everything its user can do, including sending email and deleting records. MintMCP's answer sits at the MCP gateway, and it's deterministic:

- risky tools are denied outright, like deleting records or sending email
- agents never hold the final keys, only temporary access through the gateway
- the gateway logs every call, so you can go back and see which agent did what
- tools are bundled by role, not by server: engineers get read and write on Jira and read-only Intercom, a marketing agent gets less
- data is scoped too: Google Drive, but only these 10 folders

Two details I liked. The GitHub MCP server's fork-repository tool is one of the first tools their customers turn off. And customers in finance and healthcare run their existing DLP on every MCP call, which adds 100 to 200 ms.

<figure class="fd-fig ac-gw-fig" aria-labelledby="ac6-cap" data-desc="MintMCP's deterministic lock at the MCP gateway. The agent never holds the final keys, only temporary access through the gateway. The gateway logs every call, bundles tools by role rather than by server, and scopes data too; customers in finance and healthcare also run their existing DLP on every call, which adds 100 to 200 ms. On the tool side, risky tools are denied outright, like deleting records and sending email, and the GitHub MCP server's fork-repository tool is one of the first tools customers turn off. Engineers get read and write on Jira and read-only Intercom, a marketing agent gets less, and Google Drive can be limited to just 10 folders.">
<div class="ac-gw">
<div class="ac-gw-col ac-gw-agent">
<p class="ac-k">Agent</p>
<p class="ac-gw-big">No final keys</p>
<p class="ac-gw-sub">Only temporary access, through the gateway</p>
</div>
<p class="ac-gw-arrow" aria-hidden="true"><span>→</span></p>
<div class="ac-gw-col ac-gw-gate">
<p class="ac-k">MCP gateway · deterministic</p>
<ul class="ac-gw-list">
<li>Every call logged</li>
<li>Tools bundled by role, not by server</li>
<li>Data scoped too</li>
<li>Finance and healthcare: their DLP on every call, +100 to 200 ms</li>
</ul>
<p class="ac-log" aria-hidden="true"><i></i><i></i><i></i><i></i></p>
</div>
<p class="ac-gw-arrow" aria-hidden="true"><span>→</span></p>
<div class="ac-gw-col ac-gw-tools">
<p class="ac-k">Tools</p>
<ul class="ac-tl">
<li class="ac-no"><s>Delete records</s><span>denied outright</span></li>
<li class="ac-no"><s>Send email</s><span>denied outright</span></li>
<li class="ac-no"><s>Fork repository</s><span>GitHub, one of the first turned off</span></li>
<li>Jira<span>read and write, for engineers</span></li>
<li>Intercom<span>read-only, for engineers</span></li>
<li>Google Drive<span>only these 10 folders</span></li>
</ul>
</div>
</div>
<p class="mp-strip"><b>The problem</b><span>OAuth scopes are often too wide: an agent inherits everything its user can do.</span></p>
<figcaption id="ac6-cap">MintMCP's answer, laid out from the agent to the tools. The struck tools are denied outright; the rest come in a role's bundle.</figcaption>
</figure>

I learned the deterministic part the hard way. In August I denied my finance app's MCP tools to most of my agents with a pattern in my gateway config. A month later I tested it: the pattern matched nothing, and an agent that was supposed to be locked out was offered all 13 tools, write tools included. The tool names used a different separator than I'd assumed. Now I check a deny rule by asking the agent to list its tools.

<figure class="fd-fig ac-deny" aria-labelledby="ac7-cap" data-desc="My own deny rule, in three steps. One, August: I denied my finance app's MCP tools to most of my agents with a pattern in my gateway config. Two, a month later: the pattern matched nothing, and an agent that was supposed to be locked out was offered all 13 tools, write tools included. The tool names used a different separator than I'd assumed. Three, now: I check a deny rule by asking the agent to list its tools.">
<ol class="mp-trio">
<li class="mp-trio-step">
<svg class="mp-grow-art" viewBox="0 0 120 110" aria-hidden="true">
<rect class="mp-w" x="14" y="8" width="92" height="94"/><rect class="mp-wbar" x="14" y="8" width="92" height="8"/>
<rect class="mp-ln" x="24" y="28" width="60" height="4"/><rect class="mp-ln" x="24" y="38" width="44" height="4"/>
<g class="fd-box-agent"><rect x="20" y="50" width="80" height="16"/></g>
<text class="mp-art-t mp-art-agent" x="28" y="62">deny</text><path class="ac-x" d="M80,53 l10,10 M90,53 l-10,10"/>
<rect class="mp-ln" x="24" y="76" width="54" height="4"/><rect class="mp-ln" x="24" y="86" width="38" height="4"/>
</svg>
<p class="mp-trio-n"><b>1</b>August</p>
<p class="mp-trio-what">A deny pattern for my finance app's tools, in my gateway config</p>
</li>
<li class="mp-trio-step">
<svg class="mp-grow-art" viewBox="0 0 120 110" aria-hidden="true">
<text class="fd-s fd-s-alert" x="60" y="12" text-anchor="middle">matched 0</text>
<g class="ac-13"><rect x="13" y="22" width="15" height="15"/><rect x="32" y="22" width="15" height="15"/><rect x="51" y="22" width="15" height="15"/><rect x="70" y="22" width="15" height="15"/><rect x="89" y="22" width="15" height="15"/><rect x="13" y="41" width="15" height="15"/><rect x="32" y="41" width="15" height="15"/><rect x="51" y="41" width="15" height="15"/><rect x="70" y="41" width="15" height="15"/><rect x="89" y="41" width="15" height="15"/><rect x="13" y="60" width="15" height="15"/><rect x="32" y="60" width="15" height="15"/><rect x="51" y="60" width="15" height="15"/></g>
<text class="mp-art-t" x="60" y="96" text-anchor="middle">13 of 13 offered</text>
</svg>
<p class="mp-trio-n"><b>2</b>A month later</p>
<p class="mp-trio-what">It matched nothing. A locked-out agent saw all 13 tools, write tools included</p>
</li>
<li class="mp-trio-step">
<svg class="mp-grow-art" viewBox="0 0 120 110" aria-hidden="true">
<g class="mp-review"><rect x="8" y="8" width="104" height="22"/></g><text class="mp-review-t ac-ask-t" x="60" y="23" text-anchor="middle">list your tools</text>
<rect class="mp-w" x="8" y="40" width="90" height="62"/>
<path class="ac-ok" d="M16,52 l4,4 l8,-8"/><rect class="mp-ln" x="34" y="50" width="50" height="4"/>
<path class="ac-ok" d="M16,68 l4,4 l8,-8"/><rect class="mp-ln" x="34" y="66" width="40" height="4"/>
<path class="ac-ok" d="M16,84 l4,4 l8,-8"/><rect class="mp-ln" x="34" y="82" width="46" height="4"/>
</svg>
<p class="mp-trio-n"><b>3</b>Now</p>
<p class="mp-trio-what">I check a deny rule by asking the agent to list its tools</p>
</li>
</ol>
<p class="mp-strip"><b>Why</b><span>The tool names used a different separator than I'd assumed.</span></p>
<figcaption id="ac7-cap">My own version of the lesson: a deny rule is only as good as the names it matches.</figcaption>
</figure>

## The 6 a.m. message nobody asked for

The best story came in the Q&A. At 6 a.m., Max posted in the deals channel that one deal had been assigned to two reps. JQ started debugging the deal, then stopped: why did the agent wake up and post that at all?

It took the audit log to find out. A colleague had asked their own agent something, that agent delegated to Max, and Max escalated to the whole channel. Without the delegation chain, nobody would have known.

<figure class="fd-fig ac-chain-fig" aria-labelledby="ac8-cap" data-desc="The 6 a.m. message, traced back. What the channel saw: at 6 a.m., Max posted in the deals channel that one deal had been assigned to two reps. What the audit log showed: a colleague had asked their own agent something, that agent delegated to Max, and Max escalated to the whole channel. Without the delegation chain, nobody would have known why Max posted at all.">
<div class="ac-seen">
<p class="ac-k">What the channel saw, at 6 a.m.</p>
<p class="ac-msg"><b class="ac-who">Max</b><span>One deal has been assigned to two reps.</span></p>
</div>
<div class="ac-trace">
<p class="ac-k">What the audit log showed</p>
<ol class="ac-chain">
<li><b>A colleague</b><span>asks their own agent something</span></li>
<li class="ac-ag"><b>Their agent</b><span>delegates to Max</span></li>
<li class="ac-ag"><b>Max</b><span>escalates to the whole channel</span></li>
<li class="ac-ch"><b>Deals channel</b><span>the 6 a.m. post</span></li>
</ol>
</div>
<p class="mp-strip"><b>Audit log</b><span>Without the delegation chain, nobody would have known why Max posted.</span></p>
<figcaption id="ac8-cap">JQ's story from the Q&amp;A, read the way he had to: from the post back to whoever started it. Max's message is paraphrased.</figcaption>
</figure>

That's the next phase he sees: who can create, message and update an agent, who orchestrates them all, and, in his words, what the HR equivalent for agents is.

## Memory needs access control too

Darren Shepherd, one of the hosts, pushed back. You can't tag every sentence with a data classification, and as models get smarter, maybe AI should make more of these calls. We trust humans, he said, because of the process around them, not because they're reliable.

JQ's answer was a second dimension: memory. When his personal agent talks to Darren, it shouldn't share JQ's finances. So memory gets silos (one per person, per team, per company, per customer), and the agent sees different silos depending on who it's talking to. Same persistent ID card at the door, different books it's allowed to read. He called that one very much R&D.

<figure class="fd-fig ac-mem" aria-labelledby="ac9-cap" data-desc="Memory silos, as JQ described them. The agent shows the same persistent ID card at the door, whoever it talks to. Behind it, memory is split into silos, one per person, per team, per company and per customer, and the agent sees different silos depending on who it's talking to. When JQ's personal agent talks to Darren, the silo with JQ's finances stays shut. He called this very much R&amp;D.">
<div class="ac-mem-in">
<div class="ac-door">
<svg class="ac-card-art" viewBox="0 0 160 100" aria-hidden="true">
<g class="mp-id"><rect x="10" y="10" width="140" height="80"/><rect class="mp-id-photo" x="22" y="24" width="28" height="32"/></g>
<text class="mp-id-k" x="60" y="34">AGENT</text>
<text class="mp-id-t" x="60" y="52">JQ's agent</text>
<text class="mp-id-s" x="22" y="76">persistent ID</text>
<rect class="ac-scan" x="10" y="22" width="140" height="2"/>
</svg>
<p class="ac-door-t">Same ID card at the door</p>
</div>
<div class="ac-shelf">
<p class="ac-k">Different books, by who it's talking to</p>
<ul class="ac-silos">
<li class="ac-shut"><b>Per person</b><span class="ac-books" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i></span><span>JQ's finances</span><em>shut for Darren</em></li>
<li><b>Per team</b><span class="ac-books" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i></span></li>
<li><b>Per company</b><span class="ac-books" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i></span></li>
<li><b>Per customer</b><span class="ac-books" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i></span></li>
</ul>
</div>
</div>
<p class="mp-strip"><b>R&amp;D</b><span>His words for this part: very much R&amp;D.</span></p>
<figcaption id="ac9-cap">A schematic of JQ's answer to Darren. Only the silo he named is drawn shut; which others open for whom, he didn't say.</figcaption>
</figure>

## Where my own fleet sits

My agents each have their own Telegram thread. By JQ's map that's level 2: one human and one agent per thread, nobody else watching. Telegram doesn't even deliver one bot's messages to another bot, so my agents can't talk to each other there.

Today I gave two of them Slack accounts: Nova, my generalist, and Spark, my coder. They live in a workspace just for agents, each with its own Slack app, because one app is one bot user. That's the first piece of identity. Next I want Nova to open a channel for a coding job and bring Spark in, the way Dan brings in Sam.

The catch: Slack's free plan allows 10 apps per workspace, and I have 17 agents. Delegated credentials, runtime permissions and memory silos are all still ahead of me.

<figure class="fd-fig ac-fleet" aria-labelledby="ac10-cap" data-desc="Where my own fleet sits. Today each agent has its own Telegram thread: one human and one agent per thread, nobody else watching, and Telegram doesn't deliver one bot's messages to another bot. By JQ's map, that's level 2. Two of them, Nova and Spark, now have Slack accounts in a workspace just for agents, each with its own Slack app, because one app is one bot user: the first piece of identity. Next, Nova should open a channel for a coding job and bring Spark in. The catch: Slack's free plan allows 10 apps per workspace, and I have 17 agents, so 7 don't fit. Still ahead: delegated credentials, runtime permissions and memory silos.">
<div class="mp-two-row">
<div class="mp-two-card">
<p class="mp-two-who">Every agent</p>
<p class="mp-two-name">Telegram</p>
<svg class="mp-two-art" viewBox="0 0 300 96" aria-hidden="true">
<path class="ac-wall" d="M150,4 V92 M8,48 H292"/>
<circle class="mp-you-c" cx="28" cy="26" r="9"/><path class="mp-thin" d="M37,26 H92"/><rect class="ac-sq" x="92" y="18" width="16" height="16"/>
<circle class="mp-you-c" cx="28" cy="72" r="9"/><path class="mp-thin" d="M37,72 H92"/><rect class="ac-sq" x="92" y="64" width="16" height="16"/>
<rect class="ac-sq" x="192" y="18" width="16" height="16"/><path class="mp-thin" d="M208,26 H263"/><circle class="mp-you-c" cx="272" cy="26" r="9"/>
<rect class="ac-sq" x="192" y="64" width="16" height="16"/><path class="mp-thin" d="M208,72 H263"/><circle class="mp-you-c" cx="272" cy="72" r="9"/>
<path class="ac-line-alert" d="M110,26 H190"/><path class="ac-x" d="M144,20 l12,12 M156,20 l-12,12"/>
</svg>
<p class="mp-two-line">One human and one agent per thread, nobody else watching</p>
<p class="mp-two-line">Bots don't get each other's messages</p>
<p class="mp-two-line">JQ's level 2</p>
</div>
<p class="mp-two-vs" aria-hidden="true">now</p>
<div class="mp-two-card mp-two-peter">
<p class="mp-two-who">Nova and Spark</p>
<p class="mp-two-name">Slack</p>
<svg class="mp-two-art" viewBox="0 0 300 96" aria-hidden="true">
<rect class="mp-w" x="40" y="6" width="220" height="84"/><rect class="mp-wbar" x="40" y="6" width="220" height="8"/><rect class="mp-wside" x="40" y="14" width="96" height="76"/>
<rect class="ac-sq" x="50" y="28" width="16" height="16"/><text class="ac-name" x="74" y="41">Nova</text>
<rect class="ac-sq" x="50" y="58" width="16" height="16"/><text class="ac-name" x="74" y="71">Spark</text>
<text class="mp-art-t" x="148" y="34">1 app each</text>
<text class="mp-art-t" x="148" y="52">= 1 bot user</text>
<rect class="mp-ln" x="148" y="66" width="96" height="4"/><rect class="mp-ln" x="148" y="76" width="70" height="4"/>
</svg>
<p class="mp-two-line">A Slack workspace just for agents</p>
<p class="mp-two-line">One Slack app each: the first piece of identity</p>
<p class="mp-two-line">Next: Nova opens a channel for a coding job and brings Spark in</p>
</div>
</div>
<div class="ac-slots-in">
<p class="ac-k">Slack's free plan: 10 apps per workspace</p>
<div class="ac-slots" aria-hidden="true">
<p class="ac-plan"><i class="on">N</i><i class="on">S</i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></p>
<p class="ac-left"><i></i><i></i><i></i><i></i><i></i><i></i><i></i></p>
</div>
<p class="ac-slots-k"><span>N, S: Nova and Spark</span><span><b>7 of my 17 agents</b> don't fit</span></p>
</div>
<p class="mp-strip"><b>Still ahead</b><span>Delegated credentials, runtime permissions, memory silos.</span></p>
<figcaption id="ac10-cap">My setup against JQ's map. The squares in the box are the free plan's 10 app slots; the dashed ones are the agents left over.</figcaption>
</figure>

[The talk](https://www.youtube.com/watch?v=xLd-mLCN_t0) is 40 minutes, and the Q&A about permissions is the best part. MintMCP also wrote up [how to build an AI coworker in Slack](https://www.mintmcp.com/blog/build-ai-coworker-slack). And if you've solved the person A, person B thread, I'd love to hear how.
