Ecosystem Borrowing
The AI ecosystem is rich with tools, toolkits, agents, integrations, Model Context Protocol Servers (MCPs), and more, created by different orchestration frameworks, community contributors, and businesses. The depth of an ecosystem is an important consideration when choosing which AI framework to use. For example, Composio has created integrations with every major agent framework.
Thankfully, since tools are at the end of the day just functions, the ecosystems are fairly permeable within the same programming language. You can port a tool to another ecosystem by wrapping it in some logic.
Cascaide removes this friction by virtue of the fact that nodes are plain async functions. Cascaide does not know or care what goes on inside the exec step. Whatever data you return from exec is what you receive in post — handle it as you see fit.
This means you can borrow from any other ecosystem written in JS/TS. You can even go further by running full agents inside an exec step, or by calling endpoints of other agents.
In this section, we borrow LangChain Community’s Tavily tool as an example. Check out the MCP section for a guide that uses Firecrawl MCP.
Borrowing @langchain/tavily
You can clone the repository showcasing this example from https://github.com/cascaide-ts/cookbook.git .
searchToolNodeExec
The exec step receives prepared tool calls and fans them out for parallel execution. All the actual tool logic lives inside executeTool.
export async function searchToolNodeExec(prepOutput: any) {
const { toolCallsToExecute } = prepOutput;
const { executeTool } = await import('./searchAgentTools');
const results = await Promise.all(
toolCallsToExecute.map(async (toolCall: ToolCall) => {
const functionName = toolCall.function.name;
const toolCallId = toolCall.id;
try {
const toolResult = await executeTool(toolCall);
return { toolCall, toolResult };
} catch (err: any) {
return {
toolCall,
toolResult: {
role: 'tool',
tool_call_id: toolCallId,
content: JSON.stringify({
error: `Tool execution failed for ${functionName}: ${err.message || 'Unknown error'}`
}),
},
};
}
})
);
return { results, ...prepOutput };
}executeTool
This is where the LangChain tool is invoked. Instantiate the tool, call .invoke(), and return the result formatted as a tool response. That’s it.
import { TavilySearch } from "@langchain/tavily";
const tool = new TavilySearch({
maxResults: 5,
topic: "general",
});
export const executeTool = async (toolCall: ToolCall): Promise<ToolResult> => {
const { name, arguments: argsString } = toolCall.function;
try {
const args = JSON.parse(argsString);
let toolResultData: any;
switch (name) {
case 'search_tool':
toolResultData = await tool.invoke({ query: `${args.query}` });
break;
default:
throw new Error(`Unknown tool: ${name}`);
}
return {
role: 'tool',
tool_call_id: toolCall.id,
content: JSON.stringify(toolResultData),
};
} catch (error: any) {
return {
role: 'tool',
tool_call_id: toolCall.id,
content: JSON.stringify({
error: error.message || 'Tool execution failed',
}),
};
}
};That’s it. You simply invoke the LangChain tool and return the result as a standard tool response. In the post step, handle it like any other tool response.
This same pattern works for any JS/TS library — LangChain tools, Vercel AI SDK tools, raw API clients, or calls to other running agent services. Cascaide’s exec step is just a function, so the entire JS/TS ecosystem is fair game.