Kortex — AI Integration
Kortex is the platform's built-in AI assistant. Forge apps can register tools and page context so the assistant understands what your app can do and where the user currently is. With a few lines of code, the user can talk to the assistant and it will call back into your app to read data, trigger actions, and navigate on the user's behalf.
Setup
Install the @ptkl/components package and import from the forge entry point:
For React apps, hooks are also available:
Getting Started
To make your app AI-ready, you only need two things:
- Register context — tell the assistant what the user is looking at.
- Register tools — tell the assistant what your app can do.
import { registerTool, registerContext } from '@ptkl/components/forge'
import { Platform } from '@ptkl/sdk'
const sdk = new Platform()
// 1. Describe the current page
registerContext(
'Order #1234',
'Viewing order details. Status: processing. Customer: John Doe.'
)
// 2. Give the assistant a tool it can call
registerTool(
'get_order_status',
'Returns the current status of an order by UUID.',
{
type: 'object',
properties: {
uuid: { type: 'string', description: 'Order UUID.' }
},
required: ['uuid']
},
async (args) => {
const order = await sdk.component('orders').get(args.uuid)
return { status: order.status, updated_at: order.updated_at }
}
)
Now a user can ask "What's the status of this order?" and the assistant will call your tool and answer.
Registering Tools
registerTool
import { registerTool } from '@ptkl/components/forge'
registerTool(name, description, parameters, execute, policy?)
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string |
✅ | Unique tool identifier (e.g. 'get_order_details'). |
description |
string |
✅ | What the tool does — the assistant reads this to decide when to use it. |
parameters |
object |
✅ | JSON Schema describing the tool's arguments. |
execute |
function |
✅ | The function that runs when the assistant calls the tool. Receives parsed arguments, returns the result. Can be sync or async. |
policy |
object |
❌ | Override the default confirmation behavior. See Tool Policies. |
unregisterTool
Remove a tool when it's no longer relevant — for example, when the user navigates away from the page.
Registering a tool with the same name replaces the old one
You don't need to call unregisterTool before re-registering. Kortex handles deduplication automatically.
Registering Context
registerContext
| Parameter | Type | Required | Description |
|---|---|---|---|
label |
string |
✅ | Short label for the current page (e.g. "Order #1234"). |
description |
string |
✅ | Describe what the user is currently seeing or doing. |
icon |
string |
❌ | Icon identifier for visual representation. |
The description is the most important part — it gives the assistant situational awareness. Be specific:
// ❌ Too vague
registerContext('Orders', 'The orders page.')
// ✅ Specific and useful
registerContext(
'Orders',
'Browsing the orders list. Filters: status=processing, date=last 7 days. 24 results shown.'
)
unregisterContext
Clear the context when the user leaves a page.
Tool Policies
Policies control whether the user sees a confirmation prompt before a tool executes. By default, Kortex infers the policy from the tool name:
| Name your tool with… | What happens |
|---|---|
get_*, list_*, find_*, search_* |
Runs automatically — no confirmation needed. |
set_*, save_*, add_*, update_* |
Asks the user to confirm before running. |
create_*, publish_*, delete_*, remove_* |
Asks to confirm — treated as a higher-risk action. |
Name your tools with clear prefixes
A tool named get_order will run without asking. A tool named delete_order will always ask for confirmation. Choose prefixes that match the action's risk level.
Overriding the default policy
If the automatic inference doesn't fit, pass a policy object as the last parameter:
registerTool(
'export_report',
'Export the current report as PDF.',
{ type: 'object', properties: {} },
() => exportToPDF(),
{ risk: 'low', requiresConfirmation: false }
)
| Policy field | Type | Description |
|---|---|---|
risk |
'low' \| 'medium' \| 'high' |
Risk level of the action. |
requiresConfirmation |
boolean |
Whether the user must approve before the tool runs. |
category |
string |
Category label (e.g. 'read', 'write', 'destructive'). |
fullAccessAuto |
boolean |
Auto-execute when the user has granted full access. |
Helper Visibility
Signal to the user that AI help is available on the current page by activating the Kortex button glow:
import { showHelper, hideHelper } from '@ptkl/components/forge'
showHelper() // show the glow indicator
hideHelper() // hide the glow indicator
React Hooks
For React apps, hooks handle registration and cleanup automatically:
useKortexTool
Registers a tool on mount, unregisters on unmount. Re-registers if the name, description, or parameters change.
import { useKortexTool } from '@ptkl/components/forge'
function OrderDetail({ orderId }) {
useKortexTool(
'get_order_status',
'Returns the current status of this order.',
{
type: 'object',
properties: {
uuid: { type: 'string', description: 'Order UUID.' }
},
required: ['uuid']
},
async (args) => {
const order = await sdk.component('orders').get(args.uuid)
return { status: order.status }
}
)
return <div>...</div>
}
useKortexContext
Sets the page context on mount, clears on unmount. Updates when label or description change.
import { useKortexContext } from '@ptkl/components/forge'
function OrderDetail({ order }) {
useKortexContext(
`Order #${order.number}`,
`Viewing order ${order.number}. Status: ${order.status}. Customer: ${order.customer_name}.`
)
return <div>...</div>
}
useKortexHelper
Shows the helper glow on mount, hides on unmount.
import { useKortexHelper } from '@ptkl/components/forge'
function ProductCatalog() {
useKortexHelper()
return <div>...</div>
}
useIsKortexAvailable
Check whether Kortex is available in the current environment.
import { useIsKortexAvailable } from '@ptkl/components/forge'
function MyApp() {
const kortexAvailable = useIsKortexAvailable()
return (
<div>
{kortexAvailable && <span>AI assistant is available</span>}
</div>
)
}
Examples
Read-only tool — no parameters
registerTool(
'get_current_page',
'Returns the current page URL the user is viewing.',
{ type: 'object', properties: {} },
() => ({
href: window.location.href,
path: window.location.pathname
})
)
Tool with parameters
registerTool(
'search_orders',
'Search orders by status and date range.',
{
type: 'object',
properties: {
status: {
type: 'string',
enum: ['pending', 'processing', 'shipped', 'delivered'],
description: 'Order status to filter by.'
},
from_date: {
type: 'string',
description: 'Start date in ISO 8601 format.'
}
},
required: ['status']
},
async (args) => {
const orders = await fetchOrders({ status: args.status, from: args.from_date })
return { count: orders.length, orders }
}
)
Write tool — creates a record
registerTool(
'create_invoice',
'Create a new invoice for a customer.',
{
type: 'object',
properties: {
customer_uuid: { type: 'string', description: 'Customer UUID.' },
items: {
type: 'array',
items: {
type: 'object',
properties: {
product_uuid: { type: 'string' },
quantity: { type: 'number' }
}
},
description: 'Line items for the invoice.'
}
},
required: ['customer_uuid', 'items']
},
async (args) => {
const invoice = await sdk.component('invoices').create({
customer_uuid: args.customer_uuid,
items: args.items
})
return { uuid: invoice.uuid, total: invoice.total }
}
)
Best Practices
- Write clear descriptions. The assistant chooses which tool to call based on the
description. Be specific about what it does, what it returns, and when to use it. - Keep tools focused. One tool, one action. Prefer
get_order+update_order_statusover a singlemanage_order. - Return structured data. Return objects and arrays — the assistant presents structured data better than plain strings.
- Register and unregister with the page lifecycle. Only expose tools that are relevant to the current page. Clean up on unmount.
- Throw descriptive errors. If a tool fails, the error message goes to the assistant. Make it helpful:
"Order not found: abc-123"is better than"Not found".