Server-side tools (Anthropic Claude Messages API)
This guide explains how to call server-side tools using the Anthropic Claude Messages API. It presents an example of a tool that returns the weather in a given location.
Code demo available
This guide includes a code demo to help you get started. See the GitHub repository.
First, define the tool in the Anthropic Claude Messages API tool format:
const weatherTool = {
name: 'get_weather',
description: 'Returns the weather in a given location',
input_schema: {
type: 'object',
properties: {
location: {
type: 'string',
description: 'The location to get the weather for',
},
},
required: ['location'],
},
}
Then, when you call the Anthropic API, include the tool in the tools
array.
import {
AiAgentToolkit,
anthropicMessagesAdapter,
ChatMessagesFormatter,
} from '@tiptap-pro/extension-ai-agent-server'
import Anthropic from '@anthropic-ai/sdk'
const { chatMessages, schemaAwarenessData } = request
const toolkit = new AiAgentToolkit({
adapter: anthropicMessagesAdapter,
schemaAwarenessData,
})
const formatter = new ChatMessagesFormatter({
initialMessages: chatMessages,
adapter: anthropicMessagesAdapter,
})
// Initialize the Anthropic client
const anthropic = new Anthropic()
// Call the Anthropic Claude Messages API
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-0',
max_tokens: 2048,
system: `
<Your system prompt>
${toolkit.getSystemPrompt()}
`,
messages: formatter.format(),
// Include the weather tool in the tools array, beside
// the other tools provided by AiAgentToolkit
tools: [...toolkit.format(), weatherTool],
})
First, add the response to the formatter so that it is included in the conversation.
formatter.addAiResponse(response)
Then, check if the response contains a server-side tool call like the weather tool. If so, call the tool and add the result to the formatter. Then, call the Anthropic API again.
Repeat this process until there are no more server-side tool calls in the response.
const isGetWeatherToolUse = (
content: Anthropic.Messages.ContentBlock,
): content is Anthropic.Messages.ToolUseBlock =>
content.type === 'tool_use' && content.name === 'get_weather'
let response: Anthropic.Messages.Message | null = null
while (!response || response.content.some(isGetWeatherToolUse)) {
// Call the Anthropic Claude Messages API
response = await anthropic.messages.create({
model: 'claude-sonnet-4-0',
max_tokens: 2048,
system: `${systemPrompt}\n${toolkit.getSystemPrompt()}`,
messages: formatter.format(),
// Include the weather tool in the tools array, beside the other tools in AiAgentToolkit
tools: [...toolkit.format(), weatherTool],
})
// Convert the response to the format expected by the AI Agent extension
formatter.addAiResponse(response)
// Process weather tool calls
for (const content of response.content) {
if (isGetWeatherToolUse(content)) {
const weatherResult = await getWeather(content.input)
// Add the tool call result to the formatter
formatter.addChatMessage({
type: 'toolCallResult',
toolName: 'get_weather',
toolCallId: content.id,
result: weatherResult,
isError: false,
})
}
}
}
Finally, when there are no more server-side tool calls, use the formatter
to convert the response to the format expected by the AI Agent extension.
const response = formatter.getResolverResponse()
The value returned from formatter.getResolverResponse()
should be the response of your API endpoint and the return value of the resolver function.