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

1
.gitignore vendored
View File

@@ -21,3 +21,4 @@ Thumbs.db
*.log
json
gpt
.claude

View File

@@ -5,10 +5,19 @@ edition = "2021"
authors = ["syui"]
description = "Simple memory storage for Claude with MCP"
[[bin]]
name = "aigpt"
path = "src/main.rs"
[[bin]]
name = "memory-mcp"
path = "src/mcp_server.rs"
[[bin]]
name = "memory-mcp-extended"
path = "extended/src/mcp_server.rs"
[dependencies]
# CLI and async
clap = { version = "4.5", features = ["derive"] }
@@ -25,3 +34,15 @@ uuid = { version = "1.10", features = ["v4"] }
# Error handling and utilities
anyhow = "1.0"
dirs = "5.0"
# Extended features (optional)
reqwest = { version = "0.11", features = ["json"], optional = true }
scraper = { version = "0.18", optional = true }
openai = { version = "1.1", optional = true }
[features]
default = []
extended = ["semantic-search", "ai-analysis", "web-integration"]
semantic-search = ["openai"]
ai-analysis = ["openai"]
web-integration = ["reqwest", "scraper"]

View File

@@ -6,30 +6,90 @@ claude desktop, claude codeで使用できるmemory機能をmcpで作ります
2. chatgptのjsonをimportできる @json/
3. rustで作る
## 自動メモリー保存のルール
## 自動実行設定
```json
{
"mcp_auto_execute": true,
"memory_auto_save": true,
"memory_auto_search": true,
"trigger_sensitivity": "high"
}
```
以下の情報が会話に現れた場合、自動的にcreate_memory MCPツールを使用して保存してください
## 自動メモリー保存のルール(強化版)
1. **ユーザーの個人情報**
- 名前、誕生日、住所など
- 好きなもの、嫌いなもの
- 習慣や予定
**即座に自動実行** - 以下の情報が会話に現れた瞬間、create_memory MCPツールを自動実行
2. **重要な決定事項**
- プロジェクトの決定
- 設定の変更
- 今後の計画
### 1. **ユーザーの個人情報**
**トリガーワード**: 名前、誕生日、住所、年齢、職業、家族、出身
- 基本情報: 名前、誕生日、住所、年齢、性別
- 関係性: 家族構成、恋人、友人関係
- 好み: 好きなもの、嫌いなもの、趣味、音楽、映画、本
- 習慣: 日課、スケジュール、ルーティン
- 価値観: 信念、考え方、人生観
3. **技術的な解決策**
- 問題の解決方法
- 有用なコマンドやコード
- 設定手順
### 2. **重要な決定事項**
**トリガーワード**: 決めた、決定、方針、計画、予定、目標
- プロジェクト方針の決定
- 技術選択の決定
- 設定・環境の変更
- 今後のロードマップ
- 作業分担・役割
## 自動メモリー検索のルール
### 3. **技術的な解決策**
**トリガーワード**: 解決、修正、対処、設定、インストール、手順
- エラーの解決方法
- 有用なコマンド・スクリプト
- 設定手順・インストール方法
- デバッグテクニック
- 最適化手法
以下の場合、自動的にsearch_memories MCPツールを使用してください
### 4. **学習・発見事項**
**トリガーワード**: 学んだ、わかった、発見、理解、気づき
- 新しい知識・概念の理解
- ツール・ライブラリの使い方
- ベストプラクティス
- 失敗から得た教訓
1. ユーザーが「前に話した」「以前の」などと言及した場合
2. 過去の会話や情報を参照する必要がある場合
3. ユーザーの好みや設定を確認する必要がある場合
## 自動メモリー検索のルール(強化版)
**会話開始時に自動実行** - search_memories を実行してコンテキストを取得
**即座に自動実行** - 以下の場合、search_memories MCPツールを自動実行
### 1. **過去参照キーワード検出**
**トリガーワード**: 前に、以前、昔、過去、先ほど、さっき、この間
- 「前に話した〜」
- 「以前設定した〜」
- 「昔やった〜」
### 2. **記憶呼び出しキーワード**
**トリガーワード**: 覚えている、記録、メモ、保存、履歴
- 「覚えていますか?」
- 「記録していた〜」
- 「メモした〜」
### 3. **設定・好み確認**
**トリガーワード**: 好み、設定、環境、構成、preferences
- ユーザーの好みを確認する必要がある場合
- 過去の設定を参照する必要がある場合
- 環境構成を確認する必要がある場合
### 4. **不明な参照**
- ユーザーが具体的でない参照をした場合
- 「あれ」「それ」「例のやつ」などの曖昧な表現
- 文脈から過去の情報が必要と判断される場合
## 自動実行タイミング
1. **会話開始時**: search_memories を実行してコンテキスト取得
2. **リアルタイム**: トリガーワード検出後、即座にMCPツール実行
3. **会話終了時**: 重要な情報があれば create_memory で保存
4. **定期的**: 長い会話では中間地点でメモリー整理
## エラーハンドリング
- MCPツールが利用できない場合は通常の会話を継続
- メモリー保存失敗時はユーザーに通知
- 検索結果が空の場合も適切に対応

125
docs/README_CONFIG.md Normal file
View File

@@ -0,0 +1,125 @@
# Claude Memory MCP 設定ガイド
## モード選択
### 標準モード (Simple Mode)
- 基本的なメモリー機能のみ
- 軽量で高速
- 最小限の依存関係
### 拡張モード (Extended Mode)
- AI分析機能
- セマンティック検索
- Web統合機能
- 高度なインサイト抽出
## ビルド・実行方法
### 標準モード
```bash
# MCPサーバー起動
cargo run --bin memory-mcp
# CLI実行
cargo run --bin aigpt -- create "メモリー内容"
```
### 拡張モード
```bash
# MCPサーバー起動
cargo run --bin memory-mcp-extended --features extended
# CLI実行
cargo run --bin aigpt-extended --features extended -- create "メモリー内容" --analyze
```
## 設定ファイルの配置
### 標準モード
#### Claude Desktop
```bash
# macOS
cp claude_desktop_config.json ~/.config/claude-desktop/claude_desktop_config.json
# Windows
cp claude_desktop_config.json %APPDATA%\Claude\claude_desktop_config.json
```
#### Claude Code
```bash
# プロジェクトルートまたはグローバル設定
cp claude_code_config.json .claude/config.json
# または
cp claude_code_config.json ~/.claude/config.json
```
### 拡張モード
#### Claude Desktop
```bash
# macOS
cp claude_desktop_config_extended.json ~/.config/claude-desktop/claude_desktop_config.json
# Windows
cp claude_desktop_config_extended.json %APPDATA%\Claude\claude_desktop_config.json
```
#### Claude Code
```bash
# プロジェクトルートまたはグローバル設定
cp claude_code_config_extended.json .claude/config.json
# または
cp claude_code_config_extended.json ~/.claude/config.json
```
## 環境変数設定
```bash
export MEMORY_AUTO_EXECUTE=true
export MEMORY_AUTO_SAVE=true
export MEMORY_AUTO_SEARCH=true
export TRIGGER_SENSITIVITY=high
export MEMORY_DB_PATH=~/.claude/memory.db
```
## 設定オプション
### auto_execute
- `true`: 自動でMCPツールを実行
- `false`: 手動実行のみ
### trigger_sensitivity
- `high`: 多くのキーワードで反応
- `medium`: 適度な反応
- `low`: 明確なキーワードのみ
### max_memories
メモリーの最大保存数
### search_limit
検索結果の最大表示数
## カスタマイズ
`trigger_words`セクションでトリガーワードをカスタマイズ可能:
```json
"trigger_words": {
"custom_category": ["カスタム", "キーワード", "リスト"]
}
```
## トラブルシューティング
1. MCPサーバーが起動しない場合:
- Rustがインストールされているか確認
- `cargo build --release`でビルド確認
2. 自動実行されない場合:
- 環境変数が正しく設定されているか確認
- トリガーワードが含まれているか確認
3. メモリーが保存されない場合:
- データベースファイルのパスが正しいか確認
- 書き込み権限があるか確認

View File

@@ -0,0 +1,58 @@
{
"mcpServers": {
"memory": {
"command": "cargo",
"args": ["run", "--release", "--bin", "memory-mcp"],
"cwd": "/Users/syui/ai/ai/gpt",
"env": {
"MEMORY_AUTO_EXECUTE": "true",
"MEMORY_AUTO_SAVE": "true",
"MEMORY_AUTO_SEARCH": "true",
"TRIGGER_SENSITIVITY": "high",
"MEMORY_DB_PATH": "~/.claude/memory.db"
}
}
},
"tools": {
"memory": {
"enabled": true,
"auto_execute": true
}
},
"workspace": {
"memory_integration": true,
"auto_save_on_file_change": true,
"auto_search_on_context_switch": true
},
"memory": {
"auto_execute": true,
"auto_save": true,
"auto_search": true,
"trigger_sensitivity": "high",
"max_memories": 10000,
"search_limit": 50,
"session_memory": true,
"cross_session_memory": true,
"trigger_words": {
"personal_info": ["名前", "誕生日", "住所", "年齢", "職業", "家族", "出身", "好き", "嫌い", "趣味"],
"decisions": ["決めた", "決定", "方針", "計画", "予定", "目標"],
"solutions": ["解決", "修正", "対処", "設定", "インストール", "手順"],
"learning": ["学んだ", "わかった", "発見", "理解", "気づき"],
"past_reference": ["前に", "以前", "昔", "過去", "先ほど", "さっき", "この間"],
"memory_recall": ["覚えている", "記録", "メモ", "保存", "履歴"],
"preferences": ["好み", "設定", "環境", "構成", "preferences"],
"vague_reference": ["あれ", "それ", "例のやつ"]
}
},
"hooks": {
"on_conversation_start": [
"search_memories --limit 10 --recent"
],
"on_trigger_word": [
"auto_execute_memory_tools"
],
"on_conversation_end": [
"save_important_memories"
]
}
}

View File

@@ -0,0 +1,81 @@
{
"mcpServers": {
"memory-extended": {
"command": "cargo",
"args": ["run", "--bin", "memory-mcp-extended", "--features", "extended"],
"cwd": "/Users/syui/ai/ai/gpt",
"env": {
"MEMORY_AUTO_EXECUTE": "true",
"MEMORY_AUTO_SAVE": "true",
"MEMORY_AUTO_SEARCH": "true",
"TRIGGER_SENSITIVITY": "high",
"MEMORY_DB_PATH": "~/.claude/memory.db",
"OPENAI_API_KEY": "${OPENAI_API_KEY}"
}
}
},
"tools": {
"memory": {
"enabled": true,
"auto_execute": true,
"mode": "extended"
}
},
"workspace": {
"memory_integration": true,
"auto_save_on_file_change": true,
"auto_search_on_context_switch": true,
"ai_analysis_on_code_review": true,
"web_integration_for_docs": true
},
"memory": {
"mode": "extended",
"auto_execute": true,
"auto_save": true,
"auto_search": true,
"trigger_sensitivity": "high",
"max_memories": 10000,
"search_limit": 50,
"session_memory": true,
"cross_session_memory": true,
"features": {
"ai_analysis": true,
"semantic_search": true,
"web_integration": true,
"sentiment_analysis": true,
"pattern_recognition": true,
"code_analysis": true,
"documentation_import": true
},
"trigger_words": {
"personal_info": ["名前", "誕生日", "住所", "年齢", "職業", "家族", "出身", "好き", "嫌い", "趣味"],
"decisions": ["決めた", "決定", "方針", "計画", "予定", "目標"],
"solutions": ["解決", "修正", "対処", "設定", "インストール", "手順"],
"learning": ["学んだ", "わかった", "発見", "理解", "気づき"],
"past_reference": ["前に", "以前", "昔", "過去", "先ほど", "さっき", "この間"],
"memory_recall": ["覚えている", "記録", "メモ", "保存", "履歴"],
"preferences": ["好み", "設定", "環境", "構成", "preferences"],
"vague_reference": ["あれ", "それ", "例のやつ"],
"web_content": ["URL", "リンク", "サイト", "ページ", "記事", "ドキュメント"],
"analysis_request": ["分析", "パターン", "傾向", "インサイト", "統計", "レビュー"],
"code_related": ["関数", "クラス", "メソッド", "変数", "バグ", "リファクタリング"]
}
},
"hooks": {
"on_conversation_start": [
"search_memories --limit 10 --recent --semantic"
],
"on_trigger_word": [
"auto_execute_memory_tools --with-analysis"
],
"on_conversation_end": [
"save_important_memories --with-insights"
],
"on_code_change": [
"analyze_code_patterns --auto-save"
],
"on_web_reference": [
"import_webpage --auto-categorize"
]
}
}

View File

@@ -0,0 +1,34 @@
{
"mcpServers": {
"memory": {
"command": "cargo",
"args": ["run", "--release", "--bin", "memory-mcp"],
"cwd": "/Users/syui/ai/ai/gpt",
"env": {
"MEMORY_AUTO_EXECUTE": "true",
"MEMORY_AUTO_SAVE": "true",
"MEMORY_AUTO_SEARCH": "true",
"TRIGGER_SENSITIVITY": "high",
"MEMORY_DB_PATH": "~/.claude/memory.db"
}
}
},
"memory": {
"auto_execute": true,
"auto_save": true,
"auto_search": true,
"trigger_sensitivity": "high",
"max_memories": 10000,
"search_limit": 50,
"trigger_words": {
"personal_info": ["名前", "誕生日", "住所", "年齢", "職業", "家族", "出身", "好き", "嫌い", "趣味"],
"decisions": ["決めた", "決定", "方針", "計画", "予定", "目標"],
"solutions": ["解決", "修正", "対処", "設定", "インストール", "手順"],
"learning": ["学んだ", "わかった", "発見", "理解", "気づき"],
"past_reference": ["前に", "以前", "昔", "過去", "先ほど", "さっき", "この間"],
"memory_recall": ["覚えている", "記録", "メモ", "保存", "履歴"],
"preferences": ["好み", "設定", "環境", "構成", "preferences"],
"vague_reference": ["あれ", "それ", "例のやつ"]
}
}
}

View File

@@ -0,0 +1,45 @@
{
"mcpServers": {
"memory-extended": {
"command": "cargo",
"args": ["run", "--bin", "memory-mcp-extended", "--features", "extended"],
"cwd": "/Users/syui/ai/ai/gpt",
"env": {
"MEMORY_AUTO_EXECUTE": "true",
"MEMORY_AUTO_SAVE": "true",
"MEMORY_AUTO_SEARCH": "true",
"TRIGGER_SENSITIVITY": "high",
"MEMORY_DB_PATH": "~/.claude/memory.db",
"OPENAI_API_KEY": "${OPENAI_API_KEY}"
}
}
},
"memory": {
"mode": "extended",
"auto_execute": true,
"auto_save": true,
"auto_search": true,
"trigger_sensitivity": "high",
"max_memories": 10000,
"search_limit": 50,
"features": {
"ai_analysis": true,
"semantic_search": true,
"web_integration": true,
"sentiment_analysis": true,
"pattern_recognition": true
},
"trigger_words": {
"personal_info": ["名前", "誕生日", "住所", "年齢", "職業", "家族", "出身", "好き", "嫌い", "趣味"],
"decisions": ["決めた", "決定", "方針", "計画", "予定", "目標"],
"solutions": ["解決", "修正", "対処", "設定", "インストール", "手順"],
"learning": ["学んだ", "わかった", "発見", "理解", "気づき"],
"past_reference": ["前に", "以前", "昔", "過去", "先ほど", "さっき", "この間"],
"memory_recall": ["覚えている", "記録", "メモ", "保存", "履歴"],
"preferences": ["好み", "設定", "環境", "構成", "preferences"],
"vague_reference": ["あれ", "それ", "例のやつ"],
"web_content": ["URL", "リンク", "サイト", "ページ", "記事"],
"analysis_request": ["分析", "パターン", "傾向", "インサイト", "統計"]
}
}
}

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

2
src/lib.rs Normal file
View File

@@ -0,0 +1,2 @@
pub mod memory;
pub mod mcp;

View File

@@ -2,8 +2,8 @@ use anyhow::Result;
use clap::{Parser, Subcommand};
use std::path::PathBuf;
mod memory;
mod mcp;
pub mod memory;
pub mod mcp;
use memory::MemoryManager;
use mcp::MCPServer;

41
src/mcp_server.rs Normal file
View File

@@ -0,0 +1,41 @@
use anyhow::Result;
use std::env;
mod memory;
mod mcp;
use mcp::MCPServer;
#[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());
// 設定をログ出力(デバッグ用)
eprintln!("Memory MCP Server 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?;
server.run().await?;
Ok(())
}

View File

@@ -148,6 +148,7 @@ impl MemoryManager {
conversations
}
#[allow(dead_code)]
pub async fn import_chatgpt_conversations(&mut self, file_path: &PathBuf) -> Result<()> {
let content = std::fs::read_to_string(file_path)
.context("Failed to read conversations file")?;