diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4939f76 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +## [Unreleased] +### Enhanced +- Improved message transformation in CallTranscriptOutput to handle optional properties +- Enhanced type safety for message handling diff --git a/README.md b/README.md index 3460867..f6efb50 100644 --- a/README.md +++ b/README.md @@ -225,6 +225,7 @@ The Vapi MCP Server provides the following tools for integration: - Immediate or scheduled calls - Dynamic variable values through `assistantOverrides` - `get_call`: Gets details of a specific call +- `get_call_transcript`: Gets the full transcript and conversation data of a specific call > **Note:** The `create_call` action supports scheduling calls for immediate execution or for a future time. You can also pass dynamic variables using `assistantOverrides.variableValues` to personalize assistant messages. diff --git a/src/schemas/index.ts b/src/schemas/index.ts index 2638121..85db71e 100644 --- a/src/schemas/index.ts +++ b/src/schemas/index.ts @@ -304,6 +304,21 @@ export const GetCallInputSchema = z.object({ callId: z.string().describe('ID of the call to get'), }); +export const CallTranscriptOutputSchema = BaseResponseSchema.extend({ + id: z.string(), + transcript: z.string().optional(), + messages: z.array(z.object({ + role: z.string(), + message: z.string().optional(), + time: z.number().optional(), + duration: z.number().optional(), + })).optional(), + recordingUrl: z.string().optional(), + summary: z.string().optional(), +}); + +export const GetCallTranscriptInputSchema = GetCallInputSchema; + // ===== Phone Number Schemas ===== export const GetPhoneNumberInputSchema = z.object({ diff --git a/src/tools/call.ts b/src/tools/call.ts index 408e347..1160d20 100644 --- a/src/tools/call.ts +++ b/src/tools/call.ts @@ -1,10 +1,11 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { VapiClient, Vapi } from '@vapi-ai/server-sdk'; -import { CallInputSchema, GetCallInputSchema } from '../schemas/index.js'; +import { CallInputSchema, GetCallInputSchema, GetCallTranscriptInputSchema } from '../schemas/index.js'; import { transformCallInput, transformCallOutput, + transformCallTranscriptOutput, } from '../transformers/index.js'; import { createToolHandler } from './utils.js'; @@ -42,4 +43,14 @@ export const registerCallTools = ( return transformCallOutput(call); }) ); + + server.tool( + 'get_call_transcript', + 'Gets the full transcript and conversation data of a specific call', + GetCallTranscriptInputSchema.shape, + createToolHandler(async (data) => { + const call = await vapiClient.calls.get(data.callId); + return transformCallTranscriptOutput(call); + }) + ); }; diff --git a/src/transformers/index.ts b/src/transformers/index.ts index 53fa800..e89e9ad 100644 --- a/src/transformers/index.ts +++ b/src/transformers/index.ts @@ -5,6 +5,7 @@ import { CallInputSchema, AssistantOutputSchema, CallOutputSchema, + CallTranscriptOutputSchema, PhoneNumberOutputSchema, ToolOutputSchema, UpdateAssistantInputSchema, @@ -228,6 +229,44 @@ export function transformCallOutput( }; } +export function transformCallTranscriptOutput( + call: Vapi.Call +): z.infer { + return { + id: call.id, + createdAt: call.createdAt, + updatedAt: call.updatedAt, + transcript: call.artifact?.transcript, + messages: call.artifact?.messages?.map(msg => { + // Handle different message types safely + const transformedMessage: any = { + role: msg.role, + time: msg.time || 0, + }; + + // Check if message has a 'message' property (UserMessage, BotMessage, SystemMessage, ToolCallMessage) + if ('message' in msg && typeof msg.message === 'string') { + transformedMessage.message = msg.message; + } else if ('result' in msg && typeof msg.result === 'string') { + // For ToolCallResultMessage, use result as message + transformedMessage.message = msg.result; + } else { + // Fallback for unknown message types - set to undefined since schema allows optional + transformedMessage.message = undefined; + } + + // Check if message has a 'duration' property (UserMessage, BotMessage) + if ('duration' in msg && typeof msg.duration === 'number') { + transformedMessage.duration = msg.duration; + } + + return transformedMessage; + }), + recordingUrl: call.artifact?.recordingUrl, + summary: call.analysis?.summary, + }; +} + // ===== Phone Number Transformers ===== export function transformPhoneNumberOutput(