Skip to main content
Glama
jhirono

Microsoft Todo MCP Service

by jhirono

get-task-lists

Retrieve all Microsoft Todo task lists to view names, IDs, and identify default or shared containers for organizing tasks.

Instructions

Get all Microsoft Todo task lists (the top-level containers that organize your tasks). Shows list names, IDs, and indicates default or shared lists.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • Registration of the 'get-task-lists' tool with the MCP server. Includes tool name, description, empty input schema ({}), and inline async handler function that fetches and formats task lists from Microsoft To Do via Graph API.
    server.tool(
      "get-task-lists",
      "Get all Microsoft Todo task lists (the top-level containers that organize your tasks). Shows list names, IDs, and indicates default or shared lists.",
      {},
      async () => {
        try {
          const token = await getAccessToken();
          if (!token) {
            return {
              content: [
                {
                  type: "text",
                  text: "Failed to authenticate with Microsoft API",
                },
              ],
            };
          }
    
          const response = await makeGraphRequest<{ value: TaskList[] }>(
            `${MS_GRAPH_BASE}/me/todo/lists`,
            token
          );
    
          if (!response) {
            return {
              content: [
                {
                  type: "text",
                  text: "Failed to retrieve task lists",
                },
              ],
            };
          }
    
          const lists = response.value || [];
          if (lists.length === 0) {
            return {
              content: [
                {
                  type: "text",
                  text: "No task lists found.",
                },
              ],
            };
          }
    
          const formattedLists = lists.map((list) => {
            // Add well-known list name if applicable
            let wellKnownInfo = "";
            if (list.wellknownListName && list.wellknownListName !== "none") {
              if (list.wellknownListName === "defaultList") {
                wellKnownInfo = " (Default Tasks List)";
              } else if (list.wellknownListName === "flaggedEmails") {
                wellKnownInfo = " (Flagged Emails)";
              }
            }
            
            // Add sharing info if applicable
            let sharingInfo = "";
            if (list.isShared) {
              sharingInfo = list.isOwner ? " (Shared by you)" : " (Shared with you)";
            }
            
            return `ID: ${list.id}\nName: ${list.displayName}${wellKnownInfo}${sharingInfo}\n---`;
          });
    
          return {
            content: [
              {
                type: "text",
                text: `Your task lists:\n\n${formattedLists.join("\n")}`,
              },
            ],
          };
        } catch (error) {
          return {
            content: [
              {
                type: "text",
                text: `Error fetching task lists: ${error}`,
              },
            ],
          };
        }
      }
    );
  • The core handler logic for the get-task-lists tool. Handles authentication, API call to retrieve task lists, formats the lists with details like ID, name, well-known status, and sharing info, and returns structured text content.
    async () => {
      try {
        const token = await getAccessToken();
        if (!token) {
          return {
            content: [
              {
                type: "text",
                text: "Failed to authenticate with Microsoft API",
              },
            ],
          };
        }
    
        const response = await makeGraphRequest<{ value: TaskList[] }>(
          `${MS_GRAPH_BASE}/me/todo/lists`,
          token
        );
    
        if (!response) {
          return {
            content: [
              {
                type: "text",
                text: "Failed to retrieve task lists",
              },
            ],
          };
        }
    
        const lists = response.value || [];
        if (lists.length === 0) {
          return {
            content: [
              {
                type: "text",
                text: "No task lists found.",
              },
            ],
          };
        }
    
        const formattedLists = lists.map((list) => {
          // Add well-known list name if applicable
          let wellKnownInfo = "";
          if (list.wellknownListName && list.wellknownListName !== "none") {
            if (list.wellknownListName === "defaultList") {
              wellKnownInfo = " (Default Tasks List)";
            } else if (list.wellknownListName === "flaggedEmails") {
              wellKnownInfo = " (Flagged Emails)";
            }
          }
          
          // Add sharing info if applicable
          let sharingInfo = "";
          if (list.isShared) {
            sharingInfo = list.isOwner ? " (Shared by you)" : " (Shared with you)";
          }
          
          return `ID: ${list.id}\nName: ${list.displayName}${wellKnownInfo}${sharingInfo}\n---`;
        });
    
        return {
          content: [
            {
              type: "text",
              text: `Your task lists:\n\n${formattedLists.join("\n")}`,
            },
          ],
        };
      } catch (error) {
        return {
          content: [
            {
              type: "text",
              text: `Error fetching task lists: ${error}`,
            },
          ],
        };
      }
    }
  • TypeScript interface defining the TaskList type used in the response parsing for get-task-lists tool.
    interface TaskList {
      id: string;
      displayName: string;
      isOwner?: boolean;
      isShared?: boolean;
      wellknownListName?: string; // 'none', 'defaultList', 'flaggedEmails', 'unknownFutureValue'
    }
  • Generic helper function for making HTTP requests to Microsoft Graph API, used by the tool handler for fetching task lists. Handles authentication headers, error cases, and personal account limitations.
    async function makeGraphRequest<T>(url: string, token: string, method = "GET", body?: any): Promise<T | null> {
      const headers = {
        "User-Agent": USER_AGENT,
        "Accept": "application/json",
        "Authorization": `Bearer ${token}`,
        "Content-Type": "application/json"
      };
    
      try {
        const options: RequestInit = { 
          method, 
          headers 
        };
    
        if (body && (method === "POST" || method === "PATCH")) {
          options.body = JSON.stringify(body);
        }
    
        console.error(`Making request to: ${url}`);
        console.error(`Request options: ${JSON.stringify({
          method,
          headers: {
            ...headers,
            Authorization: 'Bearer [REDACTED]'
          }
        })}`);
    
        const response = await fetch(url, options);
        
        if (!response.ok) {
          const errorText = await response.text();
          console.error(`HTTP error! status: ${response.status}, body: ${errorText}`);
          
          // Check for the specific MailboxNotEnabledForRESTAPI error
          if (errorText.includes('MailboxNotEnabledForRESTAPI')) {
            console.error(`
    =================================================================
    ERROR: MailboxNotEnabledForRESTAPI
    
    The Microsoft To Do API is not available for personal Microsoft accounts 
    (outlook.com, hotmail.com, live.com, etc.) through the Graph API.
    
    This is a limitation of the Microsoft Graph API, not an authentication issue.
    Microsoft only allows To Do API access for Microsoft 365 business accounts.
    
    You can still use Microsoft To Do through the web interface or mobile apps,
    but API access is restricted for personal accounts.
    =================================================================
            `);
            
            throw new Error("Microsoft To Do API is not available for personal Microsoft accounts. See console for details.");
          }
          
          throw new Error(`HTTP error! status: ${response.status}, body: ${errorText}`);
        }
        
        const data = await response.json();
        console.error(`Response received: ${JSON.stringify(data).substring(0, 200)}...`);
        return data as T;
      } catch (error) {
        console.error("Error making Graph API request:", error);
        return null;
      }
    }
  • Helper function to obtain a valid access token, reading from file or memory, refreshing if expired. Critical dependency for the tool's authentication.
    async function getAccessToken(): Promise<string | null> {
      try {
        console.error('getAccessToken called');
        
        // First check if we have a valid current access token in memory
        if (currentAccessToken) {
          return currentAccessToken;
        }
        
        // Check for tokens in environment variables or file
        try {
          // Read token file
          const tokenData = readTokens();
          
          if (tokenData) {
            // Check if token is expired
            const now = Date.now();
            if (now > tokenData.expiresAt) {
              console.error(`Token is expired. Current time: ${now}, expires at: ${tokenData.expiresAt}`);
              
              // If we have a refresh token, try to refresh the access token
              if (tokenData.refreshToken || currentRefreshToken) {
                console.error('Attempting to refresh token...');
                const refreshTokenToUse = currentRefreshToken || tokenData.refreshToken;
                const newTokenData = await refreshAccessToken(refreshTokenToUse);
                if (newTokenData) {
                  console.error('Token refreshed successfully');
                  return newTokenData.accessToken;
                }
                console.error('Token refresh failed');
              }
              
              return null;
            }
            
            // Success - return the token and update current state
            currentAccessToken = tokenData.accessToken;
            currentRefreshToken = tokenData.refreshToken;
            console.error(`Successfully retrieved valid token (${tokenData.accessToken.substring(0, 10)}...)`);
            return tokenData.accessToken;
          }
        } catch (readError) {
          console.error(`Direct token read error: ${readError}`);
          return null;
        }
        
        return null;
      } catch (error) {
        console.error("Error getting access token:", error);
        return null;
      }
    }

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changedv1.0.0
    • removedInput schema / additionalProperties
      Removed value: -false
  2. First observed

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden of indicating safety. The verb 'Get' clearly implies a read-only operation, and the description further discloses what the return will contain (list names, IDs, default/shared status). It does not mention edge cases or limitations, but for a simple read tool this is sufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences long, front-loaded with the core action, and every word adds value. There is no repetition or unnecessary detail.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no parameters, no annotations, and no output schema, this description adequately covers what the tool does and what it returns. It is complete for the tool's simplicity, though it could mention the alternative organized view.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters and the schema is empty, so the baseline is 4. The description does not need to explain any parameter semantics, and it correctly focuses on output rather than input.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb 'Get' and a clear resource 'all Microsoft Todo task lists', clarifying its scope as top-level containers. However, it does not differentiate itself from the sibling tool get-task-lists-organized, which likely serves a similar or overlapping purpose.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided about when to use this tool versus alternatives like get-task-lists-organized. The description simply states what it does, with no context about selection criteria, exclusions, or prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.