add mcp log server via journald
All checks were successful
Build hosts / build-hosts (pull_request) Successful in 19s
Flake check / flake-check (pull_request) Successful in 19s

This commit is contained in:
sid 2026-05-19 12:56:26 +02:00
parent d6f7ce6d46
commit de573124ce
8 changed files with 131 additions and 2 deletions

View file

@ -0,0 +1,57 @@
{
pkgs,
lib,
...
}:
let
python = pkgs.python3Packages;
mcp-log-server = python.buildPythonApplication {
pname = "mcp-log-server";
version = "1.0.0";
src = ./.;
pyproject = true;
build-system = [ python.setuptools ];
propagatedBuildInputs = with python; [
fastmcp
];
meta.mainProgram = "mcp-log-server";
};
in
{
services.journald.remote = {
enable = true;
listen = "http";
port = 19532;
settings.Remote.SplitMode = "host";
};
users.users.sid.extraGroups = [
"systemd-journal"
"systemd-journal-remote"
];
systemd.services.mcp-log-server = {
description = "AI Log Access MCP Server";
after = [
"network.target"
"multi-user.target"
"systemd-journald.service"
];
wantedBy = [ "multi-user.target" ];
script = lib.getExe mcp-log-server;
serviceConfig = {
User = "root";
Group = "root";
Environment = "PYTHONUNBUFFERED=1";
Restart = "on-failure";
};
};
}

View file

@ -0,0 +1,9 @@
[build-system]
requires = ["setuptools"]
[project]
name = "mcp-log-server"
version = "1.0.0"
[project.scripts]
mcp-log-server = "server:main"

View file

@ -0,0 +1,60 @@
#!/usr/bin/env python3
import json
import subprocess
from fastmcp import FastMCP
mcp = FastMCP('ClusterLogQuery')
@mcp.tool()
async def search_cluster_logs(keyword: str, unit: str = '*', limit: int = 500):
"""
Search centralized systemd logs collected on this server.
Args:
keyword: The string to search for (e.g. 'oom-kill', 'failed').
unit: The systemd unit to filter (e.g. 'sshd'). If '*', matches all.
limit: The number of log lines to return (Max 1000).
"""
limit = min(max(limit, 10), 1000)
cmd = [
'journalctl',
'-n', str(limit),
'-o', 'json',
'--no-pager',
'--directory', '/var/log/journal/remote'
]
if unit != '*':
cmd.append(f'UNIT={unit}')
if keyword:
cmd.append(keyword)
try:
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, _ = proc.communicate()
logs = []
for line in stdout.decode('utf-8').strip().split('\n'):
if not line: continue
try:
log = json.loads(line)
logs.append({
'timestamp': log.get('TIME_ISO8601'),
'hostname': log.get('_HOSTNAME'),
'unit': log.get('_COMM'),
'message': log.get('MESSAGE')
})
except: pass
return json.dumps(logs)
except Exception as e:
return f'Error querying logs: {str(e)}'
def main():
mcp.run(transport='sse', host='0.0.0.0', port=9500)
if __name__ == '__main__':
main()