fix ref
This commit is contained in:
@@ -12,11 +12,11 @@ path = "src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "memory-mcp"
|
||||
path = "src/mcp_server.rs"
|
||||
path = "src/bin/mcp_server.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "memory-mcp-extended"
|
||||
path = "extended/src/mcp_server.rs"
|
||||
path = "src/bin/mcp_server_extended.rs"
|
||||
|
||||
[dependencies]
|
||||
# CLI and async
|
||||
|
@@ -1,398 +0,0 @@
|
||||
use anyhow::Result;
|
||||
use serde_json::{json, Value};
|
||||
use std::io::{self, BufRead, Write};
|
||||
|
||||
use aigpt::memory::MemoryManager;
|
||||
|
||||
pub struct ExtendedMCPServer {
|
||||
memory_manager: MemoryManager,
|
||||
}
|
||||
|
||||
impl ExtendedMCPServer {
|
||||
pub async fn new() -> Result<Self> {
|
||||
let memory_manager = MemoryManager::new().await?;
|
||||
Ok(ExtendedMCPServer { memory_manager })
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) -> Result<()> {
|
||||
let stdin = io::stdin();
|
||||
let mut stdout = io::stdout();
|
||||
|
||||
let reader = stdin.lock();
|
||||
let lines = reader.lines();
|
||||
|
||||
for line_result in lines {
|
||||
match line_result {
|
||||
Ok(line) => {
|
||||
let trimmed = line.trim();
|
||||
if trimmed.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Ok(request) = serde_json::from_str::<Value>(&trimmed) {
|
||||
let response = self.handle_request(request).await;
|
||||
let response_str = serde_json::to_string(&response)?;
|
||||
stdout.write_all(response_str.as_bytes())?;
|
||||
stdout.write_all(b"\n")?;
|
||||
stdout.flush()?;
|
||||
}
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_request(&mut self, request: Value) -> Value {
|
||||
let method = request["method"].as_str().unwrap_or("");
|
||||
let id = request["id"].clone();
|
||||
|
||||
match method {
|
||||
"initialize" => {
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": id,
|
||||
"result": {
|
||||
"protocolVersion": "2024-11-05",
|
||||
"capabilities": {
|
||||
"tools": {}
|
||||
},
|
||||
"serverInfo": {
|
||||
"name": "aigpt-extended",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
"tools/list" => {
|
||||
#[allow(unused_mut)]
|
||||
let mut tools = vec![
|
||||
// Basic tools
|
||||
json!({
|
||||
"name": "create_memory",
|
||||
"description": "Create a new memory entry",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Content of the memory"
|
||||
},
|
||||
"analyze": {
|
||||
"type": "boolean",
|
||||
"description": "Enable AI analysis for this memory"
|
||||
}
|
||||
},
|
||||
"required": ["content"]
|
||||
}
|
||||
}),
|
||||
json!({
|
||||
"name": "search_memories",
|
||||
"description": "Search memories with advanced options",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "Search query"
|
||||
},
|
||||
"semantic": {
|
||||
"type": "boolean",
|
||||
"description": "Use semantic search"
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "Filter by category"
|
||||
},
|
||||
"time_range": {
|
||||
"type": "string",
|
||||
"description": "Filter by time range (e.g., '1week', '1month')"
|
||||
}
|
||||
},
|
||||
"required": ["query"]
|
||||
}
|
||||
}),
|
||||
json!({
|
||||
"name": "update_memory",
|
||||
"description": "Update an existing memory entry",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "ID of the memory to update"
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "New content for the memory"
|
||||
}
|
||||
},
|
||||
"required": ["id", "content"]
|
||||
}
|
||||
}),
|
||||
json!({
|
||||
"name": "delete_memory",
|
||||
"description": "Delete a memory entry",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "ID of the memory to delete"
|
||||
}
|
||||
},
|
||||
"required": ["id"]
|
||||
}
|
||||
})
|
||||
];
|
||||
|
||||
// Add extended tools based on features
|
||||
#[cfg(feature = "web-integration")]
|
||||
{
|
||||
tools.push(json!({
|
||||
"name": "import_webpage",
|
||||
"description": "Import content from a webpage",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "URL to import from"
|
||||
}
|
||||
},
|
||||
"required": ["url"]
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
#[cfg(feature = "ai-analysis")]
|
||||
{
|
||||
tools.push(json!({
|
||||
"name": "analyze_sentiment",
|
||||
"description": "Analyze sentiment of memories",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"period": {
|
||||
"type": "string",
|
||||
"description": "Time period to analyze"
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
tools.push(json!({
|
||||
"name": "extract_insights",
|
||||
"description": "Extract insights and patterns from memories",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "Category to analyze"
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": id,
|
||||
"result": {
|
||||
"tools": tools
|
||||
}
|
||||
})
|
||||
}
|
||||
"tools/call" => {
|
||||
let tool_name = request["params"]["name"].as_str().unwrap_or("");
|
||||
let arguments = &request["params"]["arguments"];
|
||||
|
||||
let result = match tool_name {
|
||||
"create_memory" => {
|
||||
let content = arguments["content"].as_str().unwrap_or("");
|
||||
let analyze = arguments["analyze"].as_bool().unwrap_or(false);
|
||||
|
||||
let final_content = if analyze {
|
||||
#[cfg(feature = "ai-analysis")]
|
||||
{
|
||||
format!("[AI分析] 感情: neutral, カテゴリ: general\n{}", content)
|
||||
}
|
||||
#[cfg(not(feature = "ai-analysis"))]
|
||||
{
|
||||
content.to_string()
|
||||
}
|
||||
} else {
|
||||
content.to_string()
|
||||
};
|
||||
|
||||
match self.memory_manager.create_memory(&final_content) {
|
||||
Ok(id) => json!({
|
||||
"success": true,
|
||||
"id": id,
|
||||
"message": if analyze { "Memory created with AI analysis" } else { "Memory created successfully" }
|
||||
}),
|
||||
Err(e) => json!({
|
||||
"success": false,
|
||||
"error": e.to_string()
|
||||
})
|
||||
}
|
||||
}
|
||||
"search_memories" => {
|
||||
let query = arguments["query"].as_str().unwrap_or("");
|
||||
let semantic = arguments["semantic"].as_bool().unwrap_or(false);
|
||||
|
||||
let memories = if semantic {
|
||||
#[cfg(feature = "semantic-search")]
|
||||
{
|
||||
// Mock semantic search for now
|
||||
self.memory_manager.search_memories(query)
|
||||
}
|
||||
#[cfg(not(feature = "semantic-search"))]
|
||||
{
|
||||
self.memory_manager.search_memories(query)
|
||||
}
|
||||
} else {
|
||||
self.memory_manager.search_memories(query)
|
||||
};
|
||||
|
||||
json!({
|
||||
"success": true,
|
||||
"memories": memories.into_iter().map(|m| json!({
|
||||
"id": m.id,
|
||||
"content": m.content,
|
||||
"created_at": m.created_at,
|
||||
"updated_at": m.updated_at
|
||||
})).collect::<Vec<_>>(),
|
||||
"search_type": if semantic { "semantic" } else { "keyword" }
|
||||
})
|
||||
}
|
||||
"update_memory" => {
|
||||
let id = arguments["id"].as_str().unwrap_or("");
|
||||
let content = arguments["content"].as_str().unwrap_or("");
|
||||
match self.memory_manager.update_memory(id, content) {
|
||||
Ok(()) => json!({
|
||||
"success": true,
|
||||
"message": "Memory updated successfully"
|
||||
}),
|
||||
Err(e) => json!({
|
||||
"success": false,
|
||||
"error": e.to_string()
|
||||
})
|
||||
}
|
||||
}
|
||||
"delete_memory" => {
|
||||
let id = arguments["id"].as_str().unwrap_or("");
|
||||
match self.memory_manager.delete_memory(id) {
|
||||
Ok(()) => json!({
|
||||
"success": true,
|
||||
"message": "Memory deleted successfully"
|
||||
}),
|
||||
Err(e) => json!({
|
||||
"success": false,
|
||||
"error": e.to_string()
|
||||
})
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "web-integration")]
|
||||
"import_webpage" => {
|
||||
let url = arguments["url"].as_str().unwrap_or("");
|
||||
match self.import_from_web(url).await {
|
||||
Ok(content) => {
|
||||
match self.memory_manager.create_memory(&content) {
|
||||
Ok(id) => json!({
|
||||
"success": true,
|
||||
"id": id,
|
||||
"message": format!("Webpage imported successfully from {}", url)
|
||||
}),
|
||||
Err(e) => json!({
|
||||
"success": false,
|
||||
"error": e.to_string()
|
||||
})
|
||||
}
|
||||
}
|
||||
Err(e) => json!({
|
||||
"success": false,
|
||||
"error": format!("Failed to import webpage: {}", e)
|
||||
})
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "ai-analysis")]
|
||||
"analyze_sentiment" => {
|
||||
json!({
|
||||
"success": true,
|
||||
"analysis": {
|
||||
"positive": 60,
|
||||
"neutral": 30,
|
||||
"negative": 10,
|
||||
"dominant_sentiment": "positive"
|
||||
},
|
||||
"message": "Sentiment analysis completed"
|
||||
})
|
||||
}
|
||||
#[cfg(feature = "ai-analysis")]
|
||||
"extract_insights" => {
|
||||
json!({
|
||||
"success": true,
|
||||
"insights": {
|
||||
"most_frequent_topics": ["programming", "ai", "productivity"],
|
||||
"learning_frequency": "5 times per week",
|
||||
"growth_trend": "increasing",
|
||||
"recommendations": ["Focus more on advanced topics", "Consider practical applications"]
|
||||
},
|
||||
"message": "Insights extracted successfully"
|
||||
})
|
||||
}
|
||||
_ => json!({
|
||||
"success": false,
|
||||
"error": format!("Unknown tool: {}", tool_name)
|
||||
})
|
||||
};
|
||||
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": id,
|
||||
"result": {
|
||||
"content": [{
|
||||
"type": "text",
|
||||
"text": result.to_string()
|
||||
}]
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": id,
|
||||
"error": {
|
||||
"code": -32601,
|
||||
"message": "Method not found"
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "web-integration")]
|
||||
async fn import_from_web(&self, url: &str) -> Result<String> {
|
||||
let response = reqwest::get(url).await?;
|
||||
let content = response.text().await?;
|
||||
|
||||
let document = scraper::Html::parse_document(&content);
|
||||
let title_selector = scraper::Selector::parse("title").unwrap();
|
||||
let body_selector = scraper::Selector::parse("p").unwrap();
|
||||
|
||||
let title = document.select(&title_selector)
|
||||
.next()
|
||||
.map(|el| el.inner_html())
|
||||
.unwrap_or_else(|| "Untitled".to_string());
|
||||
|
||||
let paragraphs: Vec<String> = document.select(&body_selector)
|
||||
.map(|el| el.inner_html())
|
||||
.take(5)
|
||||
.collect();
|
||||
|
||||
Ok(format!("# {}\nURL: {}\n\n{}", title, url, paragraphs.join("\n\n")))
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
// Re-export core modules to make them available to extended version
|
||||
pub use aigpt::memory;
|
||||
pub use aigpt::mcp;
|
@@ -1,250 +0,0 @@
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, Subcommand};
|
||||
use std::path::PathBuf;
|
||||
|
||||
// Re-use core modules from parent
|
||||
use aigpt::memory::MemoryManager;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "aigpt-extended")]
|
||||
#[command(about = "Extended Claude Memory Tool with AI analysis and web integration")]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
/// Create a new memory entry
|
||||
Create {
|
||||
content: String,
|
||||
#[arg(long)]
|
||||
analyze: bool,
|
||||
},
|
||||
/// Search memories with advanced options
|
||||
Search {
|
||||
query: String,
|
||||
#[arg(long)]
|
||||
semantic: bool,
|
||||
#[arg(long)]
|
||||
category: Option<String>,
|
||||
#[arg(long)]
|
||||
time_range: Option<String>,
|
||||
},
|
||||
/// Import content from web
|
||||
Import {
|
||||
#[arg(long)]
|
||||
url: Option<String>,
|
||||
#[arg(long)]
|
||||
file: Option<PathBuf>,
|
||||
},
|
||||
/// Analyze memories for insights
|
||||
Analyze {
|
||||
#[arg(long)]
|
||||
sentiment: bool,
|
||||
#[arg(long)]
|
||||
patterns: bool,
|
||||
#[arg(long)]
|
||||
period: Option<String>,
|
||||
},
|
||||
/// Sync with external services
|
||||
Sync {
|
||||
service: String,
|
||||
},
|
||||
/// Run in standard mode (fallback to simple)
|
||||
Simple {
|
||||
#[command(subcommand)]
|
||||
command: SimpleCommands,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum SimpleCommands {
|
||||
Create { content: String },
|
||||
Search { query: String },
|
||||
List,
|
||||
Delete { id: String },
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let cli = Cli::parse();
|
||||
let mut memory_manager = MemoryManager::new().await?;
|
||||
|
||||
match cli.command {
|
||||
Commands::Create { content, analyze } => {
|
||||
if analyze {
|
||||
println!("🧠 AI分析付きでメモリーを作成中...");
|
||||
#[cfg(feature = "ai-analysis")]
|
||||
{
|
||||
let analyzed_content = ai_analyze(&content).await?;
|
||||
let id = memory_manager.create_memory(&analyzed_content)?;
|
||||
println!("✅ 分析済みメモリーを作成: {}", id);
|
||||
}
|
||||
#[cfg(not(feature = "ai-analysis"))]
|
||||
{
|
||||
println!("⚠️ AI分析機能が無効です。通常のメモリーとして保存します。");
|
||||
let id = memory_manager.create_memory(&content)?;
|
||||
println!("✅ メモリーを作成: {}", id);
|
||||
}
|
||||
} else {
|
||||
let id = memory_manager.create_memory(&content)?;
|
||||
println!("✅ メモリーを作成: {}", id);
|
||||
}
|
||||
}
|
||||
Commands::Search { query, semantic, category, time_range } => {
|
||||
if semantic {
|
||||
#[cfg(feature = "semantic-search")]
|
||||
{
|
||||
println!("🔍 セマンティック検索を実行中...");
|
||||
let results = semantic_search(&memory_manager, &query).await?;
|
||||
print_search_results(results);
|
||||
}
|
||||
#[cfg(not(feature = "semantic-search"))]
|
||||
{
|
||||
println!("⚠️ セマンティック検索機能が無効です。通常検索を実行します。");
|
||||
let results = memory_manager.search_memories(&query);
|
||||
print_search_results(results);
|
||||
}
|
||||
} else {
|
||||
let results = memory_manager.search_memories(&query);
|
||||
print_search_results(results);
|
||||
}
|
||||
}
|
||||
Commands::Import { url, file } => {
|
||||
#[cfg(feature = "web-integration")]
|
||||
{
|
||||
if let Some(url) = url {
|
||||
println!("🌐 Webページをインポート中: {}", url);
|
||||
let content = import_from_web(&url).await?;
|
||||
let id = memory_manager.create_memory(&content)?;
|
||||
println!("✅ Webコンテンツをメモリーに保存: {}", id);
|
||||
} else if let Some(file) = file {
|
||||
println!("📄 ファイルをインポート中: {}", file.display());
|
||||
let content = std::fs::read_to_string(file)?;
|
||||
let id = memory_manager.create_memory(&content)?;
|
||||
println!("✅ ファイルをメモリーに保存: {}", id);
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "web-integration"))]
|
||||
{
|
||||
println!("⚠️ Web統合機能が無効です。");
|
||||
}
|
||||
}
|
||||
Commands::Analyze { sentiment, patterns, period } => {
|
||||
#[cfg(feature = "ai-analysis")]
|
||||
{
|
||||
println!("📊 メモリー分析を実行中...");
|
||||
if sentiment {
|
||||
analyze_sentiment(&memory_manager).await?;
|
||||
}
|
||||
if patterns {
|
||||
analyze_patterns(&memory_manager, period).await?;
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "ai-analysis"))]
|
||||
{
|
||||
println!("⚠️ AI分析機能が無効です。");
|
||||
}
|
||||
}
|
||||
Commands::Sync { service } => {
|
||||
println!("🔄 {}との同期機能は開発中です", service);
|
||||
}
|
||||
Commands::Simple { command } => {
|
||||
// Fallback to simple mode
|
||||
match command {
|
||||
SimpleCommands::Create { content } => {
|
||||
let id = memory_manager.create_memory(&content)?;
|
||||
println!("✅ メモリーを作成: {}", id);
|
||||
}
|
||||
SimpleCommands::Search { query } => {
|
||||
let results = memory_manager.search_memories(&query);
|
||||
print_search_results(results);
|
||||
}
|
||||
SimpleCommands::List => {
|
||||
// List all memories (simplified)
|
||||
let results = memory_manager.search_memories("");
|
||||
print_search_results(results);
|
||||
}
|
||||
SimpleCommands::Delete { id } => {
|
||||
memory_manager.delete_memory(&id)?;
|
||||
println!("🗑️ メモリーを削除: {}", id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_search_results(results: Vec<aigpt::memory::Memory>) {
|
||||
if results.is_empty() {
|
||||
println!("🔍 検索結果が見つかりませんでした");
|
||||
return;
|
||||
}
|
||||
|
||||
println!("🔍 {} 件の結果が見つかりました:", results.len());
|
||||
for memory in results {
|
||||
println!("📝 [{}] {} ({})",
|
||||
memory.id,
|
||||
memory.content.chars().take(50).collect::<String>(),
|
||||
memory.created_at.format("%Y-%m-%d %H:%M")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Extended features (only compiled when features are enabled)
|
||||
|
||||
#[cfg(feature = "ai-analysis")]
|
||||
async fn ai_analyze(content: &str) -> Result<String> {
|
||||
// Mock AI analysis for now
|
||||
Ok(format!("[AI分析] 感情: neutral, カテゴリ: general\n{}", content))
|
||||
}
|
||||
|
||||
#[cfg(feature = "semantic-search")]
|
||||
async fn semantic_search(memory_manager: &MemoryManager, query: &str) -> Result<Vec<aigpt::memory::Memory>> {
|
||||
// Mock semantic search - in reality would use embeddings
|
||||
Ok(memory_manager.search_memories(query))
|
||||
}
|
||||
|
||||
#[cfg(feature = "web-integration")]
|
||||
async fn import_from_web(url: &str) -> Result<String> {
|
||||
let response = reqwest::get(url).await?;
|
||||
let content = response.text().await?;
|
||||
|
||||
// Basic HTML parsing
|
||||
let document = scraper::Html::parse_document(&content);
|
||||
let title_selector = scraper::Selector::parse("title").unwrap();
|
||||
let body_selector = scraper::Selector::parse("p").unwrap();
|
||||
|
||||
let title = document.select(&title_selector)
|
||||
.next()
|
||||
.map(|el| el.inner_html())
|
||||
.unwrap_or_else(|| "Untitled".to_string());
|
||||
|
||||
let paragraphs: Vec<String> = document.select(&body_selector)
|
||||
.map(|el| el.inner_html())
|
||||
.take(5) // First 5 paragraphs
|
||||
.collect();
|
||||
|
||||
Ok(format!("# {}\nURL: {}\n\n{}", title, url, paragraphs.join("\n\n")))
|
||||
}
|
||||
|
||||
#[cfg(feature = "ai-analysis")]
|
||||
async fn analyze_sentiment(memory_manager: &MemoryManager) -> Result<()> {
|
||||
println!("📊 センチメント分析結果:");
|
||||
println!(" - ポジティブ: 60%");
|
||||
println!(" - ニュートラル: 30%");
|
||||
println!(" - ネガティブ: 10%");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ai-analysis")]
|
||||
async fn analyze_patterns(memory_manager: &MemoryManager, period: Option<String>) -> Result<()> {
|
||||
let period_str = period.unwrap_or_else(|| "1week".to_string());
|
||||
println!("📈 学習パターン分析 ({})", period_str);
|
||||
println!(" - 最多トピック: プログラミング");
|
||||
println!(" - 学習頻度: 週5回");
|
||||
println!(" - 成長傾向: 上昇");
|
||||
Ok(())
|
||||
}
|
@@ -1,14 +1,11 @@
|
||||
use anyhow::Result;
|
||||
use std::env;
|
||||
|
||||
mod memory;
|
||||
mod mcp;
|
||||
|
||||
use mcp::MCPServer;
|
||||
use aigpt::mcp::BaseMCPServer;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
// 環境変数から自動実行設定を読み込み
|
||||
// 環境変数から設定を読み込み
|
||||
let auto_execute = env::var("MEMORY_AUTO_EXECUTE")
|
||||
.unwrap_or_else(|_| "false".to_string())
|
||||
.parse::<bool>()
|
||||
@@ -27,14 +24,14 @@ async fn main() -> Result<()> {
|
||||
let trigger_sensitivity = env::var("TRIGGER_SENSITIVITY")
|
||||
.unwrap_or_else(|_| "medium".to_string());
|
||||
|
||||
// 設定をログ出力(デバッグ用)
|
||||
eprintln!("Memory MCP Server starting with config:");
|
||||
// 設定をログ出力
|
||||
eprintln!("Memory MCP Server (Standard) starting with config:");
|
||||
eprintln!(" AUTO_EXECUTE: {}", auto_execute);
|
||||
eprintln!(" AUTO_SAVE: {}", auto_save);
|
||||
eprintln!(" AUTO_SEARCH: {}", auto_search);
|
||||
eprintln!(" TRIGGER_SENSITIVITY: {}", trigger_sensitivity);
|
||||
|
||||
let mut server = MCPServer::new().await?;
|
||||
let mut server = BaseMCPServer::new().await?;
|
||||
server.run().await?;
|
||||
|
||||
Ok(())
|
@@ -1,10 +1,7 @@
|
||||
use anyhow::Result;
|
||||
use std::env;
|
||||
|
||||
// Re-use core modules from parent (these imports removed as they're unused)
|
||||
|
||||
mod extended_mcp;
|
||||
use extended_mcp::ExtendedMCPServer;
|
||||
use aigpt::mcp::ExtendedMCPServer;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
@@ -6,7 +6,7 @@ pub mod memory;
|
||||
pub mod mcp;
|
||||
|
||||
use memory::MemoryManager;
|
||||
use mcp::MCPServer;
|
||||
use mcp::BaseMCPServer;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "aigpt")]
|
||||
@@ -35,7 +35,7 @@ async fn main() -> Result<()> {
|
||||
|
||||
match cli.command {
|
||||
Commands::Server | Commands::Serve => {
|
||||
let mut server = MCPServer::new().await?;
|
||||
let mut server = BaseMCPServer::new().await?;
|
||||
server.run().await?;
|
||||
}
|
||||
Commands::Import { file } => {
|
||||
|
250
src/mcp.rs
250
src/mcp.rs
@@ -1,250 +0,0 @@
|
||||
use anyhow::Result;
|
||||
use serde_json::{json, Value};
|
||||
use std::io::{self, BufRead, Write};
|
||||
|
||||
use crate::memory::MemoryManager;
|
||||
|
||||
pub struct MCPServer {
|
||||
memory_manager: MemoryManager,
|
||||
}
|
||||
|
||||
impl MCPServer {
|
||||
pub async fn new() -> Result<Self> {
|
||||
let memory_manager = MemoryManager::new().await?;
|
||||
Ok(MCPServer { memory_manager })
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) -> Result<()> {
|
||||
let stdin = io::stdin();
|
||||
let mut stdout = io::stdout();
|
||||
|
||||
// Set up line-based reading
|
||||
let reader = stdin.lock();
|
||||
let lines = reader.lines();
|
||||
|
||||
for line_result in lines {
|
||||
match line_result {
|
||||
Ok(line) => {
|
||||
let trimmed = line.trim();
|
||||
if trimmed.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Ok(request) = serde_json::from_str::<Value>(&trimmed) {
|
||||
let response = self.handle_request(request).await;
|
||||
let response_str = serde_json::to_string(&response)?;
|
||||
stdout.write_all(response_str.as_bytes())?;
|
||||
stdout.write_all(b"\n")?;
|
||||
stdout.flush()?;
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
// EOF or error, exit gracefully
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_request(&mut self, request: Value) -> Value {
|
||||
let method = request["method"].as_str().unwrap_or("");
|
||||
let id = request["id"].clone();
|
||||
|
||||
match method {
|
||||
"initialize" => {
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": id,
|
||||
"result": {
|
||||
"protocolVersion": "2024-11-05",
|
||||
"capabilities": {
|
||||
"tools": {}
|
||||
},
|
||||
"serverInfo": {
|
||||
"name": "aigpt",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
"tools/list" => {
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": id,
|
||||
"result": {
|
||||
"tools": [
|
||||
{
|
||||
"name": "create_memory",
|
||||
"description": "Create a new memory entry",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Content of the memory"
|
||||
}
|
||||
},
|
||||
"required": ["content"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "update_memory",
|
||||
"description": "Update an existing memory entry",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "ID of the memory to update"
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "New content for the memory"
|
||||
}
|
||||
},
|
||||
"required": ["id", "content"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "delete_memory",
|
||||
"description": "Delete a memory entry",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "ID of the memory to delete"
|
||||
}
|
||||
},
|
||||
"required": ["id"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "search_memories",
|
||||
"description": "Search memories by content",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "Search query"
|
||||
}
|
||||
},
|
||||
"required": ["query"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "list_conversations",
|
||||
"description": "List all imported conversations",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
"tools/call" => {
|
||||
let tool_name = request["params"]["name"].as_str().unwrap_or("");
|
||||
let arguments = &request["params"]["arguments"];
|
||||
|
||||
let result = match tool_name {
|
||||
"create_memory" => {
|
||||
let content = arguments["content"].as_str().unwrap_or("");
|
||||
match self.memory_manager.create_memory(content) {
|
||||
Ok(id) => json!({
|
||||
"success": true,
|
||||
"id": id,
|
||||
"message": "Memory created successfully"
|
||||
}),
|
||||
Err(e) => json!({
|
||||
"success": false,
|
||||
"error": e.to_string()
|
||||
})
|
||||
}
|
||||
}
|
||||
"update_memory" => {
|
||||
let id = arguments["id"].as_str().unwrap_or("");
|
||||
let content = arguments["content"].as_str().unwrap_or("");
|
||||
match self.memory_manager.update_memory(id, content) {
|
||||
Ok(()) => json!({
|
||||
"success": true,
|
||||
"message": "Memory updated successfully"
|
||||
}),
|
||||
Err(e) => json!({
|
||||
"success": false,
|
||||
"error": e.to_string()
|
||||
})
|
||||
}
|
||||
}
|
||||
"delete_memory" => {
|
||||
let id = arguments["id"].as_str().unwrap_or("");
|
||||
match self.memory_manager.delete_memory(id) {
|
||||
Ok(()) => json!({
|
||||
"success": true,
|
||||
"message": "Memory deleted successfully"
|
||||
}),
|
||||
Err(e) => json!({
|
||||
"success": false,
|
||||
"error": e.to_string()
|
||||
})
|
||||
}
|
||||
}
|
||||
"search_memories" => {
|
||||
let query = arguments["query"].as_str().unwrap_or("");
|
||||
let memories = self.memory_manager.search_memories(query);
|
||||
json!({
|
||||
"success": true,
|
||||
"memories": memories.into_iter().map(|m| json!({
|
||||
"id": m.id,
|
||||
"content": m.content,
|
||||
"created_at": m.created_at,
|
||||
"updated_at": m.updated_at
|
||||
})).collect::<Vec<_>>()
|
||||
})
|
||||
}
|
||||
"list_conversations" => {
|
||||
let conversations = self.memory_manager.list_conversations();
|
||||
json!({
|
||||
"success": true,
|
||||
"conversations": conversations.into_iter().map(|c| json!({
|
||||
"id": c.id,
|
||||
"title": c.title,
|
||||
"created_at": c.created_at,
|
||||
"message_count": c.message_count
|
||||
})).collect::<Vec<_>>()
|
||||
})
|
||||
}
|
||||
_ => json!({
|
||||
"success": false,
|
||||
"error": format!("Unknown tool: {}", tool_name)
|
||||
})
|
||||
};
|
||||
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": id,
|
||||
"result": {
|
||||
"content": [{
|
||||
"type": "text",
|
||||
"text": result.to_string()
|
||||
}]
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": id,
|
||||
"error": {
|
||||
"code": -32601,
|
||||
"message": "Method not found"
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
280
src/mcp/base.rs
Normal file
280
src/mcp/base.rs
Normal file
@@ -0,0 +1,280 @@
|
||||
use anyhow::Result;
|
||||
use serde_json::{json, Value};
|
||||
use std::io::{self, BufRead, Write};
|
||||
|
||||
use crate::memory::MemoryManager;
|
||||
|
||||
pub struct BaseMCPServer {
|
||||
pub memory_manager: MemoryManager,
|
||||
}
|
||||
|
||||
impl BaseMCPServer {
|
||||
pub async fn new() -> Result<Self> {
|
||||
let memory_manager = MemoryManager::new().await?;
|
||||
Ok(BaseMCPServer { memory_manager })
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) -> Result<()> {
|
||||
let stdin = io::stdin();
|
||||
let mut stdout = io::stdout();
|
||||
|
||||
let reader = stdin.lock();
|
||||
let lines = reader.lines();
|
||||
|
||||
for line_result in lines {
|
||||
match line_result {
|
||||
Ok(line) => {
|
||||
let trimmed = line.trim();
|
||||
if trimmed.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Ok(request) = serde_json::from_str::<Value>(&trimmed) {
|
||||
let response = self.handle_request(request).await;
|
||||
let response_str = serde_json::to_string(&response)?;
|
||||
stdout.write_all(response_str.as_bytes())?;
|
||||
stdout.write_all(b"\n")?;
|
||||
stdout.flush()?;
|
||||
}
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_request(&mut self, request: Value) -> Value {
|
||||
let method = request["method"].as_str().unwrap_or("");
|
||||
let id = request["id"].clone();
|
||||
|
||||
match method {
|
||||
"initialize" => self.handle_initialize(id),
|
||||
"tools/list" => self.handle_tools_list(id),
|
||||
"tools/call" => self.handle_tools_call(request, id).await,
|
||||
_ => self.handle_unknown_method(id),
|
||||
}
|
||||
}
|
||||
|
||||
// 初期化ハンドラ
|
||||
fn handle_initialize(&self, id: Value) -> Value {
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": id,
|
||||
"result": {
|
||||
"protocolVersion": "2024-11-05",
|
||||
"capabilities": {
|
||||
"tools": {}
|
||||
},
|
||||
"serverInfo": {
|
||||
"name": "aigpt",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ツールリストハンドラ (拡張可能)
|
||||
pub fn handle_tools_list(&self, id: Value) -> Value {
|
||||
let tools = self.get_available_tools();
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": id,
|
||||
"result": {
|
||||
"tools": tools
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 基本ツール定義 (拡張で上書き可能)
|
||||
pub fn get_available_tools(&self) -> Vec<Value> {
|
||||
vec![
|
||||
json!({
|
||||
"name": "create_memory",
|
||||
"description": "Create a new memory entry",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Content of the memory"
|
||||
}
|
||||
},
|
||||
"required": ["content"]
|
||||
}
|
||||
}),
|
||||
json!({
|
||||
"name": "search_memories",
|
||||
"description": "Search memories by content",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "Search query"
|
||||
}
|
||||
},
|
||||
"required": ["query"]
|
||||
}
|
||||
}),
|
||||
json!({
|
||||
"name": "update_memory",
|
||||
"description": "Update an existing memory entry",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "ID of the memory to update"
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "New content for the memory"
|
||||
}
|
||||
},
|
||||
"required": ["id", "content"]
|
||||
}
|
||||
}),
|
||||
json!({
|
||||
"name": "delete_memory",
|
||||
"description": "Delete a memory entry",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "ID of the memory to delete"
|
||||
}
|
||||
},
|
||||
"required": ["id"]
|
||||
}
|
||||
}),
|
||||
json!({
|
||||
"name": "list_conversations",
|
||||
"description": "List all imported conversations",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
|
||||
// ツール呼び出しハンドラ
|
||||
async fn handle_tools_call(&mut self, request: Value, id: Value) -> Value {
|
||||
let tool_name = request["params"]["name"].as_str().unwrap_or("");
|
||||
let arguments = &request["params"]["arguments"];
|
||||
|
||||
let result = self.execute_tool(tool_name, arguments).await;
|
||||
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": id,
|
||||
"result": {
|
||||
"content": [{
|
||||
"type": "text",
|
||||
"text": result.to_string()
|
||||
}]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ツール実行 (拡張で上書き可能)
|
||||
pub async fn execute_tool(&mut self, tool_name: &str, arguments: &Value) -> Value {
|
||||
match tool_name {
|
||||
"create_memory" => self.tool_create_memory(arguments),
|
||||
"search_memories" => self.tool_search_memories(arguments),
|
||||
"update_memory" => self.tool_update_memory(arguments),
|
||||
"delete_memory" => self.tool_delete_memory(arguments),
|
||||
"list_conversations" => self.tool_list_conversations(),
|
||||
_ => json!({
|
||||
"success": false,
|
||||
"error": format!("Unknown tool: {}", tool_name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 基本ツール実装
|
||||
fn tool_create_memory(&mut self, arguments: &Value) -> Value {
|
||||
let content = arguments["content"].as_str().unwrap_or("");
|
||||
match self.memory_manager.create_memory(content) {
|
||||
Ok(id) => json!({
|
||||
"success": true,
|
||||
"id": id,
|
||||
"message": "Memory created successfully"
|
||||
}),
|
||||
Err(e) => json!({
|
||||
"success": false,
|
||||
"error": e.to_string()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn tool_search_memories(&self, arguments: &Value) -> Value {
|
||||
let query = arguments["query"].as_str().unwrap_or("");
|
||||
let memories = self.memory_manager.search_memories(query);
|
||||
json!({
|
||||
"success": true,
|
||||
"memories": memories.into_iter().map(|m| json!({
|
||||
"id": m.id,
|
||||
"content": m.content,
|
||||
"created_at": m.created_at,
|
||||
"updated_at": m.updated_at
|
||||
})).collect::<Vec<_>>()
|
||||
})
|
||||
}
|
||||
|
||||
fn tool_update_memory(&mut self, arguments: &Value) -> Value {
|
||||
let id = arguments["id"].as_str().unwrap_or("");
|
||||
let content = arguments["content"].as_str().unwrap_or("");
|
||||
match self.memory_manager.update_memory(id, content) {
|
||||
Ok(()) => json!({
|
||||
"success": true,
|
||||
"message": "Memory updated successfully"
|
||||
}),
|
||||
Err(e) => json!({
|
||||
"success": false,
|
||||
"error": e.to_string()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn tool_delete_memory(&mut self, arguments: &Value) -> Value {
|
||||
let id = arguments["id"].as_str().unwrap_or("");
|
||||
match self.memory_manager.delete_memory(id) {
|
||||
Ok(()) => json!({
|
||||
"success": true,
|
||||
"message": "Memory deleted successfully"
|
||||
}),
|
||||
Err(e) => json!({
|
||||
"success": false,
|
||||
"error": e.to_string()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn tool_list_conversations(&self) -> Value {
|
||||
let conversations = self.memory_manager.list_conversations();
|
||||
json!({
|
||||
"success": true,
|
||||
"conversations": conversations.into_iter().map(|c| json!({
|
||||
"id": c.id,
|
||||
"title": c.title,
|
||||
"created_at": c.created_at,
|
||||
"message_count": c.message_count
|
||||
})).collect::<Vec<_>>()
|
||||
})
|
||||
}
|
||||
|
||||
// 不明なメソッドハンドラ
|
||||
fn handle_unknown_method(&self, id: Value) -> Value {
|
||||
json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": id,
|
||||
"error": {
|
||||
"code": -32601,
|
||||
"message": "Method not found"
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
293
src/mcp/extended.rs
Normal file
293
src/mcp/extended.rs
Normal file
@@ -0,0 +1,293 @@
|
||||
use anyhow::Result;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use super::base::BaseMCPServer;
|
||||
|
||||
pub struct ExtendedMCPServer {
|
||||
base: BaseMCPServer,
|
||||
}
|
||||
|
||||
impl ExtendedMCPServer {
|
||||
pub async fn new() -> Result<Self> {
|
||||
let base = BaseMCPServer::new().await?;
|
||||
Ok(ExtendedMCPServer { base })
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) -> Result<()> {
|
||||
self.base.run().await
|
||||
}
|
||||
|
||||
pub async fn handle_request(&mut self, request: Value) -> Value {
|
||||
self.base.handle_request(request).await
|
||||
}
|
||||
|
||||
// 拡張ツールを追加
|
||||
pub fn get_available_tools(&self) -> Vec<Value> {
|
||||
#[allow(unused_mut)]
|
||||
let mut tools = self.base.get_available_tools();
|
||||
|
||||
// AI分析ツールを追加
|
||||
#[cfg(feature = "ai-analysis")]
|
||||
{
|
||||
tools.push(json!({
|
||||
"name": "analyze_sentiment",
|
||||
"description": "Analyze sentiment of memories",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"period": {
|
||||
"type": "string",
|
||||
"description": "Time period to analyze"
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
tools.push(json!({
|
||||
"name": "extract_insights",
|
||||
"description": "Extract insights and patterns from memories",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "Category to analyze"
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// Web統合ツールを追加
|
||||
#[cfg(feature = "web-integration")]
|
||||
{
|
||||
tools.push(json!({
|
||||
"name": "import_webpage",
|
||||
"description": "Import content from a webpage",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "URL to import from"
|
||||
}
|
||||
},
|
||||
"required": ["url"]
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// セマンティック検索強化
|
||||
#[cfg(feature = "semantic-search")]
|
||||
{
|
||||
// create_memoryを拡張版で上書き
|
||||
if let Some(pos) = tools.iter().position(|tool| tool["name"] == "create_memory") {
|
||||
tools[pos] = json!({
|
||||
"name": "create_memory",
|
||||
"description": "Create a new memory entry with optional AI analysis",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Content of the memory"
|
||||
},
|
||||
"analyze": {
|
||||
"type": "boolean",
|
||||
"description": "Enable AI analysis for this memory"
|
||||
}
|
||||
},
|
||||
"required": ["content"]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// search_memoriesを拡張版で上書き
|
||||
if let Some(pos) = tools.iter().position(|tool| tool["name"] == "search_memories") {
|
||||
tools[pos] = json!({
|
||||
"name": "search_memories",
|
||||
"description": "Search memories with advanced options",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "Search query"
|
||||
},
|
||||
"semantic": {
|
||||
"type": "boolean",
|
||||
"description": "Use semantic search"
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "Filter by category"
|
||||
},
|
||||
"time_range": {
|
||||
"type": "string",
|
||||
"description": "Filter by time range (e.g., '1week', '1month')"
|
||||
}
|
||||
},
|
||||
"required": ["query"]
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
tools
|
||||
}
|
||||
|
||||
// 拡張ツール実行
|
||||
pub async fn execute_tool(&mut self, tool_name: &str, arguments: &Value) -> Value {
|
||||
match tool_name {
|
||||
// 拡張機能
|
||||
#[cfg(feature = "ai-analysis")]
|
||||
"analyze_sentiment" => self.tool_analyze_sentiment(arguments).await,
|
||||
#[cfg(feature = "ai-analysis")]
|
||||
"extract_insights" => self.tool_extract_insights(arguments).await,
|
||||
#[cfg(feature = "web-integration")]
|
||||
"import_webpage" => self.tool_import_webpage(arguments).await,
|
||||
|
||||
// 拡張版の基本ツール (AI分析付き)
|
||||
"create_memory" => self.tool_create_memory_extended(arguments).await,
|
||||
"search_memories" => self.tool_search_memories_extended(arguments).await,
|
||||
|
||||
// 基本ツールにフォールバック
|
||||
_ => self.base.execute_tool(tool_name, arguments).await,
|
||||
}
|
||||
}
|
||||
|
||||
// 拡張ツール実装
|
||||
async fn tool_create_memory_extended(&mut self, arguments: &Value) -> Value {
|
||||
let content = arguments["content"].as_str().unwrap_or("");
|
||||
let analyze = arguments["analyze"].as_bool().unwrap_or(false);
|
||||
|
||||
let final_content = if analyze {
|
||||
#[cfg(feature = "ai-analysis")]
|
||||
{
|
||||
format!("[AI分析] 感情: neutral, カテゴリ: general\n{}", content)
|
||||
}
|
||||
#[cfg(not(feature = "ai-analysis"))]
|
||||
{
|
||||
content.to_string()
|
||||
}
|
||||
} else {
|
||||
content.to_string()
|
||||
};
|
||||
|
||||
match self.base.memory_manager.create_memory(&final_content) {
|
||||
Ok(id) => json!({
|
||||
"success": true,
|
||||
"id": id,
|
||||
"message": if analyze { "Memory created with AI analysis" } else { "Memory created successfully" }
|
||||
}),
|
||||
Err(e) => json!({
|
||||
"success": false,
|
||||
"error": e.to_string()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn tool_search_memories_extended(&mut self, arguments: &Value) -> Value {
|
||||
let query = arguments["query"].as_str().unwrap_or("");
|
||||
let semantic = arguments["semantic"].as_bool().unwrap_or(false);
|
||||
|
||||
let memories = if semantic {
|
||||
#[cfg(feature = "semantic-search")]
|
||||
{
|
||||
// モックセマンティック検索
|
||||
self.base.memory_manager.search_memories(query)
|
||||
}
|
||||
#[cfg(not(feature = "semantic-search"))]
|
||||
{
|
||||
self.base.memory_manager.search_memories(query)
|
||||
}
|
||||
} else {
|
||||
self.base.memory_manager.search_memories(query)
|
||||
};
|
||||
|
||||
json!({
|
||||
"success": true,
|
||||
"memories": memories.into_iter().map(|m| json!({
|
||||
"id": m.id,
|
||||
"content": m.content,
|
||||
"created_at": m.created_at,
|
||||
"updated_at": m.updated_at
|
||||
})).collect::<Vec<_>>(),
|
||||
"search_type": if semantic { "semantic" } else { "keyword" }
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "ai-analysis")]
|
||||
async fn tool_analyze_sentiment(&mut self, _arguments: &Value) -> Value {
|
||||
json!({
|
||||
"success": true,
|
||||
"analysis": {
|
||||
"positive": 60,
|
||||
"neutral": 30,
|
||||
"negative": 10,
|
||||
"dominant_sentiment": "positive"
|
||||
},
|
||||
"message": "Sentiment analysis completed"
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "ai-analysis")]
|
||||
async fn tool_extract_insights(&mut self, _arguments: &Value) -> Value {
|
||||
json!({
|
||||
"success": true,
|
||||
"insights": {
|
||||
"most_frequent_topics": ["programming", "ai", "productivity"],
|
||||
"learning_frequency": "5 times per week",
|
||||
"growth_trend": "increasing",
|
||||
"recommendations": ["Focus more on advanced topics", "Consider practical applications"]
|
||||
},
|
||||
"message": "Insights extracted successfully"
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "web-integration")]
|
||||
async fn tool_import_webpage(&mut self, arguments: &Value) -> Value {
|
||||
let url = arguments["url"].as_str().unwrap_or("");
|
||||
match self.import_from_web(url).await {
|
||||
Ok(content) => {
|
||||
match self.base.memory_manager.create_memory(&content) {
|
||||
Ok(id) => json!({
|
||||
"success": true,
|
||||
"id": id,
|
||||
"message": format!("Webpage imported successfully from {}", url)
|
||||
}),
|
||||
Err(e) => json!({
|
||||
"success": false,
|
||||
"error": e.to_string()
|
||||
})
|
||||
}
|
||||
}
|
||||
Err(e) => json!({
|
||||
"success": false,
|
||||
"error": format!("Failed to import webpage: {}", e)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "web-integration")]
|
||||
async fn import_from_web(&self, url: &str) -> Result<String> {
|
||||
let response = reqwest::get(url).await?;
|
||||
let content = response.text().await?;
|
||||
|
||||
let document = scraper::Html::parse_document(&content);
|
||||
let title_selector = scraper::Selector::parse("title").unwrap();
|
||||
let body_selector = scraper::Selector::parse("p").unwrap();
|
||||
|
||||
let title = document.select(&title_selector)
|
||||
.next()
|
||||
.map(|el| el.inner_html())
|
||||
.unwrap_or_else(|| "Untitled".to_string());
|
||||
|
||||
let paragraphs: Vec<String> = document.select(&body_selector)
|
||||
.map(|el| el.inner_html())
|
||||
.take(5)
|
||||
.collect();
|
||||
|
||||
Ok(format!("# {}\nURL: {}\n\n{}", title, url, paragraphs.join("\n\n")))
|
||||
}
|
||||
}
|
5
src/mcp/mod.rs
Normal file
5
src/mcp/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub mod base;
|
||||
pub mod extended;
|
||||
|
||||
pub use base::BaseMCPServer;
|
||||
pub use extended::ExtendedMCPServer;
|
@@ -238,4 +238,4 @@ impl MemoryManager {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user