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
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user