diff --git a/backend/__tests__/fixtures/hostedMcp/asana-tools-list-2026-10-19.json b/backend/__tests__/fixtures/hostedMcp/asana-tools-list-2026-10-19.json new file mode 100644 index 0000000000..42d3373831 --- /dev/null +++ b/backend/__tests__/fixtures/hostedMcp/asana-tools-list-2026-10-19.json @@ -0,0 +1,4206 @@ +{ + "server": "https://mcp.asana.com/v2/mcp", + "capturedAt": "2026-10-19T07:15:23.899Z", + "scopeRequested": "default", + "grantedScope": "default identity", + "toolCount": 39, + "tools": [ + { + "name": "get_workspace_agents", + "title": "List agents in workspace", + "description": "Returns a list of AI Teammate agents (automated agents, not human users) configured in a workspace. AI Teammates are Asana-specific automation agents — they are distinct from human coworkers, teammates, or workspace members. Do NOT use this tool when the user asks about people, users, teammates, or team members; use get_users or search_objects instead. If you have a name or description to search against, use search_objects with resource_type='agent' instead. Each agent record includes GID, name, resource_type, and resource_subtype. Use to discover available AI Teammate agents before fetching full details with get_agent. Supports pagination for workspaces with many agents.", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "description": "Results per page (1-100).", + "type": "number", + "minimum": 1, + "maximum": 100 + }, + "offset": { + "description": "Pagination offset token from a previous response.", + "type": "string" + }, + "opt_fields": { + "description": "Comma-separated list of optional fields to include in the response. Name each subfield explicitly as parent.child; wildcard and glob syntax such as parent.* is not supported. Available fields: gid, resource_type, resource_subtype, name.", + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema" + }, + "annotations": { + "title": "List agents in workspace", + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false + } + }, + { + "name": "get_agent", + "title": "Get agent", + "description": "Returns the full record for a single AI Teammate agent by GID. Includes name, description, behavior_guidance, workspace, and photo URLs. Use get_workspace_agents first to discover agent GIDs, then call this tool for full details.", + "inputSchema": { + "type": "object", + "properties": { + "agent_gid": { + "type": "string", + "description": "Globally unique identifier for the agent." + }, + "opt_fields": { + "description": "Comma-separated list of optional fields to include in the response. Name each subfield explicitly as parent.child; wildcard and glob syntax such as parent.* is not supported. Available fields: gid, resource_type, resource_subtype, name, description, behavior_guidance, workspace, photo.", + "type": "string" + } + }, + "required": [ + "agent_gid" + ], + "$schema": "https://json-schema.org/draft/2020-12/schema" + }, + "annotations": { + "title": "Get agent", + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false + } + }, + { + "name": "get_attachments", + "title": "Get attachments for object", + "description": "List all attachments for a project, project brief, or task. By default, returns attachment names, IDs, and URLs (download_url, permanent_url, view_url). To expose other attachment fields use opt_fields. Use for accessing files attached to Asana objects. Supports pagination for objects with many attachments.", + "inputSchema": { + "type": "object", + "properties": { + "parent": { + "type": "string", + "description": "The GID of the parent project, project_brief, or task to retrieve attachments for. Must be a GID for a project, project_brief, or task." + }, + "limit": { + "description": "Results per page (1-100)", + "type": "number", + "minimum": 1, + "maximum": 100 + }, + "offset": { + "description": "Pagination offset token", + "type": "string" + }, + "opt_fields": { + "description": "Comma-separated list of optional fields to include in the response. Name each subfield explicitly as parent.child; wildcard and glob syntax such as parent.* is not supported.", + "type": "string" + } + }, + "required": [ + "parent" + ], + "$schema": "https://json-schema.org/draft/2020-12/schema" + }, + "annotations": { + "title": "Get attachments for object", + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false + } + }, + { + "name": "create_task_from_template", + "title": "Create task from template", + "description": "List a project's task templates or create a task from one. Provide project_id alone to return all templates without creating. To create, provide either task_template_id, or template_name with project_id. When given a name, this tool searches that project's task templates and creates only when one case-insensitive exact match exists. Task instantiation starts asynchronously. Tell the user it has started and may still be in progress, then ask what they want to do next.", + "inputSchema": { + "type": "object", + "properties": { + "task_template_id": { + "description": "Globally unique identifier for the task template to instantiate. Provide this or template_name, but not both. Omit both to list every template in project_id without creating.", + "type": "string", + "minLength": 1 + }, + "template_name": { + "description": "Exact task template name to find within project_id. Matching ignores letter case. Provide this or task_template_id to create, or omit both to list every template in project_id.", + "type": "string", + "minLength": 1 + }, + "project_id": { + "description": "Globally unique identifier for the project whose task templates to search or list. Required when task_template_id is omitted.", + "type": "string", + "minLength": 1 + }, + "name": { + "description": "Name for the created task. When omitted, the task uses the template name.", + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema" + }, + "annotations": { + "title": "Create task from template", + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": false + } + }, + { + "name": "create_project_from_template", + "title": "Create project from template", + "description": "Create a project from a project template. If the user provides a template name instead of a GID, first use search_objects with resource_type 'project_template' and query set to that name. If search_objects requires workspace_gid and the user did not specify a workspace, call get_me to discover their workspaces. Use the only workspace automatically. If there are multiple workspaces, ask the user to choose by name; never ask them for a workspace GID. If the user wants the project in the template's team, request team.gid from search_objects and pass it as team. Omitting team does not inherit the template's team and may create a teamless project. Project instantiation starts asynchronously. Tell the user it has started and may still be in progress. Use the returned new_project GID for follow-up work, such as adding tasks or editing the project, and ask what they want to do next.", + "inputSchema": { + "type": "object", + "properties": { + "project_template_id": { + "type": "string", + "description": "Globally unique identifier for the project template. If the user provided a template name, resolve it with search_objects as described above." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "Name of the new project. Must contain a non-whitespace character." + }, + "team": { + "description": "Globally unique identifier for the new project's team. Omitting this does not inherit the template's team and may create a teamless project. Pass the template's team GID explicitly when that placement is desired.", + "type": "string" + }, + "privacy_setting": { + "description": "Privacy setting for the new project. private_to_team is deprecated; use POST /memberships to share a private project with a team after creation.", + "type": "string", + "enum": [ + "public_to_workspace", + "private_to_team", + "private" + ] + }, + "is_strict": { + "description": "When true, fail if a template date variable has no requested date. When false or omitted, use default dates for missing values.", + "type": "boolean" + }, + "requested_dates": { + "description": "Mappings from template date variables to calendar dates.", + "type": "array", + "items": { + "type": "object", + "properties": { + "gid": { + "type": "string", + "description": "Globally unique identifier for the template's date variable." + }, + "value": { + "type": "string", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "description": "Calendar date in YYYY-MM-DD format." + } + }, + "required": [ + "gid", + "value" + ] + } + }, + "requested_roles": { + "description": "Mappings from template roles to user identifiers.", + "type": "array", + "items": { + "type": "object", + "properties": { + "gid": { + "type": "string", + "description": "Globally unique identifier for the template role." + }, + "value": { + "type": "string", + "description": "User identifier to assign to the role. Can be \"me\", an email address, or a user GID." + } + }, + "required": [ + "gid", + "value" + ] + } + } + }, + "required": [ + "project_template_id", + "name" + ], + "$schema": "https://json-schema.org/draft/2020-12/schema" + }, + "annotations": { + "title": "Create project from template", + "readOnlyHint": false, + "destructiveHint": false, + "idempotentHint": false, + "openWorldHint": false + } + }, + { + "name": "get_portfolio", + "title": "Get portfolio", + "description": "Get detailed portfolio data by ID including name, owner, and projects. Use after finding portfolio ID via search_objects. Returns complete portfolio configuration. Essential for understanding portfolio context and content.", + "inputSchema": { + "type": "object", + "properties": { + "portfolio_gid": { + "type": "string", + "description": "Globally unique identifier for the portfolio" + }, + "opt_fields": { + "description": "Comma-separated list of optional fields to include in the response. Name each subfield explicitly as parent.child; wildcard and glob syntax such as parent.* is not supported.", + "type": "string" + } + }, + "required": [ + "portfolio_gid" + ], + "$schema": "https://json-schema.org/draft/2020-12/schema" + }, + "annotations": { + "title": "Get portfolio", + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false + } + }, + { + "name": "get_portfolios", + "title": "Get portfolios", + "description": "List portfolios in workspace owned by the current user. REQUIRES workspace parameter. Returns portfolio names and IDs for portfolios you own. Use for portfolio discovery and management. Supports pagination for workspaces with many portfolios.", + "inputSchema": { + "type": "object", + "properties": { + "limit": { + "description": "Results per page (1-100)", + "type": "number", + "minimum": 1, + "maximum": 100 + }, + "offset": { + "description": "Pagination offset token", + "type": "string" + }, + "opt_fields": { + "description": "Comma-separated list of optional fields to include in the response. Name each subfield explicitly as parent.child; wildcard and glob syntax such as parent.* is not supported.", + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema" + }, + "annotations": { + "title": "Get portfolios", + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false + } + }, + { + "name": "get_items_for_portfolio", + "title": "Get items for portfolio", + "description": "List projects, goals, and other items in a portfolio. Returns item names, IDs, and types. Use for portfolio content exploration and management. Supports pagination for portfolios with many items.", + "inputSchema": { + "type": "object", + "properties": { + "portfolio_gid": { + "type": "string", + "description": "Globally unique identifier for the portfolio" + }, + "limit": { + "description": "Results per page (1-100)", + "type": "number", + "minimum": 1, + "maximum": 100 + }, + "offset": { + "description": "Pagination offset token", + "type": "string" + }, + "opt_fields": { + "description": "Comma-separated list of optional fields to include in the response. Name each subfield explicitly as parent.child; wildcard and glob syntax such as parent.* is not supported.", + "type": "string" + } + }, + "required": [ + "portfolio_gid" + ], + "$schema": "https://json-schema.org/draft/2020-12/schema" + }, + "annotations": { + "title": "Get items for portfolio", + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false + } + }, + { + "name": "get_project", + "title": "Get project", + "description": "Get detailed project data including name, description, owner, members, and current status. Also returns task counts (num_tasks, num_incomplete_tasks, num_completed_tasks) and optionally sections. A null task_counts or sections value means the data could not be retrieved and should not be interpreted as zero or empty.", + "inputSchema": { + "type": "object", + "properties": { + "project_id": { + "type": "string", + "description": "Globally unique identifier for the project." + }, + "include_sections": { + "default": false, + "description": "Set to true to include the project's sections (up to 50) in the response.", + "type": "boolean" + }, + "opt_fields": { + "description": "Comma-separated list of optional fields to include in the response. Name each subfield explicitly as parent.child; wildcard and glob syntax such as parent.* is not supported.", + "type": "string" + } + }, + "required": [ + "project_id" + ], + "$schema": "https://json-schema.org/draft/2020-12/schema" + }, + "annotations": { + "title": "Get project", + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false + } + }, + { + "name": "get_project_internal", + "title": "[Internal] Get project", + "description": "DEPRECATED; DO NOT USE. This tool must exist so that it can be invoked programmatically by widget UI, but it should not be called directly by the model.", + "inputSchema": { + "type": "object", + "properties": { + "project_id": { + "type": "string", + "description": "Globally unique identifier for the project." + }, + "include_sections": { + "default": false, + "description": "Set to true to include the project's sections (up to 50) in the response.", + "type": "boolean" + }, + "opt_fields": { + "description": "Comma-separated list of optional fields to include in the response. Name each subfield explicitly as parent.child; wildcard and glob syntax such as parent.* is not supported.", + "type": "string" + }, + "widget_id": { + "type": "string", + "description": "ALWAYS USE A CONSTANT VALUE OF 'model-generated-widget-id' FOR THIS PARAMETER." + } + }, + "required": [ + "project_id", + "widget_id" + ], + "$schema": "https://json-schema.org/draft/2020-12/schema" + }, + "annotations": { + "title": "[Internal] Get project", + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false + }, + "_meta": { + "ui": { + "visibility": [ + "app" + ] + } + } + }, + { + "name": "get_projects", + "title": "Get projects", + "description": "List projects in a workspace, optionally filtered by team. Returns project names, IDs, and task counts (num_tasks, num_incomplete_tasks, num_completed_tasks) by default. To expose other project fields, such as members or owner, use opt_fields. Prefer using search_objects with resource_type='project' for name-based searches instead of get_projects and filtering through the results. A null task_counts value means counts could not be retrieved and should not be interpreted as zero.", + "inputSchema": { + "type": "object", + "properties": { + "team": { + "description": "When passed in, filters projects on team GID", + "type": "string" + }, + "archived": { + "default": false, + "description": "Include archived projects.", + "type": "boolean" + }, + "opt_fields": { + "description": "Comma-separated list of optional fields to include in the response. Name each subfield explicitly as parent.child; wildcard and glob syntax such as parent.* is not supported.", + "type": "string" + }, + "limit": { + "default": 20, + "description": "Number of projects to return per page (1-100). Defaults to 20.", + "type": "number", + "minimum": 1, + "maximum": 100 + }, + "offset": { + "description": "Pagination offset token from the next_page field of a previous response. If omitted, returns the first page of results.", + "type": "string" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema" + }, + "annotations": { + "title": "Get projects", + "readOnlyHint": true, + "destructiveHint": false, + "idempotentHint": true, + "openWorldHint": false + } + }, + { + "name": "create_project_status_update", + "title": "Create project status update", + "description": "Post a status update to a project or portfolio. Use for project health updates, milestone documentation, or blocker reporting. Returns created status with gid, parent, title, status_type, author, created_at, permalink_url. Exactly one of text or html_text must be provided (omit the other in the request).", + "inputSchema": { + "type": "object", + "properties": { + "parent": { + "type": "string", + "description": "GID of the project or portfolio to post the status update to." + }, + "title": { + "type": "string", + "description": "Status title" + }, + "text": { + "description": "Plain-text status body. Exactly one of text or html_text must be provided.", + "type": "string" + }, + "html_text": { + "description": "Exactly one of text or html_text must be provided. Allowed elements only: , , , , , ,
    ,