Hardcode Go default empty response

This commit is contained in:
Brandon Zhang
2026-03-27 17:55:05 +08:00
parent 372e30ff6f
commit f437f6939c
6 changed files with 26 additions and 59 deletions

View File

@@ -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 <token>`; web UI prompts for the token on first load |

View File

@@ -23,7 +23,6 @@ func handleUpdateConfig(stores Stores, broker *events.Broker) http.HandlerFunc {
// Decode partial patch
var patch struct {
DefaultWaitSeconds *int `json:"default_wait_seconds"`
DefaultEmptyResponse *string `json:"default_empty_response"`
AgentStaleAfterSeconds *int `json:"agent_stale_after_seconds"`
}
if err := json.NewDecoder(r.Body).Decode(&patch); err != nil {
@@ -42,9 +41,6 @@ func handleUpdateConfig(stores Stores, broker *events.Broker) http.HandlerFunc {
if patch.DefaultWaitSeconds != nil {
current.DefaultWaitSeconds = *patch.DefaultWaitSeconds
}
if patch.DefaultEmptyResponse != nil {
current.DefaultEmptyResponse = *patch.DefaultEmptyResponse
}
if patch.AgentStaleAfterSeconds != nil {
current.AgentStaleAfterSeconds = *patch.AgentStaleAfterSeconds
}

View File

@@ -6,6 +6,8 @@ import (
"strconv"
)
const DefaultEmptyResponse = "call this tool `get_user_request` again to fetch latest user input..."
// Config holds all runtime configuration values for local-mcp.
type Config struct {
Host string
@@ -13,7 +15,6 @@ type Config struct {
DBPath string
LogLevel string
DefaultWaitSeconds int
DefaultEmptyResponse string
AgentStaleAfterSeconds int
MCPStateless bool
APIToken string
@@ -27,7 +28,6 @@ func Load() Config {
DBPath: getEnv("DB_PATH", "data/local_mcp.sqlite3"),
LogLevel: getEnv("LOG_LEVEL", "INFO"),
DefaultWaitSeconds: getEnvInt("DEFAULT_WAIT_SECONDS", 10),
DefaultEmptyResponse: getEnv("DEFAULT_EMPTY_RESPONSE", ""),
AgentStaleAfterSeconds: getEnvInt("AGENT_STALE_AFTER_SECONDS", 30),
MCPStateless: getEnvBool("MCP_STATELESS", true),
APIToken: getEnv("API_TOKEN", ""),
@@ -58,4 +58,3 @@ func getEnvBool(key string, defaultVal bool) bool {
}
return defaultVal
}

View File

@@ -40,11 +40,10 @@ CREATE TABLE IF NOT EXISTS agent_activity (
`
// defaultSettings seeds initial values; OR IGNORE means existing rows are unchanged.
const defaultSettings = `
INSERT OR IGNORE INTO settings (key, value) VALUES ('default_wait_seconds', '10');
INSERT OR IGNORE INTO settings (key, value) VALUES ('default_empty_response', '');
INSERT OR IGNORE INTO settings (key, value) VALUES ('agent_stale_after_seconds','30');
`
const defaultSettings = "" +
"INSERT OR IGNORE INTO settings (key, value) VALUES ('default_wait_seconds', '10');\n" +
"INSERT OR IGNORE INTO settings (key, value) VALUES ('agent_stale_after_seconds', '30');\n" +
"DELETE FROM settings WHERE key = 'default_empty_response';\n"
// Open opens (creating if necessary) a SQLite database at dbPath, applies the
// schema, and seeds default settings.
@@ -74,4 +73,3 @@ func Open(dbPath string) (*sql.DB, error) {
return db, nil
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"github.com/local-mcp/local-mcp-go/internal/config"
"github.com/local-mcp/local-mcp-go/internal/events"
"github.com/local-mcp/local-mcp-go/internal/models"
"github.com/local-mcp/local-mcp-go/internal/store"
@@ -25,9 +26,6 @@ const (
// multiple keepalive progress updates.
defaultWaitSeconds = 50
// defaultEmptyResponse is returned when the queue is empty after waiting.
defaultEmptyResponse = "call this tool `get_user_request` again to fetch latest user input..."
// keepaliveInterval controls how often a log notification is sent to the
// client while waiting. Reduced to 5s (from 20s) for more frequent progress updates.
// This keeps transport-level TCP/HTTP read timeouts from firing.
@@ -69,8 +67,6 @@ If no instruction is available the tool will wait up to wait_seconds
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,
@@ -79,10 +75,6 @@ Returns:
mcp.Description("Identifier for this agent instance"),
mcp.DefaultString("unknown"),
),
mcp.WithString("default_response_override",
mcp.Description("Override the server-default empty response for this call"),
mcp.DefaultString(""),
),
),
h.handleGetUserRequest,
)
@@ -92,7 +84,6 @@ Returns:
func (h *Handler) handleGetUserRequest(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
agentID := req.GetString("agent_id", "unknown")
defaultOverride := req.GetString("default_response_override", "")
// Wait time is hardcoded to stay safely under the 60s client timeout
actualWait := defaultWaitSeconds
@@ -161,7 +152,7 @@ func (h *Handler) handleGetUserRequest(ctx context.Context, req mcp.CallToolRequ
case <-ctx.Done():
// Client disconnected.
slog.Debug("get_user_request: context cancelled", "agent", agentID)
return emptyResult(defaultOverride, 0), nil
return emptyResult(0), nil
case <-wakeup.Chan():
// Instruction may have arrived — loop back to check.
case <-time.After(sleep):
@@ -221,7 +212,7 @@ func (h *Handler) handleGetUserRequest(ctx context.Context, req mcp.CallToolRequ
}
slog.Info("get_user_request: empty", "agent", agentID, "waited", waited, "gen", myGen)
return emptyResult(defaultOverride, waited), nil
return emptyResult(waited), nil
}
func (h *Handler) deliverInstruction(ctx context.Context, item *models.Instruction, agentID string, waited int) (*mcp.CallToolResult, error) {
@@ -252,12 +243,8 @@ func (h *Handler) deliverInstruction(ctx context.Context, item *models.Instructi
return mcp.NewToolResultText(jsonMarshalStr(result)), nil
}
func emptyResult(override string, waited int) *mcp.CallToolResult {
resp := override
if resp == "" {
resp = defaultEmptyResponse
}
func emptyResult(waited int) *mcp.CallToolResult {
resp := config.DefaultEmptyResponse
resultType := "empty"
if resp != "" {
resultType = "default_response"
@@ -278,12 +265,3 @@ func jsonMarshalStr(v any) string {
b, _ := json.Marshal(v)
return string(b)
}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"strconv"
"github.com/local-mcp/local-mcp-go/internal/config"
"github.com/local-mcp/local-mcp-go/internal/models"
)
@@ -28,6 +29,7 @@ func (s *SettingsStore) Get() (models.Settings, error) {
cfg := models.Settings{
DefaultWaitSeconds: 10,
DefaultEmptyResponse: config.DefaultEmptyResponse,
AgentStaleAfterSeconds: 30,
}
@@ -41,8 +43,6 @@ func (s *SettingsStore) Get() (models.Settings, error) {
if n, err := strconv.Atoi(value); err == nil {
cfg.DefaultWaitSeconds = n
}
case "default_empty_response":
cfg.DefaultEmptyResponse = value
case "agent_stale_after_seconds":
if n, err := strconv.Atoi(value); err == nil {
cfg.AgentStaleAfterSeconds = n
@@ -58,12 +58,9 @@ func (s *SettingsStore) Update(patch models.Settings) error {
_, err := s.db.Exec(`
INSERT OR REPLACE INTO settings (key, value) VALUES
('default_wait_seconds', ?),
('default_empty_response', ?),
('agent_stale_after_seconds', ?)`,
strconv.Itoa(patch.DefaultWaitSeconds),
patch.DefaultEmptyResponse,
strconv.Itoa(patch.AgentStaleAfterSeconds),
)
return err
}