import { generateText, tool } from 'ai'
import { anthropic } from '@ai-sdk/anthropic'
import jwt from 'jsonwebtoken'
import { MembraneClient } from '@membranehq/sdk'
import { z } from 'zod'
// 1. Generate a tenant-scoped token (see Authentication docs)
function createMembraneClient(tenantId: string, tenantName: string) {
const token = jwt.sign(
{ workspaceKey: process.env.MEMBRANE_WORKSPACE_KEY!, tenantKey: tenantId, name: tenantName },
process.env.MEMBRANE_WORKSPACE_SECRET!,
{ expiresIn: 7200, algorithm: 'HS512' },
)
// All operations through this client are scoped to the tenant
return new MembraneClient({ token })
}
// 2. Build tools from tenant's connections
async function handleChat(tenantId: string, tenantName: string, userMessage: string) {
const membrane = createMembraneClient(tenantId, tenantName)
const { text } = await generateText({
model: anthropic('claude-sonnet-4-20250514'),
tools: {
listDeals: tool({
description: 'List recent deals from the CRM',
parameters: z.object({}),
execute: async () => {
// Runs against this tenant's connected CRM
const result = await membrane.action('list-deals').run()
return result
},
}),
createContact: tool({
description: 'Create a new contact in the CRM',
parameters: z.object({
email: z.string(),
name: z.string(),
}),
execute: async ({ email, name }) => {
const result = await membrane.action('create-contact').run({ email, name })
return result
},
}),
},
prompt: userMessage,
})
return text
}