feat: add workboard_get_team_objectives_tool for date-range-scoped OKR snapshots#24
feat: add workboard_get_team_objectives_tool for date-range-scoped OKR snapshots#24ghelleks wants to merge 2 commits into
Conversation
…R snapshots The existing workboard_get_objectives_tool fetches objectives by owner user ID and silently excludes those with a past target date, making it useless for quarterly OKR snapshots after quarter-end (e.g. Q1 objectives vanish after March 31). This adds workboard_get_team_objectives_tool, which calls the goalSummary endpoint used by the WorkBoard web UI. The date range is passed directly to the API so past-quarter objectives are returned correctly. New public surface: - errors.py: InvalidTeamIdError - models.py: validate_team_id, validate_mm_dd_yyyy (MM/DD/YYYY validator) - tools/objectives.py: get_team_objectives (GET /goal/goalSummary) - tools/__init__.py: export get_team_objectives - server.py: workboard_get_team_objectives_tool MCP wrapper + instructions update Tests: mock-based tests for get_team_objectives (happy path, get_nested_teams=False, invalid team_id, invalid date format); validate_team_id and validate_mm_dd_yyyy unit tests; test_tool_count bumped from 22 → 23.
There was a problem hiding this comment.
Code Review
This pull request introduces a new tool, workboard_get_team_objectives_tool, which allows fetching team-scoped objectives within a specific date range using the WorkBoard goalSummary API. The changes include new error classes, validation logic for team IDs and date formats (MM/DD/YYYY), and updated unit tests. Review feedback highlights the need to align parameter names in the tool's documentation with the implementation, ensure docstring field names match the actual output, and include an enrichment step for key result target dates to maintain consistency with other objective-related tools.
| "effort, due date, or owner.\n\n" | ||
| "TEAM OKR SNAPSHOTS: For quarterly OKR reviews or weekly snapshots that include " | ||
| "past-quarter objectives, use workboard_get_team_objectives_tool with a team_id " | ||
| "and explicit MM/DD/YYYY date bounds (e.g. startDate='01/01/2026', endDate='03/31/2026'). " |
There was a problem hiding this comment.
The parameter names in the example instructions (startDate, endDate) do not match the actual tool parameter names (start_date, end_date). This will cause the LLM to provide incorrect arguments when calling the tool.
| "and explicit MM/DD/YYYY date bounds (e.g. startDate='01/01/2026', endDate='03/31/2026'). " | |
| "and explicit MM/DD/YYYY date bounds (e.g. start_date='01/01/2026', end_date='03/31/2026'). " |
|
|
||
| Returns: | ||
| List of objectives with name, owner, status_color, progress, and | ||
| nested key_results (name, progress, last_updated, target). |
There was a problem hiding this comment.
The docstring lists target as a field in key_results, but the underlying _format_metric function returns this field as target_date. This discrepancy can lead to confusion for users or AI models consuming the tool output.
| nested key_results (name, progress, last_updated, target). | |
| nested key_results (name, progress, last_updated, target_date). |
| response = await client.get("/goal/goalSummary", params=params) | ||
|
|
||
| body = response.get("data", {}) | ||
| goals = _extract_goals_from_goal_response(body) | ||
|
|
||
| return {"objectives": [_format_goal(g) for g in goals]} |
There was a problem hiding this comment.
This tool is missing the enrichment step for key result target dates. Other objective-related tools in this module (like get_objectives) fetch a target date map from the /metric endpoint because the goal summary response typically leaves these fields empty. Without this, the target_date field in the returned key results will likely be empty.
| response = await client.get("/goal/goalSummary", params=params) | |
| body = response.get("data", {}) | |
| goals = _extract_goals_from_goal_response(body) | |
| return {"objectives": [_format_goal(g) for g in goals]} | |
| response, metric_target_dates = await asyncio.gather( | |
| client.get("/goal/goalSummary", params=params), | |
| _fetch_target_date_map(client), | |
| ) | |
| body = response.get("data", {}) | |
| goals = _extract_goals_from_goal_response(body) | |
| return {"objectives": [_format_goal(g, metric_target_dates) for g in goals]} |
|
Tracking issue for this work (including WorkBoard API blocker): #25 |
Problem
workboard_get_objectives_toolfetches objectives by owner user ID and silentlyfilters out any objective whose
goal_target_dateis in the past. This meansQ1 objectives disappear after March 31, making the tool unreliable for weekly
OKR snapshots or any cross-quarter review.
The WorkBoard web UI avoids this by using a team-scoped, date-range-filtered
endpoint instead:
Solution
Adds
workboard_get_team_objectives_tool, a new read-only MCP tool that wrapsthis endpoint. The date window is owned entirely by the API — no client-side
expiry filtering is applied — so objectives from any quarter are returned
correctly.
New public surface
errors.pyInvalidTeamIdErrormodels.pyvalidate_team_id,validate_mm_dd_yyyy(MM/DD/YYYY format validator)tools/objectives.pyget_team_objectives— callsGET /goal/goalSummarywith fixed paramstools/__init__.pyget_team_objectivesserver.pyworkboard_get_team_objectives_toolMCP wrapper + instructions noteTool signature
Returns
{"objectives": [...]}where each item includesname,owner,status_color,progress, andkey_results(name, progress, last_updated).Fixed query params
team_people1team_s0userTeamType2status1performance4,3,2,1,0exDate1Tests
(
userTeamid,startDate,endDate,performance,exDate), andnormalized output fields.
get_nested_teams=Falsepasses"false"string to API.team_idraisesInvalidTeamIdError.ValidationError.validate_team_idandvalidate_mm_dd_yyyy.test_tool_countbumped from 22 → 23.All 112 existing tests continue to pass.