feat: Phase 8 - MCP Server integration with Claude Code
Expose AISSIA as MCP Server to integrate with Claude Code and other MCP clients.
**New Infrastructure**:
- MCPServerTools: Bridge between MCP Server and AISSIA services
- Synchronous service methods for blocking MCP calls
- 13 total tools exposed (5 AISSIA core + 8 filesystem)
**Priority Tool**:
- chat_with_aissia: Dialogue with AISSIA's AI assistant (Claude Sonnet 4)
**AISSIA Core Tools** (5):
1. chat_with_aissia - AI conversation with Claude Sonnet 4
2. transcribe_audio - STT file transcription (stub)
3. text_to_speech - TTS file output (stub)
4. save_memory - Persistent storage (stub)
5. search_memories - Memory search (stub)
**Changes**:
- src/shared/tools/MCPServerTools.{hpp,cpp}: New tool handlers for AISSIA services
- src/services/LLMService: Added sendMessageSync() for blocking calls
- src/services/VoiceService: Added loadConfig(), transcribeFileSync(), textToSpeechSync()
- src/main.cpp: Refactored runMCPServer() to instantiate services and register AISSIA tools
- CMakeLists.txt: Added MCPServerTools to AissiaTools library
**Documentation**:
- docs/CLAUDE_CODE_INTEGRATION.md: Complete integration guide
- config/README_MCP.md: Quick setup instructions
- config/claude_code_mcp_config.json: Example MCP configuration
**Usage**:
```bash
./aissia --mcp-server
```
**Limitations (MVP)**:
- STT/TTS file operations not fully implemented (engines need file support)
- Storage sync methods return "not implemented" (async pub/sub only)
- No hot-reload modules in MCP mode
**Next Steps** (Phase 8.1-8.4):
- Complete STT/TTS sync methods
- Implement StorageService sync API
- Add advanced tools (schedule_task, get_focus_stats)
- Multi-modal support (vision, PDF parsing)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d7971e0c34
commit
cb938500cd
@ -83,10 +83,11 @@ if(OPENSSL_FOUND)
|
||||
target_compile_definitions(AissiaLLM PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
endif()
|
||||
|
||||
# Tools Library (Internal tools + FileSystem tools + MCP client + MCP server)
|
||||
# Tools Library (Internal tools + FileSystem tools + MCP client + MCP server + MCP Server Tools)
|
||||
add_library(AissiaTools STATIC
|
||||
src/shared/tools/InternalTools.cpp
|
||||
src/shared/tools/FileSystemTools.cpp
|
||||
src/shared/tools/MCPServerTools.cpp
|
||||
src/shared/mcp/StdioTransport.cpp
|
||||
src/shared/mcp/MCPClient.cpp
|
||||
src/shared/mcp/MCPServer.cpp
|
||||
|
||||
305
config/README_MCP.md
Normal file
305
config/README_MCP.md
Normal file
@ -0,0 +1,305 @@
|
||||
# AISSIA MCP Configuration for Claude Code
|
||||
|
||||
This directory contains an example MCP (Model Context Protocol) configuration for integrating AISSIA with Claude Code.
|
||||
|
||||
## Quick Setup
|
||||
|
||||
### 1. Locate Claude Code MCP Settings
|
||||
|
||||
The MCP configuration file location depends on your operating system:
|
||||
|
||||
**Windows**:
|
||||
```
|
||||
%APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json
|
||||
```
|
||||
|
||||
Full path example:
|
||||
```
|
||||
C:\Users\YourUsername\AppData\Roaming\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json
|
||||
```
|
||||
|
||||
**macOS**:
|
||||
```
|
||||
~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json
|
||||
```
|
||||
|
||||
**Linux**:
|
||||
```
|
||||
~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json
|
||||
```
|
||||
|
||||
### 2. Copy Configuration
|
||||
|
||||
Copy the contents of `claude_code_mcp_config.json` to the Claude Code MCP settings file.
|
||||
|
||||
**Important**: Update the `command` path to point to your actual AISSIA executable:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"aissia": {
|
||||
"command": "C:\\path\\to\\your\\aissia\\build\\aissia.exe",
|
||||
"args": ["--mcp-server"],
|
||||
"disabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Restart Claude Code
|
||||
|
||||
Restart VS Code (or reload window: `Ctrl+Shift+P` → "Developer: Reload Window") to apply the changes.
|
||||
|
||||
### 4. Verify Integration
|
||||
|
||||
Open Claude Code and check that AISSIA tools are available:
|
||||
|
||||
```
|
||||
You: Can you list the available MCP servers?
|
||||
Claude: I have access to the following MCP servers:
|
||||
- aissia: 13 tools available
|
||||
```
|
||||
|
||||
## Available Tools
|
||||
|
||||
Once configured, Claude will have access to these 13 AISSIA tools:
|
||||
|
||||
### AISSIA Core (5 tools)
|
||||
1. **chat_with_aissia** ⭐ - Dialogue with AISSIA's AI assistant (Claude Sonnet 4)
|
||||
2. **transcribe_audio** - Transcribe audio files to text
|
||||
3. **text_to_speech** - Convert text to speech audio files
|
||||
4. **save_memory** - Save notes to AISSIA's persistent storage
|
||||
5. **search_memories** - Search through saved memories
|
||||
|
||||
### File System (8 tools)
|
||||
6. **read_file** - Read file contents
|
||||
7. **write_file** - Write content to files
|
||||
8. **list_directory** - List files in a directory
|
||||
9. **search_files** - Search for files by pattern
|
||||
10. **file_exists** - Check if a file exists
|
||||
11. **create_directory** - Create directories
|
||||
12. **delete_file** - Delete files
|
||||
13. **move_file** - Move or rename files
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"aissia": {
|
||||
"command": "path/to/aissia.exe",
|
||||
"args": ["--mcp-server"],
|
||||
"disabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### With Auto-Approval
|
||||
|
||||
To skip confirmation prompts for specific tools:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"aissia": {
|
||||
"command": "path/to/aissia.exe",
|
||||
"args": ["--mcp-server"],
|
||||
"disabled": false,
|
||||
"alwaysAllow": ["chat_with_aissia", "read_file", "write_file"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Disable Server
|
||||
|
||||
To temporarily disable AISSIA without removing the configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"aissia": {
|
||||
"command": "path/to/aissia.exe",
|
||||
"args": ["--mcp-server"],
|
||||
"disabled": true // <-- Set to true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before running AISSIA in MCP server mode, ensure these config files exist:
|
||||
|
||||
### config/ai.json
|
||||
```json
|
||||
{
|
||||
"provider": "claude",
|
||||
"api_key": "sk-ant-api03-...",
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"max_iterations": 10,
|
||||
"system_prompt": "Tu es AISSIA, un assistant personnel intelligent..."
|
||||
}
|
||||
```
|
||||
|
||||
### config/storage.json
|
||||
```json
|
||||
{
|
||||
"database_path": "./data/aissia.db",
|
||||
"journal_mode": "WAL",
|
||||
"busy_timeout_ms": 5000
|
||||
}
|
||||
```
|
||||
|
||||
### config/voice.json (optional)
|
||||
```json
|
||||
{
|
||||
"tts": {
|
||||
"enabled": true,
|
||||
"rate": 0,
|
||||
"volume": 80
|
||||
},
|
||||
"stt": {
|
||||
"active_mode": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing MCP Server
|
||||
|
||||
You can test the MCP server independently before integrating with Claude Code:
|
||||
|
||||
```bash
|
||||
# Test tools/list
|
||||
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | ./build/aissia.exe --mcp-server
|
||||
|
||||
# Test chat_with_aissia tool
|
||||
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"chat_with_aissia","arguments":{"message":"What time is it?"}}}' | ./build/aissia.exe --mcp-server
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Server not found" or "Connection failed"
|
||||
|
||||
1. Verify the `command` path is correct and points to `aissia.exe`
|
||||
2. Make sure AISSIA compiles successfully: `cmake --build build`
|
||||
3. Test running `./build/aissia.exe --mcp-server` manually
|
||||
|
||||
### "LLMService not initialized"
|
||||
|
||||
AISSIA requires `config/ai.json` with a valid Claude API key. Check:
|
||||
1. File exists: `config/ai.json`
|
||||
2. API key is valid: `"api_key": "sk-ant-api03-..."`
|
||||
3. Provider is set: `"provider": "claude"`
|
||||
|
||||
### "Tool execution failed"
|
||||
|
||||
Some tools have limited functionality in Phase 8 MVP:
|
||||
- `transcribe_audio` - Not fully implemented yet (STT file support needed)
|
||||
- `text_to_speech` - Not fully implemented yet (TTS file output needed)
|
||||
- `save_memory` - Not fully implemented yet (Storage sync methods needed)
|
||||
- `search_memories` - Not fully implemented yet (Storage sync methods needed)
|
||||
|
||||
These will be completed in Phase 8.1 and 8.2.
|
||||
|
||||
### Server starts but tools don't appear
|
||||
|
||||
1. Check Claude Code logs: `Ctrl+Shift+P` → "Developer: Open Extension Logs"
|
||||
2. Look for MCP server initialization errors
|
||||
3. Verify JSON syntax in the MCP configuration file
|
||||
|
||||
## Example Use Cases
|
||||
|
||||
### 1. Ask AISSIA for Help
|
||||
|
||||
```
|
||||
You: Use chat_with_aissia to ask "What are my top productivity patterns?"
|
||||
Claude: [calls chat_with_aissia tool]
|
||||
AISSIA: Based on your activity data, your most productive hours are 9-11 AM...
|
||||
```
|
||||
|
||||
### 2. File Operations + AI
|
||||
|
||||
```
|
||||
You: Read my TODO.md file and ask AISSIA to prioritize the tasks
|
||||
Claude: [calls read_file("TODO.md")]
|
||||
Claude: [calls chat_with_aissia with task list]
|
||||
AISSIA: Here's a prioritized version based on urgency and dependencies...
|
||||
```
|
||||
|
||||
### 3. Voice Transcription (future)
|
||||
|
||||
```
|
||||
You: Transcribe meeting-notes.wav to text
|
||||
Claude: [calls transcribe_audio("meeting-notes.wav")]
|
||||
Result: "Welcome to the team meeting. Today we're discussing..."
|
||||
```
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Multiple MCP Servers
|
||||
|
||||
You can configure multiple MCP servers alongside AISSIA:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"aissia": {
|
||||
"command": "C:\\path\\to\\aissia\\build\\aissia.exe",
|
||||
"args": ["--mcp-server"],
|
||||
"disabled": false
|
||||
},
|
||||
"filesystem": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-filesystem", "C:\\Users"],
|
||||
"disabled": false
|
||||
},
|
||||
"brave-search": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-brave-search"],
|
||||
"disabled": false,
|
||||
"env": {
|
||||
"BRAVE_API_KEY": "your-brave-api-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Pass environment variables to AISSIA:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"aissia": {
|
||||
"command": "C:\\path\\to\\aissia\\build\\aissia.exe",
|
||||
"args": ["--mcp-server"],
|
||||
"disabled": false,
|
||||
"env": {
|
||||
"AISSIA_LOG_LEVEL": "debug",
|
||||
"CLAUDE_API_KEY": "sk-ant-api03-..."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- **Full Documentation**: `docs/CLAUDE_CODE_INTEGRATION.md`
|
||||
- **MCP Specification**: https://github.com/anthropics/mcp
|
||||
- **Claude Code Extension**: https://marketplace.visualstudio.com/items?itemName=saoudrizwan.claude-dev
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
1. Check the full documentation: `docs/CLAUDE_CODE_INTEGRATION.md`
|
||||
2. Review logs: AISSIA writes to stderr in MCP mode
|
||||
3. Test manually: `./build/aissia.exe --mcp-server` and send JSON-RPC requests
|
||||
10
config/claude_code_mcp_config.json
Normal file
10
config/claude_code_mcp_config.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"aissia": {
|
||||
"command": "C:\\Users\\alexi\\Documents\\projects\\aissia\\build\\aissia.exe",
|
||||
"args": ["--mcp-server"],
|
||||
"disabled": false,
|
||||
"alwaysAllow": []
|
||||
}
|
||||
}
|
||||
}
|
||||
449
docs/CLAUDE_CODE_INTEGRATION.md
Normal file
449
docs/CLAUDE_CODE_INTEGRATION.md
Normal file
@ -0,0 +1,449 @@
|
||||
# AISSIA - Claude Code Integration (Phase 8)
|
||||
|
||||
## Overview
|
||||
|
||||
AISSIA can now be exposed as an **MCP Server** (Model Context Protocol) to integrate with Claude Code and other MCP-compatible clients. This allows Claude to use AISSIA's capabilities as tools during conversations.
|
||||
|
||||
**Mode MCP Server**: `./aissia --mcp-server`
|
||||
|
||||
This mode exposes AISSIA's services via JSON-RPC 2.0 over stdio, following the MCP specification.
|
||||
|
||||
## Available Tools
|
||||
|
||||
AISSIA exposes **13 tools** total:
|
||||
|
||||
### 1. AISSIA Core Tools (Priority)
|
||||
|
||||
#### `chat_with_aissia` ⭐ **PRIORITY**
|
||||
Dialogue with AISSIA's built-in AI assistant (Claude Sonnet 4). Send a message and get an intelligent response with access to AISSIA's knowledge and capabilities.
|
||||
|
||||
**Input**:
|
||||
```json
|
||||
{
|
||||
"message": "string (required) - Message to send to AISSIA",
|
||||
"conversation_id": "string (optional) - Conversation ID for continuity",
|
||||
"system_prompt": "string (optional) - Custom system prompt"
|
||||
}
|
||||
```
|
||||
|
||||
**Output**:
|
||||
```json
|
||||
{
|
||||
"response": "AISSIA's response text",
|
||||
"conversation_id": "conversation-id",
|
||||
"tokens": 1234,
|
||||
"iterations": 2
|
||||
}
|
||||
```
|
||||
|
||||
**Example use case**: "Hey AISSIA, can you analyze my focus patterns this week?"
|
||||
|
||||
#### `transcribe_audio`
|
||||
Transcribe audio file to text using Speech-to-Text engines (Whisper.cpp, OpenAI Whisper API, Google Speech).
|
||||
|
||||
**Input**:
|
||||
```json
|
||||
{
|
||||
"file_path": "string (required) - Path to audio file",
|
||||
"language": "string (optional) - Language code (e.g., 'fr', 'en'). Default: 'fr'"
|
||||
}
|
||||
```
|
||||
|
||||
**Output**:
|
||||
```json
|
||||
{
|
||||
"text": "Transcribed text from audio",
|
||||
"file": "/path/to/audio.wav",
|
||||
"language": "fr"
|
||||
}
|
||||
```
|
||||
|
||||
**Status**: ⚠️ Not yet implemented - requires STT service file transcription support
|
||||
|
||||
#### `text_to_speech`
|
||||
Convert text to speech audio file using Text-to-Speech synthesis. Generates audio in WAV format.
|
||||
|
||||
**Input**:
|
||||
```json
|
||||
{
|
||||
"text": "string (required) - Text to synthesize",
|
||||
"output_file": "string (required) - Output audio file path (WAV)",
|
||||
"voice": "string (optional) - Voice identifier (e.g., 'fr-fr', 'en-us'). Default: 'fr-fr'"
|
||||
}
|
||||
```
|
||||
|
||||
**Output**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"file": "/path/to/output.wav",
|
||||
"voice": "fr-fr"
|
||||
}
|
||||
```
|
||||
|
||||
**Status**: ⚠️ Not yet implemented - requires TTS engine file output support
|
||||
|
||||
#### `save_memory`
|
||||
Save a note or memory to AISSIA's persistent storage. Memories can be tagged and searched later.
|
||||
|
||||
**Input**:
|
||||
```json
|
||||
{
|
||||
"title": "string (required) - Memory title",
|
||||
"content": "string (required) - Memory content",
|
||||
"tags": ["array of strings (optional) - Tags for categorization"]
|
||||
}
|
||||
```
|
||||
|
||||
**Output**:
|
||||
```json
|
||||
{
|
||||
"id": "memory-uuid",
|
||||
"title": "Meeting notes",
|
||||
"timestamp": "2025-01-30T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Status**: ⚠️ Not yet implemented - requires StorageService sync methods
|
||||
|
||||
#### `search_memories`
|
||||
Search through saved memories and notes in AISSIA's storage. Returns matching memories with relevance scores.
|
||||
|
||||
**Input**:
|
||||
```json
|
||||
{
|
||||
"query": "string (required) - Search query",
|
||||
"limit": "integer (optional) - Maximum results to return. Default: 10"
|
||||
}
|
||||
```
|
||||
|
||||
**Output**:
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"id": "memory-uuid",
|
||||
"title": "Meeting notes",
|
||||
"content": "...",
|
||||
"score": 0.85,
|
||||
"tags": ["work", "meeting"]
|
||||
}
|
||||
],
|
||||
"count": 5
|
||||
}
|
||||
```
|
||||
|
||||
**Status**: ⚠️ Not yet implemented - requires StorageService sync methods
|
||||
|
||||
### 2. File System Tools (8 tools)
|
||||
|
||||
- `read_file` - Read a file from the filesystem
|
||||
- `write_file` - Write content to a file
|
||||
- `list_directory` - List files in a directory
|
||||
- `search_files` - Search for files by pattern
|
||||
- `file_exists` - Check if a file exists
|
||||
- `create_directory` - Create a new directory
|
||||
- `delete_file` - Delete a file
|
||||
- `move_file` - Move or rename a file
|
||||
|
||||
These tools provide Claude with direct filesystem access to work with files on your system.
|
||||
|
||||
## Installation for Claude Code
|
||||
|
||||
### 1. Configure Claude Code MCP
|
||||
|
||||
Create or edit your Claude Code MCP configuration file:
|
||||
|
||||
**Windows**: `%APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json`
|
||||
**macOS/Linux**: `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
|
||||
|
||||
Add AISSIA as an MCP server:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"aissia": {
|
||||
"command": "C:\\path\\to\\aissia\\build\\aissia.exe",
|
||||
"args": ["--mcp-server"],
|
||||
"disabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: Replace `C:\\path\\to\\aissia\\build\\aissia.exe` with the actual path to your compiled AISSIA executable.
|
||||
|
||||
### 2. Verify Configuration
|
||||
|
||||
Restart Claude Code (or VS Code) to reload the MCP configuration.
|
||||
|
||||
Claude should now have access to all 13 AISSIA tools during conversations.
|
||||
|
||||
### 3. Test Integration
|
||||
|
||||
In Claude Code, try:
|
||||
|
||||
```
|
||||
"Can you use the chat_with_aissia tool to ask AISSIA what time it is?"
|
||||
```
|
||||
|
||||
Claude will call the `chat_with_aissia` tool, which internally uses AISSIA's LLM service to process the query.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Synchronous Mode (MCP Server)
|
||||
|
||||
When running as an MCP server, AISSIA uses **synchronous blocking calls** instead of the async pub/sub architecture used in normal mode:
|
||||
|
||||
```cpp
|
||||
// Normal mode (async)
|
||||
io->publish("llm:request", data);
|
||||
// ... wait for response on "llm:response" topic
|
||||
|
||||
// MCP mode (sync)
|
||||
auto response = llmService->sendMessageSync(message, conversationId);
|
||||
// immediate result
|
||||
```
|
||||
|
||||
This is necessary because:
|
||||
1. MCP protocol expects immediate JSON-RPC responses
|
||||
2. No event loop in MCP server mode (stdin/stdout blocking I/O)
|
||||
3. Simplifies integration with external tools
|
||||
|
||||
### Service Integration
|
||||
|
||||
```
|
||||
MCPServer (stdio JSON-RPC)
|
||||
↓
|
||||
MCPServerTools (tool handlers)
|
||||
↓
|
||||
Services (sync methods)
|
||||
├── LLMService::sendMessageSync()
|
||||
├── VoiceService::transcribeFileSync()
|
||||
├── VoiceService::textToSpeechSync()
|
||||
└── StorageService (stub implementations)
|
||||
```
|
||||
|
||||
### Tool Registry
|
||||
|
||||
All tools are registered in a central `ToolRegistry`:
|
||||
|
||||
```cpp
|
||||
ToolRegistry registry;
|
||||
|
||||
// 1. Internal tools (get_current_time)
|
||||
registry.registerTool("get_current_time", ...);
|
||||
|
||||
// 2. FileSystem tools (8 tools)
|
||||
for (auto& toolDef : FileSystemTools::getToolDefinitions()) {
|
||||
registry.registerTool(toolDef);
|
||||
}
|
||||
|
||||
// 3. AISSIA tools (5 tools)
|
||||
MCPServerTools aissiaTools(llmService, storageService, voiceService);
|
||||
for (const auto& toolDef : aissiaTools.getToolDefinitions()) {
|
||||
registry.registerTool(toolDef);
|
||||
}
|
||||
```
|
||||
|
||||
Total: **13 tools**
|
||||
|
||||
## Configuration Files
|
||||
|
||||
AISSIA MCP Server requires these config files (same as normal mode):
|
||||
|
||||
- `config/ai.json` - LLM provider configuration (Claude API key)
|
||||
- `config/storage.json` - Database path and settings
|
||||
- `config/voice.json` - TTS/STT engine settings
|
||||
|
||||
**Important**: Make sure these files are present before running `--mcp-server` mode.
|
||||
|
||||
## Limitations (Phase 8 MVP)
|
||||
|
||||
1. **STT/TTS file operations**: `transcribe_audio` and `text_to_speech` are not fully implemented yet
|
||||
- STT service needs file transcription support (currently only streaming)
|
||||
- TTS engine needs file output support (currently only direct playback)
|
||||
|
||||
2. **Storage sync methods**: `save_memory` and `search_memories` return "not implemented" errors
|
||||
- StorageService needs `saveMemorySync()` and `searchMemoriesSync()` methods
|
||||
- Current storage only works via async pub/sub
|
||||
|
||||
3. **No hot-reload**: MCP server mode doesn't load hot-reloadable modules
|
||||
- Only services and tools are available
|
||||
- No SchedulerModule, MonitoringModule, etc.
|
||||
|
||||
4. **Single-threaded**: MCP server runs synchronously on main thread
|
||||
- LLMService worker thread still runs for agentic loops
|
||||
- But overall server is blocking on stdin
|
||||
|
||||
## Roadmap
|
||||
|
||||
### Phase 8.1 - Complete STT/TTS Sync Methods
|
||||
- [ ] Implement `VoiceService::transcribeFileSync()` using STT engines
|
||||
- [ ] Implement `VoiceService::textToSpeechSync()` with file output
|
||||
- [ ] Test audio file transcription via MCP
|
||||
|
||||
### Phase 8.2 - Storage Sync Methods
|
||||
- [ ] Implement `StorageService::saveMemorySync()`
|
||||
- [ ] Implement `StorageService::searchMemoriesSync()`
|
||||
- [ ] Add vector embeddings for semantic search
|
||||
|
||||
### Phase 8.3 - Advanced Tools
|
||||
- [ ] `schedule_task` - Add tasks to AISSIA's scheduler
|
||||
- [ ] `get_focus_stats` - Retrieve hyperfocus detection stats
|
||||
- [ ] `list_active_apps` - Get current monitored applications
|
||||
- [ ] `send_notification` - Trigger system notifications
|
||||
|
||||
### Phase 8.4 - Multi-Modal Support
|
||||
- [ ] Image input for LLM (Claude vision)
|
||||
- [ ] PDF/document parsing tools
|
||||
- [ ] Web scraping integration
|
||||
|
||||
## Use Cases
|
||||
|
||||
### 1. AI Assistant Collaboration
|
||||
|
||||
Claude Code can delegate complex reasoning tasks to AISSIA:
|
||||
|
||||
```
|
||||
Claude: "I need to analyze user behavior patterns. Let me ask AISSIA."
|
||||
→ calls chat_with_aissia("Analyze recent focus patterns")
|
||||
AISSIA: "Based on monitoring data, user has 3 hyperfocus sessions daily averaging 2.5 hours..."
|
||||
```
|
||||
|
||||
### 2. Voice Transcription Workflow
|
||||
|
||||
```
|
||||
Claude: "Transcribe meeting-2025-01-30.wav"
|
||||
→ calls transcribe_audio(file_path="meeting-2025-01-30.wav", language="en")
|
||||
→ calls write_file(path="transcript.txt", content=result)
|
||||
```
|
||||
|
||||
### 3. Knowledge Management
|
||||
|
||||
```
|
||||
Claude: "Save this important insight to AISSIA's memory"
|
||||
→ calls save_memory(
|
||||
title="Project architecture decision",
|
||||
content="We decided to use hot-reload modules for business logic...",
|
||||
tags=["architecture", "project"]
|
||||
)
|
||||
```
|
||||
|
||||
### 4. File + AI Operations
|
||||
|
||||
```
|
||||
Claude: "Read todos.md, ask AISSIA to prioritize tasks, update file"
|
||||
→ calls read_file("todos.md")
|
||||
→ calls chat_with_aissia("Prioritize these tasks: ...")
|
||||
→ calls write_file("todos-prioritized.md", content=...)
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
### Adding New Tools
|
||||
|
||||
1. **Declare tool in MCPServerTools.hpp**:
|
||||
```cpp
|
||||
json handleNewTool(const json& input);
|
||||
```
|
||||
|
||||
2. **Implement in MCPServerTools.cpp**:
|
||||
```cpp
|
||||
json MCPServerTools::handleNewTool(const json& input) {
|
||||
// Extract input parameters
|
||||
std::string param = input["param"];
|
||||
|
||||
// Call service
|
||||
auto result = m_someService->doSomethingSync(param);
|
||||
|
||||
// Return JSON result
|
||||
return {
|
||||
{"output", result},
|
||||
{"status", "success"}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
3. **Register in getToolDefinitions()**:
|
||||
```cpp
|
||||
tools.push_back({
|
||||
"new_tool",
|
||||
"Description of what this tool does",
|
||||
{
|
||||
{"type", "object"},
|
||||
{"properties", {
|
||||
{"param", {
|
||||
{"type", "string"},
|
||||
{"description", "Parameter description"}
|
||||
}}
|
||||
}},
|
||||
{"required", json::array({"param"})}
|
||||
},
|
||||
[this](const json& input) { return handleNewTool(input); }
|
||||
});
|
||||
```
|
||||
|
||||
4. **Add to execute() switch**:
|
||||
```cpp
|
||||
if (toolName == "new_tool") {
|
||||
return handleNewTool(input);
|
||||
}
|
||||
```
|
||||
|
||||
### Testing MCP Server
|
||||
|
||||
Test with `nc` or `socat`:
|
||||
|
||||
```bash
|
||||
# Send tools/list request
|
||||
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | ./build/aissia.exe --mcp-server
|
||||
|
||||
# Send tool call
|
||||
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"chat_with_aissia","arguments":{"message":"Hello AISSIA"}}}' | ./build/aissia.exe --mcp-server
|
||||
```
|
||||
|
||||
Expected output format:
|
||||
```json
|
||||
{"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"chat_with_aissia","description":"...","inputSchema":{...}}]}}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "LLMService not initialized"
|
||||
|
||||
Make sure `config/ai.json` exists with valid API key:
|
||||
```json
|
||||
{
|
||||
"provider": "claude",
|
||||
"api_key": "sk-ant-...",
|
||||
"model": "claude-sonnet-4-20250514"
|
||||
}
|
||||
```
|
||||
|
||||
### "VoiceService not available"
|
||||
|
||||
Voice tools are optional. If you don't need STT/TTS, this is normal.
|
||||
|
||||
### "StorageService not available"
|
||||
|
||||
Make sure `config/storage.json` exists:
|
||||
```json
|
||||
{
|
||||
"database_path": "./data/aissia.db",
|
||||
"journal_mode": "WAL",
|
||||
"busy_timeout_ms": 5000
|
||||
}
|
||||
```
|
||||
|
||||
### "Tool not found"
|
||||
|
||||
Check `tools/list` output to see which tools are actually registered.
|
||||
|
||||
## References
|
||||
|
||||
- **MCP Specification**: https://github.com/anthropics/mcp
|
||||
- **AISSIA Architecture**: `docs/project-overview.md`
|
||||
- **GroveEngine Guide**: `docs/GROVEENGINE_GUIDE.md`
|
||||
- **LLM Service**: `src/services/LLMService.hpp`
|
||||
- **MCPServer**: `src/shared/mcp/MCPServer.hpp`
|
||||
36
src/main.cpp
36
src/main.cpp
@ -8,6 +8,7 @@
|
||||
#include "services/VoiceService.hpp"
|
||||
#include "shared/mcp/MCPServer.hpp"
|
||||
#include "shared/tools/FileSystemTools.hpp"
|
||||
#include "shared/tools/MCPServerTools.hpp"
|
||||
#include "shared/llm/ToolRegistry.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
@ -177,14 +178,32 @@ private:
|
||||
// Run AISSIA as MCP server (stdio mode)
|
||||
int runMCPServer() {
|
||||
// Log to stderr so stdout stays clean for JSON-RPC
|
||||
// Use stderr_color_sink from stdout_color_sinks.h
|
||||
auto logger = spdlog::stderr_color_mt("MCPServer");
|
||||
spdlog::set_default_logger(logger);
|
||||
spdlog::set_level(spdlog::level::info);
|
||||
|
||||
spdlog::info("AISSIA MCP Server starting...");
|
||||
|
||||
// Create tool registry with FileSystem tools
|
||||
// === Initialize Services ===
|
||||
|
||||
// 1. LLMService (PRIORITY: for chat_with_aissia)
|
||||
auto llmService = std::make_unique<aissia::LLMService>();
|
||||
if (!llmService->loadConfig("config/llm.json")) {
|
||||
spdlog::warn("Failed to load LLM config, chat_with_aissia will be unavailable");
|
||||
}
|
||||
llmService->initialize(nullptr); // No IIO in MCP mode
|
||||
llmService->initializeTools(); // Load internal tools + MCP tools
|
||||
|
||||
// 2. StorageService (for save_memory/search_memories)
|
||||
auto storageService = std::make_unique<aissia::StorageService>();
|
||||
storageService->initialize(nullptr);
|
||||
|
||||
// 3. VoiceService (for TTS/STT)
|
||||
auto voiceService = std::make_unique<aissia::VoiceService>();
|
||||
// Note: Voice config is optional
|
||||
voiceService->initialize(nullptr);
|
||||
|
||||
// === Create Tool Registry ===
|
||||
aissia::ToolRegistry registry;
|
||||
|
||||
// Register get_current_time tool
|
||||
@ -214,7 +233,18 @@ int runMCPServer() {
|
||||
);
|
||||
}
|
||||
|
||||
spdlog::info("Registered {} tools", registry.size());
|
||||
// === Register AISSIA Tools (Phase 8) ===
|
||||
aissia::tools::MCPServerTools aissiaTools(
|
||||
llmService.get(),
|
||||
storageService.get(),
|
||||
voiceService.get()
|
||||
);
|
||||
|
||||
for (const auto& toolDef : aissiaTools.getToolDefinitions()) {
|
||||
registry.registerTool(toolDef);
|
||||
}
|
||||
|
||||
spdlog::info("Registered {} tools total", registry.size());
|
||||
|
||||
// Create and run MCP server
|
||||
aissia::mcp::MCPServer server(registry);
|
||||
|
||||
@ -340,4 +340,36 @@ void LLMService::shutdown() {
|
||||
m_logger->info("LLMService shutdown");
|
||||
}
|
||||
|
||||
LLMService::SyncResponse LLMService::sendMessageSync(
|
||||
const std::string& message,
|
||||
const std::string& conversationId,
|
||||
const std::string& systemPrompt
|
||||
) {
|
||||
SyncResponse syncResp;
|
||||
|
||||
// Create request (same as async mode)
|
||||
Request request;
|
||||
request.query = message;
|
||||
request.conversationId = conversationId.empty() ? "mcp-session" : conversationId;
|
||||
request.systemPrompt = systemPrompt.empty() ? m_defaultSystemPrompt : systemPrompt;
|
||||
request.maxIterations = m_maxIterations;
|
||||
|
||||
// Process synchronously (blocking call)
|
||||
auto response = processRequest(request);
|
||||
|
||||
// Convert to SyncResponse
|
||||
if (!response.isError) {
|
||||
syncResp.text = response.text;
|
||||
syncResp.tokens = response.tokens;
|
||||
syncResp.iterations = response.iterations;
|
||||
} else {
|
||||
// On error, return error in text
|
||||
syncResp.text = "Error: " + response.text;
|
||||
syncResp.tokens = 0;
|
||||
syncResp.iterations = 0;
|
||||
}
|
||||
|
||||
return syncResp;
|
||||
}
|
||||
|
||||
} // namespace aissia
|
||||
|
||||
@ -60,6 +60,29 @@ public:
|
||||
/// Load MCP server configurations
|
||||
bool loadMCPConfig(const std::string& configPath);
|
||||
|
||||
/**
|
||||
* @brief Synchronous response structure for MCP Server mode
|
||||
*/
|
||||
struct SyncResponse {
|
||||
std::string text;
|
||||
int tokens = 0;
|
||||
int iterations = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Send message synchronously (blocking, for MCP Server mode)
|
||||
*
|
||||
* @param message User message
|
||||
* @param conversationId Conversation ID (optional)
|
||||
* @param systemPrompt Custom system prompt (optional)
|
||||
* @return Sync response with text, tokens, iterations
|
||||
*/
|
||||
SyncResponse sendMessageSync(
|
||||
const std::string& message,
|
||||
const std::string& conversationId = "",
|
||||
const std::string& systemPrompt = ""
|
||||
);
|
||||
|
||||
private:
|
||||
struct Request {
|
||||
std::string query;
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include <fstream>
|
||||
|
||||
// Include VoiceService.hpp BEFORE spdlog to avoid logger macro conflicts
|
||||
#include "VoiceService.hpp"
|
||||
@ -225,4 +226,69 @@ void VoiceService::shutdown() {
|
||||
m_logger->info("[VoiceService] Shutdown");
|
||||
}
|
||||
|
||||
bool VoiceService::loadConfig(const std::string& configPath) {
|
||||
try {
|
||||
std::ifstream file(configPath);
|
||||
if (!file.is_open()) {
|
||||
m_logger->warn("[VoiceService] Config file not found: {}", configPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
nlohmann::json config;
|
||||
file >> config;
|
||||
|
||||
// Load TTS config
|
||||
if (config.contains("tts")) {
|
||||
const auto& ttsConfig = config["tts"];
|
||||
m_ttsEnabled = ttsConfig.value("enabled", true);
|
||||
m_ttsRate = ttsConfig.value("rate", 0);
|
||||
m_ttsVolume = ttsConfig.value("volume", 80);
|
||||
}
|
||||
|
||||
// Load STT config (Phase 7 format)
|
||||
if (config.contains("stt")) {
|
||||
configureSTT(config["stt"]);
|
||||
}
|
||||
|
||||
m_logger->info("[VoiceService] Config loaded from {}", configPath);
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
m_logger->error("[VoiceService] Failed to load config: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string VoiceService::transcribeFileSync(
|
||||
const std::string& filePath,
|
||||
const std::string& language
|
||||
) {
|
||||
m_logger->info("[VoiceService] transcribeFileSync: {}", filePath);
|
||||
|
||||
if (!m_sttService) {
|
||||
throw std::runtime_error("STT service not initialized");
|
||||
}
|
||||
|
||||
// Use STT service to transcribe file synchronously
|
||||
// Note: This requires STT service to support file transcription
|
||||
// For MVP, we'll throw not implemented
|
||||
throw std::runtime_error("transcribeFileSync not yet implemented - STT service needs file transcription support");
|
||||
}
|
||||
|
||||
bool VoiceService::textToSpeechSync(
|
||||
const std::string& text,
|
||||
const std::string& outputFile,
|
||||
const std::string& voice
|
||||
) {
|
||||
m_logger->info("[VoiceService] textToSpeechSync: {} -> {}", text.substr(0, 50), outputFile);
|
||||
|
||||
if (!m_ttsEngine) {
|
||||
throw std::runtime_error("TTS engine not initialized");
|
||||
}
|
||||
|
||||
// For MVP, we don't support saving to file yet
|
||||
// The TTS engine currently only speaks directly
|
||||
throw std::runtime_error("textToSpeechSync file output not yet implemented - TTS engine needs file output support");
|
||||
}
|
||||
|
||||
} // namespace aissia
|
||||
|
||||
@ -55,6 +55,35 @@ public:
|
||||
/// Configure STT with full config (Phase 7)
|
||||
void configureSTT(const nlohmann::json& sttConfig);
|
||||
|
||||
/// Load configuration from JSON file
|
||||
bool loadConfig(const std::string& configPath);
|
||||
|
||||
/**
|
||||
* @brief Transcribe audio file synchronously (for MCP Server mode)
|
||||
*
|
||||
* @param filePath Path to audio file
|
||||
* @param language Language code (e.g., "fr", "en")
|
||||
* @return Transcribed text
|
||||
*/
|
||||
std::string transcribeFileSync(
|
||||
const std::string& filePath,
|
||||
const std::string& language = "fr"
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Convert text to speech synchronously (for MCP Server mode)
|
||||
*
|
||||
* @param text Text to synthesize
|
||||
* @param outputFile Output audio file path
|
||||
* @param voice Voice identifier (e.g., "fr-fr")
|
||||
* @return true if successful
|
||||
*/
|
||||
bool textToSpeechSync(
|
||||
const std::string& text,
|
||||
const std::string& outputFile,
|
||||
const std::string& voice = "fr-fr"
|
||||
);
|
||||
|
||||
private:
|
||||
// Configuration
|
||||
bool m_ttsEnabled = true;
|
||||
|
||||
310
src/shared/tools/MCPServerTools.cpp
Normal file
310
src/shared/tools/MCPServerTools.cpp
Normal file
@ -0,0 +1,310 @@
|
||||
#include "MCPServerTools.hpp"
|
||||
#include "../../services/LLMService.hpp"
|
||||
#include "../../services/StorageService.hpp"
|
||||
#include "../../services/VoiceService.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace aissia::tools {
|
||||
|
||||
MCPServerTools::MCPServerTools(
|
||||
LLMService* llm,
|
||||
StorageService* storage,
|
||||
VoiceService* voice
|
||||
) : m_llmService(llm),
|
||||
m_storageService(storage),
|
||||
m_voiceService(voice)
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<ToolDefinition> MCPServerTools::getToolDefinitions() {
|
||||
std::vector<ToolDefinition> tools;
|
||||
|
||||
// Tool 1: chat_with_aissia (PRIORITÉ)
|
||||
if (m_llmService) {
|
||||
tools.push_back({
|
||||
"chat_with_aissia",
|
||||
"Dialogue with AISSIA assistant (Claude Sonnet 4). Send a message and get an intelligent response with access to AISSIA's knowledge and capabilities.",
|
||||
{
|
||||
{"type", "object"},
|
||||
{"properties", {
|
||||
{"message", {
|
||||
{"type", "string"},
|
||||
{"description", "Message to send to AISSIA"}
|
||||
}},
|
||||
{"conversation_id", {
|
||||
{"type", "string"},
|
||||
{"description", "Conversation ID for continuity (optional)"}
|
||||
}},
|
||||
{"system_prompt", {
|
||||
{"type", "string"},
|
||||
{"description", "Custom system prompt (optional)"}
|
||||
}}
|
||||
}},
|
||||
{"required", json::array({"message"})}
|
||||
},
|
||||
[this](const json& input) { return handleChatWithAissia(input); }
|
||||
});
|
||||
}
|
||||
|
||||
// Tool 2: transcribe_audio
|
||||
if (m_voiceService) {
|
||||
tools.push_back({
|
||||
"transcribe_audio",
|
||||
"Transcribe audio file to text using Speech-to-Text (Whisper.cpp, OpenAI Whisper API, or Google Speech). Supports WAV, MP3, and other common audio formats.",
|
||||
{
|
||||
{"type", "object"},
|
||||
{"properties", {
|
||||
{"file_path", {
|
||||
{"type", "string"},
|
||||
{"description", "Path to audio file"}
|
||||
}},
|
||||
{"language", {
|
||||
{"type", "string"},
|
||||
{"description", "Language code (e.g., 'fr', 'en'). Default: 'fr'"}
|
||||
}}
|
||||
}},
|
||||
{"required", json::array({"file_path"})}
|
||||
},
|
||||
[this](const json& input) { return handleTranscribeAudio(input); }
|
||||
});
|
||||
|
||||
// Tool 3: text_to_speech
|
||||
tools.push_back({
|
||||
"text_to_speech",
|
||||
"Convert text to speech audio file using Text-to-Speech synthesis. Generates audio in WAV format.",
|
||||
{
|
||||
{"type", "object"},
|
||||
{"properties", {
|
||||
{"text", {
|
||||
{"type", "string"},
|
||||
{"description", "Text to synthesize"}
|
||||
}},
|
||||
{"output_file", {
|
||||
{"type", "string"},
|
||||
{"description", "Output audio file path (WAV)"}
|
||||
}},
|
||||
{"voice", {
|
||||
{"type", "string"},
|
||||
{"description", "Voice identifier (e.g., 'fr-fr', 'en-us'). Default: 'fr-fr'"}
|
||||
}}
|
||||
}},
|
||||
{"required", json::array({"text", "output_file"})}
|
||||
},
|
||||
[this](const json& input) { return handleTextToSpeech(input); }
|
||||
});
|
||||
}
|
||||
|
||||
// Tool 4: save_memory
|
||||
if (m_storageService) {
|
||||
tools.push_back({
|
||||
"save_memory",
|
||||
"Save a note or memory to AISSIA's persistent storage. Memories can be tagged and searched later.",
|
||||
{
|
||||
{"type", "object"},
|
||||
{"properties", {
|
||||
{"title", {
|
||||
{"type", "string"},
|
||||
{"description", "Memory title"}
|
||||
}},
|
||||
{"content", {
|
||||
{"type", "string"},
|
||||
{"description", "Memory content"}
|
||||
}},
|
||||
{"tags", {
|
||||
{"type", "array"},
|
||||
{"items", {{"type", "string"}}},
|
||||
{"description", "Tags for categorization (optional)"}
|
||||
}}
|
||||
}},
|
||||
{"required", json::array({"title", "content"})}
|
||||
},
|
||||
[this](const json& input) { return handleSaveMemory(input); }
|
||||
});
|
||||
|
||||
// Tool 5: search_memories
|
||||
tools.push_back({
|
||||
"search_memories",
|
||||
"Search through saved memories and notes in AISSIA's storage. Returns matching memories with relevance scores.",
|
||||
{
|
||||
{"type", "object"},
|
||||
{"properties", {
|
||||
{"query", {
|
||||
{"type", "string"},
|
||||
{"description", "Search query"}
|
||||
}},
|
||||
{"limit", {
|
||||
{"type", "integer"},
|
||||
{"description", "Maximum results to return. Default: 10"}
|
||||
}}
|
||||
}},
|
||||
{"required", json::array({"query"})}
|
||||
},
|
||||
[this](const json& input) { return handleSearchMemories(input); }
|
||||
});
|
||||
}
|
||||
|
||||
return tools;
|
||||
}
|
||||
|
||||
json MCPServerTools::execute(const std::string& toolName, const json& input) {
|
||||
if (toolName == "chat_with_aissia") {
|
||||
return handleChatWithAissia(input);
|
||||
} else if (toolName == "transcribe_audio") {
|
||||
return handleTranscribeAudio(input);
|
||||
} else if (toolName == "text_to_speech") {
|
||||
return handleTextToSpeech(input);
|
||||
} else if (toolName == "save_memory") {
|
||||
return handleSaveMemory(input);
|
||||
} else if (toolName == "search_memories") {
|
||||
return handleSearchMemories(input);
|
||||
}
|
||||
|
||||
return {
|
||||
{"error", "Unknown tool: " + toolName}
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Tool Handlers
|
||||
// ============================================================================
|
||||
|
||||
json MCPServerTools::handleChatWithAissia(const json& input) {
|
||||
if (!m_llmService) {
|
||||
return {{"error", "LLMService not available"}};
|
||||
}
|
||||
|
||||
try {
|
||||
std::string message = input["message"];
|
||||
std::string conversationId = input.value("conversation_id", "");
|
||||
std::string systemPrompt = input.value("system_prompt", "");
|
||||
|
||||
spdlog::info("[chat_with_aissia] Message: {}", message.substr(0, 100));
|
||||
|
||||
// Call synchronous LLM method
|
||||
auto response = m_llmService->sendMessageSync(message, conversationId, systemPrompt);
|
||||
|
||||
return {
|
||||
{"response", response.text},
|
||||
{"conversation_id", conversationId},
|
||||
{"tokens", response.tokens},
|
||||
{"iterations", response.iterations}
|
||||
};
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("[chat_with_aissia] Error: {}", e.what());
|
||||
return {{"error", e.what()}};
|
||||
}
|
||||
}
|
||||
|
||||
json MCPServerTools::handleTranscribeAudio(const json& input) {
|
||||
if (!m_voiceService) {
|
||||
return {{"error", "VoiceService not available"}};
|
||||
}
|
||||
|
||||
try {
|
||||
std::string filePath = input["file_path"];
|
||||
std::string language = input.value("language", "fr");
|
||||
|
||||
spdlog::info("[transcribe_audio] File: {}, Language: {}", filePath, language);
|
||||
|
||||
// Call synchronous STT method
|
||||
std::string text = m_voiceService->transcribeFileSync(filePath, language);
|
||||
|
||||
return {
|
||||
{"text", text},
|
||||
{"file", filePath},
|
||||
{"language", language}
|
||||
};
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("[transcribe_audio] Error: {}", e.what());
|
||||
return {{"error", e.what()}};
|
||||
}
|
||||
}
|
||||
|
||||
json MCPServerTools::handleTextToSpeech(const json& input) {
|
||||
if (!m_voiceService) {
|
||||
return {{"error", "VoiceService not available"}};
|
||||
}
|
||||
|
||||
try {
|
||||
std::string text = input["text"];
|
||||
std::string outputFile = input["output_file"];
|
||||
std::string voice = input.value("voice", "fr-fr");
|
||||
|
||||
spdlog::info("[text_to_speech] Text: {}, Output: {}", text.substr(0, 50), outputFile);
|
||||
|
||||
// Call synchronous TTS method
|
||||
bool success = m_voiceService->textToSpeechSync(text, outputFile, voice);
|
||||
|
||||
if (success) {
|
||||
return {
|
||||
{"success", true},
|
||||
{"file", outputFile},
|
||||
{"voice", voice}
|
||||
};
|
||||
} else {
|
||||
return {{"error", "TTS generation failed"}};
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("[text_to_speech] Error: {}", e.what());
|
||||
return {{"error", e.what()}};
|
||||
}
|
||||
}
|
||||
|
||||
json MCPServerTools::handleSaveMemory(const json& input) {
|
||||
if (!m_storageService) {
|
||||
return {{"error", "StorageService not available"}};
|
||||
}
|
||||
|
||||
try {
|
||||
std::string title = input["title"];
|
||||
std::string content = input["content"];
|
||||
std::vector<std::string> tags;
|
||||
|
||||
if (input.contains("tags") && input["tags"].is_array()) {
|
||||
for (const auto& tag : input["tags"]) {
|
||||
tags.push_back(tag.get<std::string>());
|
||||
}
|
||||
}
|
||||
|
||||
spdlog::info("[save_memory] Title: {}", title);
|
||||
|
||||
// TODO: Implement saveMemorySync in StorageService
|
||||
// For now, return not implemented
|
||||
return json({
|
||||
{"error", "save_memory not yet implemented"},
|
||||
{"note", "StorageService sync methods need to be added"},
|
||||
{"title", title}
|
||||
});
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("[save_memory] Error: {}", e.what());
|
||||
return {{"error", e.what()}};
|
||||
}
|
||||
}
|
||||
|
||||
json MCPServerTools::handleSearchMemories(const json& input) {
|
||||
if (!m_storageService) {
|
||||
return {{"error", "StorageService not available"}};
|
||||
}
|
||||
|
||||
try {
|
||||
std::string query = input["query"];
|
||||
int limit = input.value("limit", 10);
|
||||
|
||||
spdlog::info("[search_memories] Query: {}, Limit: {}", query, limit);
|
||||
|
||||
// TODO: Implement searchMemoriesSync in StorageService
|
||||
// For now, return not implemented
|
||||
return json({
|
||||
{"error", "search_memories not yet implemented"},
|
||||
{"note", "StorageService sync methods need to be added"},
|
||||
{"query", query},
|
||||
{"limit", limit}
|
||||
});
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("[search_memories] Error: {}", e.what());
|
||||
return {{"error", e.what()}};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace aissia::tools
|
||||
76
src/shared/tools/MCPServerTools.hpp
Normal file
76
src/shared/tools/MCPServerTools.hpp
Normal file
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include "../llm/ToolRegistry.hpp"
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
// Forward declarations
|
||||
namespace aissia {
|
||||
class LLMService;
|
||||
class StorageService;
|
||||
class VoiceService;
|
||||
}
|
||||
|
||||
namespace aissia::tools {
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
/**
|
||||
* @brief MCP Server Tools - Bridge between MCP Server and AISSIA services
|
||||
*
|
||||
* Provides tool definitions for AISSIA modules exposed via MCP Server:
|
||||
* - chat_with_aissia: Dialogue with AISSIA (Claude Sonnet 4)
|
||||
* - transcribe_audio: Speech-to-text (Whisper.cpp/OpenAI/Google)
|
||||
* - text_to_speech: Text-to-speech synthesis
|
||||
* - save_memory: Save note/memory to storage
|
||||
* - search_memories: Search stored memories
|
||||
*
|
||||
* Note: These tools run in synchronous mode (no IIO pub/sub, no main loop)
|
||||
*/
|
||||
class MCPServerTools {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct MCP server tools with service dependencies
|
||||
*
|
||||
* @param llm LLMService for chat_with_aissia (can be nullptr)
|
||||
* @param storage StorageService for save/search memories (can be nullptr)
|
||||
* @param voice VoiceService for TTS/STT (can be nullptr)
|
||||
*/
|
||||
MCPServerTools(
|
||||
LLMService* llm,
|
||||
StorageService* storage,
|
||||
VoiceService* voice
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Get all tool definitions for registration
|
||||
*
|
||||
* @return Vector of ToolDefinition structs
|
||||
*/
|
||||
std::vector<ToolDefinition> getToolDefinitions();
|
||||
|
||||
/**
|
||||
* @brief Execute a tool by name
|
||||
*
|
||||
* @param toolName Tool to execute
|
||||
* @param input Tool arguments (JSON)
|
||||
* @return Tool result (JSON)
|
||||
*/
|
||||
json execute(const std::string& toolName, const json& input);
|
||||
|
||||
private:
|
||||
// Tool handlers
|
||||
json handleChatWithAissia(const json& input);
|
||||
json handleTranscribeAudio(const json& input);
|
||||
json handleTextToSpeech(const json& input);
|
||||
json handleSaveMemory(const json& input);
|
||||
json handleSearchMemories(const json& input);
|
||||
|
||||
// Service references (nullable)
|
||||
LLMService* m_llmService;
|
||||
StorageService* m_storageService;
|
||||
VoiceService* m_voiceService;
|
||||
};
|
||||
|
||||
} // namespace aissia::tools
|
||||
Loading…
Reference in New Issue
Block a user