-
Notifications
You must be signed in to change notification settings - Fork 256
Expand file tree
/
Copy pathadapters.ts
More file actions
74 lines (72 loc) · 2.71 KB
/
adapters.ts
File metadata and controls
74 lines (72 loc) · 2.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import { tool } from "ai";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { captureEvent } from "@/lib/posthog";
import { ToolContext, ToolDefinition } from "./types";
export function toVercelAITool<TName extends string, TShape extends z.ZodRawShape, TMetadata>(
def: ToolDefinition<TName, TShape, TMetadata>,
context: ToolContext,
) {
return tool({
description: def.description,
inputSchema: def.inputSchema,
title: def.title,
execute: async (input) => {
let success = true;
try {
return await def.execute(input, context);
} catch (error) {
success = false;
throw error;
} finally {
captureEvent('tool_used', {
toolName: def.name,
source: context.source ?? 'unknown',
success,
}, { distinctId: context.userId });
}
},
toModelOutput: ({ output }) => ({
type: "content",
value: [{ type: "text", text: output.output }],
}),
});
}
export function registerMcpTool<TName extends string, TShape extends z.ZodRawShape, TMetadata>(
server: McpServer,
def: ToolDefinition<TName, TShape, TMetadata>,
context: ToolContext,
) {
// Widening .shape to z.ZodRawShape (its base constraint) gives TypeScript a
// concrete InputArgs so it can fully resolve BaseToolCallback's conditional
// type. def.inputSchema.parse() recovers the correctly typed value inside.
server.registerTool(
def.name,
{
description: def.description,
inputSchema: def.inputSchema.shape as z.ZodRawShape,
annotations: {
readOnlyHint: def.isReadOnly,
idempotentHint: def.isIdempotent,
},
},
async (input) => {
let success = true;
try {
const parsed = def.inputSchema.parse(input);
const result = await def.execute(parsed, context);
return { content: [{ type: "text" as const, text: result.output }] };
} catch (error) {
success = false;
const message = error instanceof Error ? error.message : String(error);
return { content: [{ type: "text" as const, text: `Tool "${def.name}" failed: ${message}` }], isError: true };
} finally {
captureEvent('tool_used', {
toolName: def.name,
source: context.source ?? 'unknown',
success,
}, { distinctId: context.userId });
}
},
);
}