UNPKG

@stackone/mcp-connectors

Version:
1,215 lines (1,211 loc) 585 kB
import { mcpConnectorConfig } from "@stackone/mcp-config-types"; import { z } from "zod"; import OpenAI from "openai"; import { create, insertMultiple, search } from "@orama/orama"; import { parse } from "node-html-parser"; import { LinearClient } from "@linear/sdk"; import { ItemBuilder, OnePasswordConnect } from "@1password/connect"; //#region src/connectors/asana.ts var AsanaClient = class { headers; baseUrl = "https://app.asana.com/api/1.0"; constructor(accessToken) { this.headers = { Authorization: `Bearer ${accessToken}`, Accept: "application/json" }; } async getUser(userGid = "me") { const response = await fetch(`${this.baseUrl}/users/${userGid}`, { headers: this.headers }); if (!response.ok) throw new Error(`Asana API error: ${response.status} ${response.statusText}`); return (await response.json()).data; } async getWorkspaces() { const response = await fetch(`${this.baseUrl}/workspaces`, { headers: this.headers }); if (!response.ok) throw new Error(`Asana API error: ${response.status} ${response.statusText}`); return (await response.json()).data; } async getTeams(workspaceGid) { const response = await fetch(`${this.baseUrl}/workspaces/${workspaceGid}/teams`, { headers: this.headers }); if (!response.ok) throw new Error(`Asana API error: ${response.status} ${response.statusText}`); return (await response.json()).data; } async getProjects(workspaceGid, teamGid, limit = 50) { const params = new URLSearchParams({ limit: limit.toString() }); if (workspaceGid) params.append("workspace", workspaceGid); if (teamGid) params.append("team", teamGid); const response = await fetch(`${this.baseUrl}/projects?${params}`, { headers: this.headers }); if (!response.ok) throw new Error(`Asana API error: ${response.status} ${response.statusText}`); return (await response.json()).data; } async getProject(projectGid) { const response = await fetch(`${this.baseUrl}/projects/${projectGid}`, { headers: this.headers }); if (!response.ok) throw new Error(`Asana API error: ${response.status} ${response.statusText}`); return (await response.json()).data; } async getTasks(projectGid, assigneeGid, workspaceGid, completedSince, limit = 50) { const params = new URLSearchParams({ limit: limit.toString(), opt_fields: "name,notes,completed,assignee,assignee.name,due_at,due_on,projects,projects.name,tags,tags.name,custom_fields,created_at,modified_at,permalink_url" }); if (projectGid) params.append("project", projectGid); if (assigneeGid) params.append("assignee", assigneeGid); if (workspaceGid) params.append("workspace", workspaceGid); if (completedSince) params.append("completed_since", completedSince); const response = await fetch(`${this.baseUrl}/tasks?${params}`, { headers: this.headers }); if (!response.ok) throw new Error(`Asana API error: ${response.status} ${response.statusText}`); return (await response.json()).data; } async getTask(taskGid) { const params = new URLSearchParams({ opt_fields: "name,notes,html_notes,completed,assignee,assignee.name,assignee_status,due_at,due_on,projects,projects.name,memberships,memberships.project,memberships.project.name,memberships.section,memberships.section.name,tags,tags.name,followers,followers.name,custom_fields,custom_fields.name,custom_fields.type,custom_fields.number_value,custom_fields.text_value,custom_fields.enum_value,custom_fields.enum_value.name,created_at,modified_at,permalink_url" }); const response = await fetch(`${this.baseUrl}/tasks/${taskGid}?${params}`, { headers: this.headers }); if (!response.ok) throw new Error(`Asana API error: ${response.status} ${response.statusText}`); return (await response.json()).data; } async createTask(data) { const response = await fetch(`${this.baseUrl}/tasks`, { method: "POST", headers: { ...this.headers, "Content-Type": "application/json" }, body: JSON.stringify({ data }) }); if (!response.ok) throw new Error(`Asana API error: ${response.status} ${response.statusText}`); return (await response.json()).data; } async updateTask(taskGid, data) { const response = await fetch(`${this.baseUrl}/tasks/${taskGid}`, { method: "PUT", headers: { ...this.headers, "Content-Type": "application/json" }, body: JSON.stringify({ data }) }); if (!response.ok) throw new Error(`Asana API error: ${response.status} ${response.statusText}`); return (await response.json()).data; } async deleteTask(taskGid) { const response = await fetch(`${this.baseUrl}/tasks/${taskGid}`, { method: "DELETE", headers: this.headers }); if (!response.ok) throw new Error(`Asana API error: ${response.status} ${response.statusText}`); return (await response.json()).data; } async createProject(data) { const response = await fetch(`${this.baseUrl}/projects`, { method: "POST", headers: { ...this.headers, "Content-Type": "application/json" }, body: JSON.stringify({ data }) }); if (!response.ok) throw new Error(`Asana API error: ${response.status} ${response.statusText}`); return (await response.json()).data; } async updateProject(projectGid, data) { const response = await fetch(`${this.baseUrl}/projects/${projectGid}`, { method: "PUT", headers: { ...this.headers, "Content-Type": "application/json" }, body: JSON.stringify({ data }) }); if (!response.ok) throw new Error(`Asana API error: ${response.status} ${response.statusText}`); return (await response.json()).data; } }; const AsanaConnectorConfig = mcpConnectorConfig({ name: "Asana", key: "asana", version: "1.0.0", logo: "https://stackone-logos.com/api/asana/filled/svg", credentials: z.object({ accessToken: z.string().describe("Asana Personal Access Token from Settings > Apps > Personal Access Tokens :: 0/68a9e79b868c6789e79a124c30b0") }), setup: z.object({}), examplePrompt: "Create a new task called \"Review quarterly reports\" assigned to me with a due date of next Friday, and list all projects in our main workspace.", tools: (tool) => ({ GET_USER: tool({ name: "asana_get_user", description: "Get information about the current user or a specific user", schema: z.object({ userGid: z.string().optional().describe("User GID to get info for (omit for current user)") }), handler: async (args, context) => { try { const { accessToken } = await context.getCredentials(); const user = await new AsanaClient(accessToken).getUser(args.userGid); return JSON.stringify(user, null, 2); } catch (error) { return `Failed to get user: ${error instanceof Error ? error.message : String(error)}`; } } }), LIST_WORKSPACES: tool({ name: "asana_list_workspaces", description: "List all workspaces the user has access to", schema: z.object({}), handler: async (_args, context) => { try { const { accessToken } = await context.getCredentials(); const workspaces = await new AsanaClient(accessToken).getWorkspaces(); return JSON.stringify(workspaces, null, 2); } catch (error) { return `Failed to list workspaces: ${error instanceof Error ? error.message : String(error)}`; } } }), LIST_TEAMS: tool({ name: "asana_list_teams", description: "List teams in a workspace", schema: z.object({ workspaceGid: z.string().describe("Workspace GID to list teams for") }), handler: async (args, context) => { try { const { accessToken } = await context.getCredentials(); const teams = await new AsanaClient(accessToken).getTeams(args.workspaceGid); return JSON.stringify(teams, null, 2); } catch (error) { return `Failed to list teams: ${error instanceof Error ? error.message : String(error)}`; } } }), LIST_PROJECTS: tool({ name: "asana_list_projects", description: "List projects with optional filtering", schema: z.object({ workspaceGid: z.string().optional().describe("Workspace GID to filter projects"), teamGid: z.string().optional().describe("Team GID to filter projects"), limit: z.number().default(50).describe("Maximum number of projects to return") }), handler: async (args, context) => { try { const { accessToken } = await context.getCredentials(); const projects = await new AsanaClient(accessToken).getProjects(args.workspaceGid, args.teamGid, args.limit); return JSON.stringify(projects, null, 2); } catch (error) { return `Failed to list projects: ${error instanceof Error ? error.message : String(error)}`; } } }), GET_PROJECT: tool({ name: "asana_get_project", description: "Get detailed information about a specific project", schema: z.object({ projectGid: z.string().describe("Project GID to retrieve") }), handler: async (args, context) => { try { const { accessToken } = await context.getCredentials(); const project = await new AsanaClient(accessToken).getProject(args.projectGid); return JSON.stringify(project, null, 2); } catch (error) { return `Failed to get project: ${error instanceof Error ? error.message : String(error)}`; } } }), LIST_TASKS: tool({ name: "asana_list_tasks", description: "List tasks with optional filtering", schema: z.object({ projectGid: z.string().optional().describe("Project GID to filter tasks"), assigneeGid: z.string().optional().describe("Assignee GID to filter tasks"), workspaceGid: z.string().optional().describe("Workspace GID to filter tasks"), completedSince: z.string().optional().describe("ISO 8601 datetime to filter completed tasks since"), limit: z.number().default(50).describe("Maximum number of tasks to return") }), handler: async (args, context) => { try { const { accessToken } = await context.getCredentials(); const tasks = await new AsanaClient(accessToken).getTasks(args.projectGid, args.assigneeGid, args.workspaceGid, args.completedSince, args.limit); return JSON.stringify(tasks, null, 2); } catch (error) { return `Failed to list tasks: ${error instanceof Error ? error.message : String(error)}`; } } }), GET_TASK: tool({ name: "asana_get_task", description: "Get detailed information about a specific task", schema: z.object({ taskGid: z.string().describe("Task GID to retrieve") }), handler: async (args, context) => { try { const { accessToken } = await context.getCredentials(); const task = await new AsanaClient(accessToken).getTask(args.taskGid); return JSON.stringify(task, null, 2); } catch (error) { return `Failed to get task: ${error instanceof Error ? error.message : String(error)}`; } } }), CREATE_TASK: tool({ name: "asana_create_task", description: "Create a new task", schema: z.object({ name: z.string().describe("Task name"), notes: z.string().optional().describe("Task notes (plain text)"), htmlNotes: z.string().optional().describe("Task notes (HTML format)"), assignee: z.string().optional().describe("Assignee GID or \"me\""), projects: z.array(z.string()).optional().describe("Array of project GIDs"), dueAt: z.string().optional().describe("Due date and time (ISO 8601)"), dueOn: z.string().optional().describe("Due date (YYYY-MM-DD)"), completed: z.boolean().optional().describe("Whether the task is completed") }), handler: async (args, context) => { try { const { accessToken } = await context.getCredentials(); const task = await new AsanaClient(accessToken).createTask({ name: args.name, notes: args.notes, html_notes: args.htmlNotes, assignee: args.assignee, projects: args.projects, due_at: args.dueAt, due_on: args.dueOn, completed: args.completed }); return JSON.stringify(task, null, 2); } catch (error) { return `Failed to create task: ${error instanceof Error ? error.message : String(error)}`; } } }), UPDATE_TASK: tool({ name: "asana_update_task", description: "Update an existing task", schema: z.object({ taskGid: z.string().describe("Task GID to update"), name: z.string().optional().describe("Task name"), notes: z.string().optional().describe("Task notes (plain text)"), htmlNotes: z.string().optional().describe("Task notes (HTML format)"), assignee: z.string().optional().describe("Assignee GID or \"me\""), dueAt: z.string().optional().describe("Due date and time (ISO 8601)"), dueOn: z.string().optional().describe("Due date (YYYY-MM-DD)"), completed: z.boolean().optional().describe("Whether the task is completed") }), handler: async (args, context) => { try { const { accessToken } = await context.getCredentials(); const task = await new AsanaClient(accessToken).updateTask(args.taskGid, { name: args.name, notes: args.notes, html_notes: args.htmlNotes, assignee: args.assignee, due_at: args.dueAt, due_on: args.dueOn, completed: args.completed }); return JSON.stringify(task, null, 2); } catch (error) { return `Failed to update task: ${error instanceof Error ? error.message : String(error)}`; } } }), DELETE_TASK: tool({ name: "asana_delete_task", description: "Delete a task", schema: z.object({ taskGid: z.string().describe("Task GID to delete") }), handler: async (args, context) => { try { const { accessToken } = await context.getCredentials(); const result = await new AsanaClient(accessToken).deleteTask(args.taskGid); return JSON.stringify(result, null, 2); } catch (error) { return `Failed to delete task: ${error instanceof Error ? error.message : String(error)}`; } } }), CREATE_PROJECT: tool({ name: "asana_create_project", description: "Create a new project", schema: z.object({ name: z.string().describe("Project name"), notes: z.string().optional().describe("Project notes"), team: z.string().optional().describe("Team GID"), workspace: z.string().optional().describe("Workspace GID"), public: z.boolean().optional().describe("Whether the project is public"), color: z.string().optional().describe("Project color") }), handler: async (args, context) => { try { const { accessToken } = await context.getCredentials(); const project = await new AsanaClient(accessToken).createProject({ name: args.name, notes: args.notes, team: args.team, workspace: args.workspace, public: args.public, color: args.color }); return JSON.stringify(project, null, 2); } catch (error) { return `Failed to create project: ${error instanceof Error ? error.message : String(error)}`; } } }), UPDATE_PROJECT: tool({ name: "asana_update_project", description: "Update an existing project", schema: z.object({ projectGid: z.string().describe("Project GID to update"), name: z.string().optional().describe("Project name"), notes: z.string().optional().describe("Project notes"), color: z.string().optional().describe("Project color"), public: z.boolean().optional().describe("Whether the project is public"), archived: z.boolean().optional().describe("Whether the project is archived") }), handler: async (args, context) => { try { const { accessToken } = await context.getCredentials(); const project = await new AsanaClient(accessToken).updateProject(args.projectGid, { name: args.name, notes: args.notes, color: args.color, public: args.public, archived: args.archived }); return JSON.stringify(project, null, 2); } catch (error) { return `Failed to update project: ${error instanceof Error ? error.message : String(error)}`; } } }) }) }); //#endregion //#region src/connectors/attio.ts var AttioClient = class { headers; baseUrl = "https://api.attio.com/v2"; constructor(apiKey) { this.headers = { Authorization: `Bearer ${apiKey}`, Accept: "application/json", "Content-Type": "application/json" }; } async getFilteredLists(keyword = "customer_success") { const response = await fetch(`${this.baseUrl}/lists`, { headers: this.headers }); if (!response.ok) throw new Error(`Attio API error: ${response.status} ${response.statusText}`); return ((await response.json()).data || []).filter((list) => list.name.toLowerCase().includes(keyword.toLowerCase())); } async getListDetails(listSlug) { const response = await fetch(`${this.baseUrl}/lists/${listSlug}`, { headers: this.headers }); if (!response.ok) throw new Error(`Attio API error: ${response.status} ${response.statusText}`); return (await response.json()).data; } async getListEntries(listSlug, limit = 50) { const body = { limit }; const response = await fetch(`${this.baseUrl}/lists/${listSlug}/entries/query`, { method: "POST", headers: this.headers, body: JSON.stringify(body) }); if (!response.ok) throw new Error(`Attio API error: ${response.status} ${response.statusText}`); return (await response.json()).data || []; } }; const AttioConnectorConfig = mcpConnectorConfig({ name: "Attio", key: "attio", logo: "https://stackone-logos.com/api/attio/filled/svg", version: "2.0.0", credentials: z.object({ apiKey: z.string().describe("Attio API Key from Workspace settings > Developers tab > New access token. :: sk_live_1234567890abcdefghijklmnopqrstuvwxyz :: https://attio.com/help/apps/other-apps/generating-an-api-key") }), setup: z.object({}), examplePrompt: "Get all my lists on Attio", tools: (tool) => ({ GET_LIST: tool({ name: "attio_get_list", description: "Get lists filtered by keyword to avoid context overflow. Defaults to customer_success list.", schema: z.object({ keyword: z.string().default("customer_success").describe("Keyword to filter lists by (e.g., \"customer_success\", \"sales\")") }), handler: async (args, context) => { try { const { apiKey } = await context.getCredentials(); const lists = await new AttioClient(apiKey).getFilteredLists(args.keyword); if (lists.length === 0) return `No lists found matching keyword: "${args.keyword}"`; const listSummary = lists.map((list) => ({ id: list.id, name: list.name, parent_object: list.parent_object, created_at: list.created_at })); return JSON.stringify({ keyword_used: args.keyword, total_matches: lists.length, lists: listSummary }, null, 2); } catch (error) { return `Failed to get filtered lists: ${error instanceof Error ? error.message : String(error)}`; } } }), GET_LIST_FIELDS: tool({ name: "attio_get_list_fields", description: "Get all fields or attributes within a specific list", schema: z.object({ listSlug: z.string().describe("List slug or ID (e.g., \"customer_success_list\")") }), handler: async (args, context) => { try { const { apiKey } = await context.getCredentials(); const listDetails = await new AttioClient(apiKey).getListDetails(args.listSlug); const fieldInfo = { list_id: listDetails.id, list_name: listDetails.name, parent_object: listDetails.parent_object, attributes: listDetails.attributes || [], created_at: listDetails.created_at }; return JSON.stringify(fieldInfo, null, 2); } catch (error) { return `Failed to get list fields: ${error instanceof Error ? error.message : String(error)}`; } } }), GET_LIST_ENTRIES: tool({ name: "attio_get_list_entries", description: "Get all entries found within a specific list, with limited results to avoid context overflow", schema: z.object({ listSlug: z.string().describe("List slug or ID"), limit: z.number().default(50).describe("Maximum number of entries to return (default 50 to avoid context overflow)") }), handler: async (args, context) => { try { const { apiKey } = await context.getCredentials(); const entries = await new AttioClient(apiKey).getListEntries(args.listSlug, args.limit); if (entries.length === 0) return `No entries found in list: "${args.listSlug}"`; const entrySummary = entries.map((entry) => ({ id: entry.id, parent_record_id: entry.parent_record_id, created_at: entry.created_at, values: entry.values })); return JSON.stringify({ list_slug: args.listSlug, total_entries: entries.length, limit_applied: args.limit, entries: entrySummary }, null, 2); } catch (error) { return `Failed to get list entries: ${error instanceof Error ? error.message : String(error)}`; } } }) }) }); //#endregion //#region src/connectors/aws.ts var AwsClient = class { region; credentials; constructor(credentials) { this.credentials = credentials; this.region = credentials.region; } async makeAwsRequest(service, action, params = {}, method = "POST") { const maxRetries = 3; const baseDelay = 1e3; for (let attempt = 0; attempt <= maxRetries; attempt++) try { return await this.executeAwsRequest(service, action, params, method); } catch (error) { if (attempt === maxRetries || !this.isRetryableError(error)) throw error; const delay = baseDelay * 2 ** attempt + Math.random() * 1e3; await new Promise((resolve) => setTimeout(resolve, delay)); } } isRetryableError(error) { if (error instanceof Error && (error.name === "NetworkError" || error.name === "TimeoutError")) return true; if (error instanceof Error && "status" in error && typeof error.status === "number") return error.status >= 500 || error.status === 429; return false; } async executeAwsRequest(service, action, params = {}, method = "POST") { const endpoint = this.getServiceEndpoint(service); const headers = await this.getServiceHeaders(service, action); const body = this.getRequestBody(service, params, method); const signedRequest = await this.signRequest(method, endpoint, headers, body, service); const response = await fetch(signedRequest.url, { method, headers: signedRequest.headers, body }); if (!response.ok) { const errorText = await response.text(); throw await this.handleAwsError(response, errorText, service); } const contentType = response.headers.get("content-type") || ""; if (contentType.includes("application/json")) return response.json(); if (contentType.includes("text/xml") || contentType.includes("application/xml")) return this.parseXmlResponse(await response.text()); return response.text(); } getServiceEndpoint(service) { return { ec2: `https://ec2.${this.region}.amazonaws.com`, s3: `https://s3.${this.region}.amazonaws.com`, lambda: `https://lambda.${this.region}.amazonaws.com`, monitoring: `https://monitoring.${this.region}.amazonaws.com`, ce: "https://ce.us-east-1.amazonaws.com" }[service] || `https://${service}.${this.region}.amazonaws.com`; } getServiceHeaders(service, action) { const headers = { Host: `${service}.${this.region}.amazonaws.com`, "X-Amz-Date": (/* @__PURE__ */ new Date()).toISOString().replace(/[:\-]|\.\d{3}/g, "") }; if (this.credentials.sessionToken) headers["X-Amz-Security-Token"] = this.credentials.sessionToken; switch (service) { case "ec2": headers["Content-Type"] = "application/x-www-form-urlencoded"; break; case "lambda": headers["Content-Type"] = "application/x-amz-json-1.0"; break; case "monitoring": headers["Content-Type"] = "application/x-www-form-urlencoded"; break; case "ce": headers["Content-Type"] = "application/x-amz-json-1.1"; headers["X-Amz-Target"] = `AWSInsightsIndexService.${action}`; break; case "s3": headers["Content-Type"] = "application/xml"; break; default: headers["Content-Type"] = "application/x-amz-json-1.1"; headers["X-Amz-Target"] = `${service}.${action}`; } return headers; } getRequestBody(service, params, method) { if (method === "GET") return; switch (service) { case "ec2": case "monitoring": return this.encodeFormData(params); case "s3": return params.body || ""; default: return JSON.stringify(params); } } encodeFormData(params) { const formData = new URLSearchParams(); for (const [key, value] of Object.entries(params)) if (Array.isArray(value)) value.forEach((item, index) => { formData.append(`${key}.${index + 1}`, String(item)); }); else formData.append(key, String(value)); return formData.toString(); } async signRequest(method, endpoint, headers, body, service) { const url = new URL(endpoint); const path = url.pathname; const queryString = url.search.slice(1); const timestamp = headers["X-Amz-Date"]; if (!timestamp) throw new Error("X-Amz-Date header is required"); const date = timestamp.slice(0, 8); const canonicalHeaders = Object.entries(headers).sort(([a], [b]) => a.toLowerCase().localeCompare(b.toLowerCase())).map(([key, value]) => `${key.toLowerCase()}:${value.trim()}`).join("\n"); const signedHeaders = Object.keys(headers).map((key) => key.toLowerCase()).sort().join(";"); const payloadHash = await this.sha256(body || ""); const canonicalRequest = [ method, path, queryString, canonicalHeaders, "", signedHeaders, payloadHash ].join("\n"); const algorithm = "AWS4-HMAC-SHA256"; const credentialScope = `${date}/${this.region}/${service}/aws4_request`; const stringToSign = [ algorithm, timestamp, credentialScope, await this.sha256(canonicalRequest) ].join("\n"); const signature = await this.calculateSignature(stringToSign, date, service); const authorizationHeader = `${algorithm} Credential=${this.credentials.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`; return { url: endpoint, headers: { ...headers, Authorization: authorizationHeader } }; } async calculateSignature(stringToSign, date, service) { const kDate = await this.hmacSha256(`AWS4${this.credentials.secretAccessKey}`, date); const kRegion = await this.hmacSha256(kDate, this.region); const kService = await this.hmacSha256(kRegion, service); const kSigning = await this.hmacSha256(kService, "aws4_request"); const signature = await this.hmacSha256(kSigning, stringToSign); return this.toHex(signature); } async sha256(data) { const dataBuffer = new TextEncoder().encode(data); const hashBuffer = await crypto.subtle.digest("SHA-256", dataBuffer); return this.toHex(hashBuffer); } async hmacSha256(key, data) { const encoder = new TextEncoder(); const keyBuffer = typeof key === "string" ? encoder.encode(key) : key; const dataBuffer = encoder.encode(data); const cryptoKey = await crypto.subtle.importKey("raw", keyBuffer, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]); return crypto.subtle.sign("HMAC", cryptoKey, dataBuffer); } toHex(buffer) { return Array.from(new Uint8Array(buffer)).map((byte) => byte.toString(16).padStart(2, "0")).join(""); } async handleAwsError(response, errorText, service) { try { const contentType = response.headers.get("content-type") || ""; let errorInfo = {}; if (contentType.includes("application/json")) errorInfo = JSON.parse(errorText); else if (contentType.includes("xml")) errorInfo = this.parseXmlResponse(errorText); else errorInfo = { message: errorText }; const errorCode = errorInfo.code || errorInfo.Code || errorInfo.__type || "UnknownError"; const errorMessage = errorInfo.message || errorInfo.Message || errorInfo.error || errorText; const error = /* @__PURE__ */ new Error(`AWS ${service} API error (${response.status}): ${errorCode} - ${errorMessage}`); error.status = response.status; error.code = errorCode; error.service = service; return error; } catch (_parseError) { const error = /* @__PURE__ */ new Error(`AWS ${service} API error (${response.status}): ${response.statusText}`); error.status = response.status; error.service = service; return error; } } parseXmlResponse(xml) { const parseXmlString = (xmlStr) => { const obj = {}; const tagRegex = /<([^>\/\s]+)(?:[^>]*)>([^<]*(?:<(?!\1[>\s])[^<]*)*)<\/\1>/g; const selfClosingRegex = /<([^>\/\s]+)(?:[^>]*)\s*\/>/g; let match = selfClosingRegex.exec(xmlStr); while (match !== null) { const key = match[1]; if (key) obj[key] = ""; match = selfClosingRegex.exec(xmlStr); } tagRegex.lastIndex = 0; match = tagRegex.exec(xmlStr); while (match !== null) { const key = match[1]; const rawContent = match[2]; if (key && rawContent !== void 0) { const content = rawContent.trim(); if (content.includes("<")) { const nestedValue = parseXmlString(content); const currentValue = obj[key]; if (currentValue !== void 0) if (Array.isArray(currentValue)) currentValue.push(nestedValue); else obj[key] = [currentValue, nestedValue]; else obj[key] = nestedValue; } else { const currentValue = obj[key]; if (currentValue !== void 0) if (Array.isArray(currentValue)) currentValue.push(content); else obj[key] = [currentValue, content]; else obj[key] = content; } } match = tagRegex.exec(xmlStr); } return obj; }; return parseXmlString(xml); } async listEC2Instances() { try { const result = await this.makeAwsRequest("ec2", "DescribeInstances", { Action: "DescribeInstances", Version: "2016-11-15" }); const instances = []; if (result.reservationSet) { for (const reservation of result.reservationSet) if (reservation.instancesSet) instances.push(...reservation.instancesSet); } return instances; } catch (error) { throw new Error(`Failed to list EC2 instances: ${error instanceof Error ? error.message : String(error)}`); } } async getEC2Instance(instanceId) { try { const result = await this.makeAwsRequest("ec2", "DescribeInstances", { Action: "DescribeInstances", Version: "2016-11-15", "InstanceId.1": instanceId }); if (result.reservationSet?.[0]?.instancesSet?.[0]) return result.reservationSet[0].instancesSet[0]; throw new Error("Instance not found"); } catch (error) { throw new Error(`Failed to get EC2 instance: ${error instanceof Error ? error.message : String(error)}`); } } async listS3Buckets() { try { return (await this.makeAwsRequest("s3", "ListBuckets", {}, "GET")).ListAllMyBucketsResult?.Buckets?.Bucket || []; } catch (error) { throw new Error(`Failed to list S3 buckets: ${error instanceof Error ? error.message : String(error)}`); } } async listS3Objects(bucketName, prefix, maxKeys = 100) { try { const endpoint = `https://${bucketName}.s3.${this.region}.amazonaws.com`; const queryParams = new URLSearchParams(); queryParams.append("list-type", "2"); queryParams.append("max-keys", maxKeys.toString()); if (prefix) queryParams.append("prefix", prefix); const url = `${endpoint}?${queryParams.toString()}`; const headers = await this.getServiceHeaders("s3", "ListObjectsV2"); const signedRequest = await this.signRequest("GET", url, headers, void 0, "s3"); const response = await fetch(signedRequest.url, { method: "GET", headers: signedRequest.headers }); if (!response.ok) { const errorText = await response.text(); throw await this.handleAwsError(response, errorText, "s3"); } const xmlText = await response.text(); return this.parseXmlResponse(xmlText).ListBucketResult?.Contents || []; } catch (error) { throw new Error(`Failed to list S3 objects: ${error instanceof Error ? error.message : String(error)}`); } } async listLambdaFunctions() { try { return (await this.makeAwsRequest("lambda", "ListFunctions", {})).Functions || []; } catch (error) { throw new Error(`Failed to list Lambda functions: ${error instanceof Error ? error.message : String(error)}`); } } async getLambdaFunction(functionName) { try { const endpoint = `https://lambda.${this.region}.amazonaws.com/2015-03-31/functions/${encodeURIComponent(functionName)}`; const headers = await this.getServiceHeaders("lambda", "GetFunction"); const signedRequest = await this.signRequest("GET", endpoint, headers, void 0, "lambda"); const response = await fetch(signedRequest.url, { method: "GET", headers: signedRequest.headers }); if (!response.ok) { const errorText = await response.text(); throw await this.handleAwsError(response, errorText, "lambda"); } return (await response.json()).Configuration; } catch (error) { throw new Error(`Failed to get Lambda function: ${error instanceof Error ? error.message : String(error)}`); } } async invokeLambdaFunction(functionName, payload) { try { const endpoint = `https://lambda.${this.region}.amazonaws.com/2015-03-31/functions/${encodeURIComponent(functionName)}/invocations`; const body = payload ? JSON.stringify(payload) : ""; const headers = await this.getServiceHeaders("lambda", "Invoke"); headers["X-Amz-Invocation-Type"] = "RequestResponse"; const signedRequest = await this.signRequest("POST", endpoint, headers, body, "lambda"); const response = await fetch(signedRequest.url, { method: "POST", headers: signedRequest.headers, body }); if (!response.ok) { const errorText = await response.text(); throw await this.handleAwsError(response, errorText, "lambda"); } return response.json(); } catch (error) { throw new Error(`Failed to invoke Lambda function: ${error instanceof Error ? error.message : String(error)}`); } } async getCloudWatchMetrics(namespace, metricName, dimensions, startTime, endTime) { try { const params = { Action: "GetMetricStatistics", Version: "2010-08-01", Namespace: namespace, MetricName: metricName, StartTime: startTime || (/* @__PURE__ */ new Date(Date.now() - 1440 * 60 * 1e3)).toISOString(), EndTime: endTime || (/* @__PURE__ */ new Date()).toISOString(), Period: 300, "Statistics.member.1": "Average", "Statistics.member.2": "Maximum", "Statistics.member.3": "Sum" }; if (dimensions) dimensions.forEach((dimension, index) => { params[`Dimensions.member.${index + 1}.Name`] = dimension.Name; params[`Dimensions.member.${index + 1}.Value`] = dimension.Value; }); return (await this.makeAwsRequest("monitoring", "GetMetricStatistics", params)).GetMetricStatisticsResult?.Datapoints?.member || []; } catch (error) { throw new Error(`Failed to get CloudWatch metrics: ${error instanceof Error ? error.message : String(error)}`); } } async getCostAndUsage(startTime, endTime, granularity = "DAILY") { try { const params = { TimePeriod: { Start: startTime || (/* @__PURE__ */ new Date(Date.now() - 720 * 60 * 60 * 1e3)).toISOString().split("T")[0], End: endTime || (/* @__PURE__ */ new Date()).toISOString().split("T")[0] }, Granularity: granularity, Metrics: ["BlendedCost", "UsageQuantity"], GroupBy: [{ Type: "DIMENSION", Key: "SERVICE" }] }; return (await this.makeAwsRequest("ce", "GetCostAndUsage", params)).ResultsByTime || []; } catch (error) { throw new Error(`Failed to get cost and usage: ${error instanceof Error ? error.message : String(error)}`); } } }; const AwsConnectorConfig = mcpConnectorConfig({ name: "AWS", key: "aws", version: "1.0.0", logo: "https://stackone-logos.com/api/amazon-redshift/filled/svg", credentials: z.object({ accessKeyId: z.string().describe("AWS Access Key ID :: AKIAIOSFODNN7EXAMPLE"), secretAccessKey: z.string().describe("AWS Secret Access Key :: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"), sessionToken: z.string().optional().describe("AWS Session Token (for temporary credentials) :: AQoDYXdzEJr...<remainder of session token>") }), setup: z.object({ region: z.string().default("us-east-1").describe("AWS region (e.g., us-east-1, eu-west-1) :: us-east-1") }), examplePrompt: "List all my EC2 instances in the us-east-1 region, check my S3 buckets, and show me the cost breakdown for the last 30 days.", tools: (tool) => ({ LIST_EC2_INSTANCES: tool({ name: "aws_list_ec2_instances", description: "List all EC2 instances in the specified region", schema: z.object({}), handler: async (_args, context) => { try { const { accessKeyId, secretAccessKey, sessionToken } = await context.getCredentials(); const { region } = await context.getSetup(); const instances = await new AwsClient({ accessKeyId, secretAccessKey, region, sessionToken }).listEC2Instances(); return JSON.stringify(instances, null, 2); } catch (error) { return `Failed to list EC2 instances: ${error instanceof Error ? error.message : String(error)}`; } } }), GET_EC2_INSTANCE: tool({ name: "aws_get_ec2_instance", description: "Get details of a specific EC2 instance", schema: z.object({ instanceId: z.string().describe("The EC2 instance ID") }), handler: async (args, context) => { try { const { accessKeyId, secretAccessKey, sessionToken } = await context.getCredentials(); const { region } = await context.getSetup(); const instance = await new AwsClient({ accessKeyId, secretAccessKey, region, sessionToken }).getEC2Instance(args.instanceId); return JSON.stringify(instance, null, 2); } catch (error) { return `Failed to get EC2 instance: ${error instanceof Error ? error.message : String(error)}`; } } }), LIST_S3_BUCKETS: tool({ name: "aws_list_s3_buckets", description: "List all S3 buckets in the account", schema: z.object({}), handler: async (_args, context) => { try { const { accessKeyId, secretAccessKey, sessionToken } = await context.getCredentials(); const { region } = await context.getSetup(); const buckets = await new AwsClient({ accessKeyId, secretAccessKey, region, sessionToken }).listS3Buckets(); return JSON.stringify(buckets, null, 2); } catch (error) { return `Failed to list S3 buckets: ${error instanceof Error ? error.message : String(error)}`; } } }), LIST_S3_OBJECTS: tool({ name: "aws_list_s3_objects", description: "List objects in an S3 bucket", schema: z.object({ bucketName: z.string().describe("The S3 bucket name"), prefix: z.string().optional().describe("Filter objects by prefix"), maxKeys: z.number().default(100).describe("Maximum number of objects to return") }), handler: async (args, context) => { try { const { accessKeyId, secretAccessKey, sessionToken } = await context.getCredentials(); const { region } = await context.getSetup(); const objects = await new AwsClient({ accessKeyId, secretAccessKey, region, sessionToken }).listS3Objects(args.bucketName, args.prefix, args.maxKeys); return JSON.stringify(objects, null, 2); } catch (error) { return `Failed to list S3 objects: ${error instanceof Error ? error.message : String(error)}`; } } }), LIST_LAMBDA_FUNCTIONS: tool({ name: "aws_list_lambda_functions", description: "List all Lambda functions in the specified region", schema: z.object({}), handler: async (_args, context) => { try { const { accessKeyId, secretAccessKey, sessionToken } = await context.getCredentials(); const { region } = await context.getSetup(); const functions = await new AwsClient({ accessKeyId, secretAccessKey, region, sessionToken }).listLambdaFunctions(); return JSON.stringify(functions, null, 2); } catch (error) { return `Failed to list Lambda functions: ${error instanceof Error ? error.message : String(error)}`; } } }), GET_LAMBDA_FUNCTION: tool({ name: "aws_get_lambda_function", description: "Get details of a specific Lambda function", schema: z.object({ functionName: z.string().describe("The Lambda function name or ARN") }), handler: async (args, context) => { try { const { accessKeyId, secretAccessKey, sessionToken } = await context.getCredentials(); const { region } = await context.getSetup(); const func = await new AwsClient({ accessKeyId, secretAccessKey, region, sessionToken }).getLambdaFunction(args.functionName); return JSON.stringify(func, null, 2); } catch (error) { return `Failed to get Lambda function: ${error instanceof Error ? error.message : String(error)}`; } } }), INVOKE_LAMBDA_FUNCTION: tool({ name: "aws_invoke_lambda_function", description: "Invoke a Lambda function with optional payload", schema: z.object({ functionName: z.string().describe("The Lambda function name or ARN"), payload: z.record(z.any()).optional().describe("JSON payload to send to the function") }), handler: async (args, context) => { try { const { accessKeyId, secretAccessKey, sessionToken } = await context.getCredentials(); const { region } = await context.getSetup(); const result = await new AwsClient({ accessKeyId, secretAccessKey, region, sessionToken }).invokeLambdaFunction(args.functionName, args.payload); return JSON.stringify(result, null, 2); } catch (error) { return `Failed to invoke Lambda function: ${error instanceof Error ? error.message : String(error)}`; } } }), GET_CLOUDWATCH_METRICS: tool({ name: "aws_get_cloudwatch_metrics", description: "Get CloudWatch metrics for monitoring", schema: z.object({ namespace: z.string().describe("CloudWatch namespace (e.g., AWS/EC2, AWS/Lambda)"), metricName: z.string().describe("Name of the metric to retrieve"), dimensions: z.array(z.object({ Name: z.string(), Value: z.string() })).optional().describe("Metric dimensions for filtering"), startTime: z.string().optional().describe("Start time (ISO 8601 format)"), endTime: z.string().optional().describe("End time (ISO 8601 format)") }), handler: async (args, context) => { try { const { accessKeyId, secretAccessKey, sessionToken } = await context.getCredentials(); const { region } = await context.getSetup(); const metrics = await new AwsClient({ accessKeyId, secretAccessKey, region, sessionToken }).getCloudWatchMetrics(args.namespace, args.metricName, args.dimensions, args.startTime, args.endTime); return JSON.stringify(metrics, null, 2); } catch (error) { return `Failed to get CloudWatch metrics: ${error instanceof Error ? error.message : String(error)}`; } } }), GET_COST_AND_USAGE: tool({ name: "aws_get_cost_and_usage", description: "Get AWS cost and usage information", schema: z.object({ startTime: z.string().optional().describe("Start date (YYYY-MM-DD format)"), endTime: z.string().optional().describe("End date (YYYY-MM-DD format)"), granularity: z.enum([ "DAILY", "MONTHLY", "HOURLY" ]).default("DAILY").describe("Time granularity") }), handler: async (args, context) => { try { const { accessKeyId, secretAccessKey, sessionToken } = await context.getCredentials(); const { region } = await context.getSetup(); const costData = await new AwsClient({ accessKeyId, secretAccessKey, region, sessionToken }).getCostAndUsage(args.startTime, args.endTime, args.granularity); return JSON.stringify(costData, null, 2); } catch (error) { return `Failed to get cost and usage: ${error instanceof Error ? error.message : String(error)}`; } } }) }) }); //#endregion //#region src/connectors/datadog.ts var DatadogClient = class { headers; baseUrl; constructor(apiKey, appKey, site = "datadoghq.com") { this.headers = { "DD-API-KEY": apiKey, "DD-APPLICATION-KEY": appKey, "Content-Type": "application/json" }; this.baseUrl = `https://api.${site}`; } async listIncidents(limit = 30, state, severity) { const params = new URLSearchParams({ "page[size]": limit.toString() }); if (state) params.append("filter[state]", state); if (severity) params.append("filter[severity]", severity); const response = await fetch(`${this.baseUrl}/api/v2/incidents?${params}`, { headers: this.headers }); if (!response.ok) throw new Error(`Datadog API error: ${response.status} ${response.statusText}`); return (await response.json()).data || []; } async getIncident(incidentId) { const response = await fetch(`${this.baseUrl}/api/v2/incidents/${incidentId}`, { headers: this.headers }); if (!response.ok) throw new Error(`Datadog API error: ${response.status} ${response.statusText}`); return (await response.json()).data; } async getMonitors(limit = 30, state, tags) { const params = new URLSearchParams({ page_size: limit.toString() }); if (state) params.append("group_states", state); if (tags && tags.length > 0) params.append("tags", tags.join(",")); const response = await fetch(`${this.baseUrl}/api/v1/monitor?${params}`, { headers: this.headers }); if (!response.ok) throw new Error(`Datadog API error: ${response.status} ${response.statusText}`); return response.json(); } async getMonitor(monitorId) { const response = await fetch(`${this.baseUrl}/api/v1/monitor/${monitorId}`, { headers: this.headers }); if (!response.ok) throw new Error(`Datadog API error: ${response.status} ${response.statusText}`); return response.json(); } async searchLogs(query, from, to, limit = 50) { const body = { filter: { query, from: from || (/* @__PURE__ */ new Date(Date.now() - 1440 * 60 * 1e3)).toISOString(), to: to || (/* @__PURE__ */ new Date()).toISOString() }, page: { limit }, sort: "-timestamp" }; const response = await fetch(`${this.baseUrl}/api/v2/logs/events/search`, { method: "POST", headers: this.headers, body: JSON.stringify(body) }); if (!response.ok) throw new Error(`Datadog API error: ${response.status} ${response.statusText}`); return (await response.json()).data || []; } async getMetrics(query, from, to) { const params = new URLSearchParams({ query, from: from.toString(), to: to.toString() }); const response = await fetch(`${this.baseUrl}/api/v1/query?${params}`, { headers: this.headers }); if (!response.ok) throw new Error(`Datadog API error: ${response.status} ${response.statusText}`); return (await response.json()).series || []; } async getDowntimes(limit = 30) { const response = await fetch(`${this.baseUrl}/api/v1/downtime`, { headers: this.headers }); if (!response.ok) throw new Error(`Datadog API error: ${response.status} ${response.statusText}`); const result = await response.json(); return Array.isArray(result) ? result.slice(0, limit) : []; } async getHosts(limit = 30, filter) { const params = new URLSearchParams({ count: limit.toString() }); if (filter) params.append("filter", filter); const response = await fetch(`${this.baseUrl}/api/v1/hosts?${params}`, { headers: this.headers }); if (!response.ok) throw new Error(`Datadog API error: ${response.status} ${response.statusText}`); return (await response.json()).host_list || []; } }; const DatadogConnectorConfig = mcpConnectorConfig({ name: "Datadog", key: "datadog", logo: "https://stackone-logos.com/api/datadog/filled/svg", version: "1.0.0", credentials: z.object({ apiKey: z.string().describe("Datadog API key from Organization Settings > API Keys :: 1234567890abcdef1234567890abcdef :: https://docs.datadoghq.com/account_management/api-app-keys/"), appKey: z.string().describe("Datadog application key from Organization Settings > Application Keys :: 1234567890abcdef1234567890abcdef12345678") }), setup: z.object({ site: z.string().default("datadoghq.com").describe("Datadog site (e.g., datadoghq.com, datadoghq.eu) :: datadoghq.com") }), examplePrompt: "Check my active incidents with SEV-1 severity, list all monitors that are alerting, and search for error logs from the last hour.", tools: (tool) => ({ LIST_INCIDENTS: tool({ name: "datadog_list_incidents", description: "List incidents with optional filtering", schema: z.object({ limit: z.number().default(30).describe("Maximum number of incidents to return"), state: z.string().optional().describe("Filter by incident state (active, stable, resolved)"), severity: z.string().optional().describe("Filter by severity (SEV-1, SEV-2, SEV-3, SEV-4, SEV-5)") }), handler: async (args, context) => { try { const { apiKey, appKey } = await context.getCredentials(); const { site } = await context.getSetup(); const incidents = await new DatadogClient(apiKey, appKey, site).listIncidents(args.limit, args.state, args.severity); return JSON.stringify(incidents, null, 2); } catch (error) { return `Failed to list incidents: ${error instanceof Error ? error.message : String(error)}`; } } }), GET_INCIDENT: tool({ name: "datadog_get_incident", description: "Get detailed information about a specific incident", schema: z.object({ incidentId: z.string().describe("The incident ID to retrieve") }), handler: async (args, context) => { try { const { apiKey, appKey } = await context.getCredentials(); const { site } = await context.getSetup(); const incident = await new DatadogClient(apiKey, appKey, site).getIncident(args.incidentId); return JSON.stringify(incident, null, 2); } catch (error) { return `Failed to get incident: ${error instanceof Error ? error.message : String(error)}`; } } }), GET_MONITORS: tool({ name: "datadog_get_monitors", description: "Get monitor statuses with optional filtering", schema: z.object({ limit: z.number().default(30).describe("Maximum number of monitors to return"), state: z.string().optional().describe("Filter by monitor state (Alert, Warn,