{"id":51141269,"url":"https://github.com/markmhendrickson/mcp-server-asana","last_synced_at":"2026-06-25T23:30:22.690Z","repository":{"id":354882950,"uuid":"1139821088","full_name":"markmhendrickson/mcp-server-asana","owner":"markmhendrickson","description":"MCP server for Asana integration providing bidirectional sync, import, export, and metadata management","archived":false,"fork":false,"pushed_at":"2026-04-30T16:48:12.000Z","size":290,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-30T18:20:21.553Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/markmhendrickson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-22T13:11:37.000Z","updated_at":"2026-04-30T16:48:16.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/markmhendrickson/mcp-server-asana","commit_stats":null,"previous_names":["markmhendrickson/mcp-server-asana"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/markmhendrickson/mcp-server-asana","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markmhendrickson%2Fmcp-server-asana","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markmhendrickson%2Fmcp-server-asana/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markmhendrickson%2Fmcp-server-asana/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markmhendrickson%2Fmcp-server-asana/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/markmhendrickson","download_url":"https://codeload.github.com/markmhendrickson/mcp-server-asana/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markmhendrickson%2Fmcp-server-asana/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34796761,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-25T02:00:05.521Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-25T23:30:20.118Z","updated_at":"2026-06-25T23:30:22.682Z","avatar_url":"https://github.com/markmhendrickson.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Asana MCP Server\n\nMCP server for Asana integration providing bidirectional sync, import, export, and metadata management between Asana workspaces and local parquet data.\n\n## Credits\n\nThis MCP server consolidates functionality from the Asana integration scripts in the personal workflow repository, providing a standardized interface for AI assistants.\n\n## Features\n\n- **Bidirectional Sync**: Three-way merge sync between Asana workspaces and local parquet\n- **Task Import**: Import tasks from Asana with intelligent merge\n- **Task Export**: Export local tasks to Asana with duplicate detection\n- **Comments Import**: Import task comments and attachments\n- **Metadata Import**: Import custom fields, dependencies, and stories\n- **Webhook Registration**: Register webhooks for real-time sync\n- **Utility Tools**: Get individual tasks, list projects, get workspace info\n- **Parquet MCP Integration**: All data operations go through parquet MCP server\n- **Comprehensive Test Coverage**: \u003e90% coverage across all functionality\n\n## Installation\n\n```bash\ncd execution/mcp-servers/asana\npip install -r requirements.txt\n```\n\n## Testing\n\nSee [TESTING.md](TESTING.md) for comprehensive testing guide.\n\n**Quick Start:**\n```bash\n# Install test dependencies\npip install -r requirements-test.txt\n\n# Run unit tests (fast, no external dependencies)\npytest -m unit\n\n# Run all tests\npytest\n```\n\n## Configuration\n\n### Authentication\n\nThe server supports multiple authentication methods (checked in priority order):\n\n1. **Environment Variables** (recommended, highest priority):\n   ```bash\n   export ASANA_SOURCE_PAT=\"your-source-pat\"\n   export ASANA_TARGET_PAT=\"your-target-pat\"  # Optional, defaults to source\n   export SOURCE_WORKSPACE_GID=\"source-workspace-gid\"\n   export TARGET_WORKSPACE_GID=\"target-workspace-gid\"\n   export FALLBACK_ASSIGNEE_EMAIL=\"your@email.com\"  # Optional\n   ```\n\n2. **Config Directory `.env` File** (portable, user-specific):\n   - Location: `~/.config/asana-mcp/.env`\n   - Format:\n     ```\n     ASANA_SOURCE_PAT=your-source-pat\n     ASANA_TARGET_PAT=your-target-pat\n     SOURCE_WORKSPACE_GID=source-workspace-gid\n     TARGET_WORKSPACE_GID=target-workspace-gid\n     FALLBACK_ASSIGNEE_EMAIL=your@email.com\n     ```\n   - The config directory is created automatically on first use\n\n3. **1Password Integration** (optional, for backward compatibility):\n   - Only available if parent repository structure exists\n   - Configure 1Password item titled \"Asana\"\n   - Add fields: \"source_pat\", \"target_pat\", \"source_workspace_gid\", \"target_workspace_gid\"\n\n**Note:** The MCP server is self-contained and portable. It does not require any specific repository structure.\n\n### Parquet MCP Server\n\nThis server requires the parquet MCP server to be configured and accessible. The server auto-detects the parquet server location or you can set:\n\n```bash\nexport PARQUET_MCP_SERVER_PATH=\"/path/to/parquet_mcp_server.py\"\n```\n\n### Cursor Configuration\n\nAdd to your Cursor MCP settings (typically `~/.cursor/mcp.json` or Cursor settings):\n\n```json\n{\n  \"mcpServers\": {\n    \"asana\": {\n      \"command\": \"python3\",\n      \"args\": [\n        \"/path/to/asana_mcp_server.py\"\n      ],\n      \"env\": {\n        \"ASANA_SOURCE_PAT\": \"your-source-pat\",\n        \"SOURCE_WORKSPACE_GID\": \"source-workspace-gid\",\n        \"TARGET_WORKSPACE_GID\": \"target-workspace-gid\"\n      }\n    }\n  }\n}\n```\n\n**Note:** Replace `/path/to/asana_mcp_server.py` with the actual path to the server file.\n\n### Claude Desktop Configuration\n\nAdd to `claude_desktop_config.json` (typically `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):\n\n```json\n{\n  \"mcpServers\": {\n    \"asana\": {\n      \"command\": \"python3\",\n      \"args\": [\n        \"/path/to/asana_mcp_server.py\"\n      ],\n      \"env\": {\n        \"ASANA_SOURCE_PAT\": \"your-source-pat\",\n        \"SOURCE_WORKSPACE_GID\": \"source-workspace-gid\",\n        \"TARGET_WORKSPACE_GID\": \"target-workspace-gid\"\n      }\n    }\n  }\n}\n```\n\n**Note:** Replace `/path/to/asana_mcp_server.py` with the actual path to the server file.\n\n## Available Tools\n\n### `import_asana_tasks`\n\nImport tasks from Asana workspace to local parquet via parquet MCP server.\n\n**Parameters:**\n- `workspace` (optional): Workspace to import from (\"source\" or \"target\", default: \"source\")\n- `only_incomplete` (optional): Only fetch incomplete tasks (default: false)\n- `assignee_gid` (optional): Filter tasks by assignee GID\n- `max_tasks` (optional): Maximum number of tasks to import\n- `recalculate` (optional): Force recalculation of domain (default: false)\n- `include_archived` (optional): Include tasks from archived projects (default: true)\n\n**Returns:**\n- `success`: Boolean indicating success\n- `workspace`: Workspace imported from\n- `fetched`: Number of tasks fetched from Asana\n- `updated`: Number of existing tasks updated\n- `new`: Number of new tasks added\n- `tasks`: Detailed lists of updated and new tasks\n\n**Example Request:**\n```json\n{\n  \"workspace\": \"source\",\n  \"only_incomplete\": true,\n  \"max_tasks\": 50\n}\n```\n\n**Example Response:**\n```json\n{\n  \"success\": true,\n  \"workspace\": \"source\",\n  \"fetched\": 45,\n  \"updated\": 30,\n  \"new\": 15,\n  \"tasks\": {\n    \"updated\": [...],\n    \"new\": [...]\n  }\n}\n```\n\n### `export_asana_tasks`\n\nExport local tasks to Asana workspace.\n\n**Parameters:**\n- `workspace` (optional): Workspace to export to (\"target\", default: \"target\")\n- `task_ids` (optional): Specific task IDs to export (if provided, ignores limit and sync_log_filter)\n- `limit` (optional): Maximum number of tasks to export (default: 10)\n- `sync_log_filter` (optional): Filter tasks by sync_log value (e.g., \"pending_export\")\n\n**Returns:**\n- `success`: Boolean indicating success\n- `workspace`: Workspace exported to\n- `processed`: Number of tasks processed\n- `created`: Number of tasks created in Asana\n- `updated`: Number of tasks updated in Asana\n- `failed`: Number of tasks that failed to export\n- `tasks`: Detailed lists of created, updated, and failed tasks\n\n**Example Request:**\n```json\n{\n  \"workspace\": \"target\",\n  \"task_ids\": [\"task_1\", \"task_2\"],\n  \"limit\": 20\n}\n```\n\n**Example Response:**\n```json\n{\n  \"success\": true,\n  \"workspace\": \"target\",\n  \"processed\": 2,\n  \"created\": 1,\n  \"updated\": 1,\n  \"failed\": 0,\n  \"tasks\": {\n    \"created\": [...],\n    \"updated\": [...],\n    \"failed\": []\n  }\n}\n```\n\n### `sync_asana_tasks`\n\nBidirectional sync between Asana workspaces and local parquet using three-way merge for field-level reconciliation.\n\n**Parameters:**\n- `task_ids` (optional): Specific task IDs to sync (if provided, syncs only these tasks)\n- `sync_scope` (optional): Scope of sync (\"both\", \"source\", or \"target\", default: \"both\")\n- `dry_run` (optional): Preview changes without applying them (default: false)\n\n**Returns:**\n- `success`: Boolean indicating success\n- `source_to_local`: Statistics for source → local sync\n- `target_to_local`: Statistics for target → local sync\n- `local_to_source`: Statistics for local → source sync\n- `local_to_target`: Statistics for local → target sync\n- `sync_scope`: Scope of sync performed\n- `dry_run`: Whether changes were applied\n- `pending` (when dry_run=true): Pending updates/creates for each direction\n\n**Example Request:**\n```json\n{\n  \"task_ids\": [\"task_1\", \"task_2\"],\n  \"sync_scope\": \"both\",\n  \"dry_run\": false\n}\n```\n\n**Example Response:**\n```json\n{\n  \"success\": true,\n  \"source_to_local\": {\n    \"updated\": 5,\n    \"new\": 2,\n    \"tasks\": {...}\n  },\n  \"target_to_local\": {\n    \"updated\": 3,\n    \"new\": 1,\n    \"tasks\": {...}\n  },\n  \"local_to_source\": {\n    \"updated\": 2,\n    \"created\": 0,\n    \"tasks\": {...}\n  },\n  \"local_to_target\": {\n    \"updated\": 4,\n    \"created\": 1,\n    \"tasks\": {...}\n  },\n  \"sync_scope\": \"both\"\n}\n```\n\n### `import_asana_task_comments`\n\nImport comments for specific tasks.\n\n**Parameters:**\n- `task_gids` (required): Array of task GIDs to import comments for\n- `workspace` (optional): Workspace (\"source\" or \"target\", default: \"source\")\n\n**Returns:**\n- `success`: Boolean indicating success\n- `workspace`: Workspace imported from\n- `task_count`: Number of tasks processed\n- `comments_imported`: Number of comments imported\n\n### `import_asana_task_metadata`\n\nImport task metadata: custom fields, dependencies, and stories.\n\n**Parameters:**\n- `task_gids` (required): Array of task GIDs to import metadata for\n- `workspace` (optional): Workspace (\"source\" or \"target\", default: \"source\")\n- `metadata_types` (optional): Types of metadata to import (array, default: [\"custom_fields\", \"dependencies\", \"stories\"])\n\n**Returns:**\n- `success`: Boolean indicating success\n- `workspace`: Workspace imported from\n- `task_count`: Number of tasks processed\n- `custom_fields`: Number of custom fields imported\n- `dependencies`: Number of dependencies imported\n- `stories`: Number of stories imported\n\n### `register_asana_webhooks`\n\nRegister Asana webhooks for projects in workspaces.\n\n**Parameters:**\n- `webhook_url` (required): Public webhook URL (must be HTTPS)\n- `workspace` (optional): Workspace to register webhooks for (\"source\", \"target\", or \"both\", default: \"both\")\n\n**Returns:**\n- Registration results for each workspace with counts and details\n\n### `get_asana_task`\n\nGet a single task from Asana by GID.\n\n**Parameters:**\n- `task_gid` (required): Asana task GID\n- `workspace` (optional): Workspace (\"source\" or \"target\", default: \"source\")\n\n**Returns:**\n- `success`: Boolean indicating success\n- `workspace`: Workspace fetched from\n- `task`: Task data object\n\n### `list_asana_projects`\n\nList projects in an Asana workspace.\n\n**Parameters:**\n- `workspace` (optional): Workspace (\"source\" or \"target\", default: \"source\")\n- `archived` (optional): Include archived projects (true, false, or null for all)\n\n**Returns:**\n- `success`: Boolean indicating success\n- `workspace`: Workspace queried\n- `workspace_gid`: Workspace GID\n- `count`: Number of projects\n- `projects`: Array of project objects\n\n### `get_asana_workspace_info`\n\nGet information about an Asana workspace.\n\n**Parameters:**\n- `workspace` (optional): Workspace (\"source\" or \"target\", default: \"source\")\n\n**Returns:**\n- `success`: Boolean indicating success\n- `workspace`: Workspace queried\n- `workspace_data`: Workspace information object\n\n## Error Handling\n\nThe server returns structured error messages in JSON format when operations fail. Common errors include:\n\n- **Authentication errors**: Missing or invalid PATs\n- **Workspace errors**: Invalid workspace GIDs\n- **API errors**: Asana API errors with status codes and messages\n- **MCP errors**: Parquet MCP server connection or operation errors\n\n**Example error response:**\n```json\n{\n  \"error\": \"Missing required configuration values: ASANA_SOURCE_PAT, SOURCE_WORKSPACE_GID\",\n  \"type\": \"RuntimeError\",\n  \"context\": \"Tool: import_asana_tasks\"\n}\n```\n\n## Security Notes\n\n- API tokens are never logged or exposed in error messages\n- Tokens can be stored in environment variables, `~/.config/asana-mcp/.env`, or 1Password\n- All API requests use HTTPS\n- Config directory `.env` file has restricted permissions (owner read/write only)\n- Environment variables are the most secure method (not persisted to disk)\n- All data operations go through parquet MCP server (automatic snapshots and audit logging)\n\n## Troubleshooting\n\n1. **Authentication Fails**\n   - Verify PATs are correct and not expired\n   - Get new PAT from: https://app.asana.com/0/my-apps\n   - Ensure workspace GIDs are correct\n   - Check 1Password integration is configured correctly (if using)\n\n2. **Workspace Not Found**\n   - Verify workspace GIDs are correct\n   - Check PAT has access to workspaces\n   - Ensure workspace is not archived or deleted\n\n3. **Parquet MCP Connection Fails**\n   - Verify parquet MCP server is configured in Cursor/Claude Desktop\n   - Check `PARQUET_MCP_SERVER_PATH` environment variable\n   - Ensure parquet server is accessible\n\n4. **Tasks Not Syncing**\n   - Check task has required fields (title)\n   - Verify workspace permissions\n   - Check sync_log field for error codes\n   - Review Asana API rate limits\n\n5. **Duplicate Tasks**\n   - The server checks for duplicates by title before creating\n   - If duplicates exist, it updates the existing task instead\n\n6. **Tests Failing**\n   - See [TESTING.md](TESTING.md) for troubleshooting guide\n   - Ensure test dependencies installed\n   - Configure test workspaces for integration tests\n\n## Plan Limitations\n\nSee [PLAN_LIMITATIONS.md](PLAN_LIMITATIONS.md) for detailed documentation of premium features and plan-specific limitations.\n\n## Notes\n\n- The server uses the parquet MCP server for all data operations (never accesses parquet files directly)\n- Webhook registration requires the webhook server to be running and accessible\n- The webhook server (`asana_webhook_server.py`) remains a separate background service\n- Three-way merge preserves changes on both sides when different properties are modified\n- All date fields are returned as strings in ISO format\n- The server runs in stdio mode for MCP communication\n- Monitoring scripts (`monitor_asana_import.py`, `monitor_asana_export.py`) remain separate\n\n## Sync Strategy\n\nThe bidirectional sync uses a three-way merge approach:\n\n1. **Last Synced State**: Stores task state after last successful sync\n2. **Three-Way Comparison**: Compares last_synced vs current_local vs current_asana\n3. **Merge Logic**:\n   - If only Asana changed: Use Asana value\n   - If only Local changed: Use Local value\n   - If both changed: Use newer timestamp (conflict resolution)\n   - If neither changed: Keep existing value\n\nThis approach preserves changes on both sides when different properties are modified, without requiring per-property timestamps.\n\n## Architecture\n\n```\nAsana MCP Server\n├── Asana API (read/write tasks, comments, metadata)\n├── Parquet MCP Server (via MCP client)\n│   ├── tasks.parquet\n│   ├── task_comments.parquet\n│   ├── task_custom_fields.parquet\n│   ├── task_dependencies.parquet\n│   ├── task_stories.parquet\n│   └── task_attachments.parquet\n└── Automatic snapshots \u0026 audit logging (via parquet MCP)\n```\n\n## License\n\nMIT\n\n## Support\n\n- [GitHub Issues](https://github.com/markmhendrickson/mcp-server-asana/issues)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarkmhendrickson%2Fmcp-server-asana","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarkmhendrickson%2Fmcp-server-asana","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarkmhendrickson%2Fmcp-server-asana/lists"}