---
title: "TypeScript SDK for Grafana plugins | Grafana Cloud documentation"
description: "Use the @grafana/assistant package to integrate AI assistance into your Grafana plugins and apps."
---

> For a curated documentation index, see [llms.txt](/llms.txt). For the complete documentation index, see [llms-full.txt](/llms-full.txt).

# TypeScript SDK for Grafana plugins

The `@grafana/assistant` package provides a lightweight TypeScript SDK for integrating Grafana Assistant into your Grafana plugins and applications.

## What you’ll achieve

This article helps you integrate the Assistant into your plugin UI in a few targeted ways:

- Check availability: Detect whether the Assistant is available before rendering UI.
- Open the Assistant: Trigger the Assistant with prompts and context.
- Use React hooks: Work with Assistant state and actions in components.
- Use UI components: Add prebuilt buttons and prompt cards that open the Assistant.
- Generate inline content: Add AI-powered text inputs and text areas to forms.
- Register page context: Provide dashboard, data source, or structured context.
- Update context dynamically: Adjust context in response to user actions.

## Before you begin

Make sure you have the following:

- A Grafana deployment with the Grafana Assistant app installed and enabled. This can be a Grafana Cloud stack or a self-managed Grafana instance connected to a Grafana Cloud Assistant backend.
- An existing Grafana plugin or app project to integrate with.
- Node.js 22 and npm installed locally.
- Access to install the `@grafana/assistant` package from npm.

## When to use the TypeScript SDK

Use the TypeScript SDK when:

- Building Grafana plugins or extensions
- Adding AI assistance to custom Grafana apps
- Integrating Assistant features into Grafana UI workflows
- Creating context-aware help experiences based on page location

## Install the SDK

Install the package from npm:

Bash ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```bash
npm install @grafana/assistant
```

The package contains TypeScript type definitions, helper functions, hooks, and UI components for Grafana plugin code.

## Create and monitor investigations

Use `createInvestigation`, `getInvestigationStatus`, and `getInvestigationReport` from a Grafana plugin to start an investigation, read its lifecycle state, and retrieve its report. They call the [public-preview investigation HTTP API](/docs/grafana-cloud/platform/grafana-assistant/reference/http-apis/#manage-investigations) through Grafana’s plugin proxy as the signed-in user. Your backend must expose the v2 investigation routes.

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
import { createInvestigation, getInvestigationStatus, getInvestigationReport } from '@grafana/assistant';

async function startInvestigation(signal: AbortSignal) {
  return createInvestigation({ instruction: 'Investigate elevated API latency' }, { signal });
}

async function readInvestigation(investigationId: string, signal: AbortSignal) {
  const status = await getInvestigationStatus(investigationId, { signal });
  const report = status.state === 'completed'
    ? await getInvestigationReport(investigationId, { signal })
    : undefined;
  return { status, report };
}
```

Start from an explicit user action and retain the returned `investigationId` for reads. The separate `chatId` identifies the backing conversation. Each method unwraps and validates one response. The caller owns error handling, polling intervals, and deadlines; there are no automatic retries or error notifications. Aborting a request doesn’t cancel server-side work, so don’t retry creation automatically after an ambiguous failure.

Use `state` to track progress. Stop ordinary polling for `completed`, `failed`, or `cancelled`; `paused` needs intervention. Handle unknown or absent states with a deadline. Report fields are optional even when `reportPending` is false, and a snapshot can return 404 before the session starts. HTTP errors preserve Grafana’s `FetchError` contract; malformed responses reject with a validation error.

Refer to **Create and monitor investigations** in the Integration hub and the SDK README for the full method contract. Use the HTTP reference for server-side authentication and team visibility. Never put service account tokens in browser code. Use `observeInvestigation` below for live updates.

The separate `useInvestigationThroughLens` hook derives a structured view from an existing report using a JSON Schema. Refer to **Structured investigation views (lens)** in the Integration hub for an example.

### Observe live progress

`observeInvestigation(investigationId)` returns an RxJS `Observable<InvestigationEvent>`. It emits a current `status` snapshot, available persisted `report` snapshots, and best-effort live text `progress` events. Each subscription opens its own authenticated event stream and periodically refreshes persisted state.

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
import { observeInvestigation, type InvestigationEvent } from '@grafana/assistant';

// Pass your UI's update, error, and completion handlers.
function watchInvestigation(
  investigationId: string,
  onEvent: (event: InvestigationEvent) => void,
  onError: (error: unknown) => void,
  onComplete: () => void,
) {
  const subscription = observeInvestigation(investigationId).subscribe({
    next: onEvent,
    error: onError,
    complete: onComplete,
  });

  // Call when leaving the view. The investigation keeps running.
  return () => subscription.unsubscribe();
}
```

Handle events by `event.type`: read `event.status` for lifecycle changes, `event.report` for persisted report updates, and append `event.delta` by `(event.messageId, event.contentIndex)` for live text. Report snapshots can lag in-flight report edits. Text emitted before connection or during a disconnect is not replayed.

The Observable completes after a terminal investigation state (`completed`, `failed`, or `cancelled`) and a report check with no pending report. A terminal investigation may have no report. Paused investigations remain observable. Investigation failure is a status event; transport or validation failure goes to `error` and closes the subscription. There is no automatic reconnect. Subscribe again to recover current snapshots, and handle errors in your UI.

Unsubscribe when leaving the view to close the connection and abort pending reads. This stops observation only; the investigation keeps running. Refer to the SDK README for event payloads and connection behavior.

## Extend the Assistant UI

To add conversation starters, expanded tool details, inline reply components, or artifact renderers from your app plugin, refer to [Extend Assistant from an app plugin](/docs/grafana-cloud/platform/grafana-assistant/reference/plugin-extensions). The extension manifest API is experimental and requires enablement in your Assistant environment.

## Key capabilities

Use these capabilities to integrate Assistant entry points and context into your plugin:

### Check availability

Detect if the Assistant is available in the current environment before showing UI elements:

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
import { isAssistantAvailable } from '@grafana/assistant';
import { firstValueFrom } from 'rxjs';

async function checkAvailability() {
  const available = await firstValueFrom(isAssistantAvailable());
  console.log('Assistant available:', available);
}
```

### Open the Assistant programmatically

Open the Assistant with custom prompts and context from your code:

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
import { openAssistant } from '@grafana/assistant';

openAssistant({
  origin: 'grafana/my-plugin',
  prompt: 'Show me CPU usage over the last hour',
  autoSend: true,
});
```

**Parameters:**

- `origin` (required): Namespace identifying your plugin (e.g., `grafana/my-plugin/feature`)
- `prompt` (optional): Pre-filled prompt text
- `autoSend` (optional): Automatically send the prompt when opening
- `context` (optional): Additional context items (dashboards, datasources, structured data)

### Use React hooks

Use the `useAssistant()` hook for reactive integration in React components:

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
import { useAssistant } from '@grafana/assistant';

export function MyComponent() {
  const { isLoading, isAvailable, openAssistant, closeAssistant } = useAssistant();

  if (isLoading || !isAvailable) {
    return null;
  }

  return (
    <button onClick={() => openAssistant?.({
      origin: 'grafana/my-plugin',
      prompt: 'Help me analyze this data'
    })}>
      Ask Assistant
    </button>
  );
}
```

### Use UI components

Use the `OpenAssistantButton` component for drop-in integration:

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
import { OpenAssistantButton } from '@grafana/assistant';

export function MyComponent() {
  return (
    <OpenAssistantButton
      prompt="Analyze this dashboard"
      origin="grafana/my-plugin/dashboard"
    />
  );
}
```

The button automatically hides when the Assistant is unavailable.

#### Add query-editor assistance

Use `QueryWithAssistantButton` in data source query editors. The component passes the selected data source, current query, and other non-empty queries to Assistant:

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
import { CoreApp } from '@grafana/data';
import { QueryWithAssistantButton } from '@grafana/assistant';

export function QueryEditorActions() {
  return (
    <QueryWithAssistantButton
      currentQuery={query}
      queries={queries}
      dataSourceInstanceSettings={dataSourceInstanceSettings}
      datasourceApi={datasourceApi}
      app={CoreApp.Explore}
      title="Query with Assistant"
    />
  );
}
```

The component opens Assistant with a prompt to create or update a query and automatically hides when Assistant is unavailable.

#### Add a prompt card

Use `AssistantPromptCard` to embed an Assistant prompt entry point in a plugin surface:

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
import { AssistantPromptCard, createAssistantContextItem } from '@grafana/assistant';

const context = [
  createAssistantContextItem('dashboard', {
    dashboardUid: 'abc123',
    dashboardTitle: 'Service health',
  }),
];

export function AssistantEntryPoint() {
  return (
    <AssistantPromptCard
      origin="grafana/my-plugin/dashboard"
      context={context}
      mode="dashboarding"
      placeholder="Ask Assistant about this dashboard"
      examplePrompts={[
        'Find panels with high error rates',
        'Suggest dashboard improvements',
      ]}
    />
  );
}
```

The prompt card opens Assistant with the user’s typed prompt and provided context. It renders nothing when Assistant is unavailable.

#### Generate inline form content

Use `AITextInput` and `AITextArea` when you want Assistant to generate content inside an existing form instead of opening the Assistant sidebar. These components are controlled inputs and call your `onChange` handler as generated text is applied.

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
import { AITextInput, AITextArea } from '@grafana/assistant';
import { useState } from 'react';

export function PanelMetadataEditor() {
  const [title, setTitle] = useState('');
  const [description, setDescription] = useState('');

  return (
    <>
      <AITextInput
        value={title}
        onChange={setTitle}
        origin="grafana/my-plugin/panel-title"
        systemPrompt="Generate a concise panel title. Return only the title text."
        placeholder="Ask AI to generate a title"
        autoGenerate
        streaming
      />
      <AITextArea
        value={description}
        onChange={setDescription}
        origin="grafana/my-plugin/panel-description"
        systemPrompt="Generate a clear panel description in 2 sentences."
        getUserPrompt={(text) => `Write a panel description using this context: ${text}`}
        rows={4}
        streaming
      />
    </>
  );
}
```

Use `autoGenerate` to generate once on mount when the value is empty, `streaming` to stream generated text into the input, and `getUserPrompt` to transform the user’s input into the prompt sent to Assistant.

### Register page context

Automatically provide context to the Assistant based on the current URL:

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
import { providePageContext, createAssistantContextItem } from '@grafana/assistant';

// Register context for dashboard pages
const dashboardContext = createAssistantContextItem('structured', {
  data: {
    name: 'Dashboard Context',
    pageType: 'dashboard',
    capabilities: ['analyze', 'troubleshoot', 'optimize'],
    help: 'I can help you analyze dashboard data and optimize performance.',
  },
});

// Register for all dashboard URLs
providePageContext('/d/*', [dashboardContext]);
```

**URL pattern matching:**

- Wildcards: `/d/*` matches `/d/abc`
- Glob patterns: `/explore**` matches `/explore` and all subpaths
- Regex: `/^\/datasources\/edit\/[^\/]+$/` for complex patterns

### Update context dynamically

Update context as users interact with your application:

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
const setContext = providePageContext('/d/*', [dashboardContext]);

// Update when user selects a panel
function onPanelSelect(panelId: string, panelTitle: string) {
  const panelContext = createAssistantContextItem('structured', {
    data: {
      name: 'Selected Panel',
      panelId,
      panelTitle,
      actions: ['edit', 'duplicate', 'inspect'],
    },
  });

  setContext([dashboardContext, panelContext]);
}
```

## Choose context item types

Use context items to provide relevant information to the Assistant. The SDK supports three context types:

### Dashboard context

Provide the current dashboard to give the Assistant page awareness:

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
createAssistantContextItem('dashboard', {
  dashboardUid: 'abc123',
  dashboardTitle: 'My Dashboard',
});
```

### Data source context

Provide the active data source to scope Assistant suggestions:

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
createAssistantContextItem('datasource', {
  datasourceUid: 'prom-123',
});
```

### Structured data context

Provide arbitrary JSON to describe custom state or metadata:

typescript ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```typescript
createAssistantContextItem('structured', {
  data: {
    // Any JSON-serializable data
    name: 'Custom Context',
    metadata: { ... }
  }
})
```

All context item helpers accept `bypassLimits`. Use `bypassLimits: true` only for fixed-size static content such as bounded metadata, static instructions, or small configuration objects. Keep the default for query results, logs, user-generated content, or any content that can grow unexpectedly.

## Try interactive examples

Explore interactive, working examples in Grafana Assistant:

1. Navigate to **AI** &gt; **Assistant** in the main menu
2. Click **Integration hub**
3. Explore the TypeScript SDK section with:
   
   - Live code examples you can modify and test
   - Complete working samples for each API
   - Copy-to-clipboard code snippets
   - Interactive forms to try different parameters

## Next steps

Continue by exploring these resources:

- Open **Grafana Assistant** and click **Integration hub** to try interactive SDK examples
- View the [package on npm](https://www.npmjs.com/package/@grafana/assistant)
- Learn about [Grafana plugin development](/docs/grafana/latest/developers/plugins/)
