Notes Oct 1, 2026 8 min read

One Context Hub, Two Kinds of Agents

Half of our team lives in a terminal, the other half in a chat window. This is how I built a small context hub so their AI agents can share what we know about each client and ask each other questions, and why the hard part was the people, not the protocol.

Lushano Perera
Lushano Perera
Author

How I gave a small agency’s AI agents a shared memory and a way to ask each other questions, and why the hard part was the people, not the protocol.


The Answer That Started It

I asked a coding agent on a clean machine, with none of my local notes, what to watch out for before running a WordPress CLI command in one client repo.

Without help, it gave the generic answer: back up first, check the environment. With access to a small shared service, it named the exact trap I had hit months earlier: in that repo, a CLI command without one specific setting talks to the production database, not the local one.

That answer is the whole project. A fact I learned the hard way, on one machine, reached another agent that had never seen it.


The Problem: Knowledge Stuck in One Terminal

I run the technical side of a web agency with more than forty client sites: WordPress, Next.js, a few custom stacks. Over the last year most of my work moved into coding agents: Claude Code and Codex, running side by side in terminal panes.

Those agents got good because they learned. Every repo has a CLAUDE.md or AGENTS.md, and my machine keeps a memory of the traps: the host that writes empty files over FTP unless one option is off, the client who must approve copy before any deploy.

My colleagues work differently. They use the Claude app in a browser, with one Project per client: copy, campaigns, newsletters, brochures. Their Claude knew nothing my agents knew, and my agents knew nothing about their campaigns.

So I had two questions:

  1. Can the facts that live on my machine reach every agent in the team?
  2. Can those agents ask each other questions, when one side is a terminal that runs all day and the other is a chat that exists only while a person has it open?

Both answers turned out to be one small service: the context hub.


Rule One: Enrich Context, Never Duplicate It

The hub is an MCP server that serves context packs, one per client.

The most important decision was what a pack must not contain. Inside a repo, Claude Code already reads CLAUDE.md and Codex reads AGENTS.md. If a pack repeats the stack, the commands and the conventions, it only adds tokens and a second source that drifts out of date.

So a pack holds only facts that the repo does not hold:

  • Traps: the things that break production, and why.
  • Status: what is live, what is blocked, and on whom.
  • Profile: who the client is, the brand rules, the tone.

One Person per Login

The first version lived on a lab server on our office network. To reach colleagues on the Claude app, the hub had to be on the internet.

It now runs on a small VPS, with OAuth through a hosted identity provider. Each person signs in with their own company account, and a per-person grant says which client packs they can read.

One rule came from the providers’ terms, not from me: no shared logins. Each colleague connects the hub from their own Claude account, and every call in the log carries a real name.


Writing Back, Without Opening a Door

Reading packs was easy. Writing needed care, because an agent that reads text written by another agent is a prompt-injection target.

Agents can do two things that write:

  • leave a handoff note for a colleague’s agent (“the staging deploy is done, check the form email”);
  • propose a new fact for a client pack.

The rule that keeps this safe: agents get headers, people get text. An agent sees “one open handoff from Lushano about project X”, never the body. The body is on a small approval page where a human reads it. A pack update goes live only after a person approves it there.


Agents That Ask Each Other Questions

Handoffs go one way. The next step was questions with answers, and that is where the two kinds of agents meet.

Every question has a kind:

  • fact: something already true, for example “which domain is canonical, bare or www?”
  • decision: something a person must choose, for example “do we publish the landing page before the client approves the photos?”

Every answer carries a label that says where it came from, and the labels are deliberately modest:

  • pack excerpt, chosen by a model: a line copied from a client pack;
  • agent answer, not confirmed: another agent answered from its own context;
  • confirmed by the account of …: a person pressed a button on the approval page.

A decision never goes to a model. It resolves only when a person confirms it.

And even the strongest label proves an account action, not a human presence. No label can authorise a deploy.

A responder that selects instead of writing

Many questions are facts that are already in a pack. For those, a small worker tries to answer first.

The first version asked Claude Haiku to pick the answer. Then I tried TypeSafe’s Jev, a model that returns typed judgments instead of text. It fits the job well:

  1. Code splits the pack into candidate lines.
  2. Jev picks one candidate by its number, or none.
  3. A second judgment asks whether the question is really a decision. If it is, the answer is “none”.
  4. Code copies the chosen line verbatim.

The model never writes a word of the answer. It can only point at a line that already exists. The hub still checks that every excerpt is an exact copy of a pack line, because the same check guards the Haiku fallback, which runs when Jev is unavailable.

When I asked from my phone “when do we start the next version of the product?”, the responder found no line in the pack and left the question open. That is the right result: a plan date must come from a person.


Plugins for the Terminal Side

On my side, I did not want to paste a check-in prompt into every repo. So the hub ships as one plugin for both Claude Code and Codex.

The plugin has a session-start hook. It reads a one-line .hub-project file in the repo root:

acme-shop

If the file is there and valid, the hook adds the check-in instructions to the session: list open handoffs, load the pack, list questions in both directions. In a repo without the file, the hook prints nothing.

The test that mattered: I removed the check-in text from my global instruction files, opened a session with a neutral prompt, and both agents still ran the full check-in from the hook alone.

Real-time notices, report only

A check-in runs only when a session starts. For long sessions, Claude Code has channels: a local MCP server can push an event into a running session.

The plugin includes a tiny channel server that asks the hub for news every half minute or so and pushes one line: “new question from X about project Y, text not included”.

The first version let the agent read and answer the question on its own. A security review rejected that, and the review was right. A notice starts an agent turn with nobody watching, so a malicious question could reach an agent with full tool access. Notices are now report only: the agent tells me in one line and does nothing until I ask.


The Real Problem: Terminal People and Chat People

Once everything worked, the hardest part was not technical.

My side has hooks, background agents, push notices, and approvals I control. The chat side has none of that. A chat starts only when a person opens it. There is no hook, no push, and an approval for every write. The person carries every message in both directions.

I spent a while trying to make the chat agents behave like terminal agents. That was the wrong goal. Two changes worked better.

1. Make the approval page the colleague’s main tool

Every question email now links straight to that one question, and the link survives the sign-in. On that page a colleague can read, answer, confirm a decision, and send a new question, from a phone, without opening a chat at all. The Claude app stays for what it is good at: reading packs and drafting longer text.

Because colleagues now use the page every day, it had to look like ours: the agency’s colours, logo and font, Italian labels, a dark mode, a layout that works on a phone, and the same branding on the sign-in screen.

2. Write messages that stand alone

A chat reader has no repo, no history, and often no time. So every question and answer must make sense on its own, with one short line that says what is waiting for the answer. That rule now lives in the tool descriptions, in the plugin check-in, and in the instructions of each client Project on the chat side.


What I Learned

  • Design for the slowest participant. Real-time features on my side did not help, because the delay was always on the person reading a chat.
  • Review with a different model before you ship. Every part went through a plan review and at least one code review by a second model. The reviews caught a queue deadlock, a redirect bug in the sign-in flow, and the unattended-turn problem above.

The hub is small: one VPS and one Node server. Its value is not the code. It is that a fact I learn at midnight in a terminal is there the next morning, in my colleague’s chat.

Written by Lushano Perera

Digital craftsman exploring the intersection of design, technology, and human experience.