Vercel AI SDK
Connect Unizo MCP to Vercel AI SDK for TypeScript/JavaScript applications with native MCP client support.
Overview
Vercel AI SDK v4.2+ includes native MCP support via experimental_createMCPClient, enabling direct integration with Unizo's MCP server for building AI-powered web applications.
Installation
npm install ai @ai-sdk/openai
Quick Start
Connect to Unizo MCP and use tools in your Next.js/Node.js app:
import { experimental_createMCPClient as createMCPClient } from 'ai';
// Connect to Unizo MCP server
const mcp = createMCPClient({
transport: {
type: 'streamable-http',
url: 'https://api.unizo.ai/mcp',
headers: {
'Authorization': `Bearer ${process.env.UNIZO_API_KEY}`,
'x-mcp-scopes': 'edr,vms,ticketing'
}
}
});
// Get Unizo tools
const { tools } = await mcp.tools();
// Use with any AI SDK provider
import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';
const result = await generateText({
model: openai('gpt-4o'),
tools,
prompt: 'Check for critical security incidents in SentinelOne'
});
Environment Variables
UNIZO_API_KEY=<unizo_api_key>
Server Component Example (Next.js)
// app/api/chat/route.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { experimental_createMCPClient as createMCPClient } from 'ai';
export async function POST(req: Request) {
const { messages } = await req.json();
const mcp = createMCPClient({
transport: {
type: 'streamable-http',
url: 'https://api.unizo.ai/mcp',
headers: {
'Authorization': `Bearer ${process.env.UNIZO_API_KEY}`,
'x-mcp-scopes': 'edr,vms,ticketing'
}
}
});
const { tools } = await mcp.tools();
const result = await streamText({
model: openai('gpt-4o'),
messages,
tools,
});
return result.toDataStreamResponse();
}
Advanced Usage
Security Operations Dashboard
Create a real-time security dashboard with streaming updates:
// app/security/page.tsx
'use client';
import { useChat } from 'ai/react';
import { useState } from 'react';
export default function SecurityDashboard() {
const { messages, input, handleInputChange, handleSubmit } = useChat({
api: '/api/security-chat'
});
return (
<div className="max-w-4xl mx-auto p-6">
<h1 className="text-2xl font-bold mb-6">Security Operations Dashboard</h1>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<SecurityMetrics />
<IncidentSummary />
</div>
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-lg font-semibold mb-4">Security Assistant</h2>
<div className="space-y-4 mb-4">
{messages.map((message) => (
<div key={message.id} className={`p-3 rounded ${
message.role === 'user' ? 'bg-blue-100' : 'bg-gray-100'
}`}>
<div className="font-medium">{message.role}</div>
<div>{message.content}</div>
</div>
))}
</div>
<form onSubmit={handleSubmit} className="flex gap-2">
<input
value={input}
onChange={handleInputChange}
placeholder="Ask about security incidents, vulnerabilities..."
className="flex-1 border rounded px-3 py-2"
/>
<button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">
Send
</button>
</form>
</div>
</div>
);
}
// app/api/security-chat/route.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { experimental_createMCPClient as createMCPClient } from 'ai';
export async function POST(req: Request) {
const { messages } = await req.json();
const mcp = createMCPClient({
transport: {
type: 'streamable-http',
url: 'https://api.unizo.ai/mcp',
headers: {
'Authorization': `Bearer ${process.env.UNIZO_API_KEY}`,
'x-mcp-scopes': 'edr,vms,ticketing'
}
}
});
const { tools } = await mcp.tools();
const result = await streamText({
model: openai('gpt-4o'),
messages,
tools,
system: `You are a security operations assistant with access to:
- Endpoint Detection & Response (EDR) systems
- Vulnerability Management systems
- Incident ticketing systems
Provide clear, actionable security insights and recommendations.`,
});
return result.toDataStreamResponse();
}
Business Intelligence Agent
Create an AI agent for business operations insights:
// components/BusinessIntelligenceAgent.tsx
'use client';
import { useChat } from 'ai/react';
import { useState } from 'react';
interface MetricCard {
title: string;
value: string;
trend: 'up' | 'down' | 'stable';
}
export default function BusinessIntelligenceAgent() {
const [metrics, setMetrics] = useState<MetricCard[]>([]);
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
api: '/api/business-intelligence',
onFinish: (message) => {
// Parse any metrics from the AI response
if (message.content.includes('METRICS:')) {
const metricsData = parseMetricsFromResponse(message.content);
setMetrics(metricsData);
}
}
});
const quickQueries = [
'Show me this week\'s security incident summary',
'What are our top vulnerability risks?',
'Create tickets for critical findings',
'Analyze development team productivity'
];
return (
<div className="space-y-6">
{/* Quick Actions */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{quickQueries.map((query, index) => (
<button
key={index}
onClick={() => handleSubmit(undefined, { data: { message: query } })}
className="p-3 text-sm bg-blue-50 hover:bg-blue-100 rounded border text-left"
disabled={isLoading}
>
{query}
</button>
))}
</div>
{/* Metrics Dashboard */}
{metrics.length > 0 && (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{metrics.map((metric, index) => (
<div key={index} className="bg-white p-4 rounded-lg shadow border">
<h3 className="text-sm font-medium text-gray-500">{metric.title}</h3>
<div className="flex items-center">
<span className="text-2xl font-bold">{metric.value}</span>
<TrendIndicator trend={metric.trend} />
</div>
</div>
))}
</div>
)}
{/* Chat Interface */}
<div className="bg-white rounded-lg shadow border">
<div className="p-4 border-b">
<h2 className="font-semibold">Business Intelligence Assistant</h2>
</div>
<div className="h-96 overflow-y-auto p-4 space-y-4">
{messages.map((message) => (
<div key={message.id} className={`flex ${
message.role === 'user' ? 'justify-end' : 'justify-start'
}`}>
<div className={`max-w-xs lg:max-w-md px-4 py-2 rounded-lg ${
message.role === 'user'
? 'bg-blue-500 text-white'
: 'bg-gray-100 text-gray-800'
}`}>
{message.content}
</div>
</div>
))}
{isLoading && (
<div className="flex justify-start">
<div className="bg-gray-100 px-4 py-2 rounded-lg">
<div className="flex space-x-2">
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce"></div>
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '0.1s' }}></div>
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: '0.2s' }}></div>
</div>
</div>
</div>
)}
</div>
<div className="p-4 border-t">
<form onSubmit={handleSubmit} className="flex gap-2">
<input
value={input}
onChange={handleInputChange}
placeholder="Ask about business metrics, security, operations..."
className="flex-1 border rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
disabled={isLoading}
/>
<button
type="submit"
disabled={isLoading}
className="bg-blue-500 hover:bg-blue-600 disabled:bg-gray-400 text-white px-6 py-2 rounded-lg"
>
Send
</button>
</form>
</div>
</div>
</div>
);
}
// app/api/business-intelligence/route.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { experimental_createMCPClient as createMCPClient } from 'ai';
export async function POST(req: Request) {
const { messages } = await req.json();
const mcp = createMCPClient({
transport: {
type: 'streamable-http',
url: 'https://api.unizo.ai/mcp',
headers: {
'Authorization': `Bearer ${process.env.UNIZO_API_KEY}`,
'x-mcp-scopes': 'edr,vms,ticketing,scm,communications,observability,infra'
}
}
});
const { tools } = await mcp.tools();
const result = await streamText({
model: openai('gpt-4o'),
messages,
tools,
system: `You are a business intelligence assistant with access to:
- Security systems (EDR, vulnerability management)
- IT service management (ticketing)
- Development tools (source code management)
- Team communications
- Infrastructure monitoring
- Observability tools
Provide actionable insights and data-driven recommendations. When presenting metrics,
format them as "METRICS: [metric_name]=[value]=[trend]" for dashboard integration.`,
});
return result.toDataStreamResponse();
}
function parseMetricsFromResponse(content: string): MetricCard[] {
// Implementation to parse metrics from AI response
const metricRegex = /METRICS: (\w+)=([^=]+)=(up|down|stable)/g;
const metrics: MetricCard[] = [];
let match;
while ((match = metricRegex.exec(content)) !== null) {
metrics.push({
title: match[1].replace(/([A-Z])/g, ' $1').trim(),
value: match[2],
trend: match[3] as 'up' | 'down' | 'stable'
});
}
return metrics;
}
function TrendIndicator({ trend }: { trend: 'up' | 'down' | 'stable' }) {
const colors = {
up: 'text-green-500',
down: 'text-red-500',
stable: 'text-gray-500'
};
const icons = {
up: '↗',
down: '↘',
stable: '→'
};
return (
<span className={`ml-2 ${colors[trend]}`}>
{icons[trend]}
</span>
);
}
Multi-Provider Support
Use Unizo MCP with different AI providers:
// utils/mcpClient.ts
import { experimental_createMCPClient as createMCPClient } from 'ai';
export function createUnizoMCPClient(scopes: string[] = ['edr', 'vms', 'ticketing']) {
return createMCPClient({
transport: {
type: 'streamable-http',
url: 'https://api.unizo.ai/mcp',
headers: {
'Authorization': `Bearer ${process.env.UNIZO_API_KEY}`,
'x-mcp-scopes': scopes.join(',')
}
}
});
}
// app/api/security-analysis/route.ts
import { generateText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { openai } from '@ai-sdk/openai';
import { createUnizoMCPClient } from '@/utils/mcpClient';
export async function POST(req: Request) {
const { prompt, provider = 'openai' } = await req.json();
const mcp = createUnizoMCPClient(['edr', 'vms']);
const { tools } = await mcp.tools();
const models = {
openai: openai('gpt-4o'),
anthropic: anthropic('claude-3-5-sonnet-20241022')
};
const result = await generateText({
model: models[provider as keyof typeof models],
tools,
prompt,
system: 'You are a security analyst with access to EDR and vulnerability management tools.'
});
return Response.json(result);
}
React Components with useChat
Create reusable security monitoring components:
// components/SecurityMonitor.tsx
'use client';
import { useChat } from 'ai/react';
import { useEffect, useState } from 'react';
interface SecurityAlert {
id: string;
severity: 'low' | 'medium' | 'high' | 'critical';
message: string;
timestamp: string;
}
export default function SecurityMonitor() {
const [alerts, setAlerts] = useState<SecurityAlert[]>([]);
const [autoRefresh, setAutoRefresh] = useState(true);
const { messages, append, isLoading } = useChat({
api: '/api/security-monitor',
onFinish: (message) => {
// Parse alerts from AI response
const newAlerts = parseAlertsFromResponse(message.content);
setAlerts(prev => [...newAlerts, ...prev].slice(0, 50)); // Keep last 50 alerts
}
});
useEffect(() => {
if (autoRefresh) {
const interval = setInterval(() => {
append({ role: 'user', content: 'Check for new security incidents and vulnerabilities' });
}, 30000); // Refresh every 30 seconds
return () => clearInterval(interval);
}
}, [autoRefresh, append]);
const severityColors = {
low: 'bg-gray-100 text-gray-800',
medium: 'bg-yellow-100 text-yellow-800',
high: 'bg-orange-100 text-orange-800',
critical: 'bg-red-100 text-red-800'
};
return (
<div className="bg-white rounded-lg shadow border">
<div className="p-4 border-b flex justify-between items-center">
<h2 className="font-semibold">Security Monitor</h2>
<div className="flex items-center gap-2">
<label className="flex items-center">
<input
type="checkbox"
checked={autoRefresh}
onChange={(e) => setAutoRefresh(e.target.checked)}
className="mr-2"
/>
Auto-refresh
</label>
<button
onClick={() => append({ role: 'user', content: 'Run full security scan' })}
disabled={isLoading}
className="bg-blue-500 hover:bg-blue-600 disabled:bg-gray-400 text-white px-3 py-1 rounded text-sm"
>
{isLoading ? 'Scanning...' : 'Scan Now'}
</button>
</div>
</div>
<div className="max-h-96 overflow-y-auto">
{alerts.length === 0 ? (
<div className="p-4 text-gray-500 text-center">
No alerts. Click "Scan Now" to check for security issues.
</div>
) : (
<div className="divide-y">
{alerts.map((alert) => (
<div key={alert.id} className="p-4 flex items-start gap-3">
<span className={`px-2 py-1 rounded text-xs font-medium ${severityColors[alert.severity]}`}>
{alert.severity.toUpperCase()}
</span>
<div className="flex-1">
<p className="text-sm">{alert.message}</p>
<p className="text-xs text-gray-500 mt-1">{alert.timestamp}</p>
</div>
</div>
))}
</div>
)}
</div>
</div>
);
}
function parseAlertsFromResponse(content: string): SecurityAlert[] {
// Implementation to parse alerts from AI response
// This would parse structured data from the AI's response
return [];
}
Available Scopes
Configure x-mcp-scopes to control which Unizo services your application can access:
edr: Endpoint Detection & Response toolsvms: Vulnerability Management toolsticketing: Incident and ticket management toolsscm: Source Code Management toolscommunications: Team communication toolsobservability: Monitoring and logging toolsinfra: Infrastructure management tools
Error Handling
Implement robust error handling for production applications:
// utils/errorHandling.ts
import { experimental_createMCPClient as createMCPClient } from 'ai';
export async function createMCPClientWithRetry(maxRetries = 3) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
const mcp = createMCPClient({
transport: {
type: 'streamable-http',
url: 'https://api.unizo.ai/mcp',
headers: {
'Authorization': `Bearer ${process.env.UNIZO_API_KEY}`,
'x-mcp-scopes': 'edr,vms,ticketing'
}
}
});
// Test the connection
await mcp.tools();
return mcp;
} catch (error) {
lastError = error;
if (i < maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); // Exponential backoff
}
}
}
throw lastError;
}
// app/api/secure-chat/route.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { createMCPClientWithRetry } from '@/utils/errorHandling';
export async function POST(req: Request) {
try {
const { messages } = await req.json();
const mcp = await createMCPClientWithRetry();
const { tools } = await mcp.tools();
const result = await streamText({
model: openai('gpt-4o'),
messages,
tools,
});
return result.toDataStreamResponse();
} catch (error) {
console.error('MCP Client Error:', error);
return new Response(
JSON.stringify({ error: 'Failed to connect to security systems' }),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
);
}
}