# Directory Structure
```
├── .gitignore
├── LICENSE
├── package.json
├── README.md
├── src
│ └── server.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
# Dependencies
node_modules/
yarn.lock
package-lock.json
# Build output
dist/
build/
*.tsbuildinfo
# Environment variables and secrets
.env
.env.*
.cursor/mcp.json
# IDE and editor files
.idea/
.vscode/
*.swp
*.swo
.DS_Store
Thumbs.db
# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Testing
coverage/
.nyc_output/
# Temporary files
tmp/
temp/
# Workato specific
.workato-token
workato.config.json
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
🤖 Workato MCP Server
Welcome to your Workato API integration toolkit, designed as a Model Context Protocol (MCP) server for Cursor or Claude! This project provides seamless interaction with Workato's API through custom AI tools.
✨ Features
🔄 Recipe Management
- List, create, start, and stop recipes
- Monitor recipe execution jobs
- Manage recipe folders and projects
🔌 Connection Management
- List and create connections
- View connection details and status
- Manage connection configurations
🔍 Connector Discovery
- List available connectors and their capabilities
- View connector metadata and supported operations
- Browse all platform connectors
📂 Folder & Project Organization
- Create and manage folders
- Organize recipes and connections
- Handle project-level configurations
📊 Activity Logs
- Track all activities within your workspace
- Filter logs by time range, users, and event types
- Monitor resource changes and user actions
- Support for multiple environments (dev, sandbox, prod, etc.)
- Advanced filtering by resource and event types
🔖 Tag Management
- Create, update, and delete tags in your workspace
- List and retrieve available tags with advanced filtering options
- Apply or remove tags from assets (recipes and connections)
- Supports batch operations for multiple assets and tags
- Filter tags by title, description, author, and usage
- Sort tags by various criteria (title, usage count, etc.)
- Customize tag appearance with color options
🚀 Getting Started
2. Installation
```bash
npm install
# or
yarn install
```
3. Build the Server
```bash
npm run build
```
4. Adding to Cursor
This project is designed to be used as an MCP server in Cursor. Here's how to set it up:
1. Open Cursor
2. Go to Cursor Settings > Features > MCP
3. Click + Add New MCP Server
4. Fill out the form:
- Name: Workato MCP Server
- Type: stdio
- Command: node /path/to/your/project/dist/server.js
- Environment Variables:
- Click "Add Environment Variable"
- Name: WORKATO_API_TOKEN
- Value: your_token_here
📘 Pro Tip: Use the full path to your project's built server.js file.
Alternative Configuration:
You can also configure the MCP server using a `.cursor/mcp.json` file in your project:
```json
{
"mcpServers": {
"workato-tools": {
"command": "node",
"args": ["/path/to/your/project/dist/server.js"],
"env": {
"WORKATO_API_TOKEN": "your_token_here"
}
}
}
}
```
Using with Claude Desktop:
If you're using Claude Desktop instead of Cursor, you can configure the MCP server by editing the Claude desktop configuration:
1. Open or create the configuration file:
```bash
# On macOS
~/Library/Application Support/Claude/claude_desktop_config.json
# On Windows
%APPDATA%\Claude\claude_desktop_config.json
# On Linux
~/.config/Claude/claude_desktop_config.json
```
2. Add your MCP server configuration:
```json
{
"mcp_servers": {
"workato-tools": {
"command": "node",
"args": ["/path/to/your/project/dist/server.js"],
"env": {
"WORKATO_API_TOKEN": "your_token_here"
}
}
}
}
```
3. Save the file and restart Claude Desktop for the changes to take effect
This method allows you to:
- Version control your MCP configuration
- Include environment variables directly in the config
- Share the same configuration across team members (excluding sensitive values)
- Automatically load the server when opening the project in Cursor
🛠️ Available Tools
Recipe Management:
- list-recipes: List all recipes with filtering options
- create-recipe: Create a new recipe
- start-recipe: Start a specific recipe
- stop-recipe: Stop a running recipe
Connection Management:
- list-connections: List all connections
- create-connection: Create a new connection
Connector Tools:
- list-connectors: Get metadata for specific connectors
- list-all-connectors: List all available connectors
Organization Tools:
- list-folders: List all folders
- create-folder: Create a new folder
- update-folder: Modify folder properties
- list-projects: List all projects
- update-project: Update project details
API Management:
- list-api-endpoints: List all API endpoints with optional filtering by collection
Activity Monitoring:
- list-activity-logs: Retrieve detailed activity logs with advanced filtering options
- Filter by time range, users, and event types
- Include or exclude specific resource types
- Track changes across different environments
- Monitor user actions and system events
Tag Management:
- list-tags: List and filter available tags in your workspace with advanced query options
- create-tag: Create a new tag with custom title, description, and color
- update-tag: Modify an existing tag's properties
- delete-tag: Remove a tag from your workspace
- manage-tags: Apply or remove tags from recipes and connections
Job Management:
- list-recipe-jobs: View jobs for a specific recipe
- get-job: Get detailed job information
- resume-job: Resume a paused job
🤝 Contributing Contributions welcome! Please feel free to submit a Pull Request.
📝 License This project is licensed under the MIT License - see the LICENSE file for details.
🐛 Issues & Support Found a bug or need help? Open an issue with:
What you were trying to do
What happened instead
Steps to reproduce
Your environment details
Made with ❤️ by Jacob Goren, for Workato automation
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"target": "es2022",
"module": "es2022",
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"skipLibCheck": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "workato-mpc-server",
"version": "1.0.0",
"type": "module",
"main": "dist/server.js",
"scripts": {
"build": "yarn tsc",
"start": "node dist/server.js",
"dev": "yarn build && yarn start"
},
"repository": {
"type": "git",
"url": "git+https://github.com/jacobgoren-sb/workato-mpc-server.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/jacobgoren-sb/workato-mpc-server/issues"
},
"homepage": "https://github.com/jacobgoren-sb/workato-mpc-server#readme",
"description": "",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.7.0",
"@types/node": "^20.11.24",
"typescript": "^5.3.3",
"zod": "^3.22.4"
}
}
```
--------------------------------------------------------------------------------
/src/server.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Create an MCP server
const server = new McpServer({
name: "workato-mcp-server",
version: "1.0.0"
});
// Add list-recipes tool
server.tool(
"list-recipes",
{
adapter_names_all: z.string().optional(),
adapter_names_any: z.string().optional(),
folder_id: z.string().optional(),
order: z.enum(["activity", "default"]).optional(),
page: z.number().int().min(1).optional().default(1),
per_page: z.number().int().min(1).max(100).optional().default(100),
running: z.boolean().optional(),
since_id: z.number().int().optional(),
stopped_after: z.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/).optional(),
stop_cause: z.enum(["trigger_errors_limit", "action_quota_limit", "trial_expired", "txn_quota_limit"]).optional(),
updated_after: z.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/).optional(),
includes: z.array(z.string()).optional()
},
async (params) => {
// Construct query parameters
const queryParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
if (Array.isArray(value)) {
value.forEach(v => queryParams.append(`${key}[]`, v));
} else {
queryParams.append(key, String(value));
}
}
});
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/recipes?${queryParams.toString()}`, {
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN
}
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add create-recipe tool
server.tool(
"create-recipe",
{
recipe: z.object({
name: z.string().optional(),
code: z.string(),
config: z.string().optional(),
folder_id: z.string().optional(),
description: z.string().optional()
})
},
async (params) => {
// Make API call to Workato
const response = await fetch('https://www.workato.com/api/recipes', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add start-recipe tool
server.tool(
"start-recipe",
{
id: z.number().int()
},
async (params) => {
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/recipes/${params.id}/start`, {
method: 'PUT',
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN
}
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add stop-recipe tool
server.tool(
"stop-recipe",
{
id: z.number().int()
},
async (params) => {
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/recipes/${params.id}/stop`, {
method: 'PUT',
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN
}
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add list-connections tool
server.tool(
"list-connections",
{
folder_id: z.string().optional(),
parent_id: z.string().optional(),
external_id: z.string().optional(),
include_runtime_connections: z.string().optional(),
includes: z.array(z.string()).optional()
},
async (params) => {
// Construct query parameters
const queryParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
if (Array.isArray(value)) {
value.forEach(v => queryParams.append(`${key}[]`, v));
} else {
queryParams.append(key, String(value));
}
}
});
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/connections?${queryParams.toString()}`, {
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN
}
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add create-connection tool
server.tool(
"create-connection",
{
name: z.string().optional(),
provider: z.string().optional(),
parent_id: z.string().optional(),
folder_id: z.string().optional(),
external_id: z.string().optional(),
shell_connection: z.boolean().optional(),
input: z.record(z.any()).optional()
},
async (params) => {
// Make API call to Workato
const response = await fetch('https://www.workato.com/api/connections', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add list-connectors tool
server.tool(
"list-connectors",
{
applications: z.string()
},
async (params) => {
// Construct query parameters
const queryParams = new URLSearchParams();
queryParams.append('applications', params.applications);
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/integrations?${queryParams.toString()}`, {
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN
}
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add list-all-connectors tool
server.tool(
"list-all-connectors",
{
page: z.number().int().min(1).optional().default(1),
per_page: z.number().int().min(1).max(100).optional().default(100)
},
async (params) => {
// Construct query parameters
const queryParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/integrations/all?${queryParams.toString()}`, {
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN
}
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add list-folders tool
server.tool(
"list-folders",
{
parent_id: z.string().optional(),
page: z.number().int().min(1).optional().default(1),
per_page: z.number().int().min(1).max(100).optional().default(100)
},
async (params) => {
// Construct query parameters
const queryParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/folders?${queryParams.toString()}`, {
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN
}
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add list-projects tool
server.tool(
"list-projects",
{
page: z.number().int().min(1).optional().default(1),
per_page: z.number().int().min(1).max(100).optional().default(100)
},
async (params) => {
// Construct query parameters
const queryParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/projects?${queryParams.toString()}`, {
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN
}
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add create-folder tool
server.tool(
"create-folder",
{
name: z.string(),
parent_id: z.string().optional()
},
async (params) => {
// Make API call to Workato
const response = await fetch('https://www.workato.com/api/folders', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add update-folder tool
server.tool(
"update-folder",
{
folder_id: z.string(),
name: z.string().optional(),
parent_id: z.string().optional()
},
async (params) => {
const { folder_id, ...updateParams } = params;
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/folders/${folder_id}`, {
method: 'PUT',
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify(updateParams)
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add update-project tool
server.tool(
"update-project",
{
project_id: z.string(),
name: z.string(),
description: z.string().optional()
},
async (params) => {
const { project_id, ...updateParams } = params;
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/projects/${project_id}`, {
method: 'PUT',
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify(updateParams)
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add list-recipe-jobs tool
server.tool(
"list-recipe-jobs",
{
recipe_id: z.number().int(),
offset_job_id: z.string().optional(),
prev: z.boolean().optional().default(false),
status: z.enum(["succeeded", "failed", "pending"]).optional(),
rerun_only: z.boolean().optional()
},
async (params) => {
// Construct query parameters
const queryParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && key !== 'recipe_id') {
queryParams.append(key, String(value));
}
});
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/recipes/${params.recipe_id}/jobs?${queryParams.toString()}`, {
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN
}
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add get-job tool
server.tool(
"get-job",
{
recipe_id: z.number().int(),
job_handle: z.string()
},
async (params) => {
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/recipes/${params.recipe_id}/jobs/${params.job_handle}`, {
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN
}
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add resume-job tool
server.tool(
"resume-job",
{
token: z.string(),
data: z.record(z.any()).optional()
},
async (params) => {
// Make API call to Workato
const response = await fetch('https://www.workato.com/api/job/resume', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
});
// Since this endpoint returns 204 with no content, we'll return a success message
return {
content: [{ type: "text", text: "Job resumed successfully" }]
};
}
);
// Add list-api-endpoints tool
server.tool(
"list-api-endpoints",
{
api_collection_id: z.string().optional(),
per_page: z.number().int().min(1).max(100).optional().default(100),
page: z.number().int().min(1).optional().default(1)
},
async (params) => {
// Construct query parameters
const queryParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/api_endpoints?${queryParams.toString()}`, {
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN
}
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add manage-tags tool
server.tool(
"manage-tags",
{
add_tags: z.array(z.string()).optional(),
remove_tags: z.array(z.string()).optional(),
recipe_ids: z.array(z.number()).optional(),
connection_ids: z.array(z.number()).optional()
},
async (params) => {
// Make API call to Workato
const response = await fetch('https://www.workato.com/api/tags_assignments', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Update list-tags tool with full parameters
server.tool(
"list-tags",
{
page: z.number().int().min(1).optional().default(1),
per_page: z.number().int().min(1).max(100).optional().default(100),
'q[title_or_description_cont]': z.string().optional(),
'q[handle_in]': z.array(z.string()).optional(),
'q[author_id_eq]': z.number().optional(),
'q[recipe_id_eq]': z.number().optional(),
'q[connection_id_eq]': z.number().optional(),
'q[only_assigned]': z.boolean().optional(),
'sort_by[]': z.array(z.enum(['title', 'assignment_count', 'updated_at', 'last_assigned_at'])).optional(),
'sort_direction[]': z.array(z.enum(['asc', 'desc'])).optional(),
'includes[]': z.array(z.enum(['assignment_count', 'author'])).optional()
},
async (params) => {
// Construct query parameters
const queryParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
if (Array.isArray(value)) {
value.forEach(v => queryParams.append(key, String(v)));
} else {
queryParams.append(key, String(value));
}
}
});
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/tags?${queryParams.toString()}`, {
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN
}
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add create-tag tool
server.tool(
"create-tag",
{
title: z.string().max(30),
description: z.string().max(150).optional(),
color: z.enum(['blue', 'violet', 'green', 'red', 'orange', 'gold', 'indigo', 'brown', 'teal', 'plum', 'slate', 'neutral']).optional()
},
async (params) => {
// Make API call to Workato
const response = await fetch('https://www.workato.com/api/tags', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add update-tag tool
server.tool(
"update-tag",
{
handle: z.string(),
title: z.string().max(30),
description: z.string().max(150).optional(),
color: z.enum(['blue', 'violet', 'green', 'red', 'orange', 'gold', 'indigo', 'brown', 'teal', 'plum', 'slate', 'neutral']).optional()
},
async (params) => {
const { handle, ...updateParams } = params;
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/tags/${handle}`, {
method: 'PUT',
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify(updateParams)
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Add delete-tag tool
server.tool(
"delete-tag",
{
handle: z.string()
},
async (params) => {
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/tags/${params.handle}`, {
method: 'DELETE',
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN
}
});
return {
content: [{ type: "text", text: "Tag deleted successfully" }]
};
}
);
// Add list-activity-logs tool
server.tool(
"list-activity-logs",
{
'page[after]': z.number().int().optional(),
'page[size]': z.number().int().min(1).max(100).optional().default(100),
from: z.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/).optional(),
to: z.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/).optional(),
'users_ids[]': z.array(z.number()).optional(),
'include_resource_types[]': z.array(z.string()).optional(),
'exclude_resource_types[]': z.array(z.string()).optional(),
'include_event_types[]': z.array(z.string()).optional(),
'exclude_event_types[]': z.array(z.string()).optional()
},
async (params) => {
// Construct query parameters
const queryParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
if (Array.isArray(value)) {
value.forEach(v => queryParams.append(key, String(v)));
} else {
queryParams.append(key, String(value));
}
}
});
// Make API call to Workato
const response = await fetch(`https://www.workato.com/api/activity_logs?${queryParams.toString()}`, {
headers: {
'Authorization': 'Bearer ' + process.env.WORKATO_API_TOKEN
}
});
const data = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
};
}
);
// Start receiving messages on stdin and sending messages on stdout
const transport = new StdioServerTransport();
await server.connect(transport);
```