Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.cognite.com/llms.txt

Use this file to discover all available pages before exploring further.

This guide is for developers building Flows custom apps in Cognite Data Fusion (CDF). You will connect your app to the built-in CDF agent: expose app state as resources, register actions the agent can call, and send messages from your UI. Complete the prerequisites before you follow the integration steps. With @cognite/app-sdk@0.3.1 or later, you can:
  • Let the agent see your Flows custom app. Expose the app’s content and state as resources the agent can add to its context.
  • Let the agent interact with your Flows custom app. Register actions as tools the agent can use to perform tasks in your app on the user’s behalf.
  • Send messages from your Flows custom app to the agent. Trigger context-aware prompts when the user clicks a button or reaches a key point in a workflow.

Prerequisites

Before you start, ensure you have:
  • Access to Cognite Data Fusion (CDF) with appropriate capabilities. See Assign capabilities for more information.
  • A Flows development environment set up. See Get started with Flows.
  • @cognite/app-sdk@0.3.1 or later installed. This version adds the agent panel, messaging, and server registration APIs used in this guide.
    npm install @cognite/app-sdk
    
  • A HostAppAPI instance from connectToHostApp on mount:
    import { connectToHostApp } from '@cognite/app-sdk';
    
    const { api } = await connectToHostApp({ applicationName: 'my-app' });
    
connectToHostApp only works when your Flows app runs inside CDF (embedded in a CDF page with a parent host window). If you open only the Vite dev URL, for example, https://localhost:3001, without loading the app through CDF, there is no host to connect to and the promise rejects.Wrap the call in try/catch. When connect fails, skip registerAgentServer and other agent APIs, and hide or disable buttons that open the agent panel or send messages. See Development and production behavior for an example.

Integrate the agent

Use your HostAppAPI instance to control the agent panel, send messages from your app, and register an agent server with the resources and actions the agent can use.
1

Open the agent panel

Call api.sendAgentLayoutMode to change the visibility or layout of the agent panel.
await api.sendAgentLayoutMode({ mode: 'sidebar' });
AgentLayoutPayload options:
FieldTypeDescription
mode'sidebar' | 'fullscreen' | 'closed'Target panel layout.
conversationIdstring?Resume a specific existing conversation.
agentExternalIdstring?Target a specific registered agent.
sourceAgentSource?Surface identifier; omit unless required by your host.
{ mode: 'sidebar' } is the most common call, typically wired to a persistent toolbar button that opens the assistant at any time.
2

Send the agent a message

Call api.sendAgentMessage to inject text into the agent’s chat, optionally starting a fresh session. Use sendAgentMessage to pre-populate the agent with context when the user clicks a contextual trigger in your app.
await api.sendAgentMessage({
  message: 'Summarize the current state and highlight any risks.',
  newSession: true,
});
AgentMessagePayload options:
FieldTypeDescription
messagestringText to inject into the chat.
newSessionboolean?Clear the existing conversation before injecting.
Call sendAgentLayoutMode before sendAgentMessage. The agent panel must be open for the user to see the injected message.
await api.sendAgentLayoutMode({ mode: 'sidebar' });
await api.sendAgentMessage({
  message: `Analyze the schedule for "${tarName}" and suggest how to reduce the total duration.`,
  newSession: true,
});
Use newSession: true for contextual entry points where the user is starting a new task. Omit it when continuing an existing conversation.
3

Register the agent server

An agent server exposes resources (read-only context the agent can request) and actions (tools the agent can call). Register it once on mount, and unregister it on unmount.
import { createAgentServer, registerAgentServer } from '@cognite/app-sdk';

const server = createAgentServer({
  uri: 'my-app',           // namespaced by CDF with the instance ID
  actions: [...],
  resources: [...],
});

await registerAgentServer(api, server);

// On unmount:
await api.unregisterAgentServer('my-app');
In React, manage the lifecycle with useEffect:
useEffect(() => {
  if (!api) return;

  const server = createAgentServer({ uri: 'my-app', actions, resources });
  void registerAgentServer(api, server);

  return () => {
    void api.unregisterAgentServer('my-app');
  };
}, [api]);
4

Define resources

Resources are read-only data the agent reads to understand the current app state. Each resource has a URI, a name, and a read function that returns content.
import { createAgentResource } from '@cognite/app-sdk';

createAgentResource({
  uri: 'my-app://current-state',
  name: 'Current application state',
  description: 'Describes what data the agent will receive and when it should read this resource.',
  async read() {
    const data = getStateFromSomewhere();
    return [{ type: 'json', data }];
  },
})
The read function returns an array of content parts. Each element is one of:
  • { type: 'json', data: unknown } — structured data. The agent reasons over JSON directly; prefer this format when you can.
  • { type: 'text', text: string } — free-form text.
The description field is visible to the agent. Use it to explain what the resource contains and when to read it. Treat it like a function docstring.

Example: TAR planner resources

The TAR planner application exposes two resources. Both are populated from TARStorageService (localStorage) and the critical path algorithm, giving the agent a live view of what the user is managing.
createAgentResource({
  uri: 'tar-planner://current-tar',
  name: 'Current TARs',
  description: 'All TARs with name, facility, scope, dates, status, and work order count.',
  async read() {
    return [{ type: 'json', data: storage.getAllTars() }];
  },
});

createAgentResource({
  uri: 'tar-planner://schedule-summary',
  name: 'Schedule summary',
  description: 'Per-TAR computed schedule with critical path flags and float.',
  async read() {
    return [{ type: 'json', data: computeScheduleSummary(storage) }];
  },
});
5

Define actions

Actions are tools the agent can invoke. They can be read-only queries or mutating operations. Define parameters with a Zod schema. The .describe() call on each field is the agent’s documentation for that parameter.
import { createAgentAction } from '@cognite/app-sdk';
import { z } from 'zod';

createAgentAction({
  name: 'get_item_details',           // snake_case; shown to the agent as the tool name
  description: 'What this does, what it returns, and when to call it.',
  parameters: z.object({
    item_id: z.string().describe('The ID of the item to retrieve'),
    include_history: z.boolean().optional().describe('Whether to include audit history'),
  }),
  async handler({ item_id, include_history }) {
    const item = await myService.getItem(item_id, { history: include_history });
    return {
      content: [{ type: 'json', data: item }],
    };
  },
})

Mutating actions

Mutating actions can write to your app’s state.
The agent does not currently confirm with the user before calling mutating actions. Use mutating actions with caution and make the agent’s responsibility explicit in the action’s description.
createAgentAction({
  name: 'add_work_order_to_tar',
  description:
    'Add a maintenance order to a TAR. Call this ONLY when the user has explicitly approved. The app UI updates immediately.',
  parameters: z.object({
    tar_id: z.string().describe('The TAR to add the work order to'),
    maintenance_order_external_id: z.string().describe('The externalId of the order'),
  }),
  async handler({ tar_id, maintenance_order_external_id }) {
    storage.saveEntry({ tarId: tar_id, maintenanceOrderExternalId: maintenance_order_external_id, ... });
    return { content: [{ type: 'json', data: { success: true } }] };
  },
})
After a mutating action writes to your state layer, the UI updates on the next render cycle if your components read from the same state. No extra signaling is required.

Verify integration

Load your Flows custom app inside CDF (through the CDF development URL, not only https://localhost:3001). Confirm the following:
  1. Open the agent panel from your UI control or a sendAgentLayoutMode call.
  2. Send a test message and confirm it appears in the agent chat.
  3. Ask the agent a question that should use a registered resource and confirm the answer reflects your app state.
  4. If you registered mutating actions, test them only after you understand the risks in Mutating actions.
When integration works, the agent panel opens from your app, messages appear in chat, and resource-backed answers match the state your app exposes.
Separate the registration lifecycle from the action and resource definitions to test both independently.
src/features/agent/
  agentActions.ts     — pure factory: (deps) => Action[]
  agentResources.ts   — pure factory: (deps) => Resource[]
  useAgentServer.ts   — useEffect lifecycle hook; calls the factories and registers
Inject services as parameters rather than importing them directly. That approach lets you unit test actions and resources with mock services.
// agentActions.ts
export function buildAgentActions(
  storage: StorageService,
  dataService: DataService,
): AgentActions {
  return [
    createAgentAction({ ..., async handler({ id }) { return storage.get(id); } }),
  ];
}

// useAgentServer.ts
export function useAgentServer(api: HostAppAPI | null): void {
  const storage = useStorageService();    // injected via React context
  const dataService = useDataService();

  useEffect(() => {
    if (!api) return;
    const server = createAgentServer({
      uri: 'my-app',
      actions: buildAgentActions(storage, dataService),
      resources: buildAgentResources(storage, dataService),
    });
    void registerAgentServer(api, server);
    return () => { void api.unregisterAgentServer('my-app'); };
  }, [api, storage, dataService]);
}
To test buildAgentActions, pass mock service implementations and call handler directly without mounting the hook.

Development and production behavior

connectToHostApp behaves differently depending on where your app runs:
EnvironmentconnectToHostApp behaviorEffect
Inside CDFResolves with { api }Full agent integration functionality.
Standalone development (vite dev)RejectsAgent features disabled; hide or disable agent UI triggers.
Use the following pattern to handle both environments: catch rejections from connectToHostApp and disable agent features when your app isn’t running inside CDF.
let api: HostAppAPI | null = null;
try {
  ({ api } = await connectToHostApp({ applicationName: 'my-app' }));
} catch {
  // Not running inside CDF: agent features disabled
}
In React, hold api in state or context. When the host connection succeeds, your registration effect runs and UI components can show or hide agent triggers.

Further reading

Last modified on May 26, 2026