mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-08-28 05:25:04 +00:00
add mcp
This commit is contained in:
parent
b06cff69a0
commit
2c678873a9
8 changed files with 811 additions and 3 deletions
29
README.md
29
README.md
|
|
@ -19,7 +19,8 @@
|
|||
---
|
||||
|
||||
## 📰 What's New
|
||||
- **[2025-08]** 🎉 ExperienceMaker v0.1.0 is now available on [PyPI](https://pypi.org/project/experiencemaker/)!
|
||||
- **[2025-08]** 🚀 MCP is now available! → [Quick Start Guide](./doc/mcp_quick_start.md)
|
||||
- **[2025-07]** 🎉 ExperienceMaker v0.1.1 is now available on [PyPI](https://pypi.org/project/experiencemaker/)!
|
||||
- **[2025-07]** 📚 Complete documentation and quick start guides released
|
||||
- **[2025-06]** 🚀 Multi-backend vector store support (Elasticsearch & ChromaDB)
|
||||
|
||||
|
|
@ -134,6 +135,8 @@ EMBEDDING_MODEL_BASE_URL="https://xxx.com/v1"
|
|||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### 🌐 HTTP Service
|
||||
|
||||
For testing and development, use the `local_file` backend:
|
||||
```bash
|
||||
experiencemaker \
|
||||
|
|
@ -148,6 +151,30 @@ including custom pipelines, operation parameters, and advanced configuration met
|
|||
|
||||
The service will start on `http://localhost:8001`
|
||||
|
||||
### 🔌 MCP Server
|
||||
|
||||
ExperienceMaker now supports Model Context Protocol (MCP) for seamless integration with MCP-compatible clients like Claude Desktop:
|
||||
|
||||
```bash
|
||||
experiencemaker_mcp \
|
||||
mcp_transport=stdio \
|
||||
llm.default.model_name=qwen3-32b \
|
||||
embedding_model.default.model_name=text-embedding-v4 \
|
||||
vector_store.default.backend=local_file
|
||||
```
|
||||
|
||||
For SSE transport (Server-Sent Events):
|
||||
```bash
|
||||
experiencemaker_mcp \
|
||||
mcp_transport=sse \
|
||||
http_service.port=8001 \
|
||||
llm.default.model_name=qwen3-32b \
|
||||
embedding_model.default.model_name=text-embedding-v4 \
|
||||
vector_store.default.backend=local_file
|
||||
```
|
||||
|
||||
🔗 **For detailed MCP setup and usage examples**, see our [MCP Quick Start Guide](./doc/mcp_quick_start.md).
|
||||
|
||||
### 🔍 Production Setup with Elasticsearch Backend
|
||||
```bash
|
||||
experiencemaker \
|
||||
|
|
|
|||
570
doc/mcp_quick_start.md
Normal file
570
doc/mcp_quick_start.md
Normal file
|
|
@ -0,0 +1,570 @@
|
|||
# ExperienceMaker MCP Quick Start Guide
|
||||
|
||||
This guide will help you get started with ExperienceMaker using the Model Context Protocol (MCP) interface for seamless
|
||||
integration with MCP-compatible clients.
|
||||
|
||||
## 🚀 What You'll Learn
|
||||
|
||||
- How to set up ExperienceMaker MCP server
|
||||
- Connect to the server using MCP clients
|
||||
- Run an agent and generate experiences via MCP
|
||||
- Retrieve and apply experiences through MCP tools
|
||||
- Build experience-enhanced agents with MCP integration
|
||||
|
||||
## 📋 Prerequisites
|
||||
|
||||
- Python 3.12+
|
||||
- LLM API access (OpenAI or compatible)
|
||||
- Embedding model API access
|
||||
- MCP-compatible client (Claude Desktop, or custom MCP client)
|
||||
|
||||
## 🛠️ Installation
|
||||
|
||||
### Option 1: Install from PyPI (Recommended)
|
||||
|
||||
```bash
|
||||
pip install experiencemaker
|
||||
```
|
||||
|
||||
### Option 2: Install from Source
|
||||
|
||||
```bash
|
||||
git clone https://github.com/modelscope/ExperienceMaker.git
|
||||
cd ExperienceMaker
|
||||
pip install .
|
||||
```
|
||||
|
||||
## ⚙️ Environment Setup
|
||||
|
||||
Create a `.env` file in your project directory:
|
||||
|
||||
```bash
|
||||
# Required: LLM API configuration
|
||||
LLM_API_KEY="sk-xxx"
|
||||
LLM_BASE_URL="https://xxx.com/v1"
|
||||
|
||||
# Required: Embedding model configuration
|
||||
EMBEDDING_MODEL_API_KEY="sk-xxx"
|
||||
EMBEDDING_MODEL_BASE_URL="https://xxx.com/v1"
|
||||
|
||||
# Optional: Elasticsearch configuration (if using Elasticsearch backend)
|
||||
ES_HOSTS="http://localhost:9200"
|
||||
```
|
||||
|
||||
## 🚀 Start the MCP Server
|
||||
|
||||
### Option 1: STDIO Transport (Recommended for MCP clients)
|
||||
|
||||
```bash
|
||||
experiencemaker_mcp \
|
||||
mcp_transport=stdio \
|
||||
llm.default.model_name=qwen3-32b \
|
||||
embedding_model.default.model_name=text-embedding-v4 \
|
||||
vector_store.default.backend=local_file
|
||||
```
|
||||
|
||||
### Option 2: SSE Transport (Server-Sent Events)
|
||||
|
||||
```bash
|
||||
experiencemaker_mcp \
|
||||
mcp_transport=sse \
|
||||
http_service.port=8001 \
|
||||
llm.default.model_name=qwen3-32b \
|
||||
embedding_model.default.model_name=text-embedding-v4 \
|
||||
vector_store.default.backend=local_file
|
||||
```
|
||||
|
||||
The SSE server will start on `http://localhost:8001/sse`
|
||||
|
||||
### Elasticsearch Backend
|
||||
|
||||
```bash
|
||||
experiencemaker_mcp \
|
||||
mcp_transport=stdio \
|
||||
llm.default.model_name=qwen3-32b \
|
||||
embedding_model.default.model_name=text-embedding-v4 \
|
||||
vector_store.default.backend=elasticsearch
|
||||
```
|
||||
|
||||
**Setup Elasticsearch:**
|
||||
|
||||
```bash
|
||||
export ES_HOSTS="http://localhost:9200"
|
||||
# Quick setup using Elastic's official script
|
||||
curl -fsSL https://elastic.co/start-local | sh
|
||||
```
|
||||
|
||||
📖 **Need Help?** Refer to [Vector Store Setup](vector_store_setup.md) for comprehensive deployment guidance.
|
||||
|
||||
## 🔧 Configure MCP Client
|
||||
|
||||
### Claude Desktop Configuration
|
||||
|
||||
Add to your Claude Desktop `claude_desktop_config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"experiencemaker": {
|
||||
"command": "experiencemaker_mcp",
|
||||
"args": [
|
||||
"mcp_transport=stdio",
|
||||
"llm.default.model_name=qwen3-32b",
|
||||
"embedding_model.default.model_name=text-embedding-v4",
|
||||
"vector_store.default.backend=local_file"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Custom MCP Client Configuration
|
||||
|
||||
If using a custom MCP client, connect to:
|
||||
|
||||
- **STDIO**: Use subprocess to communicate with the server
|
||||
- **SSE**: Connect to `http://localhost:8001/sse`
|
||||
|
||||
## 📝 Using ExperienceMaker MCP Tools
|
||||
|
||||
The MCP server exposes three main tools:
|
||||
|
||||
- `retriever`: Retrieve experiences from workspace
|
||||
- `summarizer`: Transform trajectories into experiences
|
||||
- `vector_store`: Manage vector store operations
|
||||
|
||||
Note: The `workspace_id` serves as your experience storage namespace. Experiences in different workspaces remain
|
||||
completely isolated.
|
||||
|
||||
### 📊 Using the Summarizer Tool
|
||||
|
||||
Transform conversation trajectories into valuable experiences using batch summarization.
|
||||
|
||||
**Tool Parameters:**
|
||||
|
||||
- `traj_list`: List of trajectories (each containing messages and score)
|
||||
- `workspace_id`: Workspace identifier (default: "default")
|
||||
- `config`: Additional configuration parameters (optional)
|
||||
|
||||
<details open>
|
||||
<summary><b>Python MCP Client Example</b></summary>
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from experiencemaker.schema.message import Message, Trajectory, Role
|
||||
from experiencemaker.schema.request import SummarizerRequest
|
||||
from experiencemaker.service.mcp_client import MCPClient
|
||||
|
||||
|
||||
async def example_summarizer():
|
||||
async with MCPClient(base_url="http://0.0.0.0:8001/sse") as client:
|
||||
# Create trajectory with conversation
|
||||
trajectory = Trajectory(
|
||||
messages=[
|
||||
Message(role=Role.USER, content="Hello, how can I solve a math problem?"),
|
||||
Message(role=Role.ASSISTANT, content="I'd be happy to help! What math problem are you working on?"),
|
||||
Message(role=Role.USER, content="What is 2+2?"),
|
||||
Message(role=Role.ASSISTANT, content="2+2 equals 4.")
|
||||
],
|
||||
score=1.0 # Success score
|
||||
)
|
||||
|
||||
request = SummarizerRequest(
|
||||
workspace_id="math_workspace",
|
||||
traj_list=[trajectory]
|
||||
)
|
||||
|
||||
response = await client.call_summarizer(request)
|
||||
print("Generated experiences:")
|
||||
for experience in response.experience_list:
|
||||
print(f"- {experience.content}")
|
||||
|
||||
|
||||
# Run the example
|
||||
asyncio.run(example_summarizer())
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>MCP Tool Call (JSON)</b></summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "summarizer",
|
||||
"arguments": {
|
||||
"traj_list": [
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello, how can I solve a math problem?"
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "I'd be happy to help! What math problem are you working on?"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What is 2+2?"
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "2+2 equals 4."
|
||||
}
|
||||
],
|
||||
"score": 1.0
|
||||
}
|
||||
],
|
||||
"workspace_id": "math_workspace"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### 🔍 Using the Retriever Tool
|
||||
|
||||
Intelligently search and retrieve the most relevant experiences from your workspace.
|
||||
|
||||
**Tool Parameters:**
|
||||
|
||||
- `query`: Search query string
|
||||
- `messages`: List of conversation messages (optional)
|
||||
- `top_k`: Number of top experiences to retrieve (default: 1)
|
||||
- `workspace_id`: Workspace identifier (default: "default")
|
||||
- `config`: Additional configuration parameters (optional)
|
||||
|
||||
<details open>
|
||||
<summary><b>Python MCP Client Example</b></summary>
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from experiencemaker.service.mcp_client import MCPClient
|
||||
from experiencemaker.schema.request import RetrieverRequest
|
||||
|
||||
|
||||
async def example_retriever():
|
||||
async with MCPClient(base_url="http://0.0.0.0:8001/sse") as client:
|
||||
request = RetrieverRequest(
|
||||
workspace_id="math_workspace",
|
||||
query="How to solve basic arithmetic problems?",
|
||||
top_k=3
|
||||
)
|
||||
|
||||
response = await client.call_retriever(request)
|
||||
print(f"Retrieved experiences: {response.experience_merged}")
|
||||
print(f"Experience list:")
|
||||
for exp in response.experience_list:
|
||||
print(f"- {exp.content}")
|
||||
|
||||
|
||||
# Run the example
|
||||
asyncio.run(example_retriever())
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>MCP Tool Call (JSON)</b></summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "retriever",
|
||||
"arguments": {
|
||||
"query": "How to solve basic arithmetic problems?",
|
||||
"top_k": 3,
|
||||
"workspace_id": "math_workspace"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### 💾 Using the Vector Store Tool
|
||||
|
||||
Manage vector store operations for workspace data.
|
||||
|
||||
**Tool Parameters:**
|
||||
|
||||
- `action`: Action to perform ("dump", "load", "delete", "copy")
|
||||
- `workspace_id`: Target workspace identifier
|
||||
- `src_workspace_id`: Source workspace (for copy operation)
|
||||
- `path`: File system path (for dump/load operations, default: "./")
|
||||
- `config`: Additional configuration parameters (optional)
|
||||
|
||||
#### Dump Experiences From Vector Store
|
||||
|
||||
<details open>
|
||||
<summary><b>Python MCP Client Example</b></summary>
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from experiencemaker.service.mcp_client import MCPClient
|
||||
from experiencemaker.schema.request import VectorStoreRequest
|
||||
|
||||
|
||||
async def example_dump():
|
||||
async with MCPClient(base_url="http://0.0.0.0:8001/sse") as client:
|
||||
request = VectorStoreRequest(
|
||||
workspace_id="math_workspace",
|
||||
action="dump",
|
||||
path="./backups/"
|
||||
)
|
||||
|
||||
response = await client.call_vector_store(request)
|
||||
print(f"Dump result: {response}")
|
||||
|
||||
|
||||
# Run the example
|
||||
asyncio.run(example_dump())
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>MCP Tool Call (JSON)</b></summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "vector_store",
|
||||
"arguments": {
|
||||
"action": "dump",
|
||||
"workspace_id": "math_workspace",
|
||||
"path": "./backups/"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Load Experiences To Vector Store
|
||||
|
||||
<details open>
|
||||
<summary><b>Python MCP Client Example</b></summary>
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from experiencemaker.service.mcp_client import MCPClient
|
||||
from experiencemaker.schema.request import VectorStoreRequest
|
||||
|
||||
|
||||
async def example_load():
|
||||
async with MCPClient(base_url="http://0.0.0.0:8001/sse") as client:
|
||||
request = VectorStoreRequest(
|
||||
workspace_id="math_workspace",
|
||||
action="load",
|
||||
path="./backups/"
|
||||
)
|
||||
|
||||
response = await client.call_vector_store(request)
|
||||
print(f"Load result: {response}")
|
||||
|
||||
|
||||
# Run the example
|
||||
asyncio.run(example_load())
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Delete Workspace
|
||||
|
||||
<details open>
|
||||
<summary><b>Python MCP Client Example</b></summary>
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from experiencemaker.service.mcp_client import MCPClient
|
||||
from experiencemaker.schema.request import VectorStoreRequest
|
||||
|
||||
|
||||
async def example_delete():
|
||||
async with MCPClient(base_url="http://0.0.0.0:8001/sse") as client:
|
||||
request = VectorStoreRequest(
|
||||
workspace_id="math_workspace",
|
||||
action="delete"
|
||||
)
|
||||
|
||||
response = await client.call_vector_store(request)
|
||||
print(f"Delete result: {response}")
|
||||
|
||||
|
||||
# Run the example
|
||||
asyncio.run(example_delete())
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Copy Workspace
|
||||
|
||||
<details open>
|
||||
<summary><b>Python MCP Client Example</b></summary>
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from experiencemaker.service.mcp_client import MCPClient
|
||||
from experiencemaker.schema.request import VectorStoreRequest
|
||||
|
||||
|
||||
async def example_copy():
|
||||
async with MCPClient(base_url="http://0.0.0.0:8001/sse") as client:
|
||||
request = VectorStoreRequest(
|
||||
workspace_id="math_workspace_copy",
|
||||
action="copy",
|
||||
src_workspace_id="math_workspace"
|
||||
)
|
||||
|
||||
response = await client.call_vector_store(request)
|
||||
print(f"Copy result: {response}")
|
||||
|
||||
|
||||
# Run the example
|
||||
asyncio.run(example_copy())
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## 🔄 Complete MCP Workflow Example
|
||||
|
||||
Here's a complete example showing the full workflow:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from experiencemaker.service.mcp_client import MCPClient
|
||||
from experiencemaker.schema.request import SummarizerRequest, RetrieverRequest
|
||||
from experiencemaker.schema.message import Message, Trajectory, Role
|
||||
|
||||
async def complete_workflow():
|
||||
async with MCPClient(base_url="http://0.0.0.0:8001/sse") as client:
|
||||
print("Available tools:", await client.list_tools())
|
||||
|
||||
# Step 1: Create experiences from trajectories
|
||||
trajectory = Trajectory(
|
||||
messages=[
|
||||
Message(role=Role.USER, content="How do I calculate compound interest?"),
|
||||
Message(role=Role.ASSISTANT,
|
||||
content="Compound interest is calculated using the formula A = P(1 + r/n)^(nt), where A is the final amount, P is the principal, r is the annual interest rate, n is the number of times interest is compounded per year, and t is the time in years."),
|
||||
Message(role=Role.USER, content="Can you give me an example?"),
|
||||
Message(role=Role.ASSISTANT,
|
||||
content="Sure! If you invest $1000 at 5% annual interest compounded monthly for 2 years: A = 1000(1 + 0.05/12)^(12*2) = $1104.94")
|
||||
],
|
||||
score=1.0
|
||||
)
|
||||
|
||||
summarizer_request = SummarizerRequest(
|
||||
workspace_id="finance_workspace",
|
||||
traj_list=[trajectory]
|
||||
)
|
||||
|
||||
summarizer_response = await client.call_summarizer(summarizer_request)
|
||||
print(f"Created {len(summarizer_response.experience_list)} experiences")
|
||||
|
||||
# Step 2: Retrieve relevant experiences
|
||||
retriever_request = RetrieverRequest(
|
||||
workspace_id="finance_workspace",
|
||||
query="How to calculate interest on investments?",
|
||||
top_k=2
|
||||
)
|
||||
|
||||
retriever_response = await client.call_retriever(retriever_request)
|
||||
print(f"Retrieved experiences: {retriever_response.experience_merged}")
|
||||
|
||||
|
||||
# Run the complete workflow
|
||||
asyncio.run(complete_workflow())
|
||||
```
|
||||
|
||||
## 🎭 Claude Desktop Integration
|
||||
|
||||
Once configured with Claude Desktop, you can directly ask Claude to use ExperienceMaker tools:
|
||||
|
||||
```
|
||||
Claude, please use the summarizer tool to create experiences from this conversation about solving math problems, then retrieve similar experiences when I ask about arithmetic.
|
||||
```
|
||||
|
||||
Claude will automatically call the appropriate MCP tools and provide contextually relevant responses based on your
|
||||
stored experiences.
|
||||
|
||||
## 🐛 Common Issues
|
||||
|
||||
### MCP Server Won't Start
|
||||
|
||||
- Check if the required ports are available (for SSE transport)
|
||||
- Verify your API keys in `.env` file
|
||||
- Ensure Python version is 3.12+
|
||||
- Check MCP transport configuration
|
||||
|
||||
### MCP Client Connection Issues
|
||||
|
||||
- For STDIO: Ensure the command path is correct in your MCP client config
|
||||
- For SSE: Verify the server URL and port accessibility
|
||||
- Check firewall settings for SSE connections
|
||||
|
||||
### No Experiences Retrieved
|
||||
|
||||
- Make sure you've run the summarizer tool first to create experiences
|
||||
- Check if workspace_id matches between operations
|
||||
- Verify vector store backend is properly configured
|
||||
|
||||
### API Connection Errors
|
||||
|
||||
- Confirm LLM_BASE_URL and API keys are correct
|
||||
- Test API access independently
|
||||
- Check network connectivity
|
||||
|
||||
## 🔧 Advanced Configuration
|
||||
|
||||
### Custom MCP Client Setup
|
||||
|
||||
```python
|
||||
# For STDIO transport
|
||||
async with MCPClient(enable_sse=False) as client:
|
||||
# Your MCP operations here
|
||||
pass
|
||||
|
||||
# For SSE transport with custom URL
|
||||
async with MCPClient(base_url="http://custom-host:8001/sse") as client:
|
||||
# Your MCP operations here
|
||||
pass
|
||||
```
|
||||
|
||||
### Server Configuration Options
|
||||
|
||||
```bash
|
||||
# Full configuration example
|
||||
experiencemaker_mcp \
|
||||
mcp_transport=stdio \
|
||||
http_service.host=0.0.0.0 \
|
||||
http_service.port=8001 \
|
||||
llm.default.model_name=qwen3-32b \
|
||||
llm.default.api_key=${LLM_API_KEY} \
|
||||
llm.default.base_url=${LLM_BASE_URL} \
|
||||
embedding_model.default.model_name=text-embedding-v4 \
|
||||
embedding_model.default.api_key=${EMBEDDING_MODEL_API_KEY} \
|
||||
embedding_model.default.base_url=${EMBEDDING_MODEL_BASE_URL} \
|
||||
vector_store.default.backend=elasticsearch \
|
||||
vector_store.default.host=localhost \
|
||||
vector_store.default.port=9200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
🎯 **You're all set!** You now have a working ExperienceMaker MCP setup that can seamlessly integrate with MCP-compatible
|
||||
clients and learn from interactions to improve over time through the standardized MCP protocol.
|
||||
|
||||
## 📚 Next Steps
|
||||
|
||||
- Explore the [Configuration Guide](configuration_guide.md) for advanced customization
|
||||
- Check out [cookbook examples](../cookbook/) for practical implementations
|
||||
- Learn about [Vector Store Setup](vector_store_setup.md) for production deployments
|
||||
- Review the [Operations Documentation](operations_documentation.md) for maintenance procedures
|
||||
|
|
@ -43,3 +43,10 @@ def main():
|
|||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
# start with:
|
||||
# experiencemaker \
|
||||
# http_service.port=8001 \
|
||||
# llm.default.model_name=qwen3-32b \
|
||||
# embedding_model.default.model_name=text-embedding-v4 \
|
||||
# vector_store.default.backend=local_file
|
||||
111
experiencemaker/mcp_server.py
Normal file
111
experiencemaker/mcp_server.py
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
import sys
|
||||
from typing import List
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from fastmcp import FastMCP
|
||||
|
||||
from experiencemaker.service.experience_maker_service import ExperienceMakerService
|
||||
|
||||
load_dotenv()
|
||||
|
||||
mcp = FastMCP("ExperienceMaker")
|
||||
service = ExperienceMakerService(sys.argv[1:])
|
||||
|
||||
|
||||
@mcp.tool
|
||||
def retriever(query: str,
|
||||
messages: List[dict] = None,
|
||||
top_k: int = 1,
|
||||
workspace_id: str = "default",
|
||||
config: dict = None) -> dict:
|
||||
"""
|
||||
Retrieve experiences from the workspace based on a query.
|
||||
|
||||
Args:
|
||||
query: Query string
|
||||
messages: List of messages
|
||||
top_k: Number of top experiences to retrieve
|
||||
workspace_id: Workspace identifier
|
||||
config: Additional configuration parameters
|
||||
|
||||
Returns:
|
||||
Dictionary containing retrieved experiences
|
||||
"""
|
||||
return service(api="retriever", request={
|
||||
"query": query,
|
||||
"messages": messages if messages else [],
|
||||
"top_k": top_k,
|
||||
"workspace_id": workspace_id,
|
||||
"config": config if config else {},
|
||||
}).model_dump()
|
||||
|
||||
|
||||
@mcp.tool
|
||||
def summarizer(traj_list: List[dict], workspace_id: str = "default", config: dict = None) -> dict:
|
||||
"""
|
||||
Summarize trajectories into experiences.
|
||||
|
||||
Args:
|
||||
traj_list: List of trajectories
|
||||
workspace_id: Workspace identifier
|
||||
config: Additional configuration parameters
|
||||
|
||||
Returns:
|
||||
experiences
|
||||
"""
|
||||
return service(api="summarizer", request={
|
||||
"traj_list": traj_list,
|
||||
"workspace_id": workspace_id,
|
||||
"config": config if config else {},
|
||||
}).model_dump()
|
||||
|
||||
|
||||
@mcp.tool
|
||||
def vector_store(action: str,
|
||||
src_workspace_id: str = "",
|
||||
workspace_id: str = "",
|
||||
path: str = "./",
|
||||
config: dict = None) -> dict:
|
||||
"""
|
||||
Perform vector store operations.
|
||||
|
||||
Args:
|
||||
action: Action to perform (e.g., "copy", "delete", "dump", "load")
|
||||
src_workspace_id: Source workspace identifier
|
||||
workspace_id: Workspace identifier
|
||||
path: Path to the vector store
|
||||
config: Additional configuration parameters
|
||||
|
||||
Returns:
|
||||
Dictionary containing the result of the vector store operation
|
||||
"""
|
||||
return service(api="vector_store", request={
|
||||
"action": action,
|
||||
"src_workspace_id": src_workspace_id,
|
||||
"workspace_id": workspace_id,
|
||||
"path": path,
|
||||
"config": config if config else {},
|
||||
}).model_dump()
|
||||
|
||||
|
||||
def main():
|
||||
mcp_transport: str = service.init_app_config.mcp_transport
|
||||
if mcp_transport == "sse":
|
||||
mcp.run(transport="sse", host=service.http_service_config.host, port=service.http_service_config.port)
|
||||
elif mcp_transport == "stdio":
|
||||
mcp.run(transport="stdio")
|
||||
else:
|
||||
raise ValueError(f"Unsupported mcp transport: {mcp_transport}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
# start with:
|
||||
# experiencemaker_mcp \
|
||||
# mcp_transport=stdio \
|
||||
# http_service.port=8001 \
|
||||
# llm.default.model_name=qwen3-32b \
|
||||
# embedding_model.default.model_name=text-embedding-v4 \
|
||||
# vector_store.default.backend=local_file
|
||||
|
|
@ -59,6 +59,7 @@ class VectorStoreConfig:
|
|||
class AppConfig:
|
||||
pre_defined_config: str = field(default="default_config")
|
||||
config_path: str = field(default="")
|
||||
mcp_transport: str = field(default="sse")
|
||||
http_service: HttpServiceConfig = field(default_factory=HttpServiceConfig)
|
||||
thread_pool: ThreadPoolConfig = field(default_factory=ThreadPoolConfig)
|
||||
api: APIConfig = field(default_factory=APIConfig)
|
||||
|
|
|
|||
|
|
@ -46,8 +46,9 @@ class ExperienceMakerService:
|
|||
|
||||
def __call__(self, api: str, request: dict | BaseRequest) -> BaseResponse:
|
||||
if isinstance(request, dict):
|
||||
request = BaseRequest(**request)
|
||||
app_config: AppConfig = self.config_parser.get_app_config(**request.config)
|
||||
app_config: AppConfig = self.config_parser.get_app_config(**request["config"])
|
||||
else:
|
||||
app_config: AppConfig = self.config_parser.get_app_config(**request.config)
|
||||
|
||||
if api == "retriever":
|
||||
if isinstance(request, dict):
|
||||
|
|
@ -76,6 +77,8 @@ class ExperienceMakerService:
|
|||
else:
|
||||
raise RuntimeError(f"Invalid service.api={api}")
|
||||
|
||||
logger.info(f"request={request.model_dump_json()}")
|
||||
|
||||
try:
|
||||
context = PipelineContext(app_config=app_config,
|
||||
thread_pool=self.thread_pool,
|
||||
|
|
|
|||
87
experiencemaker/service/mcp_client.py
Normal file
87
experiencemaker/service/mcp_client.py
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import asyncio
|
||||
import json
|
||||
from typing import List
|
||||
|
||||
from fastmcp import Client
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from experiencemaker.schema.request import RetrieverRequest, SummarizerRequest, VectorStoreRequest, AgentRequest
|
||||
from experiencemaker.schema.response import RetrieverResponse, SummarizerResponse, VectorStoreResponse, AgentResponse
|
||||
|
||||
|
||||
class MCPClient(BaseModel):
|
||||
base_url: str = Field(default="http://0.0.0.0:8001/sse")
|
||||
enable_sse: bool = Field(default=True)
|
||||
timeout: int = Field(default=300)
|
||||
|
||||
_client: Client | None = None
|
||||
|
||||
async def __aenter__(self):
|
||||
if self.enable_sse:
|
||||
self._client = Client(self.base_url)
|
||||
else:
|
||||
self._client = Client("stdio")
|
||||
|
||||
await self._client.__aenter__()
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||
if self._client:
|
||||
await self._client.__aexit__(exc_type, exc_val, exc_tb)
|
||||
|
||||
async def list_tools(self) -> List[str]:
|
||||
tools = await self._client.list_tools()
|
||||
return [tool.name for tool in tools]
|
||||
|
||||
async def call_retriever(self, request: RetrieverRequest) -> RetrieverResponse:
|
||||
result = await self._client.call_tool("retriever", request.model_dump())
|
||||
return RetrieverResponse(**result.structured_content)
|
||||
|
||||
async def call_summarizer(self, request: SummarizerRequest) -> SummarizerResponse:
|
||||
result = await self._client.call_tool("summarizer", request.model_dump())
|
||||
return SummarizerResponse(**result.structured_content)
|
||||
|
||||
async def call_vector_store(self, request: VectorStoreRequest) -> VectorStoreResponse:
|
||||
result = await self._client.call_tool("vector_store", request.model_dump())
|
||||
return VectorStoreResponse(**result.structured_content)
|
||||
|
||||
async def call_agent(self, request: AgentRequest) -> AgentResponse:
|
||||
result = await self._client.call_tool("agent", request.model_dump())
|
||||
return AgentResponse(**result.structured_content)
|
||||
|
||||
|
||||
async def main():
|
||||
"""Example usage of MCPClient"""
|
||||
async with MCPClient() as client:
|
||||
# List available tools
|
||||
tools = await client.list_tools()
|
||||
print("Available tools:", json.dumps(tools, ensure_ascii=False, indent=2))
|
||||
|
||||
# Example retriever call
|
||||
retriever_request = RetrieverRequest(
|
||||
workspace_id="test_workspace",
|
||||
query="hello world",
|
||||
top_k=5)
|
||||
|
||||
try:
|
||||
response = await client.call_retriever(retriever_request)
|
||||
print("Retriever response:", response.model_dump())
|
||||
except Exception as e:
|
||||
print(f"Error calling retriever: {e}")
|
||||
|
||||
# Example summarizer call
|
||||
from experiencemaker.schema.message import Trajectory, Message
|
||||
|
||||
summarizer_request = SummarizerRequest(
|
||||
workspace_id="test_workspace",
|
||||
traj_list=[Trajectory(messages=[Message(content="hello world!")])])
|
||||
|
||||
try:
|
||||
response = await client.call_summarizer(summarizer_request)
|
||||
print("Summarizer response:", response.model_dump())
|
||||
except Exception as e:
|
||||
print(f"Error calling summarizer: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
|
@ -21,6 +21,7 @@ dependencies = [
|
|||
"dashscope>=1.19.1",
|
||||
"elasticsearch>=8.14.0",
|
||||
"fastapi>=0.115.13",
|
||||
"fastmcp>=2.10.6",
|
||||
"loguru>=0.7.3",
|
||||
"mcp>=1.9.4",
|
||||
"numpy>=2.3.0",
|
||||
|
|
@ -45,3 +46,4 @@ experiencemaker = [
|
|||
|
||||
[project.scripts]
|
||||
experiencemaker = "experiencemaker.app:main"
|
||||
experiencemaker_mcp = "experiencemaker.mcp_server:main"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue