This commit is contained in:
2025-07-29 05:04:15 +09:00
parent 4620d0862a
commit 62b91e5e5a
12 changed files with 589 additions and 918 deletions

View File

@@ -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

View File

@@ -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")))
}
}

View File

@@ -1,3 +0,0 @@
// Re-export core modules to make them available to extended version
pub use aigpt::memory;
pub use aigpt::mcp;

View File

@@ -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(())
}

View File

@@ -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(())

View File

@@ -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<()> {

View File

@@ -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 } => {

View File

@@ -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
View 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
View 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
View File

@@ -0,0 +1,5 @@
pub mod base;
pub mod extended;
pub use base::BaseMCPServer;
pub use extended::ExtendedMCPServer;

View File

@@ -238,4 +238,4 @@ impl MemoryManager {
Ok(())
}
}
}