Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f10984c18 | ||
|
|
cb39f94584 | ||
|
|
4cb76725fe |
No files matched your search
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
name: MCP Protocol & Build Constraints
|
||||||
|
description: Strict constraints for Model Context Protocol stdio transport and project build tooling
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. MCP Stdio Protocol (Strict NDJSON)
|
||||||
|
- **Rule (CRITICAL):** The Model Context Protocol (MCP) `stdio` transport is strictly based on **pure NDJSON** (Newline Delimited JSON).
|
||||||
|
- **Rule:** NEVER inject Language Server Protocol (LSP) style HTTP headers (e.g., `Content-Length: <size>\r\n\r\n`) into MCP `stdio` streams. Payloads must be raw JSON objects delimited by a single newline character (`\n`).
|
||||||
|
|
||||||
|
# 2. Build Tooling Constraints (mcp-memory)
|
||||||
|
- **Rule:** NEVER use `cargo zigbuild` for cross-compilation in the `mcp-memory` project.
|
||||||
|
- **Rule:** ALWAYS use the project's native `justfile` targets (e.g., `just build-wsl`, `just deploy-stub-wsl`, `just deploy-all`) to manage building and deploying binaries across Windows and WSL boundaries.
|
||||||
|
|
||||||
|
# 3. Clipboard & Vision Tooling (Strict MCP Enforcement)
|
||||||
|
- **Rule (CRITICAL):** You MUST ALWAYS execute the `read_clipboard` MCP tool (`mcp-memory`) when reading or inspecting text or images from the OS clipboard.
|
||||||
|
- **Rule:** NEVER run ad-hoc PowerShell (`System.Windows.Forms.Clipboard`), bash, or shell scripts to extract clipboard images or saved files. `read_clipboard` handles native clipboard image extraction, auto-resizing, and compressed JPEG (`.jpg`) encoding to minimize LLM token usage.
|
||||||
@@ -35,12 +35,12 @@ deploy-win: deploy-server deploy-stub-win deploy-nvim-win deploy-rules-win
|
|||||||
# Stop the background server
|
# Stop the background server
|
||||||
stop port="3000":
|
stop port="3000":
|
||||||
@Write-Host "Shutting down MCP server gracefully on port {{port}}..." -ForegroundColor Cyan
|
@Write-Host "Shutting down MCP server gracefully on port {{port}}..." -ForegroundColor Cyan
|
||||||
-& { $token = Get-Content "$env:USERPROFILE\.gemini\mcp_memory\admin.token" -ErrorAction SilentlyContinue; if ($token) { $hdr = @{ Authorization = "Bearer $($token.Trim())" }; Invoke-RestMethod -Uri "http://127.0.0.1:{{port}}/shutdown" -Method Post -Headers $hdr -ErrorAction SilentlyContinue } else { Invoke-RestMethod -Uri "http://127.0.0.1:{{port}}/shutdown" -Method Post -ErrorAction SilentlyContinue }; $stopped = $false; for ($i = 1; $i -le 10; $i++) { $conn = Get-NetTCPConnection -LocalPort {{port}} -State Listen -ErrorAction SilentlyContinue; $proc = Get-Process mcp-memory-server -ErrorAction SilentlyContinue; if (-not $conn -and -not $proc) { $stopped = $true; Write-Host '✅ Server process and port {{port}} fully terminated.' -ForegroundColor Green; break }; Start-Sleep -Seconds 1 }; if (-not $stopped) { Write-Host '⚠️ Server process did not exit within 10s. Force killing process...' -ForegroundColor Yellow; Get-Process mcp-memory-server -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue; Get-NetTCPConnection -LocalPort {{port}} -State Listen -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess | ForEach-Object { Stop-Process -Id $_ -Force -ErrorAction SilentlyContinue }; Start-Sleep -Seconds 2 } }
|
-& { $token = Get-Content "$env:USERPROFILE\.gemini\mcp_memory\admin.token" -ErrorAction SilentlyContinue; if ($token) { $hdr = @{ Authorization = "Bearer $($token.Trim())" }; $res = Invoke-RestMethod -Uri "http://127.0.0.1:{{port}}/shutdown" -Method Post -Headers $hdr -ErrorAction SilentlyContinue } else { $res = Invoke-RestMethod -Uri "http://127.0.0.1:{{port}}/shutdown" -Method Post -ErrorAction SilentlyContinue }; if ($res) { Write-Host "✅ Shutdown acknowledged with version details:" -ForegroundColor Green; $res | ConvertTo-Json -Depth 5 }; $stopped = $false; for ($i = 1; $i -le 10; $i++) { $conn = Get-NetTCPConnection -LocalPort {{port}} -State Listen -ErrorAction SilentlyContinue; $proc = Get-Process mcp-memory-server -ErrorAction SilentlyContinue; if (-not $conn -and -not $proc) { $stopped = $true; Write-Host '✅ Server process and port {{port}} fully terminated.' -ForegroundColor Green; break }; Start-Sleep -Seconds 1 }; if (-not $stopped) { Write-Host '⚠️ Server process did not exit within 10s. Force killing process...' -ForegroundColor Yellow; Get-Process mcp-memory-server -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue; Get-NetTCPConnection -LocalPort {{port}} -State Listen -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess | ForEach-Object { Stop-Process -Id $_ -Force -ErrorAction SilentlyContinue }; Start-Sleep -Seconds 2 } }
|
||||||
@Write-Host "✅ Shutdown complete. Port {{port}} and process are free." -ForegroundColor Green
|
@Write-Host "✅ Shutdown complete. Port {{port}} and process are free." -ForegroundColor Green
|
||||||
|
|
||||||
# Start the server in the background (nohup equivalent)
|
# Start the server in the background (nohup equivalent)
|
||||||
start port="3000":
|
start port="3000":
|
||||||
@if (Get-NetTCPConnection -LocalPort {{port}} -State Listen -ErrorAction SilentlyContinue) { Write-Host '⚠️ Server is already running on port {{port}}. Skipping start.' -ForegroundColor Yellow; exit 0 }; Write-Host 'Starting MCP server in background on port {{port}}...' -ForegroundColor Cyan; $env:MCP_PORT='{{port}}'; $env:MCP_UDP_PORT1=$([int]{{port}} + 1); $env:MCP_UDP_PORT2=$([int]{{port}} + 2); .\start_server.ps1 -Port {{port}}; Write-Host 'Waiting up to 40 seconds for /ping endpoint...' -ForegroundColor Cyan; for ($i = 1; $i -le 40; $i++) { try { $res = Invoke-RestMethod -Uri 'http://127.0.0.1:{{port}}/ping' -ErrorAction Stop; Write-Host '✅ Server is UP!' -ForegroundColor Green; exit 0 } catch { Start-Sleep -Seconds 1 } }; Write-Error '❌ Server failed to start on port {{port}} within 40 seconds.'; exit 1
|
@if (Get-NetTCPConnection -LocalPort {{port}} -State Listen -ErrorAction SilentlyContinue) { Write-Host '⚠️ Server is already running on port {{port}}. Skipping start.' -ForegroundColor Yellow; exit 0 }; Write-Host 'Starting MCP server in background on port {{port}}...' -ForegroundColor Cyan; $env:MCP_PORT='{{port}}'; $env:MCP_UDP_PORT1=$([int]{{port}} + 1); $env:MCP_UDP_PORT2=$([int]{{port}} + 2); .\start_server.ps1 -Port {{port}}; Write-Host 'Waiting up to 40 seconds for /health endpoint...' -ForegroundColor Cyan; for ($i = 1; $i -le 40; $i++) { try { $h = Invoke-RestMethod -Uri 'http://127.0.0.1:{{port}}/health' -ErrorAction Stop; Write-Host "✅ Server is UP! Running Version: $($h.version.app_version) [$($h.version.target_os)/$($h.version.target_arch)]" -ForegroundColor Green; exit 0 } catch { Start-Sleep -Seconds 1 } }; Write-Error '❌ Server failed to start on port {{port}} within 40 seconds.'; exit 1
|
||||||
|
|
||||||
# Verify the deployment is healthy and serving correct content
|
# Verify the deployment is healthy and serving correct content
|
||||||
verify port="3000":
|
verify port="3000":
|
||||||
|
|||||||
+10
-2
@@ -104,8 +104,16 @@ pub async fn gate_set_handler(
|
|||||||
Ok((axum::http::StatusCode::OK, "Gate state updated."))
|
Ok((axum::http::StatusCode::OK, "Gate state updated."))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn health_handler() -> &'static str {
|
pub async fn health_handler() -> impl IntoResponse {
|
||||||
"OK"
|
axum::Json(serde_json::json!({
|
||||||
|
"status": "ok",
|
||||||
|
"version": {
|
||||||
|
"app_version": env!("APP_VERSION"),
|
||||||
|
"cargo_pkg_version": env!("CARGO_PKG_VERSION"),
|
||||||
|
"target_os": std::env::consts::OS,
|
||||||
|
"target_arch": std::env::consts::ARCH,
|
||||||
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
+11
-1
@@ -143,7 +143,17 @@ pub fn create_router(app_state: Arc<AppState>) -> Router {
|
|||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
(axum::http::StatusCode::OK, "Shutting down...").into_response()
|
axum::Json(serde_json::json!({
|
||||||
|
"status": "shutting_down",
|
||||||
|
"message": "MCP Memory Server gracefully shutting down.",
|
||||||
|
"version_details": {
|
||||||
|
"app_version": env!("APP_VERSION"),
|
||||||
|
"cargo_pkg_version": env!("CARGO_PKG_VERSION"),
|
||||||
|
"target_os": std::env::consts::OS,
|
||||||
|
"target_arch": std::env::consts::ARCH,
|
||||||
|
"shutdown_checkpoint": format!("chk_shutdown_{}", now)
|
||||||
|
}
|
||||||
|
})).into_response()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1142,12 +1142,18 @@
|
|||||||
if (!feed) return;
|
if (!feed) return;
|
||||||
feed.innerHTML = '';
|
feed.innerHTML = '';
|
||||||
|
|
||||||
// Sort history by timestamp descending so newest entries are at top
|
// Helper to normalize timestamp to epoch milliseconds
|
||||||
const sorted = [...history].sort((a, b) => {
|
const getMillis = (item) => {
|
||||||
const tA = (typeof a === 'object' && a) ? (a.timestamp || a.time || 0) : 0;
|
if (!item || typeof item !== 'object') return 0;
|
||||||
const tB = (typeof b === 'object' && b) ? (b.timestamp || b.time || 0) : 0;
|
let val = item.timestamp || item.time || item.updated_at || 0;
|
||||||
return tB - tA;
|
if (typeof val === 'number' && val < 10000000000) {
|
||||||
});
|
val = val * 1000;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sort history by normalized timestamp descending so newest entries are always at top
|
||||||
|
const sorted = [...history].sort((a, b) => getMillis(b) - getMillis(a));
|
||||||
|
|
||||||
sorted.forEach(item => {
|
sorted.forEach(item => {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
@@ -1396,7 +1402,7 @@
|
|||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const verEl = document.getElementById('app-version');
|
const verEl = document.getElementById('app-version');
|
||||||
if (verEl) {
|
if (verEl) {
|
||||||
verEl.textContent = `v${data.version}`;
|
verEl.innerHTML = `<span style="background:var(--card-bg); border:1px solid var(--border-color); color:var(--text-secondary); padding:3px 10px; border-radius:12px; font-size:0.85em; font-weight:600; font-family:monospace;">v${data.version}</span>`;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to load version", err);
|
console.error("Failed to load version", err);
|
||||||
|
|||||||
@@ -5,13 +5,15 @@ $HealthUrl = "http://127.0.0.1:$Port/ping"
|
|||||||
$UiUrl = "http://127.0.0.1:$Port/?t=$(Get-Random)"
|
$UiUrl = "http://127.0.0.1:$Port/?t=$(Get-Random)"
|
||||||
$ExpectedContent = "open-color" # Basic sanity check that we are serving the updated UI
|
$ExpectedContent = "open-color" # Basic sanity check that we are serving the updated UI
|
||||||
|
|
||||||
Write-Host "Verifying server HEALTH at $HealthUrl..." -ForegroundColor Cyan
|
Write-Host "Verifying server HEALTH at http://127.0.0.1:$Port/health..." -ForegroundColor Cyan
|
||||||
|
|
||||||
for ($i = 1; $i -le 40; $i++) {
|
for ($i = 1; $i -le 40; $i++) {
|
||||||
try {
|
try {
|
||||||
$healthResponse = Invoke-WebRequest -Uri $HealthUrl -UseBasicParsing -ErrorAction Stop
|
$healthResponse = Invoke-RestMethod -Uri "http://127.0.0.1:$Port/health" -UseBasicParsing -ErrorAction Stop
|
||||||
if ($healthResponse.StatusCode -eq 200) {
|
if ($healthResponse.status -eq "ok") {
|
||||||
Write-Host "✅ Server /ping is UP! (Attempt $i)" -ForegroundColor Green
|
$ver = $healthResponse.version
|
||||||
|
Write-Host "✅ Server /health is UP! Status: $($healthResponse.status.ToUpper()) (Attempt $i)" -ForegroundColor Green
|
||||||
|
Write-Host " Running Build Version: $($ver.app_version) [$($ver.target_os)/$($ver.target_arch)]" -ForegroundColor Green
|
||||||
|
|
||||||
Write-Host "Verifying UI payload at $UiUrl..." -ForegroundColor Cyan
|
Write-Host "Verifying UI payload at $UiUrl..." -ForegroundColor Cyan
|
||||||
$uiResponse = Invoke-WebRequest -Uri $UiUrl -UseBasicParsing -ErrorAction Stop
|
$uiResponse = Invoke-WebRequest -Uri $UiUrl -UseBasicParsing -ErrorAction Stop
|
||||||
@@ -23,7 +25,7 @@ for ($i = 1; $i -le 40; $i++) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
Write-Host "⏳ Server /ping not responding yet... (Attempt $i)" -ForegroundColor Yellow
|
Write-Host "⏳ Server /health not responding yet... (Attempt $i)" -ForegroundColor Yellow
|
||||||
}
|
}
|
||||||
Start-Sleep -Seconds 1
|
Start-Sleep -Seconds 1
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user