# asanamcp
[](https://crates.io/crates/asanamcp)
[](https://docs.rs/asanamcp)
[](https://github.com/adlio/asanamcp/actions/workflows/ci.yml)
[](https://codecov.io/gh/adlio/asanamcp)
[](LICENSE)
MCP server for the Asana API.
## Quick Start
1. Get an Asana Personal Access Token at https://app.asana.com/0/my-apps
2. Install:
```bash
# Homebrew
brew install adlio/tap/asanamcp
# From crates.io
cargo install asanamcp
# From GitHub
cargo install --git https://github.com/adlio/asanamcp
```
3. Add to Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"asana": {
"command": "asanamcp",
"env": {
"ASANA_TOKEN": "your-personal-access-token",
"ASANA_DEFAULT_WORKSPACE": "your-workspace-gid"
}
}
}
}
```
The `ASANA_DEFAULT_WORKSPACE` is optional but recommended if you work primarily in one workspace. When set, workspace-based operations (search, list projects, list users, etc.) will use this default, reducing the need to specify workspace GID in every request.
## Testing the Server
```bash
# Dump tool schemas (useful for debugging)
asanamcp --schema
# Dump a specific tool's schema
asanamcp --schema get
# Launch the MCP Inspector (interactive web UI)
make inspect
```
## Tools
| `asana_workspaces` | List all workspaces |
| `asana_get` | Fetch any resource (projects, tasks, portfolios, etc.) |
| `asana_create` | Create resources (tasks, comments, projects, etc.) |
| `asana_update` | Update existing resources |
| `asana_delete` | Permanently delete resources (irreversible) |
| `asana_link` | Manage relationships (task↔project, dependencies, etc.) |
| `asana_task_search` | Search for tasks with rich filters (assignee, due date, etc.) |
| `asana_resource_search` | Search for resources by name (projects, templates, users, teams, etc.) |
### asana_get
Fetch any Asana resource with recursive traversal support.
```json
{"resource_type": "portfolio", "gid": "123", "depth": -1}
```
| resource_type | gid | Options |
| `portfolio` | portfolio GID | `depth`: traversal depth |
| `task` | task GID | `include_subtasks`, `include_dependencies`, `include_comments` |
| `my_tasks` | workspace GID* | Tasks assigned to current user |
| `workspace_favorites` | workspace GID* | `depth` for portfolio traversal |
| `workspace_projects` | workspace GID* | All projects in workspace |
| `workspace_templates` | team GID (optional) | Empty = all accessible templates |
| `workspace_tags` | workspace GID* | |
| `workspace_users` | workspace GID* | |
| `workspace_teams` | workspace GID* | |
| `project_tasks` | project/portfolio GID | `subtask_depth` |
| `task_subtasks` | task GID | |
| `task_comments` | task GID | |
| `status_update` | status update GID | Single status update by GID |
| `status_updates` | parent GID | List status updates for a project/portfolio |
| `all_workspaces` | (ignored) | All accessible workspaces |
| `workspace` | workspace GID | |
| `project_template` | template GID | |
| `project_sections` | project GID | |
| `section` | section GID | |
| `tag` | tag GID | |
| `me` | (ignored) | Current authenticated user |
| `user` | user GID | |
| `team` | team GID | |
| `team_users` | team GID | |
| `project_custom_fields` | project GID | |
| `project_brief` | brief GID | Project brief (Key Resources on Overview tab, NOT the Note tab) |
| `project_project_brief` | project GID | Get project's brief via project GID |
*Uses `ASANA_DEFAULT_WORKSPACE` if gid is empty.
Depth: `-1` = unlimited, `0` = none, `N` = N levels.
### asana_create
```json
{"resource_type": "task", "project_gid": "123", "name": "New task", "assignee": "me"}
```
| resource_type | Required fields |
| `subtask` | `task_gid`, `name` |
| `project` | `workspace_gid` or `team_gid`, `name` |
| `project_from_template` | `template_gid`, `name` |
| `portfolio` | `workspace_gid`*, `name` |
| `section` | `project_gid`, `name` |
| `comment` | `task_gid`, `text` |
| `status_update` | `parent_gid`, `status_type`, `text` |
| `tag` | `workspace_gid`*, `name` |
| `project_duplicate` | `source_gid`, `name` |
| `task_duplicate` | `source_gid`, `name` |
| `project_brief` | `project_gid`, `html_text` (with `<body>` tags) | Key Resources on Overview tab (NOT the Note tab) |
*Uses `ASANA_DEFAULT_WORKSPACE` if not provided.
### asana_update
```json
{"resource_type": "task", "gid": "123", "completed": true}
```
Supports: `task`, `project`, `portfolio`, `section`, `tag`, `comment`, `status_update`, `project_brief` (Key Resources on Overview tab, NOT the Note tab).
### asana_delete
Permanently delete an Asana resource. This action is **irreversible**.
```json
{"resource_type": "task", "gid": "123"}
```
Supports: `task`, `project`, `portfolio`, `section`, `tag`, `comment`, `status_update`, `project_brief`.
### asana_link
```json
{"action": "add", "relationship": "task_project", "target_gid": "task123", "item_gid": "proj456"}
```
| `task_project` | task GID | project GID |
| `task_tag` | task GID | tag GID |
| `task_parent` | task GID | parent task GID |
| `task_dependency` | task GID | blocking task GID(s) |
| `task_dependent` | task GID | dependent task GID(s) |
| `task_follower` | task GID | user GID(s) |
| `portfolio_item` | portfolio GID | project GID |
| `portfolio_member` | portfolio GID | user GID(s) |
| `project_member` | project GID | user GID(s) |
| `project_follower` | project GID | user GID(s) |
Use `item_gid` for single items or `item_gids` for bulk operations.
### asana_task_search
Search for tasks with rich filtering options.
```json
{"workspace_gid": "123", "text": "bug", "completed": false, "assignee": "me"}
```
| `workspace_gid` | Workspace to search (uses default if not provided) |
| `text` | Search in task name and notes |
| `assignee` | User GID, `me`, or `null` for unassigned |
| `projects` | Filter by project GID(s) |
| `tags` | Filter by tag GID(s) |
| `sections` | Filter by section GID(s) |
| `completed` | `true` or `false` |
| `due_on`, `due_on_before`, `due_on_after` | Date filters (YYYY-MM-DD) |
| `sort_by` | `due_date`, `created_at`, `completed_at`, `likes`, `modified_at` |
| `sort_ascending` | `true` or `false` |
### asana_resource_search
Search for any Asana resource by name using typeahead. Use this to find projects, templates, users, teams, and more.
```json
{"query": "CloudSmith", "resource_type": "project_template"}
```
| `query` | Search text (required) |
| `resource_type` | `project`, `project_template`, `portfolio`, `user`, `team`, `tag`, or `goal` |
| `workspace_gid` | Workspace to search (uses default if not provided) |
| `count` | Max results (default 20, max 100) |
## Library Usage
```rust
use asanamcp::{AsanaClient, Resource};
#[tokio::main]
async fn main() -> Result<(), asanamcp::Error> {
let client = AsanaClient::from_env()?;
let workspaces: Vec<Resource> = client
.get_all("/workspaces", &[("opt_fields", "gid,name")])
.await?;
for ws in workspaces {
println!("{}: {:?}", ws.gid, ws.fields.get("name"));
}
Ok(())
}
```
## Development
```bash
make ci # Run all checks (fmt, lint, build, docs, test)
make test # Run tests
make coverage # Coverage report
make fmt # Format code
make lint # Run clippy
make inspect # Open MCP Inspector web UI
make schema # Dump tool schemas to stdout
```
## License
MIT