Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/sim/lib/mcp/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export class McpClient {
return result.tools.map((tool: Tool) => ({
name: tool.name,
description: tool.description,
inputSchema: tool.inputSchema,
inputSchema: tool.inputSchema as McpTool['inputSchema'],
serverId: this.config.id,
serverName: this.config.name,
}))
Expand Down
17 changes: 16 additions & 1 deletion apps/sim/lib/mcp/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,29 @@ export interface McpSecurityPolicy {
auditLevel: 'none' | 'basic' | 'detailed'
}

/**
* JSON Schema property definition for tool parameters.
* Follows JSON Schema specification with description support.
*/
export interface McpToolSchemaProperty {
type: string
description?: string
items?: McpToolSchemaProperty
properties?: Record<string, McpToolSchemaProperty>
required?: string[]
enum?: Array<string | number | boolean>
default?: unknown
}

/**
* JSON Schema for tool input parameters.
* Aligns with MCP SDK's Tool.inputSchema structure.
*/
export interface McpToolSchema {
type: 'object'
properties?: Record<string, unknown>
properties?: Record<string, McpToolSchemaProperty>
required?: string[]
description?: string
}

/**
Expand Down
135 changes: 95 additions & 40 deletions apps/sim/providers/anthropic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { MAX_TOOL_ITERATIONS } from '@/providers'
import {
checkForForcedToolUsage,
createReadableStreamFromAnthropicStream,
generateToolUseId,
} from '@/providers/anthropic/utils'
import {
getMaxOutputTokensForModel,
Expand Down Expand Up @@ -433,11 +432,32 @@ export const anthropicProvider: ProviderConfig = {

const executionResults = await Promise.allSettled(toolExecutionPromises)

// Collect all tool_use and tool_result blocks for batching
const toolUseBlocks: Array<{
type: 'tool_use'
id: string
name: string
input: Record<string, unknown>
}> = []
const toolResultBlocks: Array<{
type: 'tool_result'
tool_use_id: string
content: string
}> = []

for (const settledResult of executionResults) {
if (settledResult.status === 'rejected' || !settledResult.value) continue

const { toolName, toolArgs, toolParams, result, startTime, endTime, duration } =
settledResult.value
const {
toolUse,
toolName,
toolArgs,
toolParams,
result,
startTime,
endTime,
duration,
} = settledResult.value

timeSegments.push({
type: 'tool',
Expand All @@ -447,7 +467,7 @@ export const anthropicProvider: ProviderConfig = {
duration: duration,
})

let resultContent: any
let resultContent: unknown
if (result.success) {
toolResults.push(result.output)
resultContent = result.output
Expand All @@ -469,29 +489,34 @@ export const anthropicProvider: ProviderConfig = {
success: result.success,
})

const toolUseId = generateToolUseId(toolName)
// Add to batched arrays using the ORIGINAL ID from Claude's response
toolUseBlocks.push({
type: 'tool_use',
id: toolUse.id,
name: toolName,
input: toolArgs,
})

toolResultBlocks.push({
type: 'tool_result',
tool_use_id: toolUse.id,
content: JSON.stringify(resultContent),
})
}

// Add ONE assistant message with ALL tool_use blocks
if (toolUseBlocks.length > 0) {
currentMessages.push({
role: 'assistant',
content: [
{
type: 'tool_use',
id: toolUseId,
name: toolName,
input: toolArgs,
} as any,
],
content: toolUseBlocks as unknown as Anthropic.Messages.ContentBlock[],
})
}

// Add ONE user message with ALL tool_result blocks
if (toolResultBlocks.length > 0) {
currentMessages.push({
role: 'user',
content: [
{
type: 'tool_result',
tool_use_id: toolUseId,
content: JSON.stringify(resultContent),
} as any,
],
content: toolResultBlocks as unknown as Anthropic.Messages.ContentBlockParam[],
})
}

Expand Down Expand Up @@ -777,6 +802,8 @@ export const anthropicProvider: ProviderConfig = {
const toolCallStartTime = Date.now()
const toolName = toolUse.name
const toolArgs = toolUse.input as Record<string, any>
// Preserve the original tool_use ID from Claude's response
const toolUseId = toolUse.id

try {
const tool = request.tools?.find((t) => t.id === toolName)
Expand All @@ -787,6 +814,7 @@ export const anthropicProvider: ProviderConfig = {
const toolCallEndTime = Date.now()

return {
toolUseId,
toolName,
toolArgs,
toolParams,
Expand All @@ -800,6 +828,7 @@ export const anthropicProvider: ProviderConfig = {
logger.error('Error processing tool call:', { error, toolName })

return {
toolUseId,
toolName,
toolArgs,
toolParams: {},
Expand All @@ -817,11 +846,32 @@ export const anthropicProvider: ProviderConfig = {

const executionResults = await Promise.allSettled(toolExecutionPromises)

// Collect all tool_use and tool_result blocks for batching
const toolUseBlocks: Array<{
type: 'tool_use'
id: string
name: string
input: Record<string, unknown>
}> = []
const toolResultBlocks: Array<{
type: 'tool_result'
tool_use_id: string
content: string
}> = []

for (const settledResult of executionResults) {
if (settledResult.status === 'rejected' || !settledResult.value) continue

const { toolName, toolArgs, toolParams, result, startTime, endTime, duration } =
settledResult.value
const {
toolUseId,
toolName,
toolArgs,
toolParams,
result,
startTime,
endTime,
duration,
} = settledResult.value

timeSegments.push({
type: 'tool',
Expand All @@ -831,7 +881,7 @@ export const anthropicProvider: ProviderConfig = {
duration: duration,
})

let resultContent: any
let resultContent: unknown
if (result.success) {
toolResults.push(result.output)
resultContent = result.output
Expand All @@ -853,29 +903,34 @@ export const anthropicProvider: ProviderConfig = {
success: result.success,
})

const toolUseId = generateToolUseId(toolName)
// Add to batched arrays using the ORIGINAL ID from Claude's response
toolUseBlocks.push({
type: 'tool_use',
id: toolUseId,
name: toolName,
input: toolArgs,
})

toolResultBlocks.push({
type: 'tool_result',
tool_use_id: toolUseId,
content: JSON.stringify(resultContent),
})
}

// Add ONE assistant message with ALL tool_use blocks
if (toolUseBlocks.length > 0) {
currentMessages.push({
role: 'assistant',
content: [
{
type: 'tool_use',
id: toolUseId,
name: toolName,
input: toolArgs,
} as any,
],
content: toolUseBlocks as unknown as Anthropic.Messages.ContentBlock[],
})
}

// Add ONE user message with ALL tool_result blocks
if (toolResultBlocks.length > 0) {
currentMessages.push({
role: 'user',
content: [
{
type: 'tool_result',
tool_use_id: toolUseId,
content: JSON.stringify(resultContent),
} as any,
],
content: toolResultBlocks as unknown as Anthropic.Messages.ContentBlockParam[],
})
}

Expand Down Expand Up @@ -1061,7 +1116,7 @@ export const anthropicProvider: ProviderConfig = {
startTime: tc.startTime,
endTime: tc.endTime,
duration: tc.duration,
result: tc.result,
result: tc.result as Record<string, unknown> | undefined,
}))
: undefined,
toolResults: toolResults.length > 0 ? toolResults : undefined,
Expand Down
11 changes: 10 additions & 1 deletion apps/sim/providers/bedrock/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,17 @@ export function checkForForcedToolUsage(
return null
}

/**
* Generates a unique tool use ID for Bedrock.
* AWS Bedrock requires toolUseId to be 1-64 characters, pattern [a-zA-Z0-9_-]+
*/
export function generateToolUseId(toolName: string): string {
return `${toolName}-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`
const timestamp = Date.now().toString(36) // Base36 timestamp (9 chars)
const random = Math.random().toString(36).substring(2, 7) // 5 random chars
const suffix = `-${timestamp}-${random}` // ~15 chars
const maxNameLength = 64 - suffix.length
const truncatedName = toolName.substring(0, maxNameLength).replace(/[^a-zA-Z0-9_-]/g, '_')
return `${truncatedName}${suffix}`
}

/**
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/providers/deepseek/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export const deepseekProvider: ProviderConfig = {
: undefined

const payload: any = {
model: 'deepseek-chat',
model: request.model,
messages: allMessages,
}

Expand Down
Loading