Asana MCP Server

repository·main·Indexed 41 days ago

https://github.com/roychri/mcp-server-asana

A Model Context Protocol (MCP) server implementation version 1.8.0 that bridges AI agents with the Asana API. It enables LLMs to read and write Asana data, including managing tasks, projects, workspaces, comments, and tags through standardized MCP tools. Supports configuration for read-only mode and integrates with clients like Claude Desktop, Claude Code, and Codex CLI.

Tokens
21.4K
Snippets
67
Records
79
Agent score
98%

What's inside Asana MCP Server

  1. Overview of MCP Server for Asana

    main
    The mcp-server-asana is a Model Context Protocol (MCP) server implementation that enables MCP clients (such as Anthropic's Claude Desktop) to interact with the Asana API. It allows LLMs to access and manage Asana tasks, projects, workspaces, and comments through standardized MCP tools.
  2. Request specific properties using `opt_fields`

    main

    By default, Asana API endpoints that return arrays of results (such as getTasks or getProjects) return a compact version of the objects. To retrieve additional properties, you must use the opt_fields parameter in your request.

    opt_fields accepts a comma-separated string of the property names you wish to include in the response. This allows you to control the payload size and retrieve nested data (e.g., assignee.name or custom_fields.display_value).

    let opts = {
        "limit": 1,
        "project": "<YOUR_PROJECT_GID>",
        "opt_fields": "actual_time_minutes,assignee.name,custom_fields.display_value,due_on"
    };
    
    tasksApiInstance.getTasks(opts).then((result) => {
        console.log(JSON.stringify(result.data, null, 2));
    });
  3. Initialize the Asana client and authenticate

    main

    To authenticate with the Asana API, access the ApiClient instance and set the accessToken on the token authentication object. You will typically use UsersApi or TasksApi instances for specific operations.

    const Asana = require('asana');
    
    let client = Asana.ApiClient.instance;
    let token = client.authentications['token'];
    token.accessToken = '<YOUR_ACCESS_TOKEN>';
    
    // Example: Initialize a specific API instance
    let usersApiInstance = new Asana.UsersApi();
  4. Work with remote MCP servers (SSE and HTTP)

    main

    The MCP Inspector supports remote servers using different transport protocols.

    SSE (Server-Sent Events) Transport

    For remote SSE servers, pass the URL directly as the command.

    List tools from remote SSE:

    npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --method tools/list

    Streamable HTTP Transport

    Use the --transport http flag for HTTP-based transport. You can also pass custom headers using the --header flag.

    Call a tool on remote HTTP server with headers:

    npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com \
      --transport http \
      --method tools/call \
      --tool-name remotetool \
      --tool-arg param=value \
      --header "X-API-Key: your-api-key"
    npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --transport http --method tools/list
  5. Get events using sync tokens

    main

    To subscribe to events for a resource (task or project), you need a sync token.

    1. Initial Sync: Make an initial call to getEvents. If it fails, the error response body typically contains the required sync token.
    2. Polling: Use the sync token in subsequent requests via the sync option.
    3. Updating Token: After each successful request, update your sync token using the value provided in the response (result._response.sync) or handle 412 errors by extracting the new token from the error body.
    const Asana = require('asana');
    
    let client = Asana.ApiClient.instance;
    let token = client.authentications['token'];
    token.accessToken = '<YOUR_ACCESS_TOKEN>';
    
    let eventsApiInstance = new Asana.EventsApi();
    let resource = "<YOUR_TASK_OR_PROJECT_GID>";
    let opts = {
        "sync": ""
    };
    const timeouts = 5000
    
    const setSyncToken = async () => {
        await eventsApiInstance.getEvents(resource, opts).then((result) => {
            console.log(JSON.stringify(result.data, null, 2));
        }, (error) => {
            let syncToken = error.response.body.sync;
            opts['sync'] = syncToken;
        });
    }
    
    const getEvents = async () => {
        await setSyncToken();
        while(true) {
            await eventsApiInstance.getEvents(resource, opts).then((result) => {
                console.log(JSON.stringify(result.data, null, 2));
                opts['sync'] = result._response.sync;
            }, (error) => {
                if (error.status === 412) {
                    let syncToken = error.response.body.sync;
                    opts['sync'] = syncToken;
                } else {
                    console.error(error.response.text);
                }
            });
            await new Promise(resolve => setTimeout(resolve, timeouts));
        }
    }
    
    getEvents();
  6. Paginate through Asana resources using v3

    main

    The v3 Node SDK uses the nextPage() method for pagination. Unlike v1, you must explicitly set a limit in your initial request (the previous default of 50 is no longer automatic). The v3 SDK does not support the .fetch() or .stream() methods found in v1.

    To fetch multiple pages, call nextPage() on the response object to retrieve the subsequent page of data.

    // Fetching the next page
    tasksApiInstance.getTasks({ limit: 50 }).then(firstPage => {
      console.log(firstPage.data);
      firstPage.nextPage().then(secondPage => {
        console.log(secondPage.data);
      });
    });
    
    // Example: Fetching up to 200 tasks with a page size of 50
    tasksApiInstance
      .getTasks({ limit: 50, project: "1199684513975168" })
      .then(async (firstPage) => {
        let results = firstPage.data;
        let nextPage = await firstPage.nextPage();
        let pageIndex = 2;
    
        while (nextPage.data && pageIndex <= 4) {
          results = results.concat(nextPage.data);
          nextPage = await nextPage.nextPage();
          pageIndex += 1;
        }
      });
  7. Automate MCP testing in CI/CD

    main

    Since the MCP Inspector CLI outputs JSON by default, it is ideal for automated testing pipelines (e.g., GitHub Actions).

    Example GitHub Actions Workflow snippet:

          - name: Test with Inspector CLI
            run: |
              # Test tools listing
              npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/list
              
              # Test tool execution
              npx @modelcontextprotocol/inspector --cli node build/index.js \
                --method tools/call \
                --tool-name test_tool \
                --tool-arg input=test
    npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/list
  8. Handle pagination with Collection objects

    main

    Endpoints returning arrays (e.g., getTasks, getProjects) return a Collection object by default. To use pagination, you must provide a limit query parameter in your initial request. The limit must be between 1 and 100.

    To fetch all results, use a while loop that calls the nextPage() method on the collection object until page.data is empty.

    const Asana = require('asana');
    
    let client = Asana.ApiClient.instance;
    let token = client.authentications['token'];
    token.accessToken = '<YOUR_ACCESS_TOKEN>';
    
    let tasksApiInstance = new Asana.TasksApi();
    let opts = {
        "project": "<YOUR_PROJECT_GID>",
        "limit": 100,
    };
    
    async function getAllTasks(opts) {
        let tasks = await tasksApiInstance.getTasks(opts).then(async (response) => {
            let result = [];
            let page = response;
            while(true) {
                result = result.concat(page.data);
                page = await page.nextPage();
                if (!page.data) {
                    break;
                }
            }
            return result;
        }, (error) => {
            console.error(error.response.body);
        });
        console.log('Tasks: ' + JSON.stringify(tasks, null, 2));
    }
    
    getAllTasks(opts);
  9. Instantiate the Asana v3 Node.js SDK client

    main

    When using the Asana v3 Node.js SDK, you set your API access token once on the ApiClient instance, and then create specific API instances (e.g., UsersApi, TasksApi) for each resource you wish to access. This differs from v1, where a single client instance handled all resources.

    Note: This project requires using the v3 SDK. Do not use the v1 SDK.

    const Asana = require('asana');
    
    let client = Asana.ApiClient.instance;
    let token = client.authentications['token'];
    token.accessToken = '<YOUR_ACCESS_TOKEN>';
    
    let usersApiInstance = new Asana.UsersApi(); // instance to access users
    
    usersApiInstance.getUser("me").then(function(me) {
      console.log(me);
    });
  10. Install the Asana MCP Server

    main

    Follow these steps to set up the Asana MCP server depending on your client environment. You must first have an Asana account and a Personal Access Token from the Asana developer console.

    ### For Claude Desktop:
    Add to `claude_desktop_config.json`:
    ```json
    {
      "mcpServers": {
        "asana": {
          "command": "npx",
          "args": ["-y", "@roychri/mcp-server-asana"],
          "env": {
            "ASANA_ACCESS_TOKEN": "your-asana-access-token"
          }
        }
      }
    }

    For Claude Code:

    claude mcp add asana -e ASANA_ACCESS_TOKEN=<TOKEN> -- npx -y @roychri/mcp-server-asana

    For Codex CLI:

    Add to ~/.codex/config.toml:

    [mcp_servers.asana]
    command = "npx"
    args = [
      "-y", "@roychri/mcp-server-asana"
    ]
    env = {ASANA_ACCESS_TOKEN= "<TOKEN>"}
  11. Start the MCP Inspector in CLI Mode

    main

    The MCP Inspector CLI mode allows for programmatic interaction with MCP servers via the command line, making it suitable for automation and scripting.

    Requirements:

    • Node.js: ^22.7.5

    Basic Syntax: To start the inspector in CLI mode, use the --cli flag followed by the command used to start your server.

    Testing a local Node.js server:

    npx @modelcontextprotocol/inspector --cli node build/index.js
    npx @modelcontextprotocol/inspector --cli node build/index.js