-
Notifications
You must be signed in to change notification settings - Fork 9
feat(mcp): surface scheduled mcp_tool tasks before they break (#390) #557
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,10 +21,28 @@ | |
| from ..config_store import ConfigStore | ||
| from ..constants import ENV_CONVERSATION_ID | ||
| from ..errors import KeboolaApiError | ||
| from ..mcp_parity import ( | ||
| MCP_REMOVAL_TARGET_DATE, | ||
| MCP_REMOVAL_VERSION, | ||
| native_equivalent, | ||
| ) | ||
| from ..models import AppConfig | ||
| from .base import ClientFactory, default_client_factory | ||
| from .mcp_service import McpService, ensure_mcp_installed | ||
|
|
||
| # Cap on how many offending items a single check names inline; the rest are | ||
| # summarised as "+N more" and the full list travels in `details` for --json. | ||
| _MAX_LISTED_TASKS = 5 | ||
| AGENTS_FILENAME = "agents.json" | ||
|
|
||
|
|
||
| def _native_command_for(tool: str | None) -> str | None: | ||
| """Native CLI replacement for an MCP tool name, if the parity map knows one.""" | ||
| if not tool: | ||
| return None | ||
| entry = native_equivalent(tool) | ||
| return f"kbagent {entry.command}" if entry is not None else None | ||
|
|
||
|
|
||
| class DoctorService: | ||
| """Business logic for health checks. | ||
|
|
@@ -87,6 +105,9 @@ def run_checks(self) -> dict[str, Any]: | |
| sync_secret_check = self._check_sync_secrets() | ||
| all_checks.append(sync_secret_check) | ||
|
|
||
| mcp_tool_task_check = self._check_mcp_tool_tasks() | ||
| all_checks.append(mcp_tool_task_check) | ||
|
|
||
| # Build summary | ||
| total = len(all_checks) | ||
| passed = sum(1 for c in all_checks if c["status"] == "pass") | ||
|
|
@@ -158,6 +179,83 @@ def _check_sync_secrets(self) -> dict[str, Any]: | |
| ), | ||
| } | ||
|
|
||
| def _check_mcp_tool_tasks(self) -> dict[str, Any]: | ||
| """Check 9: flag scheduled tasks that use the MCP passthrough (epic #390). | ||
|
|
||
| ``agent --type mcp_tool`` is removed in the version named by | ||
| :data:`MCP_REMOVAL_VERSION`. Unlike an interactive ``tool call`` -- which | ||
| warns on every invocation right up to removal -- these tasks live in | ||
| ``agents.json`` and then run unattended, so nobody is present to be | ||
| warned when they break: a cron task simply starts failing. This check is | ||
| the standing reminder in the one command people run when something feels | ||
| off. Read-only: filesystem only, no API call, no MCP spawn. | ||
| """ | ||
| # Local import: keeps the server package off the doctor cold-start path. | ||
| from ..server.agents_store import AgentStore | ||
|
|
||
| agents_path = self._config_store.config_dir / AGENTS_FILENAME | ||
| if not agents_path.exists(): | ||
| return { | ||
| "check": "mcp_tool_tasks", | ||
| "name": "Deprecated mcp_tool agent tasks", | ||
| "status": "skip", | ||
| "message": f"No {AGENTS_FILENAME} in the config dir -- no agent tasks registered.", | ||
| } | ||
|
|
||
| try: | ||
| tasks = AgentStore(config_dir=self._config_store.config_dir).load_tasks() | ||
| except Exception as exc: | ||
| return { | ||
| "check": "mcp_tool_tasks", | ||
| "name": "Deprecated mcp_tool agent tasks", | ||
| "status": "warn", | ||
| "message": f"Could not read {AGENTS_FILENAME}: {exc}", | ||
| } | ||
|
|
||
| affected = [t for t in tasks if getattr(t.action, "type", None) == "mcp_tool"] | ||
| if not affected: | ||
| return { | ||
| "check": "mcp_tool_tasks", | ||
| "name": "Deprecated mcp_tool agent tasks", | ||
| "status": "pass", | ||
| "message": f"No tasks use the deprecated 'mcp_tool' action ({len(tasks)} checked).", | ||
| } | ||
|
Comment on lines
+205
to
+222
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Health check reports a clean result when the scheduled-task file is damaged A damaged list of scheduled tasks is reported as healthy (the Why the warn branch is unreachable and what the user sees
Prompt for agentsWas this helpful? React with 👍 or 👎 to provide feedback. |
||
|
|
||
| shown = ", ".join( | ||
| f"{t.id} ({t.action.params.get('tool', '?')})" for t in affected[:_MAX_LISTED_TASKS] | ||
| ) | ||
| more = ( | ||
| f" (+{len(affected) - _MAX_LISTED_TASKS} more)" | ||
| if len(affected) > _MAX_LISTED_TASKS | ||
| else "" | ||
| ) | ||
| return { | ||
| "check": "mcp_tool_tasks", | ||
| "name": "Deprecated mcp_tool agent tasks", | ||
| "status": "warn", | ||
| "message": ( | ||
| f"{len(affected)} scheduled task(s) use the deprecated 'mcp_tool' action, " | ||
| f"REMOVED in kbagent v{MCP_REMOVAL_VERSION} ({MCP_REMOVAL_TARGET_DATE}): " | ||
| f"{shown}{more}. They run unattended, so they get NO warning at removal -- " | ||
| f"they just start failing. Migrate each to --type cli_command; " | ||
| f"`kbagent tool list` prints the native command per tool." | ||
| ), | ||
| "details": { | ||
| "removal_version": MCP_REMOVAL_VERSION, | ||
| "tasks": [ | ||
| { | ||
| "id": t.id, | ||
| "name": t.name, | ||
| "tool": t.action.params.get("tool"), | ||
| "cron": None if t.manual else t.cron, | ||
| "enabled": t.enabled, | ||
| "native_command": _native_command_for(t.action.params.get("tool")), | ||
| } | ||
| for t in affected | ||
| ], | ||
| }, | ||
| } | ||
|
|
||
| def _check_config_source(self) -> dict[str, Any]: | ||
| """Check 0: Report which config source is active.""" | ||
| return { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔍 Serve REST /agents list does not carry the new deprecation key
The additive
deprecationkey is applied only in the CLI command layer, sokbagent serve's/agentslisting and the Web UI keep the old payload and give no hint about tasks that will break at v0.85.0. That matches the PR description (CLI-only, phase 2 part 1), but it means UI users of scheduled tasks — arguably the population least likely to runkbagent doctor— still get no signal. Worth confirming it is planned for part 2.Was this helpful? React with 👍 or 👎 to provide feedback.