Content
# woodenfish-mcp-host Core Library
This repository contains the backend core library for the woodenfish project, specifically the Model Context Protocol (MCP) host. It includes core functionalities such as agent logic, tool management, model loading, and session management.
This library is designed to be integrated into other Python applications (e.g., custom HTTP services, CLI tools, desktop applications, etc.) to provide the core AI capabilities of woodenfish.
## 1. Library Features
The `woodenfish-mcp-host` library provides the following core functionalities:
- **Intelligent Agent**: Manages the AI's reasoning process using **LangGraph**, including multi-turn dialogues and automated tool calls.
- **Tool Management**: Loads, manages, and interfaces with various external MCP tools (scripts or processes) that extend the AI's capabilities (e.g., file access, web search, calculations, etc.).
- **Model Loading**: Handles the loading and management of different large language models (LLMs) based on configuration.
- **Chat Session Management**: Manages individual chat sessions, maintaining dialogue history and agent state.
- **Configuration Management**: Handles the loading and application of LLM and tool configurations.
This library **does not include** a built-in web server, graphical interface, or CLI entry point (although it provides foundational building blocks for these). Developers integrating this library are responsible for building their own interfaces and service layers.
## 2. Installation
To use this library in your Python project, you can install it directly from the local directory using pip:
1. Navigate to the directory where you cloned or placed the `woodenfish-mcp-host-library` folder.
2. Run the following command:
```bash
pip install ./woodenfish-mcp-host-library
```
This command will install the `woodenfish_mcp_host` package and its dependencies (as specified in `pyproject.toml`) into your Python environment. The `./` indicates that pip should install from the local directory.
If you want to install it in editable mode for development (so that changes to the library code are reflected immediately without needing to reinstall), use the `-e` flag:
```bash
pip install -e ./woodenfish-mcp-host-library
```
**Important Notes**:
- The core functionalities of this library depend on **LangChain** and **LangGraph**. If you want to gain a deeper understanding of its internal workings, it is recommended to consult the official documentation for these two libraries.
- This library uses Python's **asynchronous programming (asyncio)**. This means that when calling most core methods in the library, you need to use the `await` keyword within an `async def` function and run the asynchronous main function using `asyncio.run()`. If you are not familiar with asynchronous programming, it is advisable to learn the relevant basics first.
## 3. Configuration
The behavior of the `mcp-host` library is controlled by a configuration object (`HostConfig`). You need to provide this configuration when initializing the core `woodenfishMcpHost` class.
We provide example configuration files:
- `model_config.example.json`: An example configuration for large models (LLMs).
- `mcp_config.example.json`: An example configuration for MCP tools.
**Using these examples**:
1. Copy the example files to your project's configuration directory and rename them (e.g., `model_config.json`, `mcp_config.json`).
2. Edit the copied configuration files to match your environment and requirements (e.g., add your LLM API key, specify local model paths, define your MCP tools).
3. Load these configuration files in your application and create a `HostConfig` object with them.
The specific implementation of configuration loading depends on your project structure and preferences (e.g., loading from JSON, YAML files, or environment variables). Please refer to the usage examples below, which include a simple implementation for loading example configurations.
## 3.1 Model Configuration (model_config.example.json)
The `model_config.example.json` file is used to configure the large language models (LLMs) that the library can use. It defines different model providers and model settings.
Here is an example content of `model_config.example.json` with detailed comments:
```json
{
"activeProvider": "ollama", // 【Required】The name of the currently active model provider. Must be one of the keys defined in "configs".
"configs": { // 【Required】A dictionary containing specific configurations for various model providers.
"ollama": { // Example configuration for the ollama model provider.
"model": "qwen3:1.7b", // 【Required】Model name. The specific name depends on the provider and the installed model.
"model_provider": "ollama", // 【Required】Internal identifier for the model provider, used by the library to identify how to interact with the model.
"base_url": "http://localhost:11434", // 【Optional】Base URL for the model service. Local models or certain APIs may require this to be specified.
"streaming": true, // 【Optional】Whether to enable streaming. Set to true to receive AI replies word by word, providing a better user experience.
"max_tokens": 4096, // 【Optional】Maximum number of tokens for the model to generate replies.
"tools_in_prompt": true, // 【Optional】Whether to include the tool schema in the prompt sent to the model. Usually set to true for models that support Function Calling.
"configuration": { // 【Optional】A dictionary containing additional configuration parameters passed to the underlying LangChain model object. Specific parameters depend on the model_provider.
"temperature": 0.6 // For example, the temperature parameter for the ollama provider.
}
},
"zhipu_glm4": { // Example for the Zhipu GLM4 model.
"model": "GLM-4-Flash", // 【Required】Model name.
"model_provider": "openai_compatible", // 【Required】Use this identifier if the model provider is compatible with the OpenAI API.
"streaming": true, // 【Optional】Whether to enable streaming.
"max_tokens": 4096, // 【Optional】Maximum number of tokens.
"tools_in_prompt": false, // 【Optional】Whether to include the tool schema in the prompt. Zhipu GLM-4-Flash supports tool calls, and this may be set to false if other means of passing tool information are used or as an example.
"api_key": "your api key", // 【Required】API key required to call this model. Please replace with your actual key.
"configuration": { // 【Optional】Additional configuration parameters.
"base_url": "https://open.bigmodel.cn/api/paas/v4/", // Zhipu's model API address.
"temperature": 0.6 // Temperature parameter.
},
"default_headers": {} // 【Optional】Additional HTTP headers to add when calling the API.
},
"openrouter": { // Example for OpenRouter, which aggregates multiple models.
"model": "deepseek/deepseek-r1:free", // 【Required】Model name selected on OpenRouter.
"model_provider": "openai_compatible", // 【Required】OpenRouter is compatible with the OpenAI API.
"streaming": true, // 【Optional】Whether to enable streaming.
"max_tokens": 4096, // 【Optional】Maximum number of tokens.
"tools_in_prompt": false, // 【Optional】Whether to include the tool schema in the prompt.
"api_key": "your api key", // 【Required】OpenRouter API key.
"configuration": { // 【Optional】Additional configuration parameters.
"base_url": "https://openrouter.ai/api/v1/", // OpenRouter API address.
"temperature": 0.6 // Temperature parameter.
},
"default_headers": {} // 【Optional】Additional HTTP headers.
},
"modelscope": { // Example for ModelScope.
"model": "Qwen/Qwen3-30B-A3B", // 【Required】Model name selected on ModelScope.
"model_provider": "openai_compatible", // 【Required】ModelScope is compatible with the OpenAI API.
"streaming": true, // 【Optional】Whether to enable streaming.
"max_tokens": 4096, // 【Optional】Maximum number of tokens.
"tools_in_prompt": true, // 【Optional】Whether to include the tool schema in the prompt.
"api_key": "your api key", // 【Required】ModelScope API key.
"configuration": { // 【Optional】Additional configuration parameters.
"base_url": "https://api-inference.modelscope.cn/v1/", // ModelScope API address.
"temperature": 0.6 // Temperature parameter.
}
}
},
"enable_tools": true // 【Optional】Global switch to enable or disable tool calling functionality. Setting to false disables the use of all tools.
}
```
## 3.2 Tool Configuration (mcp_config.example.json)
The `mcp_config.example.json` file is used to configure external MCP tool servers that the library can call. These tools communicate with the `mcp-host` library via standard protocols.
Here is an example content of `mcp_config.example.json` with detailed comments:
```json
{
"mcpServers": { // 【Required】A dictionary defining different MCP tool servers.
"tavily-mcp-server": { // Example configuration for the tavily-mcp-server tool server.
"enabled": false, // 【Required】Whether to enable this tool server. Set to true for the library to call the tools it provides.
"transport": "stdio", // 【Required】Communication method. For example, "stdio" indicates communication via standard input/output.
"description": "tavily-mcp", // 【Optional】Description of the tool server.
"registryUrl": "", // 【Optional】URL of the tool registry for discovering tools (currently may not be used or for specific scenarios).
"command": "npx", // 【Required】Command to start the tool server process.
"args": [ // 【Optional】List of arguments for the startup command.
"-y", // For example, npx arguments.
"tavily-mcp@0.1.4" // The npm package to run.
],
"env": { // 【Optional】Environment variables to set when starting the tool process.
"TAVILY_API_KEY": "tvly-dev-……" // For example, the API Key required by the Tavily tool. Please replace with your actual key.
},
"disabled": false, // 【Deprecated/Not Recommended】Please prioritize using the enabled field.
"autoApprove": [] // 【Optional】A list of tool calls that can be automatically approved without user confirmation.
},
"sequential-thinking": { // Example for the sequential-thinking tool server.
"command": "npx", // 【Required】Startup command.
"transport": "stdio", // 【Required】Communication method.
"args": [ // 【Optional】Arguments for the startup command.
"-y",
"@modelcontextprotocol/server-sequential-thinking"
],
"enabled": true // 【Required】Whether to enable.
},
"MCP-HUB": { // Example for the MCP-HUB tool server, which may be a service integrating multiple tools.
"description": "test", // 【Optional】Description.
"transport": "sse", // 【Required】Communication method, e.g., "sse" (Server-Sent Events).
"enabled": false, // 【Required】Whether to enable.
"timeout": 60, // 【Optional】Request timeout in seconds.
"url": "http://localhost:3000/sse", // 【Required】URL of the tool server.
"headers": {} // 【Optional】Additional HTTP headers to add when calling the URL.
}
}
}
```
When configuring these files, you need to ensure that the correct values are filled in, especially the API keys and addresses of local services. If the `enabled` field of a tool server is set to `false`, the library will not attempt to call any tools under that tool server.
## 4. Usage (Including Configuration Loading and Chat Example)
The main entry point for using the `mcp-host` library is the `woodenfishMcpHost` class. You need to initialize it with your configuration and then call its methods to interact with the AI.
Here’s a detailed explanation of how to use the core API:
### `woodenfishMcpHost` Class
This is the **main entry point** of the entire library. Its primary role is to manage the lifecycle, configuration, and creation of chat sessions for the entire host. You need to initialize it by passing in a `HostConfig` object:
```python
from woodenfish_mcp_host.host.host import woodenfishMcpHost
# Assume my_config is a HostConfig object that has been loaded
host = woodenfishMcpHost(config=my_config)
# Here, my_config contains the configurations for LLM and MCP tools.
```
The lifecycle management of the `woodenfishMcpHost` class (initialization and cleanup) may require additional asynchronous calls, depending on its internal implementation (e.g., whether it uses an asynchronous context manager `async with`). Please refer to the comments in the complete example code provided below, as well as the source code of the library itself (if you need a deeper understanding).
### Chat Session Object (Obtained via `host.chat()`)
By calling the `.chat()` method of the `woodenfishMcpHost` instance, you can obtain an object for managing a single chat session. This is your interface for actual dialogue with the intelligent agent.
```python
# Get the chat session object from the woodenfishMcpHost instance
chat_session = host.chat() # Creating a new session without passing chat_id
# Alternatively, if you want to restore an existing session, you can pass the session ID:
# chat_session = host.chat(chat_id="existing_session_id") # Restore an existing session
```
This `chat_session` object is responsible for maintaining the dialogue history and state of the current session, ensuring that the agent can understand the context and engage in multi-turn dialogues.
### Sending Messages and Handling Responses (`chat_session.astream()`)
The core method for interacting with the intelligent agent is `chat_session.astream()`. This is an **asynchronous generator** method used to send user messages and receive the agent's responses in a **streaming** manner. Streaming is particularly useful for real-time display of AI-generated content, especially when dealing with long-running agent thoughts or tool calls.
When calling the `astream()` method, you need to pass in the user's query text:
```python
user_message = "Hello, what can you do?" # User's input or command
print(f"User: {user_message}")
print("AI: ", end="") # To display in a streaming manner, print the prompt without a newline
# Use async for to iterate over the asynchronous generator and receive each data chunk
async for chunk in chat_session.astream(query=user_message):
# Process each received data chunk
# The specific structure of chunk depends on the underlying agent implementation (LangGraph's state machine output)
# Typically, chunk is a dictionary containing information about the agent's state changes
# You need to check the content of chunk to extract the AI-generated text, tool call prompts, tool execution results, etc.
# The most common scenario is to extract the text content from the messages list
if isinstance(chunk, dict):
if 'messages' in chunk and isinstance(chunk['messages'], list):
# Find the latest message from the messages list, usually the AI's reply
for msg in reversed(chunk['messages']): # Look for the latest message from the end
if hasattr(msg, 'content') and msg.content is not None:
# If it's text content, print it out (end="" maintains the streaming effect)
print(msg.content, end="")
break # Exit the inner loop after finding the latest message
# If you need to handle tool call information (msg.tool_calls) or tool messages (msg.tool_message), add logic here
# If chunk is not a dictionary or does not contain a messages list, it may be another type of state update, which can be processed or ignored as needed
print("\n(AI response ended)") # Print a newline and prompt after the response ends
```
Since `astream` is an asynchronous generator, you must use the `await` keyword to iterate over it within an `async def` function, and the entire asynchronous function containing the `await` call (like the `run_chat_example` in the above example) needs to be run as the program's entry point using `asyncio.run()`.
You need to extract text content or other types of information (such as tool call prompts, tool execution results, etc.) from the stream based on the structure of `chunk`, and display or process it accordingly in your application. Understanding the output structure of LangGraph will help you parse `chunk` more accurately.
### Complete Usage Example (Including Configuration Loading and Chat)
The following Python script is a **complete, runnable example** that demonstrates how to load example configurations, initialize the host, and start a basic chat session while handling streaming responses. This example integrates the concepts mentioned above.
```python
import asyncio
import json
import os
import sys
from woodenfish_mcp_host.host.host import woodenfishMcpHost
from woodenfish_mcp_host.host.conf import HostConfig, LLMConfig, McpServerConfig
# Import other necessary classes from woodenfish_mcp_host as needed, such as various message types BaseMessage, etc.
# --- Example: Load Configuration ---
def load_example_config(library_dir: str) -> HostConfig:
"""Load example configuration files and create a HostConfig object"""
llm_cfg = {}
mcp_servers = {}
# Attempt to load LLM example configuration
llm_example_path = os.path.join(library_dir, "model_config.example.json")
if os.path.exists(llm_example_path):
try:
with open(llm_example_path, "r", encoding='utf-8') as f:
llm_cfg_data = json.load(f)
llm_cfg = HostConfig.LLMConfig(**llm_cfg_data)
print(f"Loaded example LLM configuration: {llm_example_path}", file=sys.stderr)
except Exception as e:
print(f"Warning: Failed to load example LLM configuration ({llm_example_path}): {e}", file=sys.stderr)
llm_cfg = HostConfig.LLMConfig(provider="placeholder", model_name="placeholder")
else:
llm_cfg = HostConfig.LLMConfig(provider="placeholder", model_name="placeholder")
print(f"Example LLM configuration not found: {llm_example_path}. Using placeholder configuration.", file=sys.stderr)
# Attempt to load MCP example configuration
mcp_example_path = os.path.join(library_dir, "mcp_config.example.json")
if os.path.exists(mcp_example_path):
try:
with open(mcp_example_path, "r", encoding='utf-8') as f:
mcp_cfg_data = json.load(f)
mcp_servers = {name: HostConfig.McpServerConfig(**cfg) for name, cfg in mcp_cfg_data.items()}
print(f"Loaded example MCP configuration: {mcp_example_path}", file=sys.stderr)
except Exception as e:
print(f"Warning: Failed to load example MCP configuration ({mcp_example_path}): {e}", file=sys.stderr)
mcp_servers = {}
else:
mcp_servers = {}
print(f"Example MCP configuration not found: {mcp_example_path}. Using empty configuration.", file=sys.stderr)
# Create the main HostConfig object
# Note: In actual use, you may need to load log_config and other important configurations
host_config = HostConfig(
llm=llm_cfg,
mcp_servers=mcp_servers,
log_config={} # Provide actual LogConfig as needed
)
return host_config
# --- Example: Run Chat Process ---
async def run_chat_example():
"""A complete chat process example"""
# Assume the current script is located in the woodenfish-mcp-host-library directory
# If the script is in another location, adjust the library_dir variable
library_dir = "."
# 1. Load configuration
my_config = load_example_config(library_dir)
print("\nConfiguration loaded.", file=sys.stderr)
# 2. Initialize woodenfishMcpHost
# It is recommended to use an asynchronous context manager if the Host supports it
# (Check if the Host class definition has async __aenter__ / __aexit__ methods)
# If the Host does not implement an async context manager, you may need to manually call init/shutdown methods (if they exist)
host = woodenfishMcpHost(config=my_config)
# If the Host has an explicit async init method:
# await host.init()
print("woodenfishMcpHost initialized.", file=sys.stderr)
try:
# 3. Start or restore a chat session
# You can pass chat_id to restore an existing session, or not pass it to create a new session
chat_session = host.chat()
print(f"Chat session created, session ID: {chat_session._chat_id}", file=sys.stderr)
# 4. Send a message and handle the response (streaming)
user_message = "Hello, what can you do?"
print(f"\nUser: {user_message}")
print("AI:", end="") # Use end="" to avoid newline, so it connects with streaming output
# astream method returns an asynchronous generator
async for chunk in chat_session.astream(query=user_message):
# Process the received chunk in streaming
# The type and structure of chunk depend on the state definitions of the LangGraph agent
# A typical chunk is a dictionary containing state updates
if isinstance(chunk, dict):
# Example: If chunk contains a list of messages
if 'messages' in chunk and isinstance(chunk['messages'], list):
# Find the latest AI message or tool call message and print its content
for msg in reversed(chunk['messages']): # Look for the latest message from the end
if hasattr(msg, 'content') and msg.content is not None:
print(msg.content, end="") # Print message content
break # Exit the inner loop after finding the latest message
# If you need to handle tool call information (msg.tool_calls) or tool messages (msg.tool_message), add logic here
print("\n(AI response ended)\n", file=sys.stderr)
# Example of sending another message in the same session
# user_message_2 = "Use a tool to read the content of the file /tmp/test.txt"
# print(f"User: {user_message_2}")
# print("AI:", end="")
# async for chunk in chat_session.astream(query=user_message_2):
# if isinstance(chunk, dict) and 'messages' in chunk:
# for msg in reversed(chunk['messages']):
# if hasattr(msg, 'content') and msg.content is not None:
# print(msg.content, end="")
# break
# print("\n(AI response ended)\n", file=sys.stderr)
finally:
# 5. Clean up the Host instance
# If the Host class implements an asynchronous context manager (__aenter__ and __aexit__),
# it is recommended to use "async with woodenfishMcpHost(...) as host:" to automatically clean up when the Host lifecycle ends.
# If the Host has an explicit async shutdown method:
# await host.shutdown()
pass # If using async with, usually no additional cleanup is needed here
# --- Run Main Function ---
if __name__ == "__main__":
# Use asyncio.run() to run the asynchronous main function
asyncio.run(run_chat_example())
```
## 5. Integrating MCP Tools
The `mcp-host` library is designed to work in conjunction with external MCP tool processes.
1. **Define Tools**: You need to define your MCP tools as standalone scripts or executables that follow the standard input/output JSON protocol (reading from standard input and outputting to standard output).
2. **Configure Tools**: Use the `mcp_config.json` file (or your chosen configuration method) to inform the `mcp-host` library about your tools, including their names, running commands, and schemas (input parameters).
3. **Ensure Executability**: Ensure that the process running the `mcp-host` library can access and execute the tool scripts/executables.
For more detailed information about the MCP tool protocol and implementation examples (such as `echo.py`), please refer to the documentation or code in the `woodenfish_mcp_host/host/tools/` directory.
## 6. Further Development
- **Build Your Service Layer**: Integrate this library into your application, exposing functionalities through frameworks like FastAPI, Flask, or custom asyncio servers via HTTP or other protocols.
- **Implement Frontend**: Develop a user interface that interacts with your service layer.
- **Customize Agents and Tools**: Extend the `woodenfish_mcp_host` library or create new MCP tools to add custom AI behaviors and capabilities.
Connection Info
You Might Also Like
markitdown
MarkItDown-MCP is a lightweight server for converting URIs to Markdown.
servers
Model Context Protocol Servers
Time
A Model Context Protocol server for time and timezone conversions.
Filesystem
Node.js MCP Server for filesystem operations with dynamic access control.
Sequential Thinking
A structured MCP server for dynamic problem-solving and reflective thinking.
git
A Model Context Protocol server for Git automation and interaction.