// Tool titles, descriptions, and per-parameter descriptions adapted from the // official Asana MCP server tool set // (https://developers.asana.com/docs/mcp-tools-reference) and the Asana REST // API reference. Rewritten so the pi agent knows exactly WHEN to reach for // each one and what the parameters map to. // // Curated set, not a 1:1 mirror of the official MCP server: we skip the // noisy/duplicate/UI-only MCP tools and add a few purpose-built recovery // tools (full-body fetches) the MCP set lacks. Skipped MCP tools (with reason): // - search_tasks (Premium-only; overlaps asana_search_objects) // - get_portfolio(s) (niche in agent flows) // - get_items_for_portfolio (niche) // - get_attachments (returns binary blobs) // - get_user / get_users (use asana_search_objects with resource_type=user) // - get_teams (rarely requested) // - get_agent(s) (AI Teammates only) // - delete_task (destructive; add later if needed) // - create_project (high-blast-radius; add later) // - create_project_status_update (niche) // - *preview tools (Claude/ChatGPT confirmation UI; no UI in pi) // ---------------------------------------------------------------- search --- export const SEARCH_TITLE = "Asana: Search Objects"; export const SEARCH_DESCRIPTION = `Keyword search across workspace via typeahead. Use FIRST when GID is unknown to resolve partial names. Call once per resource_type ("task" | "project" | "user" | "tag", defaults to "task"); no cross-type search. Returns list of matches with GIDs. Requires workspace GID (get from asana_get_me if unknown).`; export const SEARCH_WORKSPACE_DESCRIPTION = "Workspace GID (e.g. \"1234567890123456\"). Get from asana_get_me if unknown."; export const SEARCH_QUERY_DESCRIPTION = "Search term matching names. Empty string returns recently contacted users or visited projects depending on resource_type."; export const SEARCH_RESOURCE_TYPE_DESCRIPTION = "Type to search: \"task\" | \"project\" | \"user\" | \"tag\". Defaults to \"task\"."; export const SEARCH_LIMIT_DESCRIPTION = "Limit results (1-100). Defaults to 20."; // ------------------------------------------------------------------ me --- export const ME_TITLE = "Asana: Get Authenticated User"; export const ME_DESCRIPTION = `Identity and workspace membership of ASANA_ACCESS_TOKEN owner. Use when user asks "who am I" or when workflow needs workspace/user GID. Call at session start if workspace GID is unknown.`; export const ME_OPT_FIELDS_DESCRIPTION = "Optional comma-separated opt_fields (e.g. \"email,workspaces.name\"). Defaults: name, email, gid, resource_type, workspaces."; // --------------------------------------------------------------- task(s) --- export const GET_TASK_TITLE = "Asana: Get Task"; export const GET_TASK_DESCRIPTION = `Full detail for task GID: name, notes, assignee, dates, parent, projects, sections, tags, custom fields, subtasks, dependencies. Use AFTER asana_search_objects or asana_get_tasks returns a GID. Excludes comments/stories (use asana_get_task_comments for discussion, or asana_add_comment to post).`; export const GET_TASK_GID_DESCRIPTION = "Task GID (e.g. \"1234567890123456\")."; export const GET_TASK_OPT_FIELDS_DESCRIPTION = "Optional opt_fields. Default includes subtasks, dependencies, parent, custom fields."; export const GET_TASK_NOTES_TITLE = "Asana: Get Task Description"; export const GET_TASK_NOTES_DESCRIPTION = `Full, untruncated notes (description) for a single task GID. asana_get_task caps notes at ~2000 chars to keep its payload cheap; call THIS tool when you need the complete description body to understand or perform the task (e.g. acceptance criteria, background, implementation notes). Returns only gid, name, and the full notes (output labeled \`description:\`). Use AFTER you already have a task GID from asana_search_objects, asana_get_tasks, or asana_get_task.`; export const GET_TASKS_TITLE = "Asana: List Tasks"; export const GET_TASKS_DESCRIPTION = `Filtered list of tasks. Require at least one of: project, section, tag, OR (assignee AND workspace). Assignee alone or workspace alone both 400. Use for bulk reads. For partial name search, prefer asana_search_objects.`; export const GET_TASKS_PROJECT_DESCRIPTION = "Project GID filter."; export const GET_TASKS_SECTION_DESCRIPTION = "Section GID filter. Must belong to the project parameter."; export const GET_TASKS_TAG_DESCRIPTION = "Tag GID filter."; export const GET_TASKS_ASSIGNEE_DESCRIPTION = "Assignee: \"me\" or user GID. Omit (and provide project/section/tag) for all tasks in scope."; export const GET_TASKS_WORKSPACE_DESCRIPTION = "Workspace GID. Required only if assignee is set (prevents 400)."; export const GET_TASKS_COMPLETED_SINCE_DESCRIPTION = "ISO 8601 timestamp. Returns tasks completed after this instant."; export const GET_TASKS_COMPLETED_BEFORE_DESCRIPTION = "ISO 8601 timestamp. Bounds completion window with completed_since."; export const GET_TASKS_MODIFIED_SINCE_DESCRIPTION = "ISO 8601 timestamp. Tasks modified after this instant."; export const GET_TASKS_LIMIT_DESCRIPTION = "Limit results (1-100). Defaults to 50."; export const GET_MY_TASKS_TITLE = "Asana: Get My Tasks"; export const GET_MY_TASKS_DESCRIPTION = `Shortcut for asana_get_tasks with assignee=me. Requires workspace GID (calling with assignee=me alone 400s). Get workspace via asana_get_me. Returns assigned tasks, filtered by completed status ("incomplete" or "completed", default returns both). Use for "my tasks" or "what's on my plate".`; export const GET_MY_TASKS_COMPLETED_DESCRIPTION = "Filter: \"incomplete\" or \"completed\". Omit for both."; // ------------------------------------------------------------- project(s) --- export const GET_PROJECT_TITLE = "Asana: Get Project"; export const GET_PROJECT_DESCRIPTION = `Full detail for project GID. Use AFTER resolving project GID via search. Set include_sections=true to include sections in the response.`; export const GET_PROJECT_GID_DESCRIPTION = "Project GID."; export const GET_PROJECT_INCLUDE_SECTIONS_DESCRIPTION = "Include sections array in response."; export const GET_PROJECT_OPT_FIELDS_DESCRIPTION = "Optional opt_fields (e.g. \"owner.name\", \"members.name\", \"task_counts.completed\")."; export const GET_PROJECTS_TITLE = "Asana: List Projects"; export const GET_PROJECTS_DESCRIPTION = `List projects in workspace or team. Workspace-based by default. Set team to restrict. Set archived=true to include archived projects. Use to list projects before drilling in.`; export const GET_PROJECTS_WORKSPACE_DESCRIPTION = "Workspace GID. Omit if team is passed, or pass together to disambiguate. If neither set, returns projects across all workspaces (noisy)."; export const GET_PROJECTS_TEAM_DESCRIPTION = "Team GID to restrict projects."; export const GET_PROJECTS_ARCHIVED_DESCRIPTION = "Include archived projects."; // ------------------------------------------------------- status_overview --- export const STATUS_OVERVIEW_TITLE = "Asana: Status Overview"; export const STATUS_OVERVIEW_DESCRIPTION = `Aggregated status report for projects or portfolios matched by keyword. Includes details, updates, summaries, blockers. Performs internal search, do NOT call asana_search_objects or asana_get_projects first. Use for "what is the status of X".`; // ----------------------------------------------------------------- writes --- export const CREATE_TASKS_TITLE = "Asana: Create Tasks"; export const CREATE_TASKS_DESCRIPTION = `Create up to 50 tasks. Use AFTER target project GID is known. Resolve assignee/due dates to valid GIDs via asana_search_objects first to avoid errors. Tasks created immediately without confirmation.`; export const CREATE_TASKS_NAME_DESCRIPTION = "Task title. Required. Matched 1:1 with tasks array. Defaults to \"Untitled task\" if omitted."; export const CREATE_TASKS_WORKSPACE_DESCRIPTION = "Workspace GID (e.g. \"1234567890123456\"). Required unless project, assignee, or parent is provided for every task."; export const CREATE_TASKS_TASKS_DESCRIPTION = "Array (1-50) of tasks: name, notes, projects (GID array), section (GID), parent (task GID), assignee (GID or \"me\"), due_on (YYYY-MM-DD), start_on, followers (GID array), custom_fields (object)."; export const UPDATE_TASKS_TITLE = "Asana: Update Tasks"; export const UPDATE_TASKS_DESCRIPTION = `Update up to 50 tasks. Fields: name, assignee, dates, notes, completed (boolean), parent, dependencies, projects, followers, custom_fields. Use for batch updates or closes. Only pass fields to change.`; export const UPDATE_TASKS_TASKS_DESCRIPTION = "Array (1-50) of updates. Each requires task GID (\"gid\") and at least one mutable field (e.g. { \"gid\": \"...\", \"completed\": true })."; export const UPDATE_TASKS_OPT_FIELDS_DESCRIPTION = "Optional opt_fields. Defaults to \"name,completed\"."; export const ADD_COMMENT_TITLE = "Asana: Add Comment"; export const ADD_COMMENT_DESCRIPTION = `Post comment to task. Supports plain text/HTML and @-mentions. Use for discussion, not auto-logged actions.`; export const ADD_COMMENT_TASK_DESCRIPTION = "Target task GID."; export const ADD_COMMENT_TEXT_DESCRIPTION = "Comment body. Plain text default; pass html parameter for markup."; // ----------------------------------------------------- task comments (read) - export const TASK_COMMENTS_TITLE = "Asana: Get Task Comments"; export const TASK_COMMENTS_DESCRIPTION = `Get recent human comments on a task. Comments contain discussion/decisions NOT in description (excludes system events). Use when user asks about discussion or comments on a task. Not inlined in asana_get_task. Defaults to 5 newest (newest first). Long comments truncate at 700 chars; the footer names the story_gid to pass to asana_get_comment for the full body. Filters by Asana's top-level \`type\` field (value "comment"); the \`resource_subtype\` field is an action verb like "comment_added" or "description_changed" and is NOT the discriminator.`; export const TASK_COMMENTS_TASK_GID_DESCRIPTION = "Task GID (e.g. \"1234567890123456\"). Get from workspace, search, or parent task."; export const TASK_COMMENTS_LIMIT_DESCRIPTION = "Count of recent comments to return (1-50). Defaults to 5."; // asana_get_comment: recover the full body of a single truncated comment. // asana_get_task_comments truncates each comment at 700 chars and prints // the story gid in the truncation footer; call THIS tool with that gid to // get the whole text. Mirrors the asana_get_task / asana_get_task_description // pairing for task notes. export const GET_COMMENT_TITLE = "Asana: Get Comment"; export const GET_COMMENT_DESCRIPTION = `Full, untruncated text of a single comment (story). asana_get_task_comments truncates each comment at 700 chars and prints the story gid in the truncation footer; call THIS tool with that story_gid to read the whole body. Returns the comment with NO slice. Use AFTER you already have a story gid from asana_get_task_comments's output.`; export const GET_COMMENT_STORY_GID_DESCRIPTION = "Story (comment) GID, e.g. \"1234567890123456\". Copy it from asana_get_task_comments output, where each comment is labeled \"(story gid: ...)\".";