Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions everyrow-mcp/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@
"name": "everyrow_list_sessions",
"description": "List everyrow sessions owned by the authenticated user (paginated)."
},
{
"name": "everyrow_list_session_tasks",
"description": "List all tasks in a session with their IDs, statuses, and types."
},
{
"name": "everyrow_cancel",
"description": "Cancel a running everyrow task. Use when the user wants to stop a task that is currently processing."
Expand Down
8 changes: 8 additions & 0 deletions everyrow-mcp/src/everyrow_mcp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -741,3 +741,11 @@ class ListSessionsInput(BaseModel):
limit: int = Field(
25, ge=1, le=1000, description="Max sessions per page (default 25, max 1000)"
)


class ListSessionTasksInput(BaseModel):
"""Input for listing tasks in a session."""

model_config = ConfigDict(extra="forbid")

session_id: str = Field(description="The session ID to list tasks for")
53 changes: 53 additions & 0 deletions everyrow-mcp/src/everyrow_mcp/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
ForecastInput,
HttpResultsInput,
ListSessionsInput,
ListSessionTasksInput,
MergeInput,
ProgressInput,
RankInput,
Expand Down Expand Up @@ -1273,6 +1274,58 @@ async def everyrow_balance(ctx: EveryRowContext) -> list[TextContent]:
]


@mcp.tool(
name="everyrow_list_session_tasks",
structured_output=False,
annotations=ToolAnnotations(
title="List Tasks in a Session",
readOnlyHint=True,
destructiveHint=False,
idempotentHint=True,
openWorldHint=False,
),
)
async def everyrow_list_session_tasks(
params: ListSessionTasksInput, ctx: EveryRowContext
) -> list[TextContent]:
"""List all tasks in a session with their IDs, statuses, and types.

Use this to find task IDs for a session so you can display previous results
with mcp__display__show_task(task_id, label).
"""
client = _get_client(ctx)

try:
response = await client.get_async_httpx_client().request(
method="get",
url=f"/sessions/{params.session_id}/tasks",
)
response.raise_for_status()
data = response.json()
except Exception as e:
return [TextContent(type="text", text=f"Error listing session tasks: {e!r}")]

tasks = data.get("tasks", [])
if not tasks:
return [
TextContent(
type="text", text=f"No tasks found in session {params.session_id}."
)
]

lines = [f"Found {len(tasks)} task(s) in session {params.session_id}:\n"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much work would it be to have the input artifact id(s) as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very doable — the input_artifacts and context_artifacts uuid[] columns already exist on the tasks table, and get_tasks_for_session() already fetches them. Just need to add two optional fields to SessionTaskItem and pass them through in the handler (~5 lines in the engine + MCP tool update). Will do as a follow-up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(That was Claude)

for t in tasks:
artifact = (
f" | output_artifact: {t['artifact_id']}" if t.get("artifact_id") else ""
)
lines.append(
f"- **{t['task_type']}** (task_id: {t['task_id']})\n"
f" Status: {t['status']} | Created: {t['created_at']}{artifact}"
Comment on lines +1322 to +1323

This comment was marked as outdated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLAUDE: Not a real issue. These fields (task_id, task_type, status, created_at) are required columns in our tasks table — the API endpoint that serves this data is defined by us in the engine and will always include them. Adding .get() fallbacks for schema-guaranteed fields is unnecessary defensive programming.

The artifact_id uses .get() because it's genuinely nullable — tasks don't have an artifact until processing completes. That's a semantic distinction, not an inconsistency.

👎

)

return [TextContent(type="text", text="\n".join(lines))]


@mcp.tool(
name="everyrow_cancel",
structured_output=False,
Expand Down
1 change: 1 addition & 0 deletions everyrow-mcp/tests/test_mcp_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ async def test_list_tools(self, _http_state):
"everyrow_classify",
"everyrow_dedupe",
"everyrow_forecast",
"everyrow_list_session_tasks",
"everyrow_list_sessions",
"everyrow_merge",
"everyrow_progress",
Expand Down