Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 3 additions & 15 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.22)
project(MCPServer.cpp LANGUAGES CXX C VERSION 1.0.5.0)
project(MCPServer.cpp LANGUAGES CXX C VERSION 1.0.6.0)

# Add option for Python support
option(ENABLE_PYTHON_PLUGINS "Enable Python plugin support" ON)
Expand Down Expand Up @@ -98,20 +98,8 @@ add_subdirectory(src/Resources)
add_subdirectory(src/routers)
add_subdirectory(src/Prompts)

# Conditionally add Python support
if(ENABLE_PYTHON_PLUGINS)
find_package(Python COMPONENTS Interpreter Development)
if(Python_FOUND)
message(STATUS "Python found: ${Python_VERSION}")
message(STATUS "Python executable: ${Python_EXECUTABLE}")
message(STATUS "Python include dirs: ${Python_INCLUDE_DIRS}")
message(STATUS "Python libraries: ${Python_LIBRARIES}")
else()
message(WARNING "Python not found. Disabling Python plugin support.")
set(ENABLE_PYTHON_PLUGINS OFF)
endif()
endif()

include(cmake/EnablePython.cmake)
enable_python()
# turn off mimalloc tests and examples
set(MI_BUILD_TESTS OFF CACHE BOOL "Build mimalloc tests" FORCE)
set(MI_BUILD_EXAMPLES OFF CACHE BOOL "Build mimalloc examples" FORCE)
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,30 @@ MCPServer.cpp supports a powerful plugin system that allows extending functional
- `safe_system_plugin`: Secure system command execution
- `example_stream_plugin`: Streaming data example

### Python Plugins

MCPServer++ now supports Python plugins through a new Python SDK that makes plugin development more intuitive. Python plugins are compiled to dynamic libraries (DLL/SO) that wrap Python code using pybind11.

#### Creating Python Plugins

To create a new Python plugin, use the `plugin_ctl` tool:

```bash
./plugin_ctl create -p my_python_plugin
```

This will generate a Python plugin template that uses the new Python SDK with decorators and helper functions.

#### Python Plugin Features

- Decorator-based tool definition with `@tool`
- Automatic JSON handling
- Streaming tool support
- Parameter validation helpers
- Easy integration with the MCP protocol

For detailed information about Python plugin development, see [Python Plugins Documentation](docs/PYTHON_PLUGINS.md).

### Plugin Development

See [plugins/README.md](plugins/README.md) for detailed information on developing custom plugins.
Expand Down
24 changes: 24 additions & 0 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,30 @@ MCPServer.cpp 支持强大的插件系统,允许在不修改核心服务器的
- `safe_system_plugin`: 安全系统命令执行
- `example_stream_plugin`: 流式数据示例

### Python 插件

MCPServer++ 现在通过新的 Python SDK 支持 Python 插件,这使得插件开发更加直观。Python 插件被编译为动态库 (DLL/SO),使用 pybind11 包装 Python 代码。

#### 创建 Python 插件

要创建新的 Python 插件,请使用 [plugin_ctl](file:///D:/codespace/MCPServer++/tools/plugin_ctl.cpp#L759-L759) 工具:

```bash
./plugin_ctl create -p my_python_plugin
```

这将生成一个使用新的 Python SDK 的 Python 插件模板,其中包含装饰器和辅助函数。

#### Python 插件特性

- 基于装饰器的工具定义,使用 [@tool](file://d:\codespace\MCPServer++\plugins\sdk\mcp_sdk.py#L173-L186)
- 自动 JSON 处理
- 流式工具支持
- 参数验证辅助函数
- 与 MCP 协议的轻松集成

有关 Python 插件开发的详细信息,请参阅 [Python 插件文档](docs/PYTHON_PLUGINS_zh.md)。

### 插件开发

有关开发自定义插件的详细信息,请参阅 [plugins/README_zh.md](plugins/README_zh.md)。
Expand Down
28 changes: 28 additions & 0 deletions cmake/EnablePython.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
include_guard(GLOBAL)

function(target_enable_python target_name)
if(ENABLE_PYTHON_PLUGINS)
find_package(Python COMPONENTS Interpreter Development)
if(Python_FOUND)
target_include_directories(${target_name} PUBLIC ${Python_INCLUDE_DIRS})
else()
message(WARNING "Python not found. Disabling Python plugin support.")
set(ENABLE_PYTHON_PLUGINS OFF)
endif()
endif()
endfunction()

function(enable_python)
if(ENABLE_PYTHON_PLUGINS)
find_package(Python COMPONENTS Interpreter Development)
if(Python_FOUND)
message(STATUS "Python found: ${Python_VERSION}")
message(STATUS "Python executable: ${Python_EXECUTABLE}")
message(STATUS "Python include dirs: ${Python_INCLUDE_DIRS}")
message(STATUS "Python libraries: ${Python_LIBRARIES}")
else()
message(WARNING "Python not found. Disabling Python plugin support.")
set(ENABLE_PYTHON_PLUGINS OFF)
endif()
endif()
endfunction()
118 changes: 118 additions & 0 deletions cmake/PluginCommon.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,122 @@ function(configure_plugin plugin_name src_files)
endforeach()
endif()
endif()
endfunction()

# Common function to configure a Python plugin
# args:
# plugin_name - Name of the plugin
# python_module - Path to the Python module file (.py)
function(configure_python_plugin plugin_name python_module)
# Find required packages
find_package(Python COMPONENTS Interpreter Development REQUIRED)

# Add the plugin library using the pybind wrapper
add_library(${plugin_name} SHARED
${PROJECT_SOURCE_DIR}/plugins/sdk/pybind_module_plugin.cpp
)

# Include directories
target_include_directories(${plugin_name} PRIVATE
# MCPServer++ include directories
${PROJECT_SOURCE_DIR}/plugins/sdk
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/third_party/nlohmann
${PROJECT_SOURCE_DIR}/third_party/pybind11/include
${PROJECT_SOURCE_DIR}/src
)

# Add preprocessor definition for DLL export
target_compile_definitions(${plugin_name} PRIVATE MCPSERVER_API_EXPORTS PYBIND11_EXPORT_OVERRIDE)

# Link libraries
target_link_libraries(${plugin_name} PRIVATE
pybind11::embed
mcp_business
)

# Set output directory for plugins to bin/plugins
set_target_properties(${plugin_name} PROPERTIES
PREFIX ""
RUNTIME_OUTPUT_DIRECTORY ${PLUGINS_OUTPUT_DIR}
LIBRARY_OUTPUT_DIRECTORY ${PLUGINS_OUTPUT_DIR}
)

if(WIN32)
set_target_properties(${plugin_name} PROPERTIES SUFFIX ".dll")
else()
set_target_properties(${plugin_name} PROPERTIES SUFFIX ".so")
endif()

# Install the plugin to bin/plugins (always install plugins)
install(TARGETS ${plugin_name}
RUNTIME DESTINATION bin/plugins
LIBRARY DESTINATION bin/plugins
)

# Copy the Python module file to the output directory after build
add_custom_command(TARGET ${plugin_name} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${python_module}
$<TARGET_FILE_DIR:${plugin_name}>
)

# Copy the SDK file to the output directory after build if it exists
set(sdk_file "${PROJECT_SOURCE_DIR}/plugins/sdk/mcp_sdk.py")
if(EXISTS ${sdk_file})
add_custom_command(TARGET ${plugin_name} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${sdk_file}
$<TARGET_FILE_DIR:${plugin_name}>
)
endif()

# Install the Python module file
install(FILES ${python_module}
DESTINATION bin/plugins
)

# Install the SDK file if it exists
if(EXISTS ${sdk_file})
install(FILES ${sdk_file}
DESTINATION bin/plugins
)
endif()

# automatically generate tools.json - always do this for runtime configs
set(json_source_file "${CMAKE_CURRENT_SOURCE_DIR}/tools.json")
set(json_target_file "${plugin_name}_tools.json")

if(EXISTS ${json_source_file})
# Create configs directory if not exists
file(MAKE_DIRECTORY ${CONFIGS_OUTPUT_DIR})

configure_file(
${json_source_file}
${CONFIGS_OUTPUT_DIR}/${json_target_file}
COPYONLY
)

# build when the plugin is built
add_custom_command(
OUTPUT ${CONFIGS_OUTPUT_DIR}/${json_target_file}
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${json_source_file}
${CONFIGS_OUTPUT_DIR}/${json_target_file}
DEPENDS ${json_source_file}
COMMENT "Copying ${plugin_name} tools.json file to configs directory"
)

add_custom_target(${plugin_name}_json ALL
DEPENDS ${CONFIGS_OUTPUT_DIR}/${json_target_file}
)

add_dependencies(${plugin_name} ${plugin_name}_json)

# Install configs to bin/configs (always install configs)
install(FILES ${json_source_file}
DESTINATION bin/configs
RENAME ${json_target_file}
)
endif()
endfunction()
118 changes: 118 additions & 0 deletions docs/PYTHON_PLUGINS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Python Plugins for MCP Server++

Python plugins provide an easy way to extend the functionality of the MCP Server++ using Python. With the new Python SDK, developers can create plugins more intuitively using decorators and other syntactic sugar.

## Table of Contents

- [Introduction](#introduction)
- [Python SDK Overview](#python-sdk-overview)
- [Creating a Python Plugin](#creating-a-python-plugin)
- [Plugin Structure](#plugin-structure)
- [Using the Python SDK](#using-the-python-sdk)
- [Building Python Plugins](#building-python-plugins)
- [Best Practices](#best-practices)

## Introduction

Python plugins are dynamic libraries (DLL on Windows, so on Linux) that wrap Python code using pybind11. They implement the standard MCP plugin interface while allowing the actual plugin logic to be written in Python.

## Python SDK Overview

The Python SDK (`mcp_sdk.py`) provides a set of utilities and decorators to make plugin development more intuitive:

1. `@tool` decorator - Register functions as MCP tools
2. Parameter helper functions - Easily define tool parameters
3. `ToolType` enum - Specify standard or streaming tools
4. Automatic JSON handling - Automatically convert between Python objects and JSON

## Creating a Python Plugin

Use the `plugin_ctl` tool to generate a new Python plugin template:

```bash
./plugin_ctl create -p my_python_plugin
```

This will create a new directory `my_python_plugin` with the basic Python plugin structure.

## Plugin Structure

A typical Python plugin consists of:

- `plugin_name.py` - The main Python plugin implementation using the SDK
- `tools.json` - JSON file describing the tools provided by the plugin
- `CMakeLists.txt` - CMake configuration for building the plugin
- `mcp_sdk.py` - The Python SDK (automatically copied during build)

## Using the Python SDK

### Basic Tool Definition

```python
from mcp_sdk import tool, string_param

@tool(
name="hello_world",
description="A simple greeting tool",
name_param=string_param(description="The name to greet", required=True)
)
def hello_world_tool(name_param: str):
return f"Hello, {name_param}!"
```

### Streaming Tool Definition

```python
from mcp_sdk import tool, integer_param, ToolType

@tool(
name="stream_counter",
description="Stream a sequence of numbers",
tool_type=ToolType.STREAMING,
count=integer_param(description="Number of items to stream", default=5)
)
def stream_counter_tool(count: int = 5):
for i in range(count):
yield {"number": i, "text": f"Item {i}"}
```

### Parameter Helper Functions

The SDK provides helper functions for defining common parameter types:

- `string_param()` - Define a string parameter
- `integer_param()` - Define an integer parameter
- `number_param()` - Define a float parameter
- `boolean_param()` - Define a boolean parameter
- `array_param()` - Define an array parameter
- `object_param()` - Define an object parameter

## Building Python Plugins

Python plugins are built using CMake, just like C++ plugins. The build process automatically:

1. Compiles the C++ wrapper code
2. Copies the Python plugin file
3. Copies the Python SDK

To build a Python plugin:

```bash
cd my_python_plugin
mkdir build
cd build
cmake ..
cmake --build .
```

The resulting DLL/SO file can then be used with the MCP Server++.

## Best Practices

1. **Use the SDK decorators**: They handle JSON conversion and tool registration automatically
2. **Define clear parameter schemas**: Use parameter helper functions to document your tool's interface
3. **Handle errors gracefully**: Python exceptions will be automatically converted to MCP error responses
4. **Use type hints**: They improve code readability and help catch errors early
5. **Test streaming tools**: Ensure your generators work correctly and clean up resources properly
6. **Document your tools**: Provide clear descriptions for tools and parameters
7. **Keep plugin files organized**: For complex plugins, consider splitting code into multiple modules
Loading
Loading