12035f2f39
- 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
2.6 KiB
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(seePaths.cpp:getUserDataRootPath()→ finds SDCARD_TYPE FS) - Internal
/datapartition 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 → defaultmcpEnabled=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
findOrCreateParentDirectorynotfindOrCreateDirectoryon file path loadOrGetDefault()saves default if missing
Verification
- Ad-hoc script
hermes-verify-*.pychecks 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.propertiesshould containmcpEnabled=true
Related Bugs Same Session
- 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.