pyp6xer_float_analysis
Analyze total float distribution across schedule activities, grouping them into float buckets and identifying near-critical activities to assess schedule risk.
Instructions
Analyse total float distribution across activities.
Groups activities into float buckets and flags near-critical activities.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| cache_key | No | Cache key identifying the loaded XER file (set when calling pyp6xer_load_file) | default |
| proj_id | No | Project ID or short name; uses first project if omitted | |
| max_float_days | No | Upper bound for float display in days |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- server.py:755-820 (handler)The main handler function for the pyp6xer_float_analysis tool. It retrieves the XER schedule from cache, filters to non-completed/non-LOE/non-WBS tasks with float data, buckets them by total float ranges (negative, zero, 0-5, 5-15, 15-30, over_30), and returns distribution stats including min/max/avg float.
@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=False)) def pyp6xer_float_analysis( cache_key: Annotated[str, Field(description="Cache key identifying the loaded XER file (set when calling pyp6xer_load_file)")] = "default", proj_id: Annotated[str | None, Field(description="Project ID or short name; uses first project if omitted")] = None, max_float_days: Annotated[int, Field(description="Upper bound for float display in days", ge=0)] = 30, ctx: Context = None, ) -> str: """Analyse total float distribution across activities. Groups activities into float buckets and flags near-critical activities. Args: cache_key: Cache key of the loaded file. proj_id: Optional project filter. max_float_days: Upper bound for detailed buckets (default 30). """ xer = _get_xer(ctx, cache_key) tasks = _get_tasks(xer, proj_id) # Only non-completed, non-LOE tasks relevant = [ t for t in tasks if not t.status.is_completed and not t.type.is_loe and not t.type.is_wbs and t.total_float is not None ] buckets = { "negative": [], "zero": [], "0_to_5": [], "5_to_15": [], "15_to_30": [], "over_30": [], } for t in relevant: f = t.total_float if f < 0: buckets["negative"].append(t.task_code) elif f == 0: buckets["zero"].append(t.task_code) elif f <= 5: buckets["0_to_5"].append(t.task_code) elif f <= 15: buckets["5_to_15"].append(t.task_code) elif f <= max_float_days: buckets["15_to_30"].append(t.task_code) else: buckets["over_30"].append(t.task_code) floats = [t.total_float for t in relevant] return json.dumps({ "analyzed_activities": len(relevant), "total_activities": len(tasks), "distribution": {k: len(v) for k, v in buckets.items()}, "negative_float_activities": buckets["negative"], "zero_float_activities": buckets["zero"][:50], # cap for readability "stats": { "min_float_days": min(floats) if floats else None, "max_float_days": max(floats) if floats else None, "avg_float_days": round(sum(floats) / len(floats), 1) if floats else None, }, }, indent=2) - server.py:755-761 (registration)Tool registration via the @mcp.tool decorator from FastMCP. The annotations mark it as read-only, non-destructive, idempotent, and not open-world.
@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=False)) def pyp6xer_float_analysis( cache_key: Annotated[str, Field(description="Cache key identifying the loaded XER file (set when calling pyp6xer_load_file)")] = "default", proj_id: Annotated[str | None, Field(description="Project ID or short name; uses first project if omitted")] = None, max_float_days: Annotated[int, Field(description="Upper bound for float display in days", ge=0)] = 30, ctx: Context = None, ) -> str: - server.py:756-761 (schema)Input schema/parameters: cache_key (str, default 'default'), proj_id (optional str), max_float_days (int, default 30, min 0), and ctx (FastMCP Context). Output type is str (JSON).
def pyp6xer_float_analysis( cache_key: Annotated[str, Field(description="Cache key identifying the loaded XER file (set when calling pyp6xer_load_file)")] = "default", proj_id: Annotated[str | None, Field(description="Project ID or short name; uses first project if omitted")] = None, max_float_days: Annotated[int, Field(description="Upper bound for float display in days", ge=0)] = 30, ctx: Context = None, ) -> str: