feat(mcp): add read-only tasks MCP tools#60658
Conversation
MCP UI Apps size report
|
Query snapshots: Backend query snapshots updatedChanges: 1 snapshots (1 modified, 0 added, 0 deleted) What this means:
Next steps:
|
|
Size Change: 0 B Total Size: 80.9 MB ℹ️ View Unchanged
|
Query snapshots: Backend query snapshots updatedChanges: 1 snapshots (1 modified, 0 added, 0 deleted) What this means:
Next steps:
|
36e0e87 to
c92c4e8
Compare
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
services/mcp/tests/unit/__snapshots__/tool-schemas/common/tasks-runs-session-logs-retrieve.json:24-30
**Stale description contradicts overridden default**
The description says `"default 1000, max 5000"` but `param_overrides` in `tools.yaml` sets the MCP default to `100`, which is correctly reflected in `"default": 100`. An AI agent that reads the description to understand the parameter may expect 1000 log entries when it omits `limit`, but will actually receive 100. Consider adding a note to the tool-level `description` in `tools.yaml` clarifying the effective MCP default (e.g., "defaults to 100 in this tool"), or supporting a `param_overrides.limit.description` override in the codegen so the rendered schema stays self-consistent.
Reviews (1): Last reviewed commit: "chore: update OpenAPI generated types" | Re-trigger Greptile |
| "limit": { | ||
| "default": 100, | ||
| "description": "Maximum number of entries to return (default 1000, max 5000)", | ||
| "maximum": 5000, | ||
| "minimum": 1, | ||
| "type": "number" | ||
| }, |
There was a problem hiding this comment.
Stale description contradicts overridden default
The description says "default 1000, max 5000" but param_overrides in tools.yaml sets the MCP default to 100, which is correctly reflected in "default": 100. An AI agent that reads the description to understand the parameter may expect 1000 log entries when it omits limit, but will actually receive 100. Consider adding a note to the tool-level description in tools.yaml clarifying the effective MCP default (e.g., "defaults to 100 in this tool"), or supporting a param_overrides.limit.description override in the codegen so the rendered schema stays self-consistent.
Prompt To Fix With AI
This is a comment left during a code review.
Path: services/mcp/tests/unit/__snapshots__/tool-schemas/common/tasks-runs-session-logs-retrieve.json
Line: 24-30
Comment:
**Stale description contradicts overridden default**
The description says `"default 1000, max 5000"` but `param_overrides` in `tools.yaml` sets the MCP default to `100`, which is correctly reflected in `"default": 100`. An AI agent that reads the description to understand the parameter may expect 1000 log entries when it omits `limit`, but will actually receive 100. Consider adding a note to the tool-level `description` in `tools.yaml` clarifying the effective MCP default (e.g., "defaults to 100 in this tool"), or supporting a `param_overrides.limit.description` override in the codegen so the rendered schema stays self-consistent.
How can I resolve this? If you propose a fix, please make it concise.
PR overviewAll previously flagged issues have been addressed. No open security concerns remain on this pull request. Security reviewNo open security issues remain on this pull request. Fixed/addressed: 1 · PR risk: 0/10 |
|
The new commit (
// tasksRunsRetrieve
const filtered = omitResponseFields(result, [
'log_url',
'state.sandbox_connect_token',
'state.sandbox_url',
]) as typeof result
// tasksRetrieve
const filtered = omitResponseFields(result, [
'latest_run.log_url',
'latest_run.state.sandbox_connect_token',
'latest_run.state.sandbox_url',
]) as typeof resultA |
| "limit": { | ||
| "default": 100, | ||
| "description": "Maximum number of entries to return (default 1000, max 5000)", | ||
| "maximum": 5000, | ||
| "minimum": 1, | ||
| "type": "number" | ||
| }, |
There was a problem hiding this comment.
Inconsistency between the description text and the actual schema default value. The description says "default 1000" but the schema defines "default": 100. This mismatch will confuse API consumers.
**Fix**: Update the description to match the actual default:
"Maximum number of entries to return (default 100, max 5000)"
Or if 1000 is the correct default, update the schema default value to 1000.| "limit": { | |
| "default": 100, | |
| "description": "Maximum number of entries to return (default 1000, max 5000)", | |
| "maximum": 5000, | |
| "minimum": 1, | |
| "type": "number" | |
| }, | |
| "limit": { | |
| "default": 100, | |
| "description": "Maximum number of entries to return (default 100, max 5000)", | |
| "maximum": 5000, | |
| "minimum": 1, | |
| "type": "number" | |
| }, | |
Spotted by Graphite
Is this helpful? React 👍 or 👎 to let us know.
Query snapshots: Backend query snapshots updatedChanges: 1 snapshots (1 modified, 0 added, 0 deleted) What this means:
Next steps:
|
Exposes the Tasks read surface to MCP clients: tasks-list, tasks-retrieve, tasks-runs-list, tasks-runs-retrieve, and tasks-runs-session-logs-retrieve. Backend changes are minimal — schema-aware LimitOffsetPagination so default and max limits surface in the OpenAPI spec, help_text on TaskSerializer fields so generated tool parameters are self-describing, and an extra 'tasks' tag on TaskRunViewSet so the scaffolder picks up nested run operations. Carved out from #56013, which also adds the write operations.
The Tasks API is protected at the call layer by TasksAccessPermission (checks the tasks feature flag OR a redeemed CodeInvite). Without feature_flag on the YAML the tools were still advertised to every MCP client and would only fail on call. Adding feature_flag: tasks hides them from the tool catalog at MCP init time for users who don't have access.
The presigned S3 URL embeds an auth token. Drop it from tasks-retrieve (latest_run.log_url) and tasks-runs-retrieve (log_url) so the token never reaches MCP clients.
sandbox_connect_token is a JWT for direct sandbox auth and sandbox_url is the addressable endpoint. Together a task:read client could connect to a live sandbox. Drop both from tasks-retrieve (via latest_run.state) and tasks-runs-retrieve. tasks-runs-list is already safe because its include whitelist surfaces only state.sandbox_environment_id.
The tasks-runs-retrieve description previously claimed state contains sandbox_environment_id (implying that as the noteworthy field) without mentioning that sandbox_connect_token and sandbox_url are omitted. Update both retrieve descriptions to state which fields are stripped.
…de default The MCP tool overrides the serializer default of 1000 down to 100 to keep agent responses small, but the upstream Zod describe() still said "default 1000". Override the description alongside the default so the schema and the text match.
9217026 to
acc3f07
Compare
Query snapshots: Backend query snapshots updatedChanges: 5 snapshots (0 modified, 5 added, 0 deleted) What this means:
Next steps:
|
Problem
The Tasks product still has no MCP tool definitions on master, so coding agents cannot read tasks or inspect task runs via the MCP server. This PR ships the read-only surface only — write operations (create / update / run) are intentionally out of scope and tracked in #56013.
Changes
Enabled MCP tools (
products/tasks/mcp/tools.yaml)tasks-listGET /api/projects/:project_id/tasks/tasks-retrieveGET /api/projects/:project_id/tasks/:id/tasks-runs-listGET /api/projects/:project_id/tasks/:task_id/runs/tasks-runs-retrieveGET /api/projects/:project_id/tasks/:task_id/runs/:id/tasks-runs-session-logs-retrieveGET /api/projects/:project_id/tasks/:task_id/runs/:id/session_logs/All require the
task:readscope; all are flaggedreadOnly: true, destructive: false, idempotent: true. The remaining 34 operations are scaffolded into the YAML asenabled: falseto keep--sync-allidempotent.Backend (
products/tasks/backend/)TasksPagination(LimitOffsetPaginationsubclass) that surfacesdefault_limit/max_limit/minimumin the generated OpenAPI schema, wired intoTaskViewSetandTaskRunViewSet.help_textonTaskSerializer.title,description,origin_product, andrepository— these flow into the Zod.describe()calls in the generated tool so agents see self-describing parameters."tasks"toTaskRunViewSet's@extend_schema(tags=...)so the MCP scaffolder discovers the nested run operations viax-explicit-tags.Codegen output
hogli build:openapiis idempotent on a second run. Generated diff covers the tasks frontend client, the MCP schema JSON, and the newservices/mcp/src/generated/tasks/api.ts+services/mcp/src/tools/generated/tasks.ts.products/logs/frontend/generated/api.tsshrinks by one operation becausetasks_runs_logs_retrievenow lives under thetaskstag.How did you test this code?
hogli build:openapiend-to-end — passes, idempotent on re-run, zero drift.pnpm --filter=@posthog/mcp test— 1302 tests pass, 5 new snapshots written for the new tools. The 9 failing test files are a pre-existing@shared/guidelines.mdresolution issue on master, unrelated to this PR.Publish to changelog?
no
Docs update
skip-inkeep-docs
🤖 Agent context
Authored by Claude Code (Opus 4.7).