Hardcode Go default empty response
This commit is contained in:
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -38,9 +36,9 @@ const (
|
||||
|
||||
// Handler wraps the MCP server and holds references to the stores it needs.
|
||||
type Handler struct {
|
||||
MCP *server.MCPServer
|
||||
instStore *store.InstructionStore
|
||||
settStore *store.SettingsStore
|
||||
MCP *server.MCPServer
|
||||
instStore *store.InstructionStore
|
||||
settStore *store.SettingsStore
|
||||
agentStore *store.AgentStore
|
||||
broker *events.Broker
|
||||
}
|
||||
@@ -53,7 +51,7 @@ func New(
|
||||
broker *events.Broker,
|
||||
) *Handler {
|
||||
h := &Handler{
|
||||
MCP: server.NewMCPServer("local-mcp", "1.0.0"),
|
||||
MCP: server.NewMCPServer("local-mcp", "1.0.0"),
|
||||
instStore: instStore,
|
||||
settStore: settStore,
|
||||
agentStore: agentStore,
|
||||
@@ -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) {
|
||||
@@ -230,7 +221,7 @@ func (h *Handler) deliverInstruction(ctx context.Context, item *models.Instructi
|
||||
|
||||
// Broadcast consumed event + status update.
|
||||
h.broker.Broadcast("instruction.consumed", map[string]any{
|
||||
"item": item,
|
||||
"item": item,
|
||||
"consumed_by_agent_id": agentID,
|
||||
})
|
||||
h.broker.Broadcast("status.changed", map[string]any{"queue": counts})
|
||||
@@ -238,8 +229,8 @@ func (h *Handler) deliverInstruction(ctx context.Context, item *models.Instructi
|
||||
slog.Info("get_user_request: delivered", "id", item.ID, "agent", agentID, "waited", waited)
|
||||
|
||||
result := map[string]any{
|
||||
"status": "ok",
|
||||
"result_type": "instruction",
|
||||
"status": "ok",
|
||||
"result_type": "instruction",
|
||||
"instruction": map[string]any{
|
||||
"id": item.ID,
|
||||
"content": item.Content,
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user