Skip to content

Commit da955ee

Browse files
committed
docs: add AI toolkit documentation WebMCP
1 parent 0b97149 commit da955ee

9 files changed

Lines changed: 1212 additions & 1 deletion

File tree

docs/ai/ai-toolkit.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
The AI Toolkit is an optional external resource (plugin) package that bridges SlickGrid with the browser's [Web Model Context Protocol (WebMCP)](https://github.com/webmcp/webmcp). When a user — or an automated agent — makes a natural-language request about the grid ("show me only High-priority tasks sorted by duration"), this package provides the standard MCP surface that lets an AI assistant discover what the grid looks like, read its current state, and push changes back to it — all without any custom glue code in your application.
44

5-
It is inspired by the [AG Grid AI Toolkit](https://www.ag-grid.com/angular-data-grid/ai-toolkit/) and follows the same general pattern: provide the LLM with a structured schema of the grid so it understands what it can act on, then let it produce a state object that is applied back to the grid.
5+
It is inspired by the [AG Grid AI Toolkit](https://www.ag-grid.com/angular-data-grid/ai-toolkit/) and follows the same general pattern: provide the LLM with a structured schema of the grid so it understands what it can act on, then let it produce a state object that is then applied back to the grid.
66

77
---
88

frameworks/angular-slickgrid/docs/TOC.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
## Features
4646

4747
* [Accessibility (A11y)](features/accessibility.md)
48+
* [AI Toolkit](ai/ai-toolkit.md)
4849

4950
## Slick Grid/DataView Objects
5051

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
# AI Toolkit (`@slickgrid-universal/web-mcp`)
2+
3+
This page describes using the AI Toolkit from the Angular Slickgrid wrapper.
4+
5+
The AI Toolkit is an optional external resource package that bridges SlickGrid with the browser's [Web Model Context Protocol (WebMCP)](https://github.com/webmcp/webmcp). When a user — or an automated agent — makes a natural-language request about the grid (for example, "show me only High-priority tasks sorted by duration"), this package provides the standard MCP surface that lets an AI assistant discover what the grid looks like, read its current state, and push changes back to it — all without any custom glue code in your application.
6+
7+
It is inspired by the [AG Grid AI Toolkit](https://www.ag-grid.com/angular-data-grid/ai-toolkit/) and follows the same general pattern: provide the LLM with a structured schema of the grid so it understands what it can act on, then let it produce a state object that is then applied back to the grid.
8+
9+
---
10+
11+
## Why use this?
12+
13+
Data grids are powerful but their filter and sort UIs can be intimidating for non-technical users and verbose to drive from automated agents. The AI Toolkit solves this by exposing a well-defined, LLM-friendly interface on top of SlickGrid's existing services.
14+
15+
**Key use cases:**
16+
17+
- **Natural-language grid queries** — let users type or speak queries like _"show me overdue tasks assigned to Alice, sorted by priority"_ and have an in-app assistant translate them directly into grid filters and sorts, no filter UI required.
18+
- **AI-powered dashboards** — embed a Copilot/GPT-style assistant sidebar in your application that can drive the grid on the user's behalf, reducing friction for complex multi-column filtering scenarios.
19+
- **Playwright / MCP browser automation** — Playwright's MCP-enabled browser mode can call `get_slickgrid_schema` and `apply_slickgrid_state` directly, making it trivial to write intent-based E2E tests: _"filter by status = Done, then assert row count"_ rather than hard-coding CSS selectors.
20+
- **Accessibility** — users who find filter forms difficult to use can interact with the grid through a text/voice interface backed by an LLM.
21+
- **Developer productivity** — during local development, ask an AI agent to pre-populate filters for a specific scenario without manually clicking through the UI every time.
22+
23+
Because the package is purely opt-in and makes no changes to `@slickgrid-universal/common` or any framework wrapper, adding it carries zero cost for applications that do not use it.
24+
25+
---
26+
27+
## How It Works
28+
29+
1. **Schema discovery** — the LLM calls `get_slickgrid_schema` to learn the columns available (id, type, sortable, filterable).
30+
2. **State snapshot** — the LLM calls `get_slickgrid_state` to understand what filters/sorts/column visibility are currently active.
31+
3. **Prompt + LLM call** — your application sends the user query, the schema and the current state to an LLM of your choice.
32+
4. **State application** — the LLM response is passed to `apply_slickgrid_state`, which updates filters, sorting and column visibility in a single call.
33+
34+
```
35+
User query
36+
37+
38+
get_slickgrid_schema + get_slickgrid_state → LLM → apply_slickgrid_state
39+
```
40+
41+
---
42+
43+
## Installation
44+
45+
```bash
46+
npm install @slickgrid-universal/web-mcp
47+
```
48+
49+
## Registration
50+
51+
```ts
52+
import { WebMcpService } from '@slickgrid-universal/web-mcp';
53+
54+
const gridOptions = {
55+
externalResources: [new WebMcpService()],
56+
// ...
57+
};
58+
```
59+
60+
The service silently no-ops when the browser does not expose `navigator.modelContext`, so it is safe to include unconditionally.
61+
62+
---
63+
64+
## WebMCP Tools
65+
66+
All tool names are suffixed with the grid's UID to support multiple grids on the same page.
67+
68+
| Tool | Description |
69+
|---|---|
70+
| `read_slickgrid_data_<uid>` | Returns current data rows. Accepts an optional `limit` (default 20). |
71+
| `get_slickgrid_schema_<uid>` | Returns column metadata (id, field, type, filterable, sortable). Call this first so the LLM knows what columns exist. |
72+
| `get_slickgrid_state_<uid>` | Returns the current grid state: active filters, active sorters and visible column ids. |
73+
| `apply_slickgrid_state_<uid>` | Applies a full or partial grid state. Any omitted key leaves that aspect unchanged. |
74+
75+
### `apply_slickgrid_state` payload
76+
77+
```ts
78+
{
79+
// optional — replaces all active filters
80+
filters?: Array<{
81+
columnId: string;
82+
searchTerms: string[];
83+
operator?: 'EQ' | 'NE' | 'GT' | 'GE' | 'LT' | 'LE' | 'CONTAINS' | 'NOT_CONTAINS' | 'IN' | 'NIN';
84+
}>;
85+
86+
// optional — replaces all active sorts
87+
sorters?: Array<{
88+
columnId: string;
89+
direction: 'ASC' | 'DESC';
90+
}>;
91+
92+
// optional — ids of columns that should be visible (all others are hidden)
93+
visibleColumnIds?: string[];
94+
}
95+
```
96+
97+
---
98+
99+
## Public API
100+
101+
In addition to the WebMCP tools, these methods are available directly on the service instance for use without `navigator.modelContext` (e.g. integrating with a custom LLM call):
102+
103+
```ts
104+
// Column metadata as JSON Schema
105+
service.getStructuredSchema(): SlickColumnSchema[]
106+
107+
// Snapshot of current grid state
108+
service.getGridState(): SlickGridState
109+
110+
// Apply a full or partial state
111+
await service.applyGridState(state: Partial<SlickGridState>): Promise<void>
112+
```
113+
114+
### Example: custom LLM integration
115+
116+
```ts
117+
import { WebMcpService } from '@slickgrid-universal/web-mcp';
118+
119+
const mcpService = new WebMcpService();
120+
// mcpService is already init'd via externalResources
121+
122+
async function onUserQuery(userQuery: string) {
123+
const schema = mcpService.getStructuredSchema();
124+
const currentState = mcpService.getGridState();
125+
126+
const response = await callMyLlm({
127+
query: userQuery,
128+
schema,
129+
currentState,
130+
});
131+
132+
await mcpService.applyGridState(response.newState);
133+
}
134+
```
135+
136+
---
137+
138+
## Prompting
139+
140+
The AI Toolkit does not include any prompting logic — the right prompt depends on your LLM and your data. A few practices that consistently improve results:
141+
142+
- **Include the current grid state** so the LLM understands what is already applied.
143+
- **Include a few sample rows** (or the full dataset for small data) so the LLM understands the data format and domain values.
144+
- **Ask for an `explanation` string** alongside the state change — your UI can show users what changed.
145+
- **List the available features** (filtering, sorting, column visibility) so the LLM knows what it can and cannot do.
146+
- **Include domain context** inline, e.g. _"In this dataset, 'priority' values are 'Low', 'Medium' and 'High'"_.
147+
- **Ask for only the changed state**, not the full state, so that unchanged properties are not accidentally reset.
148+
149+
### Starter system prompt
150+
151+
```ts
152+
const systemPrompt = `
153+
You are an expert data analyst working with a data grid.
154+
Respond to user requests by returning a JSON object with the following shape:
155+
156+
{
157+
"newState": { /* partial grid state — only include what changed */ },
158+
"propertiesToIgnore": [ /* list state keys you did NOT change */ ],
159+
"explanation": "short human-readable description of changes"
160+
}
161+
162+
Available state keys: "filters", "sorters", "visibleColumnIds".
163+
164+
Current grid schema (columns and their capabilities):
165+
${JSON.stringify(schema, null, 2)}
166+
167+
Current grid state:
168+
${JSON.stringify(currentState, null, 2)}
169+
`;
170+
```
171+
172+
Using `propertiesToIgnore` is optional but recommended: pass it as a hint to `applyGridState` so that partial responses do not inadvertently clear state keys the LLM left out.
173+
174+
---
175+
176+
## Schema validation
177+
178+
When using an LLM that does not support [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs), the response may not always conform to the expected shape. Validate it against a JSON schema before calling `applyGridState` to avoid runtime errors:
179+
180+
```ts
181+
import Ajv from 'ajv';
182+
183+
const ajv = new Ajv();
184+
const validate = ajv.compile({
185+
type: 'object',
186+
properties: {
187+
filters: { type: 'array' },
188+
sorters: { type: 'array' },
189+
visibleColumnIds: { type: 'array', items: { type: 'string' } },
190+
},
191+
additionalProperties: false,
192+
});
193+
194+
const parsed = JSON.parse(llmResponse);
195+
if (!validate(parsed.newState)) {
196+
console.error('LLM returned invalid grid state', validate.errors);
197+
return;
198+
}
199+
200+
await mcpService.applyGridState(parsed.newState);
201+
```
202+
203+
---
204+
205+
## Handling schema size
206+
207+
`getStructuredSchema()` returns metadata for every column. For grids with many columns this can inflate your prompt and exceed the LLM's context window. Practical mitigations:
208+
209+
- **Filter the schema** before sending — strip columns that should not be AI-manipulable:
210+
```ts
211+
const schema = mcpService.getStructuredSchema().filter(col => col.filterable || col.sortable);
212+
```
213+
- **Add column descriptions sparingly** — if you augment schema entries with free-text descriptions, keep them concise.
214+
- **Monitor total prompt size** — aim to leave at least 20–25 % of the context window for the model's response.
215+
216+
---
217+
218+
## Extending
219+
220+
Override `_registerDefaultTools()` to add custom tools or replace the built-in ones:
221+
222+
```ts
223+
import { WebMcpService, type WebMcpTool } from '@slickgrid-universal/web-mcp';
224+
225+
class MyMcpService extends WebMcpService {
226+
protected override _registerDefaultTools(modelContext: { registerTool: (t: WebMcpTool) => void }): void {
227+
super._registerDefaultTools(modelContext);
228+
229+
modelContext.registerTool({
230+
name: `highlight_row_${this._grid.getUID()}`,
231+
description: 'Highlights a specific row by its id.',
232+
inputSchema: {
233+
type: 'object',
234+
properties: { rowId: { type: 'number' } },
235+
required: ['rowId'],
236+
},
237+
execute: async ({ rowId }) => {
238+
// custom logic here
239+
return { status: 'success' };
240+
},
241+
});
242+
}
243+
}
244+
```
245+
246+
---
247+
248+
## Notes
249+
250+
- `apply_slickgrid_state` delegates to `FilterService.updateFilters`, `SortService.updateSorting` and `GridService.showColumnByIds` under the hood — all the same rules that apply to those services apply here (e.g. `enableFiltering` must be `true` in your grid options to use filters).
251+
- **Backend services (OData / GraphQL):** the service has no knowledge of remote data services. When your grid is backed by OData or GraphQL, each call to `applyGridState` will trigger a backend query just as a manual filter would. If you need to batch several state changes and fire only one request, call `filterService.updateFilters(filters, true, false)` / `sortService.updateSorting(sorters, false)` directly (the third argument suppresses the automatic backend call) and then trigger the backend query yourself once all changes are applied.
252+
- **Multiple grids on the same page:** every tool name includes the grid's UID suffix, so two grids each with their own `WebMcpService` register independent, non-conflicting tool sets.
253+
254+
### Try it locally
255+
256+
For quick experimentation you can use the Model Context Tool Inspector project which connects to the browser's WebMCP surface and lets you invoke registered tools interactively: https://github.com/beaufortfrancois/model-context-tool-inspector. It also supports recent browser LLM integrations (for example Chrome's Gemini Nano) so you can run free, local prompt-driven calls against your running demo without wiring a full assistant UI.
257+
258+
---
259+
260+
## Playwright / MCP example
261+
262+
Below is a minimal example that demonstrates the flow used by an MCP-capable client (for example a Playwright test running in an MCP-enabled browser):
263+
264+
1. Call `get_slickgrid_schema_<uid>` to discover column ids and types.
265+
2. Locate the `columnId` for the column you want to filter (e.g. `cost`).
266+
3. Call `filter_slickgrid_<uid>` (or `apply_slickgrid_state_<uid>`) with a validated payload.
267+
268+
Note: the exact tool invocation API depends on the MCP client/environment. The snippet below uses `navigator.modelContext.invokeTool(...)` as a concise example; replace with the appropriate client method if different.
269+
270+
```ts
271+
// Playwright (browser) context example — runs inside the page
272+
const uid = 'slickgrid_123456';
273+
const mc = (navigator as any).modelContext;
274+
275+
// 1) discover schema
276+
const schema = await mc.invokeTool(`get_slickgrid_schema_${uid}`, {});
277+
278+
// 2) find the cost column
279+
const costCol = schema.find((c: any) => c.field === 'cost' || (c.name && c.name.toLowerCase().includes('cost')));
280+
if (!costCol) throw new Error('cost column not found');
281+
282+
// 3) apply a filter: cost < 50
283+
await mc.invokeTool(`filter_slickgrid_${uid}`, {
284+
columnId: costCol.id,
285+
search: '50',
286+
operator: 'LT',
287+
});
288+
```
289+
290+
Tool payload (JSON) for the example above:
291+
292+
```json
293+
{
294+
"columnId": "cost",
295+
"search": "50",
296+
"operator": "LT"
297+
}
298+
```
299+
300+
Security note: exposing grid data and controls to external assistants is an opt-in decision. Consider adding consent gating, sanitization, logging and rate-limiting for production use.

frameworks/aurelia-slickgrid/docs/TOC.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
## Features
4545

4646
* [Accessibility (A11y)](features/accessibility.md)
47+
* [AI Toolkit](ai/ai-toolkit.md)
4748

4849
## Slick Grid/DataView Objects
4950

0 commit comments

Comments
 (0)