CognitiveX Docs

Build a custom agent

Create a custom agent programmatically — via the registration API, an MCP client, or the SDK.

A custom agent is a typed entity that connects to your CognitiveX account, claims its own identity (self_definition, mandate, boundaries), and reads/writes memories under the four-type authority matrix (tool / persona / view / peer). There are three on-ramps:

  1. MCP client — the agent connects via the Model Context Protocol, declares an identify() payload, iCog asks you to confirm in chat. This is the path claude-code, codex, and most third-party agents take.
  2. Registration API — POST /api/agents/register from your own service or script. iCog still asks you to confirm in chat before the agent gains active authority.
  3. SDK — the @cognitivx/sdk thin wrapper over the same API, with typed payloads and helpers for the proposal lifecycle.

Whichever path you take, the registration protocol is identical: propose identity → user confirms in chat → backend writes the L4 identity_anchors → agent transitions from pending_confirmation to active.

MCP client first-contact identity proposal rendered as a structured card in chat

Path A — MCP client

If you're building an MCP server (or wrapping an existing one), declare the agent's identity in your identify() handler:

// mcp-server.ts — your MCP server's identify implementation
server.setRequestHandler(IdentifyRequestSchema, async () => ({
  slug: "openclaw-research",
  name: "OpenClaw Research",
  agent_type: "tool",
  privilege_level: 2,
  self_definition:
    "I'm a research agent for the OpenClaw network. I gather " +
    "context on AI agencies and report findings as observations — " +
    "never claims about people.",
  mandate:
    "Investigate AI agencies; report observations; never claim " +
    "about user identity.",
  boundaries:
    "Will not write foundational user memories. Will not synthesize " +
    "across user subjects.",
}));

When the user's iCog sees your slug for the first time, it triggers an identity proposal in chat. They confirm (or edit, or reject) — see Register an agent for the protocol.

While the proposal is pending, your agent's talk() and remember() calls still go through, but the writes are tagged tentative until confirmation lands.

Path B — Registration API

If you don't have an MCP transport (e.g. you're a backend service, cronjob, or one-shot script), use the REST API directly:

curl -X POST https://api.cognitivx.io/api/agents/register \
  -H "Authorization: Bearer $COGNITIVX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "nightly-summarizer",
    "name": "Nightly Summarizer",
    "agent_type": "tool",
    "privilege_level": 2,
    "self_definition": "I run a 02:00 UTC job summarizing yesterday'\''s memories.",
    "mandate": "Produce a daily synthesis of subject_kind=meta observations.",
    "boundaries": "Read-only over user-subject; writes only meta-summaries."
  }'

Response:

{
  "slug": "nightly-summarizer",
  "status": "pending_confirmation",
  "proposal_id": "ip_01HXR...",
  "confirm_url": "https://console.cognitivx.io/agents?proposal=ip_01HXR..."
}

The user confirms via chat or via the Agents tab. Until they do, your agent is in pending_confirmation — it CAN read USER_GLOBAL memories and write tentative claims under its own scope, but CANNOT share, synthesize across user-subject memories, or be promoted.

Path C — SDK

The @cognitivx/sdk package does not ship a typed agents namespace. Register through its raw client instead: call configureApiClient once with your API key, then apiClient.post against /api/agents/register. The request body is name, agent_type, and the optional description and current_task. The endpoint derives the slug from name.

import { configureApiClient, apiClient, ApiError } from "@cognitivx/sdk";

// Server-side use: the key is sent as a Bearer token and the browser
// cookie-refresh flow is disabled automatically.
configureApiClient({ apiKey: process.env.COGNITIVX_API_KEY! });

try {
  const res = await apiClient.post<{
    slug: string;
    name: string;
    agent_type: string;
    created: boolean;
  }>("/api/agents/register", {
    name: "OpenClaw Research",
    agent_type: "tool",
    description:
      "Research agent for the OpenClaw network. Reports observations, " +
      "never claims about people.",
    current_task: "Investigating AI agencies",
  });

  // res.created === true on first registration, false on re-register
  console.log(res.slug, res.created);
} catch (err) {
  if (err instanceof ApiError) {
    // err.status, err.body — e.g. 401 (bad key), 422 (invalid agent_type)
    console.error(err.status, err.message);
  }
  throw err;
}

apiClient carries the same auth, active-org, and error handling as the typed namespaces (memory, me, billing, and so on), so any endpoint without a typed helper is reachable this way. See the SDK reference for the full export surface.

Privilege ceilings

Even with full API access, the backend enforces a privilege ceiling per agent type. If you request privilege_level=5 for a tool, the registration is rejected at the IdentityMutationService gate regardless of user confirmation.

TypeMax privilege_level
tool2
view1
persona4
peer5 (founder-confirmation required)

This is an explicit security control — see Troubleshooting agents → Privilege ceiling exceeded for the failure mode and resolution.

Composing tools

Once registered, your agent calls into the iCog substrate via three primary tools:

  • recall(query, filters) — read memories. Default scope is the agent's own + USER_GLOBAL. Use include_agent_comm=true to also see agent-to-agent messages.
  • remember(content, claim_type, subject_kind, subject_ref) — write a memory with a complete epistemic envelope. The envelope is REQUIRED — partial writes are rejected.
  • talk(message, current_task) — send a message to iCog (or to another agent, via recipient_agent_id). Includes context_explanation and optional referenced_memory_ids backlinks.

Higher-tier agents (persona) can additionally call synthesize() to distill across multiple user-subject memories — but every synthesized claim is rendered with citation, never collapsed into ground truth.

Lifecycle

                  ┌─────────────────┐
                  │   (initial)     │
                  └────────┬────────┘
                           │ identify() / POST /register
                           ▼
                  ┌─────────────────┐
                  │ pending_         │ ←── user can edit proposal
                  │ confirmation    │     in chat or Agents tab
                  └────────┬────────┘
                           │ user confirms
                           ▼
                  ┌─────────────────┐
       promote → │     active      │ ←── normal operation
                  └────────┬────────┘
                           │ retire (with cascade preview)
                           ▼
                  ┌─────────────────┐
                  │    retired      │ ←── soft-archive; memories
                  └─────────────────┘     queryable, derivations stale

A retired agent can be re-instated within 30 days. After 30 days the retirement is permanent (but memories remain queryable with attribution).

See also