feat(story-93): expose server logs to agents via get_server_logs MCP tool
- Add log_buffer module: bounded 1000-line ring buffer with push/get_recent API - Add slog! macro: drop-in for eprintln! that also captures to ring buffer - Replace all eprintln! calls across agents, watcher, search, chat, worktree, claude_code with slog! - Add get_server_logs MCP tool: accepts count (1-500) and optional filter params - 5 unit tests for log_buffer covering push/retrieve, eviction, filtering, count limits, empty buffer - 262 tests passing, clippy clean Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
use crate::agents::{close_bug_to_archive, move_story_to_archived, move_story_to_merge, move_story_to_qa};
|
||||
use crate::config::ProjectConfig;
|
||||
use crate::log_buffer;
|
||||
use crate::http::context::AppContext;
|
||||
use crate::http::settings::get_editor_command_from_store;
|
||||
use crate::http::workflow::{
|
||||
@@ -742,6 +743,23 @@ fn handle_tools_list(id: Option<Value>) -> JsonRpcResponse {
|
||||
},
|
||||
"required": ["story_id"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "get_server_logs",
|
||||
"description": "Return recent server log lines captured in the in-process ring buffer. Useful for diagnosing runtime behaviour such as WebSocket events, MCP call flow, and filesystem watcher activity.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"lines": {
|
||||
"type": "integer",
|
||||
"description": "Number of recent lines to return (default 100, max 1000)"
|
||||
},
|
||||
"filter": {
|
||||
"type": "string",
|
||||
"description": "Optional substring filter (e.g. 'watcher', 'mcp', 'permission')"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}),
|
||||
@@ -798,6 +816,8 @@ async fn handle_tools_call(
|
||||
"move_story_to_merge" => tool_move_story_to_merge(&args, ctx).await,
|
||||
// QA tools
|
||||
"request_qa" => tool_request_qa(&args, ctx).await,
|
||||
// Diagnostics
|
||||
"get_server_logs" => tool_get_server_logs(&args),
|
||||
_ => Err(format!("Unknown tool: {tool_name}")),
|
||||
};
|
||||
|
||||
@@ -1445,6 +1465,18 @@ fn parse_test_cases(value: Option<&Value>) -> Result<Vec<TestCaseResult>, String
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn tool_get_server_logs(args: &Value) -> Result<String, String> {
|
||||
let lines = args
|
||||
.get("lines")
|
||||
.and_then(|v| v.as_u64())
|
||||
.map(|n| n.min(1000) as usize)
|
||||
.unwrap_or(100);
|
||||
let filter = args.get("filter").and_then(|v| v.as_str());
|
||||
|
||||
let recent = log_buffer::global().get_recent(lines, filter);
|
||||
Ok(recent.join("\n"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -1541,7 +1573,8 @@ mod tests {
|
||||
assert!(names.contains(&"merge_agent_work"));
|
||||
assert!(names.contains(&"move_story_to_merge"));
|
||||
assert!(names.contains(&"request_qa"));
|
||||
assert_eq!(tools.len(), 26);
|
||||
assert!(names.contains(&"get_server_logs"));
|
||||
assert_eq!(tools.len(), 27);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user