Agents and chat
teamapi serve-mcp examples/acme-org starts an MCP server you can point Claude Desktop or Claude Code at, then ask about ACME Org like you'd ask a colleague — "who owns checkout-api?", "which team's overloaded?" — no query language needed.
The core tools are list_teams, get_team, get_team_roles, get_team_cognitive_load, find_service_owner, list_services, get_team_interactions, get_team_dependencies, get_context_map, render_org_diagram, search_org, get_org_graph, get_org_cognitive_load_report, and get_org_gaps. Each AI-native section adds a list_*/get_* pair — list_agents/get_agent, list_prompts/get_prompt, and so on — alongside render_prompt, get_context_bundle, get_knowledge_graph, and traverse_knowledge_graph.
Example: an assistant calling find_service_owner with { "serviceName": "checkout-api" }
{
"teamId": "stream-checkout",
"service": {
"name": "checkout-api",
"versioning": { "type": "semantic" },
"repository": "https://github.com/acme-example/checkout-api",
"boundedContext": {
"ubiquitousLanguage": [
{ "term": "Cart", "definition": "An in-progress, unpaid order" },
{ "term": "Order", "definition": "A cart that has been placed and paid for" }
],
"aggregates": ["Cart", "Order"],
"publishedEvents": ["OrderPlaced"],
"subscribedEvents": ["ChargeAuthorized", "ApplicantActivated"]
}
}
}
One endpoint for the whole org#
serve-mcp uses stdio and suits a local assistant. Organization-wide use needs another transport: stdio requires the documents on the same machine as the model, leaving every laptop with a separate copy of the org graph that is only as current as its last pull.
--mcp on serve-api serves the same tools over Streamable HTTP, on the same port and behind the same token as everything else:
TEAMAPI_API_TOKEN=$(openssl rand -hex 32) teamapi serve-api org \
--host 0.0.0.0 --mcp --watch
REST API listening on http://0.0.0.0:3000
Authentication: bearer token required
MCP (Streamable HTTP): http://0.0.0.0:3000/mcp
Reload: on file change, POST /reload, or SIGHUP
One repository behind one endpoint is the same answer for everybody, and with --watch it's the answer as of the last commit rather than the last time each person pulled.
The endpoint is stateless, creating a fresh server and transport for each request without issuing a session id. Every tool is a pure read of the org graph, so there is no per-client state to keep between calls. Any instance behind a load balancer can answer any request.
Chat#
teamapi chat examples/acme-org --team stream-checkout starts an interactive session where the assistant speaks as that team. With --member <id>, it speaks as one specific person. A live tool-use loop provides the same org-graph operations exposed by the MCP server, allowing the persona to answer questions about any team. Add --debug to see the persona's system prompt and every tool call as it happens.
export ANTHROPIC_API_KEY=sk-ant-...
teamapi chat examples/acme-org --team stream-checkout --member diego-alves
Example:
Chatting as Diego Alves via anthropic (claude-opus-4-8).
Type 'exit' or Ctrl+D to quit.
You> is payments overloaded right now?
Diego Alves> Checked Platform Payments' latest self-assessment — they're running "elevated,"
not overloaded. PCI compliance scope is adding real intrinsic load, and their onboarding docs
could use work, but nothing critical right now.
Example, with --debug — every tool call the persona makes, shown inline:
$ teamapi chat examples/acme-org --team stream-checkout --member diego-alves --debug
Chatting as Diego Alves via anthropic (claude-opus-4-8).
Type 'exit' or Ctrl+D to quit.
--- system prompt ---
You are Diego Alves (Checkout Tech Lead) on Stream Checkout, a stream-aligned team focused on:
Shopping cart, checkout flow, and order placement.
Use the available tools to look up real facts about this team or any other team in the org —
including cognitive load, services, interactions, and dependencies. Don't guess or make up
specifics; look them up. Stay in character, but you can discuss any team in the org when asked.
---------------------
You> what's the worst thing about your job?
⚙ get_team_cognitive_load({"teamId":"stream-checkout"})
→ {
"teamId": "stream-checkout",
"total": 18,
"label": "overloaded",
"assessment": {
"intrinsic": 6,
"extraneous": 8,
"germane": 4,
"notes": "High extraneous load from juggling three upstream integrations (payments,
onboarding, fulfillment) with inconsistent contracts; a strong candidate for an
anticorruption layer."
}
}
Diego Alves> Honestly? We're carrying real extraneous load — three upstream integrations
(payments, onboarding, fulfillment) with inconsistent contracts, so a chunk of my week goes to
translating between them instead of building. Our own self-assessment flags us as "overloaded."
An anticorruption layer would help a lot.
Which model#
Two providers, which is what the landscape actually has: the Anthropic API, and the OpenAI Chat Completions wire format that everything else speaks.
# Anthropic (the default). Needs ANTHROPIC_API_KEY.
teamapi chat examples/acme-org --team stream-checkout
# OpenAI, or Azure, or Together, or Groq, or OpenRouter — anything speaking that format.
teamapi chat examples/acme-org --team stream-checkout \
--provider openai --model gpt-4o
# A model on your own machine. No key, no account, no data leaving the laptop.
teamapi chat examples/acme-org --team stream-checkout \
--provider openai --base-url http://localhost:11434/v1 --model llama3.1
The OpenAI path is fetch against a base URL rather than a vendor SDK, which is the point: a base URL and an optional bearer token reach Azure OpenAI, Ollama, vLLM, llama.cpp, Together, Groq, Fireworks, OpenRouter and most self-hosted gateways. A third provider is a --base-url, not a release.
One question, one answer#
--ask runs a single turn and exits, which is what makes this usable from something other than a keyboard:
teamapi chat examples/acme-org --team stream-checkout --ask "who owns checkout-api, and are they overloaded?"
Everything except the answer goes to stderr — the banner, the tool-call progress, the note about a turn that ended early — so stdout is exactly the answer and the command composes with a pipe:
OWNER=$(teamapi chat ./org --team platform-payments --quiet \
--ask "reply with only the team id that owns the ledger service")
It exits 2, not 0, when the answer is incomplete — the model hit the tool-call ceiling, or the response was truncated. A script acting on half a reply is the failure this mode is most likely to cause and least likely to notice.