Skip to main content
Glama
eldoonreval

Activepieces MCP Server

by eldoonreval
run-agent.ts9.55 kB
import { createAction, Property, PieceAuth } from '@activepieces/pieces-framework'; import { ChatOpenAI } from "@langchain/openai"; import { ChatAnthropic } from "@langchain/anthropic"; import { MultiServerMCPClient } from "@langchain/mcp-adapters"; import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts'; import { BaseChatModel } from '@langchain/core/language_models/chat_models'; import { AgentExecutor, createToolCallingAgent } from "langchain/agents"; import { StructuredToolInterface } from '@langchain/core/tools'; import { ChainValues } from '@langchain/core/dist/utils/types'; type ModelOption = { label: string; value: string; provider: 'openai' | 'anthropic'; }; const supportedAgentModels: ModelOption[] = [ // Anthropic { label: 'Claude 3.7 Sonnet', value: 'claude-3-7-sonnet-latest', provider: 'anthropic' }, { label: 'Claude 3.5 Sonnet', value: 'claude-3-5-sonnet-latest', provider: 'anthropic' }, { label: 'Claude 3.5 Haiku', value: 'claude-3-5-haiku-latest', provider: 'anthropic' }, // OpenAI { label: 'o1', value: 'o1', provider: 'openai' }, { label: 'GPT-4o', value: 'gpt-4o', provider: 'openai' }, { label: 'GPT-4o Mini', value: 'gpt-4o-mini', provider: 'openai' }, { label: 'GPT-4', value: 'gpt-4', provider: 'openai' }, { label: 'GPT-4.1', value: 'gpt-4.1', provider: 'openai' }, { label: 'GPT-4.1 Mini', value: 'gpt-4.1-mini', provider: 'openai' }, { label: 'GPT-4.1 Nano', value: 'gpt-4.1-nano', provider: 'openai' }, ]; export const runAgent = createAction({ name: 'run_agent', displayName: 'Run Agent', description: 'Run the AI assistant to complete your task.', auth: PieceAuth.None(), props: { model: Property.StaticDropdown<string>({ displayName: 'Model', description: 'Choose which AI model powers the assistant.', required: true, defaultValue: 'claude-3-7-sonnet-latest', options: { disabled: false, options: supportedAgentModels.map(opt => ({ label: opt.label, value: opt.value })), }, }), prompt: Property.LongText({ displayName: 'Prompt', description: 'Describe what you want the assistant to do.', required: true, }), maxIterations: Property.Number({ displayName: 'Max Steps', description: 'How many steps the assistant can take before stopping.', required: true, defaultValue: 10, }), mcpUrls: Property.Array({ displayName: 'MCP Server URLs', description: 'Add MCP server URLs so the AI agent can use its tools.', required: false, defaultValue: [], }), }, async run(context) { const serverToken = context.server.token; const modelValue = context.propsValue.model; const userPrompt = context.propsValue.prompt; const maxIterations = context.propsValue.maxIterations; const mcpUrls = (context.propsValue.mcpUrls || []) as string[]; const validMcpUrls = validateMcpUrls(mcpUrls); let tools: StructuredToolInterface[] = []; let multiClient: MultiServerMCPClient | null = null; if (validMcpUrls.length > 0) { multiClient = setupMcpClient(validMcpUrls); tools = await multiClient.getTools(); console.log(`Successfully loaded ${tools.length} tools from ${validMcpUrls.length} MCP servers`); } else { console.log('No MCP server URLs provided, proceeding without MCP tools.'); } const modelInstance = await getChatModelInstance( modelValue!, supportedAgentModels, serverToken, context.server.apiUrl ); const agentExecutionResult = await executeAgent(modelInstance, tools, userPrompt, maxIterations); if (multiClient) { await multiClient.close(); } return normalizeAgentOutputResponse(agentExecutionResult); }, }); function normalizeAgentOutputResponse(agentResult: ChainValues | string): Record<string, any> { if (typeof agentResult === 'string') { return { output: agentResult }; } if (agentResult && typeof agentResult === 'object') { if (Object.prototype.hasOwnProperty.call(agentResult, 'output')) { const outputValue = agentResult['output']; if (typeof outputValue === 'string') { return { output: outputValue }; } if (Array.isArray(outputValue) && outputValue.length > 0) { const firstItem = outputValue[0]; if ( firstItem && typeof firstItem === 'object' && Object.prototype.hasOwnProperty.call(firstItem, 'type') && firstItem.type === 'text' && Object.prototype.hasOwnProperty.call(firstItem, 'text') && typeof firstItem.text === 'string' ) { return { output: firstItem.text }; } } } } return agentResult; } async function getChatModelInstance( modelValue: string, modelOptions: ModelOption[], serverToken: string, serverApiUrl: string ): Promise<BaseChatModel> { const selectedOption = modelOptions.find(opt => opt.value === modelValue); if (!selectedOption) { throw new Error(`Model configuration not found for: ${modelValue}`); } const { provider, value: modelId } = selectedOption; switch (provider) { case 'anthropic': { return new ChatAnthropic({ model: modelId, anthropicApiUrl: `${serverApiUrl}v1/ai-providers/proxy/anthropic`, anthropicApiKey: serverToken, clientOptions: { defaultHeaders: { Authorization: `Bearer ${serverToken}` } }, invocationKwargs: { thinking: { type: "disabled" } } }); } case 'openai': { return new ChatOpenAI({ model: modelId, openAIApiKey: serverToken, configuration: { baseURL: `${serverApiUrl}v1/ai-providers/proxy/openai/v1`, } }); } default: { const exhaustiveCheck: never = provider; throw new Error(`Unsupported LLM provider: ${exhaustiveCheck}`); } } } export async function executeAgent( model: BaseChatModel, tools: StructuredToolInterface[], userPrompt: string, maxIterations = 10 ): Promise<ChainValues> { const currentDate = new Date().toISOString().split('T')[0]; const systemPrompt = ` You are an autonomous assistant designed to efficiently achieve the user's single-shot goal using tools. Prioritize accuracy, minimal steps, and user-friendly clarity. **Today's Date**: ${currentDate} Use this to interpret time-based queries like "this week" or "due tomorrow." --- **1. Assumptions** Use sensible defaults (e.g., \`limit = 5\`) only when needed and not provided by the user. If any assumption could impact correctness, include: **Verification Needed**: [Brief clarification needed from user]. --- **2. Errors or Missing Data** If a tool fails or gives insufficient data: - **Self-correct once**, if the mistake is clear and fixable (e.g., typo, invalid param). - Otherwise, **ask the user clearly and directly**. When asking for information, if possible, provide brief guidance on where they might find it. Example: "I tried to fetch your Linear tasks, but I need your team ID (e.g., 'team-xyz'). You can usually find this in your Linear workspace settings or URL. Could you provide it?" Do not guess or fabricate data. `; const prompt = ChatPromptTemplate.fromMessages([ ["system", systemPrompt], ["human", "{input}"], new MessagesPlaceholder("agent_scratchpad"), ]); const agent = await createToolCallingAgent({ llm: model, tools, prompt, }); const executor = new AgentExecutor({ agent, tools, maxIterations, returnIntermediateSteps: true, handleParsingErrors: (error: unknown): string => { const message = error instanceof Error ? error.message : String(error); console.error("Agent Error during tool execution/validation:", message); if (message.includes("did not match expected schema")) { return `Tool Argument Error: The arguments provided were invalid according to the tool's schema. Please check the schema description and try again. Error: ${message}`; } return `Execution Error: An error occurred trying to process the tool call or format the response: ${message}. Please reassess the situation.`; }, }); console.log(`Running Tool Calling Agent with input: "${userPrompt}"`); try { const result = await executor.invoke({ input: userPrompt, }); console.log(`Agent finished with result:`, JSON.stringify(result, null, 2)); return result; } catch (error) { console.error("Agent execution failed with exception:", error); const errorMessage = error instanceof Error ? error.message : String(error); return { error: `Agent execution failed unexpectedly: ${errorMessage}` }; } } function validateMcpUrls(mcpUrls: string[]): string[] { return mcpUrls .filter((url): url is string => typeof url === 'string' && url.trim() !== '') .filter(isValidMcpUrl) } function isValidMcpUrl(url: string): boolean { try { new URL(url); return true; } catch (e) { return false; } } function setupMcpClient(validMcpUrls: string[]): MultiServerMCPClient { const serverConfig = Object.fromEntries( validMcpUrls.map((url, index) => { const serverName = `server${index + 1}`; return [serverName, { transport: "sse" as const, url }]; }) ); console.trace(`Connecting to ${validMcpUrls.length} MCP servers`); return new MultiServerMCPClient(serverConfig); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/eldoonreval/activepieces'

If you have feedback or need assistance with the MCP directory API, please join our Discord server