Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:49:50 +08:00
commit adc4b2be25
147 changed files with 24716 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
# MXCP Plugin Example
This example demonstrates how to create and use a custom MXCP plugin. The plugin provides various UDFs (User Defined Functions) that can be used in your SQL queries.
## Overview
This plugin implements the Caesar cipher, a simple encryption technique where each letter in the plaintext is shifted by a fixed number of positions in the alphabet.
## Project Structure
```
examples/plugin/
├── plugins/
│ └── my_plugin/
│ └── __init__.py # Plugin implementation
├── tools/
│ └── decipher.yml # Endpoint using the plugin
├── python/ # Directory for Python endpoints
├── sql/ # Directory for SQL implementations
├── config.yml # Example plugin configuration
├── mxcp-site.yml # Project configuration
└── README.md
```
## Configuration
### 1. User Configuration
The example includes two plugin configurations in `config.yml`:
- `rot1`: Rotates letters by 1 position (A->B, B->C, etc.)
- `rot10`: Rotates letters by 10 positions (A->K, B->L, etc.)
To use the plugin, register these configurations in your MXCP user config (`~/.mxcp/config.yml`):
```yaml
mxcp: 1
projects:
demo-plugin:
profiles:
dev:
plugin:
config:
rot1:
rotation: "1"
rot10:
rotation: "10"
```
Then in your `mxcp-site.yml`, you can reference one of these configurations:
```yaml
mxcp: 1
project: demo-plugin
profile: dev
plugin:
- name: str_secret
module: my_plugin
config: rot1
- name: tricky
module: my_plugin
config: rot10
```
## Running the MCP
To run the service using the example configuration:
1. Set the `MXCP_CONFIG` environment variable to point to the example's config file:
```bash
export MXCP_CONFIG=/path/to/examples/plugin/config.yml
```
2. Start the MXCP server:
```bash
mxcp serve
```
The service will now use the example configuration with both the `simple` (rot1) and `tricky` (rot10) Caesar cipher plugins.

View File

@@ -0,0 +1,12 @@
mxcp: 1
projects:
demo-plugin:
profiles:
dev:
plugin:
config:
rot1:
rotation: "1" # Rotate by 1 position (A->B, B->C, etc.)
rot10:
rotation: "10" # Rotate by 10 positions (A->K, B->L, etc.)

View File

@@ -0,0 +1,7 @@
mxcp: 1
project: demo-plugin
profile: dev
plugin:
- name: str_secret
module: my_plugin
config: rot1

View File

@@ -0,0 +1,33 @@
# MXCP Plugins Directory
This directory contains MXCP plugins that extend DuckDB with custom User Defined Functions (UDFs).
## Structure
Each plugin should be a Python module containing a class named `MXCPPlugin` that inherits from `MXCPBasePlugin`.
```
plugins/
├── my_plugin/
│ └── __init__.py # Contains MXCPPlugin class
├── utils/
│ └── string_utils.py
└── integrations/
└── api_plugin.py
```
## Usage
Plugins are referenced in `mxcp-site.yml`:
```yaml
plugin:
- name: cipher
module: my_plugin
config: rot13
```
The functions are then available in SQL as `{function_name}_{plugin_name}`:
```sql
SELECT encrypt_cipher('hello world');
```

View File

@@ -0,0 +1,134 @@
"""
Example MXCP Plugin
This plugin demonstrates how to create a simple MXCP plugin with Caesar cipher encryption capabilities
and optional user context integration for authentication-aware features.
Example usage:
>>> plugin = MXCPPlugin({"rotation": 13})
>>> plugin.encrypt("Hello, World!") # Returns "Uryyb, Jbeyq!"
>>> plugin.decrypt("Uryyb, Jbeyq!") # Returns "Hello, World!"
With user context (when authentication is enabled):
>>> plugin.get_user_info() # Returns user information
"""
from typing import Any, Dict, Optional
from mxcp.plugins import MXCPBasePlugin, udf
class MXCPPlugin(MXCPBasePlugin):
"""Plugin that provides Caesar cipher encryption and decryption functions.
This plugin implements the Caesar cipher, a type of substitution cipher where
each letter in the plaintext is shifted a certain number of places down or up
the alphabet. It also demonstrates how to use user context for authentication-aware features.
Example:
>>> plugin = MXCPPlugin({"rotation": 13})
>>> plugin.encrypt("Hello, World!") # Returns "Uryyb, Jbeyq!"
>>> plugin.decrypt("Uryyb, Jbeyq!") # Returns "Hello, World!"
"""
def __init__(self, config: Dict[str, Any], user_context=None):
"""Initialize the plugin with configuration and optional user context.
Args:
config: Configuration dictionary containing:
- rotation: Number of positions to shift (1-25), can be string or int
user_context: Optional authenticated user context (for new plugins)
"""
super().__init__(config, user_context)
rotation = config.get("rotation", 13)
# Convert string to int if needed
if isinstance(rotation, str):
try:
rotation = int(rotation)
except ValueError:
raise ValueError("Rotation must be a valid integer")
if not isinstance(rotation, int) or rotation < 1 or rotation > 25:
raise ValueError("Rotation must be an integer between 1 and 25")
self.rotation = rotation
def __rotate_char(self, char: str, forward: bool = True) -> str:
"""Rotate a single character by the configured number of positions.
Args:
char: Character to rotate
forward: True for encryption, False for decryption
Returns:
Rotated character
"""
if not char.isalpha():
return char
# Determine the base ASCII value (a=97, A=65)
base = ord("a") if char.islower() else ord("A")
# Calculate the position in the alphabet (0-25)
pos = ord(char) - base
# Apply rotation (forward or backward)
shift = self.rotation if forward else -self.rotation
# Wrap around the alphabet and convert back to character
return chr(base + ((pos + shift) % 26))
@udf
def encrypt(self, text: str) -> str:
"""Encrypt text using the Caesar cipher.
Args:
text: Text to encrypt
Returns:
Encrypted text
Example:
>>> plugin.encrypt("Hello, World!") # Returns "Uryyb, Jbeyq!"
"""
return "".join(self.__rotate_char(c, True) for c in text)
@udf
def decrypt(self, text: str) -> str:
"""Decrypt text using the Caesar cipher.
Args:
text: Text to decrypt
Returns:
Decrypted text
Example:
>>> plugin.decrypt("Uryyb, Jbeyq!") # Returns "Hello, World!"
"""
return "".join(self.__rotate_char(c, False) for c in text)
@udf
def encrypt_with_user_key(self, text: str) -> str:
"""Encrypt text using a user-specific rotation based on their username.
This demonstrates how plugins can use user context to provide
personalized functionality.
Args:
text: Text to encrypt
Returns:
Text encrypted with user-specific key, or standard encryption if not authenticated
"""
if self.is_authenticated():
# Use username length as additional rotation factor
username = self.get_username() or ""
user_rotation = (self.rotation + len(username)) % 26
# Temporarily modify rotation for this operation
original_rotation = self.rotation
self.rotation = user_rotation if user_rotation > 0 else 1
result = self.encrypt(text)
self.rotation = original_rotation # Restore original rotation
return result
else:
# Fall back to standard encryption
return self.encrypt(text)

View File

@@ -0,0 +1,20 @@
mxcp: 1
tool:
name: "decipher"
description: "Decrypt an encrypted message."
parameters:
- name: message
type: string
description: "Encrypted message"
return:
type: string
source:
code: SELECT decrypt_str_secret($message);
annotations:
readOnlyHint: true
tests:
- name: quick_check
arguments:
- key: message
value: "usbmbmb"