Skip to content

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

  1. One agent, one loop โ€” a single createAgent handles the whole turn; no routing supervisor, no subagent handoffs, no tool partitioning.
  2. CLI as the primary data edge โ€” the cred-platform CLI is the single, self-describing instrument for platform data. Curated/bespoke GraphQL tools collapse onto it; the agent discovers commands via --help rather than consuming pre-baked operations.
  3. MCP for what the CLI doesn't cover โ€” analytics, workflows, reports, data-science, and customer-connected servers remain first-class tools loaded every turn.
  4. Skills, not subagents, carry task context โ€” dynamic, prompt-level instruction blocks injected when a turn matches, instead of a dedicated agent per domain.
  5. 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.
  6. 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

  1. 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 to SingleAgentConnector.
  2. MessagesService builds conversation context (history, memory, debug).
  3. SingleAgentConnector.runTurn():
  4. Opens the CRED + customer MCP clients and discovers their tool catalogs (cached, tier-filtered, sanitized, schema-compacted).
  5. Builds the CLI tool and the export tool (singletons).
  6. 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.
  7. Resolves active skills for the turn from the skill registry and composes their instructions into the system prompt.
  8. Creates one agent (createAgent, name: "assistant") over the native Anthropic transport and runs a single streaming tool loop.
  9. Because there is no supervisor node, the stream is flat: every tool_use / tool_result is 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_result is 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_assistant is an MCP tool, not the assistant. The assistant-mcp-server exposes a platform_assistant tool โ€” a read-only, RAG-over-docs helper that answers "how does feature X work" questions. It is not the customer-facing cred-agent-ai assistant; 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 its platform_assistant tool โ€” 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. LangGraphAgentConnector and SupervisorLLMConnector still exist but are dormant โ€” kept only as a fallback until the supervisor stack is swept (M5). On the single-agent path, names like buildSupervisorPromptSuffix / supervisorPromptSuffix refer 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.