Files
tactility/.claude/skills/tactility-firmware/references/mcp-settings-persistence.md
T
Adolfo Reyna 12035f2f39 chore: move firmware skills in-repo + AGENTS.md
- Migrates tactility-firmware and tactility-bluetooth from
  ~/.hermes/skills/ into .claude/skills/ for repo co-location
- Adds AGENTS.md with local workstation context, board table,
  board-direct build rule, Hermes PYTHONPATH pitfall, common
  Tactility pitfalls, and in-repo skill index

Refs: personal Gitea mirror
2026-07-17 09:51:08 -04:00

2.6 KiB

MCP Settings Persistence Bug — SD vs Internal (2026-07-13)

Symptom

User enables MCP Screen in Settings → go back → shows disabled again. /api/mcp returns 404: MCP server is disabled. Board: ES3C28P (color 2.8" ILI9341V, 16MB + PSRAM, storage.userDataLocation=SD).

Root Cause

Tactility/Source/settings/McpSettings.cpp hardcoded:

constexpr SETTINGS_FILE = "/data/service/mcp/settings.properties";
file::findOrCreateDirectory("/data/service/mcp", 0755);
  • On SD boards, getUserDataPath() = /sdcard/tactility (see Paths.cpp:getUserDataRootPath() → finds SDCARD_TYPE FS)
  • Internal /data partition may not be mounted, is read-only, or gets wiped on SD mode
  • save() wrote to internal, load() on next boot looked at internal but file missing → return false → default mcpEnabled=false

Other settings (WebServerSettings, DisplaySettings, DevelopmentSettings) correctly used getUserDataPath() + "/settings/...".

Fix Applied 2026-07-13

#include <Tactility/Paths.h>
static std::string getSettingsFilePath() {
  return getUserDataPath() + "/settings/mcp.properties";
}
bool load(...) {
  auto p = getSettingsFilePath();
  if (!file::isFile(p)) return false;
  return file::loadPropertiesFile(p, map);
}
bool save(...) {
  auto p = getSettingsFilePath();
  if (!file::findOrCreateParentDirectory(p, 0755)) return false;
  return file::savePropertiesFile(p, map);
}
McpSettings loadOrGetDefault() {
  McpSettings s;
  if (!load(s)) { s=getDefault(); save(s); }
  return s;
}

Pitfall Checklist for New Settings

  • Never hardcode /data/...
  • Use getUserDataPath() + "/settings/.properties"
  • Include Tactility/Paths.h
  • Guard load with isFile(path) before parse (matches WebServerSettings)
  • Use findOrCreateParentDirectory not findOrCreateDirectory on file path
  • loadOrGetDefault() saves default if missing

Verification

  • Ad-hoc script hermes-verify-*.py checks old path removed + new path present + parent dir creation
  • Live probe: after enabling MCP on device, POST /api/mcp {method:tools/list} returns tool list, not 404
  • SD path physically: /sdcard/tactility/settings/mcp.properties should contain mcpEnabled=true
  • HttpServer ctrl_port collision (8080+6666 both 32768) → config.ctrl_port = 32768 + (port % 1000)
  • DisplayIdle early return on !supportsBacklightDuty() → killed timer, broke MCP manual activation on RLCD
  • CONFIG_HTTPD_MAX_URI_HANDLERS=8 < 9 needed → bump to 20
  • Video stream task stack 4096 → 8192 + idle delay 50ms

See webserver-devserver.md for full coexistence debugging.