Legible EventsKeeping pace in AIOct 29
← Blog

We watched Claude Code's new hooks fire with a mod, then used one to keep two clients' data apart

Brandon Catcho
5 MIN

Anthropic's new Claude Code mods run inside the agent loop and can read a tool's result before Claude does. We used one to watch the loop, and wrote another that asks before one session mixes two clients' Notion pages.

A hook-flow timeline of one Claude Code turn. Rows for prompt, context, turn, model, tool, perms, append and done run left to right in time. Model requests are long cyan bars, tool calls orange, permission checks red and transcript writes green.

On 1 October 2026 Anthropic launched mods for Claude Code. The announcement calls them “small TypeScript functions that change how Claude Code works.” We started with a mod that shows every hook as it fires, then used what we saw on a problem our clients bring us: keeping one client’s data out of another client’s work.

We started by building a mod that shows Claude Code’s hooks

Hooks are the main extension point in the mod system. Claude Code raises an event at each step of its work, such as a submitted prompt, a request to the model, a tool call or a permission check, and a modA plugin that changes how Claude Code looks and behaves, made of JavaScript or TypeScript functions that Claude Code calls when an event happens. hooks the events it cares about. Claude Code’s events docs explain the timing: “Your hook runs before Claude Code acts, so it can observe the event, rewrite it, or answer it in Claude Code’s place.”

Our first mod, hook-flow, hooks every event with one function that notes the time, lets the step run, and measures again when it returns. A pane beside the conversation draws the results as a flow chart of the loop and a timeline of each turn.

hook-flow's timeline filling in live during one turn, sped up. It's the client-data run from later in this post.

What one prompt looks like from inside Claude Code

With hook-flow open, we asked Claude Code how many Ambient meetings we had that day, which it answers through Ambient’s MCP server. Across two prompts, the flow chart counts 4 requests to the model and 4 tool calls, each with its permission check. The ui box counts 459 interface events over the same stretch, so the screen redraws far more often than Claude is asked anything.

The timeline lays the same 9.1-second turn out in time. Model requests are the long teal bars, with tool calls between them. A red permission check starts each tool call, and green transcript writes follow. On the pinned call, hook-flow notes that no other plugin hooked it.

A tool call is the moment outside data enters the session, and every mod that hooked it sees the result before Claude does.

Setting out to keep two clients’ data apart

Our clients often ask how to use AI on customer data without mixing one customer’s data with another’s. Consultancies, law firms and banks already have a name for the rule they need, an information barrierA rule inside a firm that keeps information held for one client or team away from another, to avoid conflicts of interest. Law firms often call it an ethical wall.. It’s related to DLPData loss prevention: controls that stop sensitive data leaving the company or going where it shouldn't.. DLP stops data leaving the company, and an information barrier keeps clients’ data apart inside it.

A Claude Code session can cross that barrier without anyone noticing. One session can read Acme’s renewal notes in Notion and, a few prompts later, pull Birchwood’s account plan into the same conversation.

So we set out to build a mod that notices when a session picks up a second client’s data from Notion and asks the person before Claude reads it. Sometimes using two clients’ material together is fine, so the person decides. For the test, we gave a Notion database a Client property, a field we invented for this post, and filed one page under each client.

One of the two test pages. The Client property, set to Birchwood here, is what the mod reads.

A mod that asks before Birchwood’s page reaches an Acme session

client-boundary, the mod we wrote for this, hooks tool.call, the same event hook-flow pinned. It lets the Notion call run, reads the Client value from the result, and remembers the first client the session touches. Claude Code also has a tool.check event for permission decisions, but it fires before the tool runs, when the page’s client isn’t known yet. Here’s the hook, simplified:

// Simplified from client-boundary's hooks/register.ts
on('tool.call', async ($, e, next) => {
  if (!isNotion(e.tool)) return next(e)
  const result = await next(e) // Notion answers; Claude hasn't read it yet
  const clients = clientsIn(result.text, 'Client')
  const fresh = clients.filter((name) => !session.has(name))
  if (session.size === 0 || fresh.length === 0) {
    clients.forEach((name) => session.add(name)) // the first client binds the session
    return result
  }
  // A second client gets a question:
  const answer = await $.ui.ask(question, {
    header: 'Client data',
    options: ['Keep it out', 'Allow once', 'Allow for this session'],
  }).catch(() => 'Keep it out')
  // Anything but an explicit allow keeps the page from Claude
  if (!answer.startsWith('Allow')) return { deny: reason }
  if (answer === 'Allow for this session') fresh.forEach((name) => session.add(name))
  return result
})

We asked Claude Code to draft a Birchwood account plan, using what worked in the Acme renewal notes. Claude read the Acme page first, and that tied the session to Acme. When the Birchwood page came back, the mod opened the dialog Claude Code uses when Claude asks you something.

The question appears when the Birchwood page comes back.

hook-flow’s timeline now lists client-boundary on that Notion fetch, and shows the fetch waiting while we decided.

The Birchwood fetch, pinned, with client-boundary listed as a plugin on it.

We chose “Keep it out”, so Claude never saw the Birchwood page. Claude got the mod’s reason in its place, after a note that the tool “ran, and a plugin withheld its result”. Claude then changed course: “I stopped before writing anything into the plan. When I opened the Birchwood page in Notion, one of your plugins blocked it.” It also noticed that the Acme notes ask for Acme’s workflow data to stay out of shared templates, and it offered three ways forward.

Claude's reply after we chose Keep it out. It explains the block, flags the Acme note about shared templates, and asks how to go ahead.

Where real data loss prevention belongs

client-boundary is a demonstration, and we haven’t published it as a tool. In our view, an MCP gateway, or a similar control between your agents and your data, is the more secure way to enforce DLP or an information barrier. The mod reads text, and the events docs say a deny after the call “undoes nothing the tool did”. While we set up the test data, the mod kept a page-creation result from Claude after Notion had already created the pages. Mods also run with your access, and Anthropic’s announcement says “They aren’t sandboxed”, so install them only from sources you trust. We built both mods on Claude Code 2.1.294–2.1.296 in October 2026, and Anthropic’s tutorial warns that “The API can change between releases.”

The experiment shows how much a few lines inside the agent loop can see, and raises the question of where each team’s rules should live. If you’re working out how Claude Code should handle your clients’ data, the Legible team would like to talk.

References

Brandon Catcho
Founding Partner at Legible. Builds the agents and automations that turn strategy into results.