Bring an existing agent
Turn a Claude Code or similar agent project into a Hub your team can use, file by file, or keep your assistant and connect it to a Hub through MCP.
You may already have an agent that works for you: a Claude Code project with a CLAUDE.md, a few skills, an MCP server and some scripts. By the end of this guide that project runs as a Hub your team can use in the web app, on a schedule and from their own assistants, with a check that proves it still does its job.
There are two routes, and they combine well:
| Route | What happens | Choose it when |
|---|---|---|
| Move it into a Hub | The instructions, skills, sub-agents, connectors and scripts become Hub files. The agent then runs in the web app, Slack and other channels, on schedules and in workflows, for everyone you grant access. | Teammates who do not use your assistant should get the result, or the work should run unattended. |
| Keep your assistant, connect it | The Hub serves its tools, knowledge and skills over MCP. You and others keep using Claude Code or another MCP client, with your own model. | People already work in an assistant and want shared context and tools inside it. |
The move is manual and quick for a typical project. Hubzoid has no converter, because most of the work is deciding what the shared agent should know and do.
What you need
- Hubzoid installed, with a working model. See the quickstart.
- Your existing project, and a new empty folder outside it for the Hub.
How the pieces map
| In a Claude Code project | In a Hub | Notes |
|---|---|---|
CLAUDE.md or .claude/CLAUDE.md (or AGENTS.md) | AGENTS.md | The body is the system prompt. Keep the parts about the work, drop the parts about the coding environment. |
@path imports in CLAUDE.md | Inline text, or knowledge/*.md | Hubzoid does not expand imports. Reference material fits best in knowledge/. |
.claude/skills/<name>/SKILL.md | skills/<name>/SKILL.md | Same file. name and description are read, other keys are ignored. |
.claude/commands/<name>.md | skills/<name>.md or schedule/<name>.md | A reusable prompt becomes a skill. A prompt you run on a timer becomes a markdown task. |
.claude/agents/<name>.md | agents/<name>.md | Rewrite tools: as a YAML list of Hubzoid tool names, and model: as a Hubzoid model id. |
.mcp.json | connectors/.mcp.json, or a connector in connectors/<id>.yaml | Same mcpServers map. HTTP servers need transport instead of type. A remote server where each person should use their own account becomes a connector. |
| Scripts the agent runs | tools_local/*.py | A function decorated with @function_tool. A script that should run on a timer without the agent can be a run: task. |
| Data files the agent reads | knowledge/ or raw_data/ | Short curated documents go in knowledge/. Large source material to search goes in raw_data/. |
| Secrets in your shell | <hub>/.env | Secrets for restricted tools go in restricted/.env. |
Permission settings in .claude/settings.json | restricted/ tools and grants | Who may use a tool is decided in code and Settings, not by the model. |
A worked example
The operations lead at Kestrel & Oak, a fictional home-goods shop with four stores, has a Claude Code project that prepares a daily report: open supplier orders, stock alerts and anything that needs escalating.
ops-briefing/
├── CLAUDE.md
├── .mcp.json
├── .claude/
│ ├── skills/daily-reports/SKILL.md
│ ├── skills/daily-reports/template.md
│ ├── agents/supplier-research.md
│ └── commands/weekly-review.md
├── scripts/open_orders.py
├── data/orders.csv
└── docs/escalation-policy.mdAfter the move, the same project is a Hub:
Create the Hub
In a new folder outside your existing project:
hubzoid init ops-briefingInitialized hub at ~/agents/ops-briefing
wrote 7 hub filesThis writes every folder Hubzoid reads, a private .env with the model and a random bridge key, and the Kestrel & Oak starter sample. Delete the sample's knowledge/house-rules.md and evals/refund-window.md, then copy your files in as below.
Turn CLAUDE.md into AGENTS.md
Copy the parts of CLAUDE.md that describe the work: the goal, the rules, the tone, when to use what. Leave out build commands and coding conventions that only mattered to you in an editor. Add frontmatter for the name the team will see and a few starter prompts.
---
name: ops-briefing
description: Daily reports and supplier follow-up for the operations lead.
suggestions:
- Prepare today's daily report
- Which supplier orders are late?
---
You prepare the operations lead's daily report for Kestrel & Oak,
a home-goods shop with four stores (north, south, east, west).
- Use the daily-reports skill for the daily briefing.
- Get open orders from the open_orders tool. Never estimate a quantity.
- Escalate according to the escalation-policy knowledge document.
- For questions about a supplier's record, use the supplier-research agent.Replace @docs/escalation-policy.md style imports with a sentence that names the knowledge document, and move the document to knowledge/escalation-policy.md. The agent sees a menu of knowledge documents and reads one with read_knowledge when it needs it.
Move skills and commands
Copy .claude/skills/daily-reports/ to skills/daily-reports/. The SKILL.md frontmatter works unchanged. Supporting files are not loaded with the skill, so point to them by their path from the Hub root:
---
name: daily-reports
description: Build the daily report for the operations lead.
---
1. Read the layout in skills/daily-reports/template.md with read_file.
2. Call open_orders and list orders due today or overdue, by store.
3. Apply the escalation policy and put anything that needs a decision first.A command such as .claude/commands/weekly-review.md becomes skills/weekly-review.md with name and description frontmatter. Hubzoid does not substitute $ARGUMENTS, so write the command as instructions that use whatever the person asked.
Move sub-agents
Copy .claude/agents/supplier-research.md to agents/supplier-research.md and adjust two keys:
---
name: supplier-research
description: When the user asks about a supplier's delivery record or recent news.
model: claude-local/opus
tools: [web_search, http_get, read_knowledge]
---
Research the supplier with web_search and http_get and reply with a short,
sourced summary of delivery issues and news from the last 90 days.What tools: and model: must contain
tools:must be a YAML list of Hubzoid tool names. A comma-separated string such asRead, Grepstops the Hub from building, and Claude Code tool names do not exist in Hubzoid.model:must be a Hubzoid model id, or be removed. Claude Code aliases such assonnet,opusorinheritare not Hubzoid ids. A sub-agent whose model differs from the Hub's on the same engine runs on its own model as a delegate. Otherwise it is loaded into the main agent as a skill, and itstools:list is ignored.
Move MCP servers
Move .mcp.json to connectors/.mcp.json, replacing the starter's empty file. Local (stdio) servers work as they are. For an HTTP server, replace "type": "http" with "transport": "streamable-http", because a server with a url and no transport is treated as SSE. Hubzoid expands ${NAME} but not ${NAME:-default}, so set every variable in the Hub's .env.
{
"mcpServers": {
"inventory": {
"type": "http",
"url": "${INVENTORY_MCP_URL:-https://mcp.example.com/mcp}",
"headers": { "Authorization": "Bearer ${INVENTORY_TOKEN}" }
}
}
}{
"mcpServers": {
"inventory": {
"transport": "streamable-http",
"url": "${INVENTORY_MCP_URL}",
"headers": { "Authorization": "Bearer ${INVENTORY_TOKEN}" }
}
}
}When each person should use their own account
In a Hub, everyone who uses the agent shares the credential of a server in connectors/.mcp.json. If each person should act with their own account, add the remote server as a connector with sign_in: oauth in connectors/<id>.yaml instead. See add a connector.
Turn scripts into tools
In Claude Code the agent ran python scripts/open_orders.py in a shell. A Hub agent has no shell, so move the logic into a tool:
"""Open supplier orders, moved from scripts/open_orders.py."""
from __future__ import annotations
import csv
import json
from pathlib import Path
from agents import function_tool
ORDERS = Path(__file__).resolve().parent.parent / "raw_data" / "orders.csv"
@function_tool
def open_orders(supplier: str = "") -> str:
"""Open supplier orders with their store and due date.
Args:
supplier: Only this supplier. Leave empty for all suppliers.
Returns:
JSON list of open orders.
"""
with ORDERS.open(newline="", encoding="utf-8") as f:
rows = [r for r in csv.DictReader(f) if r["status"] == "open"]
if supplier:
rows = [r for r in rows if r["supplier"].lower() == supplier.lower()]
return json.dumps(rows)Why a tool cannot import a sibling file
Tool files are loaded one by one rather than as a package, so a tool cannot import a sibling file from tools_local/. Keep each tool file self-contained, or install shared code as a package. See tools and connectors.
Schedule the briefing
What you started by hand each morning can now run on its own. Scheduled tasks are Markdown files:
---
schedule: "30 7 * * 1-6"
write: ["briefings/"]
---
Prepare today's daily report with the daily-reports skill and save it
to briefings/today.md.The task runs at 07:30 server time from Monday to Saturday with the same agent, skills and tools as chat. See markdown tasks.
Check it
Add an eval that proves the briefing uses real order data:
---
expect_tools: [open_orders]
---
## Prompt
Prepare today's daily report.
## Criteria
Lists overdue orders by store from the tool result. Does not invent quantities.Check the setup, then run the eval:
hubzoid doctor ops-briefinghubzoid eval run ops-briefingWhen both are clean, start it and ask a real question in the web app:
hubzoid run ops-briefingWhat changes when an agent moves
- No coding tools. On the
claude-localbackend, Claude Code's own tools (shell, file editing, its search and web fetch) are switched off. The agent has Hubzoid's built-in tools, such asread_file,list_files,grep_data,http_getandwrite_artifact, plus your own. Instructions that say "run this command" must name a tool instead. - Only Hub files count. Hubzoid reads
AGENTS.md, notCLAUDE.mdor anything under.claude/. Imports, hooks, permission settings and auto memory do not carry over.
Shared credentials, checks that belong in code, and how memory works
- Many people, one agent. In chat, each person has their own conversations and their own identity. Tools and the servers in
connectors/.mcp.jsonshare the Hub's credentials. Restricted tools and connectors reach only the people granted them, and a connector can use each person's own account. Decide who may use what in Settings. See identity and access. - Checks that must always run belong in code. A rule the model is only asked to follow can be missed. Put anything that must hold every time in a tool, a restricted tool or a code workflow step.
- Memory is explicit. Conversation history stays with each chat. Shared knowledge changes when you edit
knowledge/. The agent can save shared knowledge itself only for a person granted Save shared knowledge (curator), and only when browser editing (Beta) is turned on. See memory and history.
Keep your assistant and connect it
If you and your teammates prefer to stay in Claude Code or another MCP client, connect the assistant to the Hub over MCP instead of, or as well as, moving the agent.
A local hubzoid run serves MCP by default and prints the command to add it to Claude Code. On a shared deployment, turn it on in the Hub's .env with the public address of the endpoint:
MCP_SERVER=true
MCP_PUBLIC_URL=https://hub.example.com/mcpEach person adds the Hub to their assistant, with no key:
claude mcp add --transport http ops-briefing https://hub.example.com/mcpIn Claude Code, they run /mcp, choose the Hub and authenticate. The full setup is in connect an assistant.
Sign-in, and what the assistant receives
The browser opens Hubzoid's sign-in if needed, then a consent page where they choose Allow connection. The assistant then receives the Hub's tools (including list_skills, load_skill, list_knowledge and read_knowledge) and, when it connects, the Hub's instructions from AGENTS.md or its mcp_instructions frontmatter. Restricted tools follow the same permissions as chat, and every restricted call is audited. The MCP server leaves out the chat-only tools, delegates and the Hub's MCP servers and connectors, because the assistant brings its own model and environment.
A common pattern uses both: builders keep working in their assistant against the Hub, and everyone else uses the same Hub in chat and on schedules.
If something goes wrong
| What you see | What to do |
|---|---|
Hubzoid no longer bundles templates. The minimal template was retired. | --template is gone and nothing was created. Run hubzoid init ops-briefing without it. |
| The Hub does not build after you copy a sub-agent | Its tools: is probably a comma-separated string or uses Claude Code tool names. Write a YAML list of Hubzoid tool names. |
An HTTP MCP server from .mcp.json never answers | Replace "type": "http" with "transport": "streamable-http", and set every ${NAME} in the Hub's .env. |
| The agent says it cannot run a script | A Hub agent has no shell. Move the script's logic into a tool in tools_local/. |
hubzoid doctor names a file in tools_local/ with an import error | The tool imports a sibling file or a missing package. Make the file self-contained or install the package. |
never called: open_orders in the eval | The agent answered without the tool. Name the tool in AGENTS.md or the skill, then run the eval again. |

