@picahq/pica-mcp
Advanced tools
+556
-14
| #!/usr/bin/env node | ||
| import { Server } from "@modelcontextprotocol/sdk/server/index.js"; | ||
| import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; | ||
| import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; | ||
| import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, CreateMessageRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; | ||
| import axios from "axios"; | ||
| import FormData from 'form-data'; | ||
| // Helper functions for generating prompt context | ||
| function formatConnectionsInfo(connections) { | ||
| if (connections.length === 0) { | ||
| return "No active connections found. User needs to connect to platforms first."; | ||
| } | ||
| const groupedByPlatform = connections.reduce((acc, conn) => { | ||
| if (!acc[conn.platform]) { | ||
| acc[conn.platform] = []; | ||
| } | ||
| acc[conn.platform].push(conn.key); | ||
| return acc; | ||
| }, {}); | ||
| return Object.entries(groupedByPlatform) | ||
| .map(([platform, keys]) => `- ${platform}: ${keys.join(', ')}`) | ||
| .join('\n'); | ||
| } | ||
| function formatAvailablePlatformsInfo(connectionDefinitions) { | ||
| if (connectionDefinitions.length === 0) { | ||
| return "No available platform information found."; | ||
| } | ||
| const platforms = connectionDefinitions | ||
| .filter(def => def.active && !def.deprecated) | ||
| .map(def => `- ${def.platform}: ${def.description}`) | ||
| .join('\n'); | ||
| return platforms || "No active platforms available."; | ||
| } | ||
| class PicaClient { | ||
@@ -33,2 +59,3 @@ secret; | ||
| catch (error) { | ||
| console.error("Failed to initialize connections:", error); | ||
| this.connections = []; | ||
@@ -69,5 +96,8 @@ } | ||
| async getAvailableActions(platform) { | ||
| if (!platform) { | ||
| throw new Error("Platform name is required"); | ||
| } | ||
| try { | ||
| const headers = this.generateHeaders(); | ||
| const url = `${this.baseUrl}/v1/knowledge?supported=true&connectionPlatform=${platform}&limit=1000`; | ||
| const url = `${this.baseUrl}/v1/knowledge?supported=true&connectionPlatform=${encodeURIComponent(platform)}&limit=1000`; | ||
| const response = await axios.get(url, { headers }); | ||
@@ -78,2 +108,5 @@ return response.data?.rows || []; | ||
| console.error("Error fetching available actions:", error); | ||
| if (axios.isAxiosError(error)) { | ||
| throw new Error(`Failed to fetch available actions: ${error.response?.status} ${error.response?.statusText}`); | ||
| } | ||
| throw new Error("Failed to fetch available actions"); | ||
@@ -83,5 +116,8 @@ } | ||
| async getActionKnowledge(actionId) { | ||
| if (!actionId) { | ||
| throw new Error("Action ID is required"); | ||
| } | ||
| try { | ||
| const headers = this.generateHeaders(); | ||
| const url = `${this.baseUrl}/v1/knowledge?_id=${actionId}`; | ||
| const url = `${this.baseUrl}/v1/knowledge?_id=${encodeURIComponent(actionId)}`; | ||
| const response = await axios.get(url, { headers }); | ||
@@ -95,2 +131,5 @@ if (!response.data.rows || response.data.rows.length === 0) { | ||
| console.error("Error fetching action knowledge:", error); | ||
| if (axios.isAxiosError(error)) { | ||
| throw new Error(`Failed to fetch action knowledge: ${error.response?.status} ${error.response?.statusText}`); | ||
| } | ||
| throw new Error("Failed to fetch action knowledge"); | ||
@@ -100,8 +139,11 @@ } | ||
| replacePathVariables(path, variables) { | ||
| if (!path) | ||
| return path; | ||
| return path.replace(/\{\{([^}]+)\}\}/g, (match, variable) => { | ||
| const value = variables[variable]; | ||
| if (!value) { | ||
| throw new Error(`Missing value for path variable: ${variable}`); | ||
| const trimmedVariable = variable.trim(); | ||
| const value = variables[trimmedVariable]; | ||
| if (value === undefined || value === null || value === '') { | ||
| throw new Error(`Missing value for path variable: ${trimmedVariable}`); | ||
| } | ||
| return value.toString(); | ||
| return encodeURIComponent(value.toString()); | ||
| }); | ||
@@ -176,2 +218,59 @@ } | ||
| } | ||
| async generateRequestConfig(actionId, connectionKey, method, path, data, pathVariables, queryParams, headers, isFormData, isFormUrlEncoded) { | ||
| const newHeaders = { | ||
| ...this.generateHeaders(), | ||
| 'x-pica-connection-key': connectionKey, | ||
| 'x-pica-action-id': actionId, | ||
| ...(isFormData ? { 'Content-Type': 'multipart/form-data' } : {}), | ||
| ...(isFormUrlEncoded ? { 'Content-Type': 'application/x-www-form-urlencoded' } : {}), | ||
| ...headers | ||
| }; | ||
| // Handle path variables | ||
| let resolvedPath = path; | ||
| if (pathVariables) { | ||
| resolvedPath = this.replacePathVariables(path, pathVariables); | ||
| } | ||
| const url = `${this.baseUrl}/v1/passthrough${resolvedPath.startsWith('/') ? resolvedPath : '/' + resolvedPath}`; | ||
| const requestConfig = { | ||
| url, | ||
| method, | ||
| headers: newHeaders, | ||
| params: queryParams | ||
| }; | ||
| if (method?.toLowerCase() !== 'get') { | ||
| if (isFormData) { | ||
| const formData = new FormData(); | ||
| if (data && typeof data === 'object' && !Array.isArray(data)) { | ||
| Object.entries(data).forEach(([key, value]) => { | ||
| if (typeof value === 'object') { | ||
| formData.append(key, JSON.stringify(value)); | ||
| } | ||
| else { | ||
| formData.append(key, value); | ||
| } | ||
| }); | ||
| } | ||
| requestConfig.data = formData; | ||
| Object.assign(requestConfig.headers, formData.getHeaders()); | ||
| } | ||
| else if (isFormUrlEncoded) { | ||
| const params = new URLSearchParams(); | ||
| if (data && typeof data === 'object' && !Array.isArray(data)) { | ||
| Object.entries(data).forEach(([key, value]) => { | ||
| if (typeof value === 'object') { | ||
| params.append(key, JSON.stringify(value)); | ||
| } | ||
| else { | ||
| params.append(key, String(value)); | ||
| } | ||
| }); | ||
| } | ||
| requestConfig.data = params; | ||
| } | ||
| else { | ||
| requestConfig.data = data; | ||
| } | ||
| } | ||
| return requestConfig; | ||
| } | ||
| } | ||
@@ -186,17 +285,64 @@ const server = new Server({ | ||
| prompts: {}, | ||
| sampling: {}, | ||
| }, | ||
| }); | ||
| // Validate required environment variables | ||
| const PICA_SECRET = process.env.PICA_SECRET; | ||
| const picaClient = new PicaClient(PICA_SECRET); | ||
| if (!PICA_SECRET) { | ||
| console.error("PICA_SECRET environment variable is required"); | ||
| process.exit(1); | ||
| } | ||
| const PICA_BASE_URL = process.env.PICA_BASE_URL || "https://api.picaos.com"; | ||
| const picaClient = new PicaClient(PICA_SECRET, PICA_BASE_URL); | ||
| let picaInitialized = false; | ||
| let initializationPromise = null; | ||
| const initializePica = async () => { | ||
| if (!picaInitialized) { | ||
| await picaClient.initialize(); | ||
| picaInitialized = true; | ||
| if (picaInitialized) { | ||
| return; | ||
| } | ||
| if (initializationPromise) { | ||
| return initializationPromise; | ||
| } | ||
| initializationPromise = (async () => { | ||
| try { | ||
| await picaClient.initialize(); | ||
| picaInitialized = true; | ||
| console.error("Pica client initialized successfully"); | ||
| } | ||
| catch (error) { | ||
| console.error("Failed to initialize Pica client:", error); | ||
| // Reset so we can try again next time | ||
| initializationPromise = null; | ||
| throw error; | ||
| } | ||
| })(); | ||
| return initializationPromise; | ||
| }; | ||
| server.setRequestHandler(ListResourcesRequestSchema, async () => { | ||
| await initializePica(); | ||
| const resources = []; | ||
| // Add connections as resources | ||
| const connections = picaClient.getConnections(); | ||
| for (const connection of connections) { | ||
| if (connection.active) { | ||
| resources.push({ | ||
| uri: `pica-connection://${connection.platform}/${connection.key}`, | ||
| name: `${connection.platform} Connection (${connection.key})`, | ||
| description: `Active connection to ${connection.platform}`, | ||
| mimeType: "application/json" | ||
| }); | ||
| } | ||
| } | ||
| // Add unique platforms as resources | ||
| const platforms = [...new Set(connections.map(c => c.platform))]; | ||
| for (const platform of platforms) { | ||
| resources.push({ | ||
| uri: `pica-platform://${platform}`, | ||
| name: `${platform} Actions`, | ||
| description: `Available actions for ${platform}`, | ||
| mimeType: "application/json" | ||
| }); | ||
| } | ||
| return { | ||
| resources: [] | ||
| resources | ||
| }; | ||
@@ -243,2 +389,24 @@ }); | ||
| } | ||
| else if (scheme === 'pica-action:') { | ||
| const actionId = url.hostname; | ||
| try { | ||
| const action = await picaClient.getActionKnowledge(actionId); | ||
| return { | ||
| contents: [{ | ||
| uri: request.params.uri, | ||
| mimeType: "application/json", | ||
| text: JSON.stringify({ | ||
| id: action._id, | ||
| title: action.title, | ||
| knowledge: action.knowledge, | ||
| path: action.path, | ||
| tags: action.tags || [] | ||
| }, null, 2) | ||
| }] | ||
| }; | ||
| } | ||
| catch (error) { | ||
| throw new Error(`Failed to get action details: ${error.message}`); | ||
| } | ||
| } | ||
| throw new Error(`Unsupported resource URI scheme: ${scheme}`); | ||
@@ -335,2 +503,63 @@ }); | ||
| } | ||
| }, | ||
| { | ||
| name: "generate_action_config_knowledge", | ||
| description: "Generate request configuration for an action using knowledge-based path handling, this is to be used when the user is asking you to write code.", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| platform: { | ||
| type: "string", | ||
| description: "Platform name" | ||
| }, | ||
| action: { | ||
| type: "object", | ||
| properties: { | ||
| _id: { | ||
| type: "string", | ||
| description: "Action ID" | ||
| }, | ||
| path: { | ||
| type: "string", | ||
| description: "Action path template with variables" | ||
| } | ||
| }, | ||
| required: ["_id", "path"], | ||
| description: "Action object with ID and path" | ||
| }, | ||
| method: { | ||
| type: "string", | ||
| description: "HTTP method (GET, POST, PUT, DELETE, etc.)" | ||
| }, | ||
| connectionKey: { | ||
| type: "string", | ||
| description: "Key of the connection to use" | ||
| }, | ||
| data: { | ||
| type: "object", | ||
| description: "Request data (for POST, PUT, etc.)" | ||
| }, | ||
| pathVariables: { | ||
| type: "object", | ||
| description: "Variables to replace in the path" | ||
| }, | ||
| queryParams: { | ||
| type: "object", | ||
| description: "Query parameters" | ||
| }, | ||
| headers: { | ||
| type: "object", | ||
| description: "Additional headers" | ||
| }, | ||
| isFormData: { | ||
| type: "boolean", | ||
| description: "Whether to send data as multipart/form-data" | ||
| }, | ||
| isFormUrlEncoded: { | ||
| type: "boolean", | ||
| description: "Whether to send data as application/x-www-form-urlencoded" | ||
| } | ||
| }, | ||
| required: ["platform", "action", "method", "connectionKey"] | ||
| } | ||
| } | ||
@@ -465,2 +694,81 @@ ] | ||
| } | ||
| case "generate_action_config_knowledge": { | ||
| const { platform, action, method, connectionKey, data, pathVariables, queryParams, headers, isFormData, isFormUrlEncoded } = request.params.arguments; | ||
| try { | ||
| // Check if connection exists | ||
| const connections = picaClient.getConnections(); | ||
| if (!connections.some(conn => conn.key === connectionKey)) { | ||
| throw new Error(`Connection not found. Please add a ${platform} connection first.`); | ||
| } | ||
| // Handle path variables from action.path | ||
| const templateVariables = action.path.match(/\{\{([^}]+)\}\}/g); | ||
| let resolvedPath = action.path; | ||
| let finalPathVariables = pathVariables || {}; | ||
| let finalData = data; | ||
| if (templateVariables) { | ||
| const requiredVariables = templateVariables.map((v) => v.replace(/\{\{|\}\}/g, '')); | ||
| const combinedVariables = { | ||
| ...(Array.isArray(data) ? {} : (data || {})), | ||
| ...(pathVariables || {}) | ||
| }; | ||
| const missingVariables = requiredVariables.filter((v) => !combinedVariables[v]); | ||
| if (missingVariables.length > 0) { | ||
| throw new Error(`Missing required path variables: ${missingVariables.join(', ')}. ` + | ||
| `Please provide values for these variables.`); | ||
| } | ||
| // Clean up data object and prepare path variables | ||
| if (!Array.isArray(data) && data) { | ||
| finalData = { ...data }; | ||
| requiredVariables.forEach((v) => { | ||
| if (finalData && finalData[v] && (!pathVariables || !pathVariables[v])) { | ||
| finalPathVariables[v] = finalData[v]; | ||
| delete finalData[v]; | ||
| } | ||
| }); | ||
| } | ||
| resolvedPath = picaClient.replacePathVariables(action.path, finalPathVariables); | ||
| } | ||
| const requestConfig = await picaClient.generateRequestConfig(action._id, connectionKey, method, resolvedPath, finalData, finalPathVariables, queryParams, headers, isFormData, isFormUrlEncoded); | ||
| // Generate TypeScript code example | ||
| const tsCode = ` | ||
| import axios from 'axios'; | ||
| const requestConfig = ${JSON.stringify(requestConfig, null, 2)}; | ||
| // Make the request | ||
| try { | ||
| const response = await axios(requestConfig); | ||
| console.log('Response:', response.data); | ||
| } catch (error) { | ||
| console.error('Error:', error.response?.data || error.message); | ||
| } | ||
| IMPORTANT: For the Pica secret always use the environment variable PICA_SECRET. | ||
| `; | ||
| return { | ||
| content: [{ | ||
| type: "text", | ||
| text: JSON.stringify({ | ||
| success: true, | ||
| title: "Request config returned", | ||
| message: "Request config returned without execution. Use the TypeScript code below to make the HTTP request.", | ||
| requestConfig, | ||
| typeScriptCode: tsCode | ||
| }, null, 2) | ||
| }] | ||
| }; | ||
| } | ||
| catch (error) { | ||
| return { | ||
| content: [{ | ||
| type: "text", | ||
| text: JSON.stringify({ | ||
| success: false, | ||
| title: "Failed to create request config", | ||
| message: error.message | ||
| }, null, 2) | ||
| }] | ||
| }; | ||
| } | ||
| } | ||
| default: | ||
@@ -470,6 +778,240 @@ throw new Error("Unknown tool"); | ||
| }); | ||
| server.setRequestHandler(ListPromptsRequestSchema, async () => { | ||
| return { | ||
| prompts: [ | ||
| { | ||
| name: "create-api-integration", | ||
| description: "Generate code for integrating with a specific API endpoint", | ||
| arguments: [ | ||
| { | ||
| name: "platform", | ||
| description: "The platform to integrate with (e.g., slack, github)", | ||
| required: true | ||
| }, | ||
| { | ||
| name: "action", | ||
| description: "The specific action to perform", | ||
| required: true | ||
| }, | ||
| { | ||
| name: "language", | ||
| description: "Programming language for the integration (typescript, python, javascript)", | ||
| required: false | ||
| } | ||
| ] | ||
| }, | ||
| { | ||
| name: "list-platform-actions", | ||
| description: "Get a formatted list of available actions for a platform", | ||
| arguments: [ | ||
| { | ||
| name: "platform", | ||
| description: "The platform to list actions for", | ||
| required: true | ||
| } | ||
| ] | ||
| }, | ||
| { | ||
| name: "knowledge-agent-system", | ||
| description: "System prompt with Pica Knowledge Agent capabilities for enhanced API guidance", | ||
| arguments: [ | ||
| { | ||
| name: "includeEnvironmentVariables", | ||
| description: "Whether to include environment variable access in the prompt", | ||
| required: false | ||
| } | ||
| ] | ||
| }, | ||
| { | ||
| name: "default-system", | ||
| description: "Basic system prompt with Pica Intelligence Tools", | ||
| arguments: [] | ||
| } | ||
| ] | ||
| }; | ||
| }); | ||
| server.setRequestHandler(GetPromptRequestSchema, async (request) => { | ||
| await initializePica(); | ||
| const { name, arguments: args } = request.params; | ||
| switch (name) { | ||
| case "create-api-integration": { | ||
| const platform = args?.platform; | ||
| const action = args?.action; | ||
| const language = args?.language || "typescript"; | ||
| if (!platform || !action) { | ||
| throw new Error("Platform and action arguments are required"); | ||
| } | ||
| return { | ||
| messages: [ | ||
| { | ||
| role: "user", | ||
| content: { | ||
| type: "text", | ||
| text: `Create a ${language} integration for the ${platform} API to ${action}. Include proper error handling, type definitions (if applicable), and example usage.` | ||
| } | ||
| } | ||
| ] | ||
| }; | ||
| } | ||
| case "list-platform-actions": { | ||
| const platform = args?.platform; | ||
| if (!platform) { | ||
| throw new Error("Platform argument is required"); | ||
| } | ||
| return { | ||
| messages: [ | ||
| { | ||
| role: "user", | ||
| content: { | ||
| type: "text", | ||
| text: `List all available actions for the ${platform} platform. Format the output as a categorized list with descriptions for each action.` | ||
| } | ||
| } | ||
| ] | ||
| }; | ||
| } | ||
| case "knowledge-agent-system": { | ||
| const includeEnvironmentVariables = args?.includeEnvironmentVariables || false; | ||
| const connections = picaClient.getConnections().filter(conn => conn.active); | ||
| const connectionDefinitions = picaClient.getConnectionDefinitions(); | ||
| const connectionsInfo = formatConnectionsInfo(connections); | ||
| const availablePlatformsInfo = formatAvailablePlatformsInfo(connectionDefinitions); | ||
| const envPrompt = includeEnvironmentVariables ? ` | ||
| ENVIRONMENT VARIABLES: | ||
| You can access environment variables when needed for configuration or API keys.` : ''; | ||
| const systemPrompt = `You have access to Pica's Intelligence Tools with Knowledge Agent capabilities that can help you connect to various APIs and services. | ||
| ACTIVE CONNECTIONS: | ||
| ${connectionsInfo} | ||
| AVAILABLE PLATFORMS: | ||
| ${availablePlatformsInfo}${envPrompt} | ||
| You can: | ||
| 1. Get available actions for any platform using getAvailableActions | ||
| 2. Get detailed knowledge about specific actions using getActionKnowledge | ||
| 3. Generate request configurations (without executing) using execute | ||
| As a Knowledge Agent, you have enhanced understanding of API documentation and can provide detailed guidance on using various platforms and their APIs. | ||
| When using the execute tool, you will receive a TypeScript code block showing how to make the HTTP request using the Pica Passthrough API. | ||
| Always check what connections are available before attempting to use them.`; | ||
| return { | ||
| messages: [ | ||
| { | ||
| role: "system", | ||
| content: { | ||
| type: "text", | ||
| text: systemPrompt | ||
| } | ||
| } | ||
| ] | ||
| }; | ||
| } | ||
| case "default-system": { | ||
| const connections = picaClient.getConnections().filter(conn => conn.active); | ||
| const connectionDefinitions = picaClient.getConnectionDefinitions(); | ||
| const connectionsInfo = formatConnectionsInfo(connections); | ||
| const availablePlatformsInfo = formatAvailablePlatformsInfo(connectionDefinitions); | ||
| const systemPrompt = `You have access to Pica's Intelligence Tools that can help you connect to various APIs and services. | ||
| ACTIVE CONNECTIONS: | ||
| ${connectionsInfo} | ||
| AVAILABLE PLATFORMS: | ||
| ${availablePlatformsInfo} | ||
| You can: | ||
| 1. Get available actions for any platform using getAvailableActions | ||
| 2. Get detailed knowledge about specific actions using getActionKnowledge | ||
| 3. Generate request configurations (without executing) using execute | ||
| When using the execute tool, you will receive a TypeScript code block showing how to make the HTTP request using the Pica Passthrough API. | ||
| Always check what connections are available before attempting to use them.`; | ||
| return { | ||
| messages: [ | ||
| { | ||
| role: "system", | ||
| content: { | ||
| type: "text", | ||
| text: systemPrompt | ||
| } | ||
| } | ||
| ] | ||
| }; | ||
| } | ||
| default: | ||
| throw new Error(`Unknown prompt: ${name}`); | ||
| } | ||
| }); | ||
| server.setRequestHandler(CreateMessageRequestSchema, async (request) => { | ||
| const { messages, modelPreferences } = request.params; | ||
| // Extract context from the conversation | ||
| const lastMessage = messages[messages.length - 1]; | ||
| if (!lastMessage || lastMessage.role !== 'user') { | ||
| throw new Error("Expected user message"); | ||
| } | ||
| let content = ''; | ||
| if (typeof lastMessage.content === 'string') { | ||
| content = lastMessage.content; | ||
| } | ||
| else if (typeof lastMessage.content === 'object' && 'text' in lastMessage.content) { | ||
| content = lastMessage.content.text || ''; | ||
| } | ||
| // Analyze the request and generate appropriate response | ||
| if (content.includes('generate') && content.includes('code')) { | ||
| // Extract platform and action from context | ||
| const platformMatch = content.match(/(?:for|using)\s+(\w+)/i); | ||
| const actionMatch = content.match(/(?:to|for)\s+(.+?)(?:\.|$)/i); | ||
| if (platformMatch && actionMatch) { | ||
| const platform = platformMatch[1]; | ||
| const action = actionMatch[1]; | ||
| return { | ||
| model: modelPreferences?.hints?.find(h => h.name === 'model')?.value || 'claude-3', | ||
| role: 'assistant', | ||
| content: { | ||
| type: 'text', | ||
| text: `I'll help you generate code for ${platform} to ${action}. First, let me check what actions are available for this platform...` | ||
| } | ||
| }; | ||
| } | ||
| } | ||
| // Default response | ||
| return { | ||
| model: modelPreferences?.hints?.find(h => h.name === 'model')?.value || 'claude-3', | ||
| role: 'assistant', | ||
| content: { | ||
| type: 'text', | ||
| text: "I can help you with Pica integrations. You can ask me to list connections, get available actions for a platform, or generate integration code." | ||
| } | ||
| }; | ||
| }); | ||
| async function main() { | ||
| const transport = new StdioServerTransport(); | ||
| await server.connect(transport); | ||
| try { | ||
| const transport = new StdioServerTransport(); | ||
| await server.connect(transport); | ||
| console.error("Pica MCP server running on stdio"); | ||
| // Log debug info if requested | ||
| if (process.env.DEBUG === 'true') { | ||
| console.error("Debug mode enabled"); | ||
| console.error(`PICA_BASE_URL: ${PICA_BASE_URL}`); | ||
| } | ||
| } | ||
| catch (error) { | ||
| console.error("Failed to start server:", error); | ||
| process.exit(1); | ||
| } | ||
| } | ||
| // Handle graceful shutdown | ||
| process.on('SIGINT', async () => { | ||
| console.error("Shutting down server..."); | ||
| process.exit(0); | ||
| }); | ||
| process.on('SIGTERM', async () => { | ||
| console.error("Shutting down server..."); | ||
| process.exit(0); | ||
| }); | ||
| main().catch((error) => { | ||
@@ -476,0 +1018,0 @@ console.error("Server error:", error); |
+15
-6
| { | ||
| "name": "@picahq/pica-mcp", | ||
| "version": "1.0.2", | ||
| "version": "1.1.0", | ||
| "description": "A Model Context Protocol Server for Pica", | ||
@@ -16,12 +16,21 @@ "type": "module", | ||
| "watch": "tsc --watch", | ||
| "inspector": "npx @modelcontextprotocol/inspector build/index.js" | ||
| "inspector": "npx @modelcontextprotocol/inspector build/index.js", | ||
| "http": "npm run build && node build/index.js", | ||
| "http:dev": "npm run build && nodemon build/index.js" | ||
| }, | ||
| "dependencies": { | ||
| "@modelcontextprotocol/sdk": "0.6.0", | ||
| "@modelcontextprotocol/sdk": "^1.12.1", | ||
| "@vercel/mcp-adapter": "^0.9.1", | ||
| "axios": "^1.8.3", | ||
| "form-data": "^4.0.2" | ||
| "cors": "^2.8.5", | ||
| "express": "^5.1.0", | ||
| "form-data": "^4.0.2", | ||
| "zod": "^3.23.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/node": "^20.11.24", | ||
| "typescript": "^5.3.3" | ||
| "@types/cors": "^2.8.19", | ||
| "@types/express": "^5.0.3", | ||
| "@types/node": "^22.0.0", | ||
| "nodemon": "^3.1.10", | ||
| "typescript": "^5.6.0" | ||
| }, | ||
@@ -28,0 +37,0 @@ "author": { |
+234
-124
| # Pica MCP Server | ||
| [](https://smithery.ai/server/@picahq/pica) | ||
| A Model Context Protocol (MCP) server that integrates with the Pica API platform, enabling seamless interaction with various third-party services through a standardized interface. | ||
|  | ||
| ## Features | ||
| A [Model Context Protocol](https://modelcontextprotocol.io) Server for [Pica](https://picaos.com), built in TypeScript. | ||
| ### 🔧 Tools | ||
| - **list_user_connections_and_available_connectors** - List all available connectors and active connections | ||
| - **get_available_actions** - Get available actions for a specific platform | ||
| - **get_action_knowledge** - Get detailed information about a specific action | ||
| - **execute_action** - Execute API actions with full parameter support | ||
| - **generate_action_config_knowledge** - Generate request configurations for code generation | ||
| **Setup Video:** https://youtu.be/JJ62NUEkKAs | ||
| ### 📚 Resources | ||
| - **pica-platform://{platform}** - Browse available actions for a platform | ||
| - **pica-connection://{platform}/{key}** - View connection details | ||
| - **pica-action://{actionId}** - Get detailed action information | ||
| **Demo Video:** https://youtu.be/0jeasO20PyM | ||
| ### 💬 Prompts | ||
| - **create-api-integration** - Generate code for API integrations | ||
| - **list-platform-actions** - Get formatted lists of platform actions | ||
| ## What is MCP? | ||
| ### 🤖 Sampling | ||
| The server supports the MCP sampling capability for generating contextual responses based on conversation history. | ||
| [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is a system that lets AI apps, like [Claude Desktop](https://claude.ai/download), connect to external tools and data sources. It gives a clear and safe way for AI assistants to work with local services and APIs while keeping the user in control. | ||
| ## Architecture | ||
| ## What is Pica? | ||
| ```mermaid | ||
| graph TB | ||
| Client["MCP Client<br/>(Claude, VS Code, etc.)"] | ||
| Server["Pica MCP Server"] | ||
| Transport["StdioServerTransport"] | ||
| PicaAPI["Pica API"] | ||
| Client <-->|"JSON-RPC<br/>over stdio"| Transport | ||
| Transport <--> Server | ||
| Server -->|"HTTP/REST"| PicaAPI | ||
| ``` | ||
| [Pica](https://picaos.com) is a powerful agentic tooling platform that enables connections to 70+ third-party services and applications. This MCP server allows Claude Desktop and anything using the [Model Context Protocol](https://modelcontextprotocol.io) to securely interact with all these connections through natural language requests. | ||
| ## Installation | ||
| With Pica MCP Server, you can: | ||
| ```bash | ||
| npm install @picahq/pica-mcp | ||
| ``` | ||
| - **Access Multiple Data Sources**: Query databases, fetch files, and retrieve information across services | ||
| - **Automate Workflows**: Trigger actions and automate tasks across your connected platforms | ||
| - **Enhance LLM Capabilities**: Give Claude Desktop powerful real-world capabilities through API access | ||
| ## Deployment Options | ||
| ### Supported Connections | ||
| ### Deploy to Vercel | ||
| Pica supports [70+ connections](https://app.picaos.com/tools) (with more added regularly) across categories like: | ||
| You can deploy this MCP server to Vercel for remote access: | ||
| #### Communication & Collaboration | ||
| - Gmail, Outlook Mail, Slack, Teams, SendGrid | ||
| - Notion, Google Drive, Dropbox, OneDrive | ||
| 1. Install dependencies including Vercel adapter: | ||
| ```bash | ||
| npm install @vercel/mcp-adapter zod | ||
| ``` | ||
| #### Data & Analytics | ||
| - PostgreSQL, BigQuery, Supabase | ||
| - Google Sheets, Airtable | ||
| 2. Deploy to Vercel: | ||
| ```bash | ||
| vercel | ||
| ``` | ||
| #### Business & CRM | ||
| - Salesforce, HubSpot, Pipedrive, Zoho | ||
| - Zendesk, Freshdesk, Intercom | ||
| 3. Configure your MCP client to use the remote server: | ||
| - **For Cursor**: `https://your-project.vercel.app/api/mcp` | ||
| - **For Claude/Cline**: Use `npx mcp-remote https://your-project.vercel.app/api/mcp` | ||
| #### AI & ML Services | ||
| - OpenAI, Anthropic, Gemini, ElevenLabs | ||
| See [DEPLOYMENT.md](./DEPLOYMENT.md) for detailed Vercel deployment instructions. | ||
| #### E-commerce & Financial | ||
| - Shopify, BigCommerce, Square, Stripe | ||
| - QuickBooks, Xero, NetSuite | ||
| ## Configuration | ||
| ## Installation 🛠️ | ||
| Set the following environment variables: | ||
| ### Using npx | ||
| ```bash | ||
| # Required | ||
| export PICA_SECRET="your-pica-secret-key" | ||
| You can use this package directly with npx: | ||
| # Optional | ||
| export PICA_BASE_URL="https://api.picaos.com" # Default | ||
| export DEBUG="true" # Enable debug logging | ||
| ``` | ||
| ## Usage | ||
| ### As a Standalone Server | ||
| ```bash | ||
| # Using npx | ||
| npx @picahq/pica-mcp | ||
| # Or if installed globally | ||
| pica-mcp | ||
| ``` | ||
| Or install it globally: | ||
| ### With MCP Inspector | ||
| ```bash | ||
| npm install -g @picahq/pica-mcp | ||
| npm run inspector | ||
| ``` | ||
| ### Installing via Smithery | ||
| ### In Claude Desktop | ||
| To install pica for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@picahq/pica): | ||
| Add to your Claude configuration file: | ||
| ```bash | ||
| npx -y @smithery/cli install @picahq/pica --client claude | ||
| ```json | ||
| { | ||
| "mcpServers": { | ||
| "pica": { | ||
| "command": "npx", | ||
| "args": ["@picahq/pica-mcp"], | ||
| "env": { | ||
| "PICA_SECRET": "your-pica-secret-key" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| ### Environment Setup | ||
| ## Examples | ||
| This server requires a [Pica API key](https://app.picaos.com/settings/api-keys). Set the environment variable: | ||
| ### List Available Connections | ||
| ```bash | ||
| export PICA_SECRET=your_pica_secret_key | ||
| ```typescript | ||
| // Using the list_user_connections_and_available_connectors tool | ||
| const result = await client.callTool({ | ||
| name: "list_user_connections_and_available_connectors" | ||
| }); | ||
| ``` | ||
| ### Using Docker | ||
| ### Get Platform Actions | ||
| Build the Docker Image: | ||
| ```typescript | ||
| // Using the get_available_actions tool | ||
| const actions = await client.callTool({ | ||
| name: "get_available_actions", | ||
| arguments: { | ||
| platform: "slack" | ||
| } | ||
| }); | ||
| ``` | ||
| ```bash | ||
| docker build -t pica-mcp-server . | ||
| ### Execute an Action | ||
| ```typescript | ||
| // Using the execute_action tool | ||
| const result = await client.callTool({ | ||
| name: "execute_action", | ||
| arguments: { | ||
| actionId: "action-id", | ||
| connectionKey: "connection-key", | ||
| method: "POST", | ||
| path: "/api/messages", | ||
| data: { | ||
| channel: "#general", | ||
| text: "Hello from MCP!" | ||
| } | ||
| } | ||
| }); | ||
| ``` | ||
| Run the Docker Container: | ||
| ### Generate Integration Code | ||
| ```bash | ||
| docker run -e PICA_SECRET=your_pica_secret_key pica-mcp-server | ||
| ```typescript | ||
| // Using the generate_action_config_knowledge tool | ||
| const config = await client.callTool({ | ||
| name: "generate_action_config_knowledge", | ||
| arguments: { | ||
| platform: "slack", | ||
| action: { | ||
| _id: "send-message", | ||
| path: "/api/messages" | ||
| }, | ||
| method: "POST", | ||
| connectionKey: "slack-connection-key", | ||
| data: { | ||
| channel: "#general", | ||
| text: "Hello!" | ||
| } | ||
| } | ||
| }); | ||
| ``` | ||
| ### Manual Installation | ||
| ## Development | ||
| Install dependencies: | ||
| ### Building | ||
| ```bash | ||
| npm install | ||
| npm run build | ||
| ``` | ||
| Build the server: | ||
| ### Watching for Changes | ||
| ```bash | ||
| npm run build | ||
| npm run watch | ||
| ``` | ||
| For development with auto-rebuild: | ||
| ### Running with Debug Mode | ||
| ```bash | ||
| npm run watch | ||
| DEBUG=true npm run build && node build/index.js | ||
| ``` | ||
| ### Using Claude Desktop | ||
| ## API Reference | ||
| ### Tools | ||
| To use with [Claude Desktop](https://claude.ai/download), add the server config: | ||
| #### list_user_connections_and_available_connectors | ||
| Lists all connections in the user's Pica account and available connectors. | ||
| On MacOS: `~/Library/Application\ Support/Claude/claude_desktop_config.json` | ||
| **Parameters:** None | ||
| On Windows: `%APPDATA%/Claude/claude_desktop_config.json` | ||
| **Returns:** | ||
| - `connections`: Array of active connections | ||
| - `availablePicaConnectors`: Array of available connectors | ||
| #### Docker | ||
| #### get_available_actions | ||
| Get available actions for a specific platform. | ||
| To use the Docker container with Claude Desktop, update your `claude_desktop_config.json` with: | ||
| **Parameters:** | ||
| - `platform` (string, required): Platform name | ||
| ```json | ||
| { | ||
| "mcpServers": { | ||
| "pica-mcp-server": { | ||
| "command": "docker", | ||
| "args": [ | ||
| "run", | ||
| "--rm", | ||
| "-i", | ||
| "-e", "PICA_SECRET=YOUR_PICA_SECRET_KEY", | ||
| "pica-mcp-server" | ||
| ] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| **Returns:** | ||
| - `actions`: Array of available actions with id, title, and tags | ||
| #### Manual | ||
| #### get_action_knowledge | ||
| Get detailed information about a specific action. | ||
| ```json | ||
| { | ||
| "mcpServers": { | ||
| "pica-mcp-server": { | ||
| "command": "node", | ||
| "args": [ | ||
| "/path/to/pica-mcp-server/build/index.js" | ||
| ], | ||
| "env": { | ||
| "PICA_SECRET": "YOUR_PICA_SECRET_KEY" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| **Parameters:** | ||
| - `actionId` (string, required): Action ID | ||
| ### Debugging | ||
| **Returns:** | ||
| - `action`: Detailed action information including knowledge base | ||
| Since MCP servers communicate over stdio, debugging can be challenging. We recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector), which is available as a package script: | ||
| #### execute_action | ||
| Execute a specific action through the Pica API. | ||
| ```bash | ||
| npm run inspector | ||
| ``` | ||
| **Parameters:** | ||
| - `actionId` (string, required): Action ID | ||
| - `connectionKey` (string, required): Connection key | ||
| - `method` (string, required): HTTP method | ||
| - `path` (string, required): API path | ||
| - `data` (object, optional): Request body | ||
| - `pathVariables` (object, optional): Path variables | ||
| - `queryParams` (object, optional): Query parameters | ||
| - `headers` (object, optional): Additional headers | ||
| - `isFormData` (boolean, optional): Send as multipart/form-data | ||
| - `isFormUrlEncoded` (boolean, optional): Send as URL-encoded | ||
| The Inspector will provide a URL to access debugging tools in your browser. | ||
| **Returns:** | ||
| - `result`: API response data | ||
| - `requestConfig`: Request configuration details | ||
| ## Example Usage ✨ | ||
| #### generate_action_config_knowledge | ||
| Generate request configuration for code generation. | ||
| Once you've added the server config and connected some platforms in the [Pica dashboard](https://app.picaos.com/connections), restart Claude Desktop and try out some examples: | ||
| **Parameters:** | ||
| - `platform` (string, required): Platform name | ||
| - `action` (object, required): Action object with _id and path | ||
| - `method` (string, required): HTTP method | ||
| - `connectionKey` (string, required): Connection key | ||
| - `data` (object, optional): Request body | ||
| - `pathVariables` (object, optional): Path variables | ||
| - `queryParams` (object, optional): Query parameters | ||
| - `headers` (object, optional): Additional headers | ||
| - `isFormData` (boolean, optional): Send as multipart/form-data | ||
| - `isFormUrlEncoded` (boolean, optional): Send as URL-encoded | ||
| ### Communication & Productivity | ||
| - Send an email using Gmail to a colleague with a meeting summary | ||
| - Create a calendar event in Google Calendar for next Tuesday at 2pm | ||
| - Send a message in Slack to the #marketing channel with the latest campaign metrics | ||
| - Find documents in Google Drive related to Q3 planning | ||
| **Returns:** | ||
| - `requestConfig`: Complete request configuration | ||
| - `typeScriptCode`: Example TypeScript code | ||
| ### Data Access & Analysis | ||
| - List the top 10 customers from my PostgreSQL database | ||
| - Create a new sheet in Google Sheets with sales data | ||
| - Query Salesforce for opportunities closing this month | ||
| - Update a Notion database with project statuses | ||
| ### Resources | ||
| ### Business Operations | ||
| - Create a support ticket in Zendesk from customer feedback | ||
| - Process a refund for a customer order in Stripe | ||
| - Add a new lead to HubSpot from a website inquiry | ||
| - Generate an invoice in QuickBooks for a client project | ||
| Resources can be read using the MCP resource protocol: | ||
| ### AI & Content | ||
| - Generate an image with DALL-E based on product specifications | ||
| - Transcribe a meeting recording with ElevenLabs | ||
| - Research market trends using Tavily or SerpApi | ||
| - Analyze customer sentiment from support tickets | ||
| - `pica-platform://{platform}` - List of actions for a platform | ||
| - `pica-connection://{platform}/{key}` - Connection details | ||
| - `pica-action://{actionId}` - Detailed action information | ||
| Got any cool examples? [Open a PR](https://github.com/picahq/awesome-pica) and share them! | ||
| ### Prompts | ||
| #### create-api-integration | ||
| Generate code for API integrations. | ||
| **Arguments:** | ||
| - `platform` (string, required): Target platform | ||
| - `action` (string, required): Action to perform | ||
| - `language` (string, optional): Programming language (default: "typescript") | ||
| #### list-platform-actions | ||
| Get a formatted list of available actions. | ||
| **Arguments:** | ||
| - `platform` (string, required): Target platform | ||
| ## Error Handling | ||
| The server implements comprehensive error handling: | ||
| - Connection validation before action execution | ||
| - Path variable validation and substitution | ||
| - Graceful handling of API failures | ||
| - Detailed error messages for debugging | ||
| ## Security | ||
| - API keys are passed via environment variables | ||
| - Connections are validated before use | ||
| - All requests include proper authentication headers | ||
| ## License | ||
| This project is licensed under the GPL-3.0 license. See the [LICENSE](LICENSE) file for details. | ||
| GPL-3.0 | ||
| ## Support | ||
| For support, please contact support@picaos.com or visit https://picaos.com |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
85129
42.39%993
113.09%316
53.4%7
133.33%5
150%9
200%5
66.67%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed