fix: index_create updates tracked project for search_text
index_create and index_update now update the MCP server's tracked project path so subsequent calls like search_text use the same project. search_text also gains an optional 'project' parameter to explicitly override the default. Closes #9.
This commit is contained in:
1 parent
4b55a41ece
commit
2cd801558d
1 file changed
+25
-10
+25
-10
@@ -1,7 +1,7 @@
|
|||||||
//! Minimal MCP server: JSON-RPC over stdio, dispatching to the same ops as the CLI.
|
//! Minimal MCP server: JSON-RPC over stdio, dispatching to the same ops as the CLI.
|
||||||
|
|
||||||
use std::io::{BufRead, Write};
|
use std::io::{BufRead, Write};
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
@@ -10,6 +10,7 @@ const PROTOCOL_VERSION: &str = "2024-11-05";
|
|||||||
|
|
||||||
pub fn serve(project: &Path) -> Result<()> {
|
pub fn serve(project: &Path) -> Result<()> {
|
||||||
eprintln!("rustytokens MCP server listening on stdio");
|
eprintln!("rustytokens MCP server listening on stdio");
|
||||||
|
let mut project = project.to_path_buf();
|
||||||
let stdin = std::io::stdin();
|
let stdin = std::io::stdin();
|
||||||
let stdout = std::io::stdout();
|
let stdout = std::io::stdout();
|
||||||
for line in stdin.lock().lines() {
|
for line in stdin.lock().lines() {
|
||||||
@@ -39,7 +40,7 @@ pub fn serve(project: &Path) -> Result<()> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Some(resp) = handle(&msg, project) {
|
if let Some(resp) = handle(&msg, &mut project) {
|
||||||
let mut out = stdout.lock();
|
let mut out = stdout.lock();
|
||||||
serde_json::to_writer(&mut out, &resp)?;
|
serde_json::to_writer(&mut out, &resp)?;
|
||||||
out.write_all(b"\n")?;
|
out.write_all(b"\n")?;
|
||||||
@@ -49,7 +50,7 @@ pub fn serve(project: &Path) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(msg: &Value, project: &Path) -> Option<Value> {
|
fn handle(msg: &Value, project: &mut PathBuf) -> Option<Value> {
|
||||||
let id = msg.get("id").cloned();
|
let id = msg.get("id").cloned();
|
||||||
|
|
||||||
// Notifications have no id -> no response.
|
// Notifications have no id -> no response.
|
||||||
@@ -101,7 +102,7 @@ fn handle(msg: &Value, project: &Path) -> Option<Value> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_tool(name: &str, args: &Value, project: &Path) -> Value {
|
fn call_tool(name: &str, args: &Value, project: &mut PathBuf) -> Value {
|
||||||
let result: Result<Value> = dispatch(name, args, project);
|
let result: Result<Value> = dispatch(name, args, project);
|
||||||
match result {
|
match result {
|
||||||
Ok(v) => json!({
|
Ok(v) => json!({
|
||||||
@@ -114,7 +115,7 @@ fn call_tool(name: &str, args: &Value, project: &Path) -> Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch(name: &str, args: &Value, project: &Path) -> Result<Value> {
|
fn dispatch(name: &str, args: &Value, project: &mut PathBuf) -> Result<Value> {
|
||||||
use crate::ops;
|
use crate::ops;
|
||||||
|
|
||||||
fn req_str<'a>(args: &'a Value, key: &str, tool: &str) -> Result<&'a str> {
|
fn req_str<'a>(args: &'a Value, key: &str, tool: &str) -> Result<&'a str> {
|
||||||
@@ -128,8 +129,16 @@ fn dispatch(name: &str, args: &Value, project: &Path) -> Result<Value> {
|
|||||||
let excludes = crate::config::ExcludeSet::new(&exclude_list(args));
|
let excludes = crate::config::ExcludeSet::new(&exclude_list(args));
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
"index_create" => ops::index_create(Path::new(req_str(args, "path", name)?)),
|
"index_create" => {
|
||||||
"index_update" => ops::index_update(Path::new(req_str(args, "path", name)?)),
|
let path = PathBuf::from(req_str(args, "path", name)?);
|
||||||
|
project.clone_from(&path);
|
||||||
|
ops::index_create(&path)
|
||||||
|
}
|
||||||
|
"index_update" => {
|
||||||
|
let path = PathBuf::from(req_str(args, "path", name)?);
|
||||||
|
project.clone_from(&path);
|
||||||
|
ops::index_update(&path)
|
||||||
|
}
|
||||||
"index_file" => ops::index_file(project, req_str(args, "path", name)?),
|
"index_file" => ops::index_file(project, req_str(args, "path", name)?),
|
||||||
"search_symbols" => ops::search_symbols(
|
"search_symbols" => ops::search_symbols(
|
||||||
project,
|
project,
|
||||||
@@ -143,15 +152,20 @@ fn dispatch(name: &str, args: &Value, project: &Path) -> Result<Value> {
|
|||||||
.unwrap_or(200),
|
.unwrap_or(200),
|
||||||
&excludes,
|
&excludes,
|
||||||
),
|
),
|
||||||
"search_text" => ops::search_text(
|
"search_text" => {
|
||||||
project,
|
let search_project = opt_str("project")
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.unwrap_or_else(|| project.clone());
|
||||||
|
ops::search_text(
|
||||||
|
&search_project,
|
||||||
req_str(args, "query", name)?,
|
req_str(args, "query", name)?,
|
||||||
args.get("limit")
|
args.get("limit")
|
||||||
.and_then(|v| v.as_u64())
|
.and_then(|v| v.as_u64())
|
||||||
.map(|n| n as usize)
|
.map(|n| n as usize)
|
||||||
.unwrap_or(100),
|
.unwrap_or(100),
|
||||||
&excludes,
|
&excludes,
|
||||||
),
|
)
|
||||||
|
}
|
||||||
"inspect_symbol" => {
|
"inspect_symbol" => {
|
||||||
if let Some(id) = args.get("id").and_then(|v| v.as_i64()) {
|
if let Some(id) = args.get("id").and_then(|v| v.as_i64()) {
|
||||||
ops::inspect_symbol(project, &id.to_string())
|
ops::inspect_symbol(project, &id.to_string())
|
||||||
@@ -272,6 +286,7 @@ fn tools() -> Value {
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"query": { "type": "string", "description": "Text or phrase to search for" },
|
"query": { "type": "string", "description": "Text or phrase to search for" },
|
||||||
|
"project": { "type": "string", "description": "Project directory to search (defaults to the last indexed project)" },
|
||||||
"limit": { "type": "integer", "description": "Maximum matching files (default 100)" },
|
"limit": { "type": "integer", "description": "Maximum matching files (default 100)" },
|
||||||
"exclude": {
|
"exclude": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
|
|||||||
Reference in new issue
Block a user