Initial commit
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# Python Endpoints Demo
|
||||
|
||||
This example demonstrates how to create and use Python-based endpoints in MXCP.
|
||||
|
||||
## Features Demonstrated
|
||||
|
||||
### 1. Basic Python Functions
|
||||
- `analyze_numbers` - Statistical analysis with various operations
|
||||
- `create_sample_data` - Database operations from Python
|
||||
|
||||
### 2. Async Functions
|
||||
- `process_time_series` - Demonstrates async Python endpoint
|
||||
|
||||
### 3. Database Access
|
||||
- Using `mxcp.runtime.db` to execute SQL queries
|
||||
- Parameter binding for safe SQL execution
|
||||
|
||||
## Running the Examples
|
||||
|
||||
In a terminal, test the endpoints:
|
||||
|
||||
```bash
|
||||
# Create sample data
|
||||
mxcp run tool create_sample_data --param table_name=test_data --param row_count=100
|
||||
|
||||
# Analyze numbers
|
||||
mxcp run tool analyze_numbers --param numbers="[1, 2, 3, 4, 5]" --param operation=mean
|
||||
|
||||
# Process time series (async function)
|
||||
mxcp run tool process_time_series --param table_name=test_data --param window_days=7
|
||||
```
|
||||
|
||||
Or, if you prefer, you can also start the MXCP server and use any MCP client to call the tools:
|
||||
```bash
|
||||
mxcp serve
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
python-demo/
|
||||
├── mxcp-site.yml # Project configuration
|
||||
├── python/ # Python modules
|
||||
│ └── data_analysis.py # Python endpoint implementations
|
||||
├── tools/ # Tool definitions
|
||||
│ ├── analyze_numbers.yml
|
||||
│ ├── create_sample_data.yml
|
||||
│ └── process_time_series.yml
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
|
||||
1. **Language Declaration**: Set `language: python` in the tool definition
|
||||
2. **Function Names**: The function name must match the tool name
|
||||
3. **Return Types**: Functions must return data matching the declared return type
|
||||
4. **Database Access**: Use `db.execute()` for SQL queries
|
||||
5. **Async Support**: Both sync and async functions are supported
|
||||
@@ -0,0 +1,3 @@
|
||||
mxcp: 1
|
||||
project: python-demo
|
||||
profile: default
|
||||
@@ -0,0 +1,145 @@
|
||||
"""
|
||||
Example Python endpoints for data analysis.
|
||||
"""
|
||||
|
||||
import statistics
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from mxcp.runtime import config, db
|
||||
|
||||
|
||||
def analyze_numbers(numbers: list, operation: str = "mean") -> dict:
|
||||
"""
|
||||
Analyze a list of numbers with various statistical operations.
|
||||
"""
|
||||
if not numbers:
|
||||
return {"error": "No numbers provided"}
|
||||
|
||||
operations = {
|
||||
"mean": statistics.mean,
|
||||
"median": statistics.median,
|
||||
"mode": statistics.mode,
|
||||
"stdev": statistics.stdev if len(numbers) > 1 else lambda x: 0,
|
||||
"sum": sum,
|
||||
"min": min,
|
||||
"max": max,
|
||||
}
|
||||
|
||||
if operation not in operations:
|
||||
return {"error": f"Unknown operation: {operation}"}
|
||||
|
||||
try:
|
||||
result = operations[operation](numbers)
|
||||
return {"operation": operation, "result": result, "count": len(numbers)}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
def create_sample_data(table_name: str, row_count: int) -> dict:
|
||||
"""
|
||||
Create a sample table with test data.
|
||||
"""
|
||||
try:
|
||||
# Drop table if exists
|
||||
db.execute(f"DROP TABLE IF EXISTS {table_name}")
|
||||
|
||||
# Create table
|
||||
db.execute(
|
||||
f"""
|
||||
CREATE TABLE {table_name} (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name VARCHAR,
|
||||
value DOUBLE,
|
||||
category VARCHAR,
|
||||
created_at TIMESTAMP
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
# Insert sample data
|
||||
for i in range(row_count):
|
||||
db.execute(
|
||||
f"""
|
||||
INSERT INTO {table_name} (id, name, value, category, created_at)
|
||||
VALUES (
|
||||
$id,
|
||||
'Item ' || $item_num,
|
||||
ROUND(RANDOM() * 1000, 2),
|
||||
CASE
|
||||
WHEN RANDOM() < 0.33 THEN 'A'
|
||||
WHEN RANDOM() < 0.66 THEN 'B'
|
||||
ELSE 'C'
|
||||
END,
|
||||
CURRENT_TIMESTAMP - INTERVAL ($days || ' days')
|
||||
)
|
||||
""",
|
||||
{"id": i + 1, "item_num": i + 1, "days": i % 30},
|
||||
)
|
||||
|
||||
return {"status": "success", "table": table_name, "rows_created": row_count}
|
||||
except Exception as e:
|
||||
return {"status": "error", "error": str(e)}
|
||||
|
||||
|
||||
def aggregate_by_category(table_name: str) -> list:
|
||||
"""
|
||||
Aggregate data by category from a table.
|
||||
"""
|
||||
try:
|
||||
results = db.execute(
|
||||
f"""
|
||||
SELECT
|
||||
category,
|
||||
COUNT(*) as count,
|
||||
ROUND(AVG(value), 2) as avg_value,
|
||||
ROUND(SUM(value), 2) as total_value,
|
||||
MIN(value) as min_value,
|
||||
MAX(value) as max_value
|
||||
FROM {table_name}
|
||||
GROUP BY category
|
||||
ORDER BY category
|
||||
"""
|
||||
)
|
||||
|
||||
return results
|
||||
except Exception as e:
|
||||
return [{"error": str(e)}]
|
||||
|
||||
|
||||
async def process_time_series(table_name: str, window_days: int = 7) -> list:
|
||||
"""
|
||||
Async function to process time series data with rolling windows.
|
||||
"""
|
||||
import asyncio
|
||||
|
||||
# Simulate some async processing
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
results = db.execute(
|
||||
f"""
|
||||
WITH daily_data AS (
|
||||
SELECT
|
||||
DATE_TRUNC('day', created_at) as date,
|
||||
category,
|
||||
COUNT(*) as daily_count,
|
||||
ROUND(AVG(value), 2) as daily_avg
|
||||
FROM {table_name}
|
||||
GROUP BY DATE_TRUNC('day', created_at), category
|
||||
)
|
||||
SELECT
|
||||
date,
|
||||
category,
|
||||
daily_count,
|
||||
daily_avg,
|
||||
ROUND(AVG(daily_avg) OVER (
|
||||
PARTITION BY category
|
||||
ORDER BY date
|
||||
ROWS BETWEEN {window_days - 1} PRECEDING AND CURRENT ROW
|
||||
), 2) as rolling_avg
|
||||
FROM daily_data
|
||||
ORDER BY date DESC, category
|
||||
LIMIT 50
|
||||
"""
|
||||
)
|
||||
|
||||
return results
|
||||
@@ -0,0 +1,16 @@
|
||||
"""Example showing Python endpoints can return primitive arrays."""
|
||||
|
||||
|
||||
def show_primitive_arrays() -> list:
|
||||
"""Return Fibonacci sequence as array of numbers."""
|
||||
return [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
|
||||
|
||||
|
||||
def get_languages() -> list:
|
||||
"""Return list of programming languages."""
|
||||
return ["Python", "JavaScript", "Go", "Rust", "TypeScript"]
|
||||
|
||||
|
||||
def get_pi_digits() -> list:
|
||||
"""Return digits of pi."""
|
||||
return [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]
|
||||
@@ -0,0 +1,28 @@
|
||||
mxcp: 1
|
||||
tool:
|
||||
name: aggregate_by_category
|
||||
description: Aggregate data by category with statistics
|
||||
language: python
|
||||
source:
|
||||
file: ../python/data_analysis.py
|
||||
parameters:
|
||||
- name: table_name
|
||||
type: string
|
||||
description: Name of the table to aggregate
|
||||
return:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
category:
|
||||
type: string
|
||||
count:
|
||||
type: integer
|
||||
avg_value:
|
||||
type: number
|
||||
total_value:
|
||||
type: number
|
||||
min_value:
|
||||
type: number
|
||||
max_value:
|
||||
type: number
|
||||
@@ -0,0 +1,27 @@
|
||||
mxcp: 1
|
||||
tool:
|
||||
name: analyze_numbers
|
||||
description: Analyze a list of numbers with statistical operations
|
||||
language: python
|
||||
source:
|
||||
file: ../python/data_analysis.py
|
||||
parameters:
|
||||
- name: numbers
|
||||
type: array
|
||||
items:
|
||||
type: number
|
||||
description: List of numbers to analyze
|
||||
- name: operation
|
||||
type: string
|
||||
enum: ["mean", "median", "mode", "stdev", "sum", "min", "max"]
|
||||
default: "mean"
|
||||
description: Statistical operation to perform
|
||||
return:
|
||||
type: object
|
||||
properties:
|
||||
operation:
|
||||
type: string
|
||||
result:
|
||||
type: number
|
||||
count:
|
||||
type: integer
|
||||
@@ -0,0 +1,25 @@
|
||||
mxcp: 1
|
||||
tool:
|
||||
name: create_sample_data
|
||||
description: Create a sample table with test data
|
||||
language: python
|
||||
source:
|
||||
file: ../python/data_analysis.py
|
||||
parameters:
|
||||
- name: table_name
|
||||
type: string
|
||||
description: Name of the table to create
|
||||
- name: row_count
|
||||
type: integer
|
||||
description: Number of rows to generate
|
||||
minimum: 1
|
||||
maximum: 10000
|
||||
return:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
table:
|
||||
type: string
|
||||
rows_created:
|
||||
type: integer
|
||||
@@ -0,0 +1,21 @@
|
||||
mxcp: 1
|
||||
tool:
|
||||
name: process_time_series
|
||||
description: Process time series data with rolling window calculations (async)
|
||||
language: python
|
||||
source:
|
||||
file: ../python/data_analysis.py
|
||||
parameters:
|
||||
- name: table_name
|
||||
type: string
|
||||
description: Name of the table containing time series data
|
||||
- name: window_days
|
||||
type: integer
|
||||
default: 7
|
||||
description: Size of the rolling window in days
|
||||
minimum: 1
|
||||
maximum: 365
|
||||
return:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
Reference in New Issue
Block a user