add extended

This commit is contained in:
2025-07-29 04:08:29 +09:00
parent 93b523b1ba
commit 4620d0862a
16 changed files with 1189 additions and 21 deletions

View File

@@ -0,0 +1,398 @@
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")))
}
}

3
extended/src/lib.rs Normal file
View File

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

250
extended/src/main.rs Normal file
View File

@@ -0,0 +1,250 @@
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

@@ -0,0 +1,48 @@
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;
#[tokio::main]
async fn main() -> Result<()> {
// 環境変数から拡張機能の設定を読み込み
let auto_execute = env::var("MEMORY_AUTO_EXECUTE")
.unwrap_or_else(|_| "false".to_string())
.parse::<bool>()
.unwrap_or(false);
let auto_save = env::var("MEMORY_AUTO_SAVE")
.unwrap_or_else(|_| "false".to_string())
.parse::<bool>()
.unwrap_or(false);
let auto_search = env::var("MEMORY_AUTO_SEARCH")
.unwrap_or_else(|_| "false".to_string())
.parse::<bool>()
.unwrap_or(false);
let trigger_sensitivity = env::var("TRIGGER_SENSITIVITY")
.unwrap_or_else(|_| "medium".to_string());
let enable_ai_analysis = cfg!(feature = "ai-analysis");
let enable_semantic_search = cfg!(feature = "semantic-search");
let enable_web_integration = cfg!(feature = "web-integration");
// 拡張設定をログ出力
eprintln!("Memory MCP Server (Extended) starting with config:");
eprintln!(" AUTO_EXECUTE: {}", auto_execute);
eprintln!(" AUTO_SAVE: {}", auto_save);
eprintln!(" AUTO_SEARCH: {}", auto_search);
eprintln!(" TRIGGER_SENSITIVITY: {}", trigger_sensitivity);
eprintln!(" AI_ANALYSIS: {}", enable_ai_analysis);
eprintln!(" SEMANTIC_SEARCH: {}", enable_semantic_search);
eprintln!(" WEB_INTEGRATION: {}", enable_web_integration);
let mut server = ExtendedMCPServer::new().await?;
server.run().await?;
Ok(())
}