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
61 lines
2.6 KiB
Markdown
61 lines
2.6 KiB
Markdown
# 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:
|
|
```cpp
|
|
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
|
|
```cpp
|
|
#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/<name>.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`
|
|
|
|
## 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.
|