lyzr-cortex-sdk
v0.1.4
Published
SDK for building tools that integrate with Cortex Platform
Maintainers
Readme
Cortex SDK for TypeScript
Build tools that integrate with the Cortex Platform for bidirectional knowledge flow.
Installation
npm install lyzr-cortex-sdk
# or
yarn add lyzr-cortex-sdk
# or
pnpm add lyzr-cortex-sdkQuick Start
React Integration
import { CortexProvider, useCortex } from 'lyzr-cortex-sdk';
// 1. Wrap your app with CortexProvider
function App() {
return (
<CortexProvider
apiUrl={process.env.REACT_APP_CORTEX_API_URL}
apiKey={process.env.REACT_APP_CORTEX_API_KEY}
>
<YourApp />
</CortexProvider>
);
}
// 2. Use the hook in your components
function MeetingCard({ meeting }) {
const { isEmbedded, openChat, showNotification, push } = useCortex();
const handleSave = async () => {
await push({
id: meeting.id,
type: 'meeting_transcript',
content: meeting.transcript,
name: meeting.title,
scope: 'team',
});
showNotification('Saved to Cortex', 'success');
};
return (
<div>
<h3>{meeting.title}</h3>
<button onClick={handleSave}>Save to Cortex</button>
{/* Only show when embedded in Cortex */}
{isEmbedded && (
<button onClick={() => openChat(`What action items from "${meeting.title}"?`)}>
Ask Cortex
</button>
)}
</div>
);
}Direct API Client (without React)
import { CortexClient } from 'lyzr-cortex-sdk';
const client = new CortexClient({
apiUrl: 'https://api.cortex.ai',
apiKey: 'your-api-key',
toolId: 'your-tool',
});
// Push a document
await client.push({
id: 'doc_123',
type: 'meeting_transcript',
content: 'Full transcript...',
name: 'Product Sync',
scope: 'team',
});
// Query
const result = await client.query('What decisions were made about Q1?');
console.log(result.answer);
console.log(result.sources);PostMessage Bridge (Low-level)
import { CortexBridge, isEmbeddedInCortex } from 'lyzr-cortex-sdk';
// Check if embedded
if (isEmbeddedInCortex()) {
const bridge = new CortexBridge();
// Wait for initialization
const user = await bridge.waitForReady();
console.log('Cortex user:', user?.email);
// Send messages
bridge.openChat('Hello from my tool!');
bridge.showNotification('Tool loaded', 'success');
// Listen for messages
bridge.on('CORTEX_THEME', (payload) => {
document.body.className = payload.theme;
});
}Frontend Integration Guide
Step-by-step guide for integrating Cortex into your React/TypeScript frontend.
1. Create Cortex Provider
File: frontend/lib/cortex.tsx
'use client';
import { createContext, useContext, useEffect, useState, useCallback, useMemo, ReactNode } from 'react';
interface CortexUser {
id: string;
email: string;
name?: string;
orgId: string;
teams: Array<{ id: string; name: string }>;
}
interface CortexContextValue {
user: CortexUser | null;
isEmbedded: boolean;
isReady: boolean;
theme: 'light' | 'dark';
openChat: (query?: string) => void;
}
const CortexContext = createContext<CortexContextValue | null>(null);
function isInIframe(): boolean {
if (typeof window === 'undefined') return false;
try {
return window.self !== window.top;
} catch {
return true;
}
}
export function CortexProvider({ children }: { children: ReactNode }) {
const [isEmbedded] = useState(() => isInIframe());
const [isReady, setIsReady] = useState(!isInIframe());
const [user, setUser] = useState<CortexUser | null>(null);
const [theme, setTheme] = useState<'light' | 'dark'>('light');
useEffect(() => {
if (!isEmbedded) return;
const handleMessage = (event: MessageEvent) => {
const message = event.data;
if (!message?.type) return;
if (message.type === 'CORTEX_INIT') {
setUser(message.payload?.user || null);
setTheme(message.payload?.theme || 'light');
setIsReady(true);
}
if (message.type === 'CORTEX_THEME') {
setTheme(message.payload?.theme || 'light');
}
};
window.addEventListener('message', handleMessage);
window.parent.postMessage({ type: 'TOOL_READY' }, '*');
const timeout = setTimeout(() => setIsReady(true), 5000);
return () => {
window.removeEventListener('message', handleMessage);
clearTimeout(timeout);
};
}, [isEmbedded]);
const openChat = useCallback((query?: string) => {
if (isEmbedded) {
window.parent.postMessage({ type: 'OPEN_CHAT', payload: { query } }, '*');
}
}, [isEmbedded]);
const value = useMemo(() => ({
user, isEmbedded, isReady, theme, openChat
}), [user, isEmbedded, isReady, theme, openChat]);
return (
<CortexContext.Provider value={value}>
{children}
</CortexContext.Provider>
);
}
export function useCortex(): CortexContextValue {
const context = useContext(CortexContext);
if (!context) {
return {
user: null,
isEmbedded: false,
isReady: true,
theme: 'light',
openChat: () => {},
};
}
return context;
}2. Wrap App with Provider
File: frontend/app/providers.tsx
'use client';
import { CortexProvider } from '@/lib/cortex';
export function Providers({ children }: { children: React.ReactNode }) {
return <CortexProvider>{children}</CortexProvider>;
}File: frontend/app/layout.tsx
import { Providers } from './providers';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}3. Use in Components
import { useCortex } from '@/lib/cortex';
function TranscriptCard({ meeting }) {
const { isEmbedded, openChat } = useCortex();
return (
<div>
<h3>{meeting.title}</h3>
<p>{meeting.transcript}</p>
{isEmbedded && (
<button onClick={() => openChat(`Summarize meeting: ${meeting.title}`)}>
Ask Cortex
</button>
)}
</div>
);
}4. Embedded Mode Authentication
When your tool is embedded in Cortex, the frontend receives the user's OGI JWT via CORTEX_INIT postMessage. Always send both custom headers and the standard Authorization header to survive production reverse proxies:
// In your tool's frontend
const headers = {
'X-Cortex-Embedded': 'true',
'X-Cortex-User-Email': user.email,
'Authorization': `Bearer ${ogiJwtToken}`, // Standard header — proxy-safe
};Your tool's backend should accept both modes:
- Custom
X-Cortex-Embedded+X-Cortex-User-Emailheaders (fast path, works locally) Authorization: Bearer {ogiJWT}decoded withCORTEX_JWT_PUBLIC_KEY(works through all proxies)
Production Note: Reverse proxies, CDNs, and load balancers may strip custom
X-Cortex-*headers. Always send the OGI JWT asAuthorization: Beareralongside custom headers. The tool backend should accept both auth modes.
API Reference
CortexProvider
React context provider for Cortex integration.
<CortexProvider
apiUrl="https://api.cortex.ai" // Cortex gateway URL
apiKey="your-api-key" // Tool's API key
toolId="your-tool" // Tool identifier (optional)
debug={false} // Enable debug logging (optional)
>
{children}
</CortexProvider>useCortex()
React hook returning Cortex context:
interface CortexContextValue {
user: CortexUser | null; // Current user (null if standalone)
isEmbedded: boolean; // Whether in Cortex iframe
isReady: boolean; // Whether SDK is initialized
theme: 'light' | 'dark'; // Current theme
push(doc): Promise<void>; // Push document to Knowledge Graph
query(question, options?): Promise<CortexQueryResult>; // Query
openChat(query?): void; // Open Cortex chat
showNotification(message, type?): void; // Show notification
}Additional Hooks
// More efficient if you only need specific values
const isEmbedded = useIsEmbedded();
const user = useCortexUser();
const theme = useCortexTheme();CortexClient
HTTP client for direct API calls:
const client = new CortexClient(config);
// Check if configured
client.isConfigured; // boolean
// Methods
await client.push(document);
await client.query(question, options);
await client.getUserContext(email);CortexBridge
Low-level postMessage bridge:
const bridge = new CortexBridge();
// Properties
bridge.isEmbedded; // boolean
bridge.isReady; // boolean
bridge.user; // CortexUser | null
bridge.theme; // 'light' | 'dark'
// Methods
await bridge.waitForReady();
bridge.openChat(query?);
bridge.showNotification(message, type);
bridge.navigate(path);
bridge.on(messageType, handler);
bridge.destroy();Types
interface CortexUser {
id: string;
email: string;
name?: string;
orgId: string;
orgName?: string;
teams: Array<{ id: string; name: string }>;
role: string;
permissions: string[];
}
interface CortexDocument {
id: string; // External ID
type: string; // Document type
content: string; // Text content
name?: string; // Display name
externalUrl?: string;
scope?: 'global' | 'team' | 'personal';
teamIds?: string[];
metadata?: Record<string, unknown>;
}
interface CortexQueryResult {
answer: string;
sources: CortexSource[];
query: string;
}PostMessage Protocol
When embedded in Cortex, the SDK uses postMessage for communication:
| Direction | Message Type | Payload |
|-----------|--------------|---------|
| Tool → Cortex | TOOL_READY | - |
| Cortex → Tool | CORTEX_INIT | { user, theme } |
| Cortex → Tool | CORTEX_THEME | { theme } |
| Tool → Cortex | OPEN_CHAT | { query? } |
| Tool → Cortex | SHOW_NOTIFICATION | { message, type } |
| Tool → Cortex | NAVIGATE | { path } |
Graceful Degradation
The SDK works in both embedded and standalone modes:
function MyComponent() {
const { isEmbedded, push, openChat } = useCortex();
// Push always works (no-op if not configured)
await push({ id: '1', type: 'doc', content: '...' });
// openChat is a no-op if not embedded
openChat('Hello');
// Conditionally render embedded-only features
return (
<div>
{isEmbedded && <CortexOnlyFeature />}
</div>
);
}License
MIT
