Architecture
System Overview
CRED Agent AI is a single-agent assistant. One agent loop, with a simplified prompt, handles every user request directly โ it plans, calls tools, and answers in one flat turn. There is no supervisor and no subagents on the customer chat path.
Its primary data edge is the cred-platform CLI, a self-describing binary
the agent shells out to for the highest-cardinality platform work (entity
lookups, list building, filtering/screening, chart and view/report authoring,
schema introspection, raw GraphQL). Alongside the CLI it still calls a residual
set of MCP tools (analytics, workflows, reports, data-science,
customer-connected) that the CLI does not yet cover, plus a document-export tool
and built-in web search. A lightweight skill system injects task-specific
instructions into the prompt when a turn matches.
This replaced the old supervisor/subagent design (COM-43041). Earlier, a routing supervisor delegated to ~7 domain subagents, each holding a partitioned slice of the MCP tool catalog, purely to keep any one agent's context small. The CLI collapsed the highest-cardinality tools into one surface, so that context-splitting layer was no longer needed and was removed. See What changed and why.
flowchart TB
subgraph inputs [Input Channels]
slack[Slack]
teams[Teams]
ws["GraphQL / WebSocket"]
linear[Linear]
batch["eval / BullMQ jobs"]
end
subgraph agent [cred-agent-ai]
messages["Messages Service\nhistory, memory, debug"]
processor["LLMProcessor\n(LLM_CONNECTOR_TOKEN)"]
single["SingleAgentConnector\none createAgent, name: assistant\nno supervisor ยท no subagents"]
skills["Skill registry\n(reports โ prompt)"]
end
subgraph tools [Tool surface for the single agent]
cli["cred-platform CLI\nPRIMARY data edge"]
residual["Residual MCP tools\nanalytics ยท workflows ยท reports\ndata-science ยท customer-connected"]
export["export_document"]
websearch["built-in web search"]
end
subgraph mcp [MCP Servers via gateway]
graphql["graphql-mcp-server"]
asst["assistant-mcp-server\nplatform_assistant ยท follow_up_assistant\nnl_company_search ยท 40+ tools"]
ds["data-science-mcp-server\n(internal supervisor + subagents,\nencapsulated as one tool)"]
devpipe["dev-pipeline-mcp-server\n(internal, not customer-facing)"]
end
router["Federated GraphQL\nApollo Router"]
slack --> messages
teams --> messages
ws --> messages
linear --> messages
batch --> messages
messages --> processor
processor --> single
skills -.instructions.-> single
single --> cli
single --> residual
single --> export
single --> websearch
cli --> router
residual --> asst
residual --> ds
export --> router
Core Design Principles
- One agent, one loop โ a single
createAgenthandles the whole turn; no routing supervisor, no subagent handoffs, no tool partitioning. - CLI as the primary data edge โ the
cred-platformCLI is the single, self-describing instrument for platform data. Curated/bespoke GraphQL tools collapse onto it; the agent discovers commands via--helprather than consuming pre-baked operations. - MCP for what the CLI doesn't cover โ analytics, workflows, reports, data-science, and customer-connected servers remain first-class tools loaded every turn.
- Skills, not subagents, carry task context โ dynamic, prompt-level instruction blocks injected when a turn matches, instead of a dedicated agent per domain.
- Context budget by supersession, not partition โ the CLI eats the highest-cardinality tool schemas, keeping the per-turn tool count well under the schema-overflow ceiling the old supervisor hit โ without splitting tools across agents.
- Security โ the agent mints a per-user service token for the CLI and MCP
calls (never forwards the inbound user JWT โ confused-deputy safe); the CLI
receives it via
--token-file, never on argv.
The Single-Agent Loop
How it works
- A message arrives from any ingress surface (Slack, Teams, WebSocket, Linear,
eval, or BullMQ). They all resolve the connector through one DI token
(
LLM_CONNECTOR_TOKEN), which is now bound toSingleAgentConnector. MessagesServicebuilds conversation context (history, memory, debug).SingleAgentConnector.runTurn():- Opens the CRED + customer MCP clients and discovers their tool catalogs (cached, tier-filtered, sanitized, schema-compacted).
- Builds the CLI tool and the export tool (singletons).
- Selects the visible tool set (
selectVisibleTools): drops the CLI-superseded MCP tools, applies the per-request source + vendor gates to the remaining MCP tools, and always includes the CLI + export. Built-in web search is appended. - Resolves active skills for the turn from the skill registry and composes their instructions into the system prompt.
- Creates one agent (
createAgent,name: "assistant") over the native Anthropic transport and runs a single streaming tool loop. - Because there is no supervisor node, the stream is flat: every
tool_use/tool_resultis top-level.
Tool surface
| Group | What it is | Notes |
|---|---|---|
cred_platform_cli |
The cred-platform binary as a tool |
Primary data edge: lists, company/person resolve + read, screening/filtering, chart authoring, view/report assembly, schema introspection, raw GraphQL. Self-describing via --help. |
| Residual MCP tools | analytics, workflows, reports, data-science, customer-connected | Loaded every turn; the CLI does not (yet) cover these. |
export_document |
Document/report export | Always included. |
| Built-in web search | Model-native web search | Appended by the caller. |
What the CLI supersedes (dropped from the loop's MCP set): company/person
search, list create/fill/add, FindLists, chart authoring
(CreateAgentCustomChart, CreateCustomChart, UpdateCustomChart,
FindDataDescriptions), and view/report assembly (CreateView,
AddViewWidget, UpdateViewWidget). One data path means the loop can't thrash
across two overlapping surfaces.
What stays on MCP (NOT superseded): person fit-ranking / warmth
(naturalLanguagePersonSearch, personsAtCompanyRankedByFit) and
collection-from-search โ the CLI has no equivalent path yet.
Excluded entirely: the Apollo-MCP introspection tools
(introspect/search/validate/execute) โ a generic "run arbitrary
GraphQL" surface the agent rabbit-holes into โ and follow_up_assistant, which
is host-invoked after a turn, never an LLM tool.
The skill system
Skills are plain instruction objects with a trigger (surface + keyword gate).
assistantSkillRegistry.resolve() picks the ones matching the turn, and
composeSkillInstructions folds them into the system prompt. Today the registry
holds one skill โ reports โ which carries shared report/dashboard build
mechanics and appends a report-template shape per turn when the intent matches
(templates are entries in report-templates.ts, not separate skills). New
assistant skills register in skills/index.ts.
What changed and why
The assistant used to run a routing supervisor over ~7 domain subagents, each
holding a partitioned slice of the MCP tool catalog. That layer existed for one
reason: context budget. The full MCP catalog approached ~180 tool schemas โ
too many to put in front of a single model โ so the supervisor split them across
subagents to keep any one agent's tool surface small, and used @agents: tags to
decide which tool went to which subagent.
The cred-platform CLI removed the reason for that split. By collapsing the
highest-cardinality tools (entity search, list build/fill, chart and view/report
authoring) into one self-describing binary, the per-turn tool count dropped well
under the schema-overflow ceiling โ so a single agent can now hold the whole
visible tool set at once. With the context pressure gone, the supervisor/subagent
layer was pure overhead and was removed (COM-43041).
Second-order wins from the collapse:
- One data path. With the CLI as the single data edge, the loop can't thrash across โ or fabricate around โ two overlapping surfaces (CLI vs. bespoke MCP GraphQL tools).
- Flat stream. No supervisor node means every
tool_use/tool_resultis top-level, so the deterministic rollup fires off a tool result directly. - Simpler prompt. One base prompt instead of a routing prompt plus seven domain prompts.
What to unlearn: there is no supervisor prompt and no per-domain subagent prompts
on this path; @agents: tags do not route the main assistant (see
the MCP-server doc); and names
like supervisorPromptSuffix are vestigial (see
Vestigial naming).
Where supervisor/subagent architecture still lives
Removing the supervisor from cred-agent-ai did not remove multi-agent
patterns from the platform. Two things are easy to conflate โ keep them separate:
-
The data-science MCP tool is internally a supervisor + subagent loop. Inside
assistant-mcp-server, the data-science pipeline runs an LLM-routed LangGraph with distinct roles (supervisor,data_selector,data_engineer,modeler,evaluator,predictor) as a durable Cloud Run Job. This is fully encapsulated โ the MCP client (and therefore the assistant) sees a single tool; the individual roles are never exposed. This layer is independent of the assistant's architecture and was untouched by COM-43041. -
platform_assistantis an MCP tool, not the assistant. Theassistant-mcp-serverexposes aplatform_assistanttool โ a read-only, RAG-over-docs helper that answers "how does feature X work" questions. It is not the customer-facingcred-agent-aiassistant; the single agent calls it as one of its residual MCP tools. It remains available to users. (Do not confuse "the assistant" with "the assistant-mcp-server" or itsplatform_assistanttool โ they are three different things.)
Core Modules
LLM processing (connectors/llm/)
| Component | Purpose |
|---|---|
SingleAgentConnector |
The live customer chat path: discovers tools, selects the visible set, composes skills, runs the single agent loop and streaming. |
tools/cred-platform-cli.tool.ts |
Wraps the cred-platform binary as a LangChain tool; token-file auth via an AsyncLocalStorage scope; within-turn call cache. |
agent/tool-selection.ts |
selectVisibleTools + the CLI_SUPERSEDED / excluded-tool lists. |
agent/lifecycle.ts |
MCP tool discovery (cache, tier filter, sanitize, compact). |
agent/streaming.ts |
Flat streaming pump (text / tool_use / tool_result). |
skills/ |
SkillRegistry, the reports skill, report templates, prompt composition. |
Vestigial naming.
LangGraphAgentConnectorandSupervisorLLMConnectorstill exist but are dormant โ kept only as a fallback until the supervisor stack is swept (M5). On the single-agent path, names likebuildSupervisorPromptSuffix/supervisorPromptSuffixrefer only to the per-turn context suffix (history + formatting + persona), not a supervisor. The@agents:tags do not affect the customer-facing single-agent path; vertical-agent routing may still use them elsewhere.
Messages module (messages/)
Central message-processing orchestrator: builds conversation context (history,
memory), persists conversation, and hands off to LLMProcessor, which resolves
LLM_CONNECTOR_TOKEN โ SingleAgentConnector.
MCP connector (connectors/mcp/)
McpHttpHelper.getClients()โ opens the CRED + customer MCP clients per turn, minting a per-user service token (never forwarding the inbound JWT).- Universal content sanitizer โ modifies tool descriptions/results and emits telemetry when it changes content.
Prompts (connectors/llm/agent/ + prompts/)
single-agent-prompt.tsโbuildAssistantSystemPrompt(base prompt + CLI help snapshot + composed skill instructions).- Persona prompt composer โ optional workspace/user memory + instruction blocks for the web surface.