From 372e30ff6f66f67af813079e9c342563ce885d1a Mon Sep 17 00:00:00 2001 From: Brandon Zhang Date: Fri, 27 Mar 2026 17:54:38 +0800 Subject: [PATCH] Hardcode Python default empty response --- README.md | 13 ++++++------- app/config.py | 7 +++++-- app/database.py | 8 +++++++- app/mcp_server.py | 14 ++------------ app/services/config_service.py | 5 ++--- 5 files changed, 22 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 09c2789..006ba25 100644 --- a/README.md +++ b/README.md @@ -146,15 +146,14 @@ All routes are local-only and intended for `localhost` usage. **Purpose** - Return the next pending instruction, if one exists. -- If none exists, wait for a configurable duration, then return either an empty response or a configured default response. +- If none exists, wait for a configurable duration, then return the server-controlled default response. - Record agent activity so the UI can infer whether an agent is currently connected/recently active. **Suggested input schema** ```json { - "agent_id": "optional-string", - "default_response_override": "optional-string" + "agent_id": "optional-string" } ``` @@ -194,7 +193,7 @@ All routes are local-only and intended for `localhost` usage. "status": "ok", "result_type": "default_response", "instruction": null, - "response": "No new instructions available.", + "response": "call this tool `get_user_request` again to fetch latest user input...", "remaining_pending": 0, "waited_seconds": 10 } @@ -250,7 +249,7 @@ Returns current server and agent summary. }, "settings": { "default_wait_seconds": 10, - "default_empty_response": "No new instructions available.", + "default_empty_response": "call this tool `get_user_request` again to fetch latest user input...", "agent_stale_after_seconds": 30 } } @@ -347,7 +346,7 @@ Returns editable runtime settings. ```json { "default_wait_seconds": 10, - "default_empty_response": "No new instructions available.", + "default_empty_response": "call this tool `get_user_request` again to fetch latest user input...", "agent_stale_after_seconds": 30 } ``` @@ -562,7 +561,7 @@ The server starts on `http://localhost:8000` by default. | `DB_PATH` | `data/local_mcp.sqlite3` | SQLite database path | | `LOG_LEVEL` | `INFO` | Logging level | | `DEFAULT_WAIT_SECONDS` | `10` | Default tool wait timeout | -| `DEFAULT_EMPTY_RESPONSE` | _(empty)_ | Default response when queue is empty | +| `DEFAULT_EMPTY_RESPONSE` | `call this tool \`get_user_request\` again to fetch latest user input...` | Default response when queue is empty | | `AGENT_STALE_AFTER_SECONDS` | `30` | Seconds of inactivity before agent shown as idle | | `MCP_STATELESS` | `true` | `true` for stateless sessions (survives restarts, recommended); `false` for stateful | | `API_TOKEN` | _(empty)_ | When set, all `/api/*` and `/mcp` requests require `Authorization: Bearer `; web UI prompts for the token on first load | diff --git a/app/config.py b/app/config.py index bc24bec..7ac8607 100644 --- a/app/config.py +++ b/app/config.py @@ -9,6 +9,9 @@ import os from dataclasses import dataclass, field +DEFAULT_EMPTY_RESPONSE = "call this tool `get_user_request` again to fetch latest user input..." + + @dataclass class Settings: # Server @@ -23,7 +26,7 @@ class Settings: # MCP / queue behaviour (runtime-editable values are stored in DB; these are defaults for first run) default_wait_seconds: int = 10 - default_empty_response: str = "" + default_empty_response: str = DEFAULT_EMPTY_RESPONSE agent_stale_after_seconds: int = 30 # MCP server name @@ -53,7 +56,7 @@ def load_settings() -> Settings: db_path=os.getenv("DB_PATH", "data/local_mcp.sqlite3"), log_level=os.getenv("LOG_LEVEL", "INFO"), default_wait_seconds=int(os.getenv("DEFAULT_WAIT_SECONDS", "10")), - default_empty_response=os.getenv("DEFAULT_EMPTY_RESPONSE", ""), + default_empty_response=os.getenv("DEFAULT_EMPTY_RESPONSE", DEFAULT_EMPTY_RESPONSE), agent_stale_after_seconds=int(os.getenv("AGENT_STALE_AFTER_SECONDS", "30")), mcp_server_name=os.getenv("MCP_SERVER_NAME", "local-mcp"), mcp_stateless=_parse_bool(os.getenv("MCP_STATELESS", "true"), default=True), diff --git a/app/database.py b/app/database.py index 2c07001..17b1a7a 100644 --- a/app/database.py +++ b/app/database.py @@ -12,6 +12,8 @@ import threading from contextlib import contextmanager from typing import Generator +from app.config import DEFAULT_EMPTY_RESPONSE + logger = logging.getLogger(__name__) # Module-level lock for write serialisation (consume atomicity, settings writes, etc.) @@ -102,7 +104,7 @@ CREATE TABLE IF NOT EXISTS agent_activity ( _DEFAULT_SETTINGS = { "default_wait_seconds": "10", - "default_empty_response": "", + "default_empty_response": DEFAULT_EMPTY_RESPONSE, "agent_stale_after_seconds": "30", } @@ -119,6 +121,10 @@ def _seed_defaults(conn: sqlite3.Connection) -> None: "INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)", (key, value), ) + conn.execute( + "UPDATE settings SET value = ? WHERE key = 'default_empty_response' AND value = ''", + (_DEFAULT_SETTINGS["default_empty_response"],), + ) conn.commit() logger.debug("Default settings seeded") diff --git a/app/mcp_server.py b/app/mcp_server.py index 1589fba..263b579 100644 --- a/app/mcp_server.py +++ b/app/mcp_server.py @@ -13,7 +13,7 @@ from typing import Optional from mcp.server.fastmcp import FastMCP, Context -from app.config import settings +from app.config import DEFAULT_EMPTY_RESPONSE, settings from app.services import config_service, event_service, instruction_service, status_service logger = logging.getLogger(__name__) @@ -38,9 +38,6 @@ _MAX_WAIT_SECONDS = 86400 # 24 hours # multiple keepalive progress updates. DEFAULT_WAIT_SECONDS = 50 -# Default response when queue is empty after waiting -DEFAULT_EMPTY_RESPONSE = "call this tool `get_user_request` again to fetch latest user input..." - # Per-agent generation counter — incremented on every new call. # The wait loop only consumes an instruction when it holds the latest generation, # preventing abandoned (timed-out) coroutines from silently consuming queue items. @@ -65,7 +62,6 @@ KEEPALIVE_INTERVAL_SECONDS: float = 5.0 @mcp.tool() async def get_user_request( agent_id: str = "unknown", - default_response_override: Optional[str] = None, ctx: Optional[Context] = None, ) -> dict: """ @@ -76,8 +72,6 @@ async def get_user_request( Args: agent_id: An identifier for this agent instance (used to track connectivity). - default_response_override: Override the server-default empty response text - for this single call. Returns: A dict with keys: status, result_type, instruction, response, @@ -222,11 +216,7 @@ async def get_user_request( status_service.record_agent_activity(agent_id, "empty") event_service.broadcast("status.changed", {}) - empty_response = ( - default_response_override - if default_response_override is not None - else DEFAULT_EMPTY_RESPONSE - ) + empty_response = DEFAULT_EMPTY_RESPONSE result_type = "default_response" if empty_response else "empty" if _i_am_active(): diff --git a/app/services/config_service.py b/app/services/config_service.py index 61998a6..9ff2379 100644 --- a/app/services/config_service.py +++ b/app/services/config_service.py @@ -7,21 +7,20 @@ from __future__ import annotations import logging +from app.config import DEFAULT_EMPTY_RESPONSE from app.database import get_conn, get_write_conn from app.models import ConfigResponse logger = logging.getLogger(__name__) _SETTING_KEYS = {"default_wait_seconds", "default_empty_response", "agent_stale_after_seconds"} - - def get_config() -> ConfigResponse: with get_conn() as conn: rows = conn.execute("SELECT key, value FROM settings").fetchall() data = {r["key"]: r["value"] for r in rows} return ConfigResponse( default_wait_seconds=int(data.get("default_wait_seconds", 10)), - default_empty_response=data.get("default_empty_response", ""), + default_empty_response=data.get("default_empty_response") or DEFAULT_EMPTY_RESPONSE, agent_stale_after_seconds=int(data.get("agent_stale_after_seconds", 30)), )