added new gemini creations
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
GOOGLE_API_KEY=
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# Gemini MCP Integration
|
||||||
|
|
||||||
|
This directory contains a boilerplate setup for integrating Google Gemini with the Model Context Protocol (MCP).
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
- **`simple_server.py`**: A simple MCP server using `FastMCP` that exposes two tools (`add_numbers`, `get_current_time`).
|
||||||
|
- **`gemini_client.py`**: A Python script that acts as an MCP client. It connects to `simple_server.py`, lists the available tools, and provides guidance on integrating them with the `google-genai` SDK.
|
||||||
|
- **`setup.sh`**: A provision script that sets up the Python virtual environment and installs dependencies.
|
||||||
|
- **`requirements.txt`**: List of Python dependencies.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Python 3
|
||||||
|
- Node.js & npm (for MCP Inspector)
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. Run the setup script:
|
||||||
|
```bash
|
||||||
|
./setup.sh
|
||||||
|
```
|
||||||
|
2. Activate the virtual environment:
|
||||||
|
```bash
|
||||||
|
source venv/bin/activate
|
||||||
|
```
|
||||||
|
3. Set your Google API Key in `.env`:
|
||||||
|
```bash
|
||||||
|
# Edit .env file
|
||||||
|
GOOGLE_API_KEY=your_api_key_here
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the Example
|
||||||
|
|
||||||
|
Run the client script to see the connection to the server and the list of tools:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python gemini_client.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using MCP Inspector
|
||||||
|
|
||||||
|
You can use the official MCP Inspector to interact with the `simple_server.py` visually.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx @modelcontextprotocol/inspector python simple_server.py
|
||||||
|
```
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import asyncio
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from mcp import ClientSession, StdioServerParameters
|
||||||
|
from mcp.client.stdio import stdio_client
|
||||||
|
from google import genai
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
# Load environment variables
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
print("Starting MCP Client...")
|
||||||
|
|
||||||
|
# Check for API Key
|
||||||
|
api_key = os.getenv("GOOGLE_API_KEY")
|
||||||
|
if not api_key:
|
||||||
|
print("Warning: GOOGLE_API_KEY is not set in .env. Gemini calls will fail.")
|
||||||
|
else:
|
||||||
|
print("GOOGLE_API_KEY found.")
|
||||||
|
|
||||||
|
# Define the server parameters
|
||||||
|
# We run the simple_server.py we created earlier
|
||||||
|
server_script = os.path.join(os.path.dirname(__file__), "simple_server.py")
|
||||||
|
|
||||||
|
server_params = StdioServerParameters(
|
||||||
|
command=sys.executable, # Use the current python interpreter
|
||||||
|
args=[server_script],
|
||||||
|
env=None
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"Connecting to MCP server at: {server_script}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with stdio_client(server_params) as (read, write):
|
||||||
|
async with ClientSession(read, write) as session:
|
||||||
|
# Initialize the session
|
||||||
|
await session.initialize()
|
||||||
|
|
||||||
|
# List available tools
|
||||||
|
result = await session.list_tools()
|
||||||
|
tools = result.tools
|
||||||
|
|
||||||
|
print(f"\nSuccessfully connected! Found {len(tools)} tools:")
|
||||||
|
for tool in tools:
|
||||||
|
print(f" - {tool.name}: {tool.description}")
|
||||||
|
print(f" Schema: {tool.inputSchema}")
|
||||||
|
|
||||||
|
print("\n--- Integration with Gemini (google-genai) ---")
|
||||||
|
print("To integrate these tools with Gemini using the new SDK, you would:")
|
||||||
|
print("1. Convert the MCP tool schemas to Gemini tool definitions.")
|
||||||
|
print("2. Initialize the Gemini client:")
|
||||||
|
print(" from google import genai")
|
||||||
|
print(" client = genai.Client(api_key=api_key)")
|
||||||
|
print("3. Pass the tools to the generate_content call.")
|
||||||
|
print("4. Handle the tool calls returned by the model.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error connecting to MCP server: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
mcp
|
||||||
|
google-genai
|
||||||
|
python-dotenv
|
||||||
Executable
+48
@@ -0,0 +1,48 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Exit on error
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Provisioning MCP environment..."
|
||||||
|
|
||||||
|
# Check for Python 3
|
||||||
|
if ! command -v python3 &> /dev/null; then
|
||||||
|
echo "Python 3 could not be found. Please install Python 3."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create Virtual Environment
|
||||||
|
if [ ! -d "venv" ]; then
|
||||||
|
echo "Creating virtual environment..."
|
||||||
|
python3 -m venv venv
|
||||||
|
else
|
||||||
|
echo "Virtual environment already exists."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Activate venv
|
||||||
|
source venv/bin/activate
|
||||||
|
|
||||||
|
# Upgrade pip
|
||||||
|
echo "Upgrading pip..."
|
||||||
|
pip install --upgrade pip
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
if [ -f "requirements.txt" ]; then
|
||||||
|
echo "Installing dependencies from requirements.txt..."
|
||||||
|
pip install -r requirements.txt
|
||||||
|
else
|
||||||
|
echo "requirements.txt not found!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create .env from template if it doesn't exist
|
||||||
|
if [ ! -f ".env" ]; then
|
||||||
|
echo "Creating .env file..."
|
||||||
|
touch .env
|
||||||
|
echo "GOOGLE_API_KEY=" >> .env
|
||||||
|
echo "Please add your GOOGLE_API_KEY to mcp/.env"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Setup complete! To start working:"
|
||||||
|
echo "cd mcp"
|
||||||
|
echo "source venv/bin/activate"
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
# Initialize FastMCP server
|
||||||
|
mcp = FastMCP("Simple Demo Server")
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def add_numbers(a: int, b: int) -> int:
|
||||||
|
"""Add two numbers together."""
|
||||||
|
return a + b
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def get_current_time() -> str:
|
||||||
|
"""Get the current local time."""
|
||||||
|
from datetime import datetime
|
||||||
|
return datetime.now().isoformat()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Run the server over stdio
|
||||||
|
mcp.run()
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
TRUENAS_API_URL=http://your-truenas-ip/api/v2.0
|
||||||
|
TRUENAS_API_KEY=your-api-key-here
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# TrueNAS Scale Storage Tracker
|
||||||
|
|
||||||
|
A CLI tool to list storage usage for each data pool in TrueNAS SCALE using the TrueNAS API.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Lists all data pools with total size, used size, free size, and percentage used.
|
||||||
|
- Supports output in Text (default), JSON, and CSV formats.
|
||||||
|
- Configurable via `.env` file.
|
||||||
|
- Robust error handling.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Python 3.x
|
||||||
|
- `requests`
|
||||||
|
- `python-dotenv`
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Clone the repository or navigate to the project directory.
|
||||||
|
2. Install dependencies:
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
1. Copy the `.env.template` to `.env`:
|
||||||
|
```bash
|
||||||
|
cp .env.template .env
|
||||||
|
```
|
||||||
|
2. Edit `.env` and provide your TrueNAS Scale credentials:
|
||||||
|
```ini
|
||||||
|
TRUENAS_API_URL=http://your-truenas-ip/api/v2.0
|
||||||
|
TRUENAS_API_KEY=your-api-key-here
|
||||||
|
```
|
||||||
|
|
||||||
|
* **TRUENAS_API_URL**: The URL to your TrueNAS API (e.g., `http://192.168.1.100/api/v2.0`).
|
||||||
|
* **TRUENAS_API_KEY**: An API Key generated from the TrueNAS web interface (Settings -> API Keys).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Run the tool from the command line:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 src/tracker.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
* `--format`: Specify the output format (`text`, `json`, `csv`). Default is `text`.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
**Default Text Output:**
|
||||||
|
```bash
|
||||||
|
python3 src/tracker.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**JSON Output:**
|
||||||
|
```bash
|
||||||
|
python3 src/tracker.py --format json
|
||||||
|
```
|
||||||
|
|
||||||
|
**CSV Output:**
|
||||||
|
```bash
|
||||||
|
python3 src/tracker.py --format csv
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Run the unit tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m unittest discover tests
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
* `src/tracker.py`: Main application logic.
|
||||||
|
* `tests/test_tracker.py`: Unit tests.
|
||||||
|
* `.env.template`: Template for environment variables.
|
||||||
|
* `requirements.txt`: Python dependencies.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
requests
|
||||||
|
python-dotenv
|
||||||
|
argparse
|
||||||
Binary file not shown.
@@ -0,0 +1,109 @@
|
|||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import csv
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
try:
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
# Load environment variables
|
||||||
|
load_dotenv()
|
||||||
|
except ImportError:
|
||||||
|
print("Warning: python-dotenv not installed. Environment variables from .env file will not be loaded.", file=sys.stderr)
|
||||||
|
|
||||||
|
def get_headers():
|
||||||
|
TRUENAS_API_KEY = os.getenv("TRUENAS_API_KEY")
|
||||||
|
if not TRUENAS_API_KEY:
|
||||||
|
raise ValueError("TRUENAS_API_KEY not found in environment variables.")
|
||||||
|
return {
|
||||||
|
"Authorization": f"Bearer {TRUENAS_API_KEY}",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
def fetch_pool_data():
|
||||||
|
TRUENAS_API_URL = os.getenv("TRUENAS_API_URL")
|
||||||
|
if not TRUENAS_API_URL:
|
||||||
|
raise ValueError("TRUENAS_API_URL not found in environment variables.")
|
||||||
|
|
||||||
|
url = f"{TRUENAS_API_URL}/pool"
|
||||||
|
try:
|
||||||
|
response = requests.get(url, headers=get_headers(), timeout=10)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"Error connecting to TrueNAS API: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def process_pool_data(raw_data):
|
||||||
|
processed_data = []
|
||||||
|
for pool in raw_data:
|
||||||
|
# Extract relevant metrics.
|
||||||
|
# Note: TrueNAS API structure varies slightly, but usually:
|
||||||
|
# 'name' is the pool name.
|
||||||
|
# 'size' is total size in bytes.
|
||||||
|
# 'allocated' is used size in bytes.
|
||||||
|
# 'free' is free size in bytes.
|
||||||
|
# Values might be nested differently depending on version,
|
||||||
|
# but typically top-level or under 'scan' or similar.
|
||||||
|
# We will assume standard keys based on typical v2.0 response.
|
||||||
|
|
||||||
|
# Adjusting based on common TrueNAS Scale API response for /pool
|
||||||
|
# It often returns a list where each item has 'name' and 'scan' or standard ZFS properties.
|
||||||
|
# Let's try to find the size properties. often found in 'used', 'available', 'size' (top level for ZFS properties usually don't exist directly, usually inside 'properties' or computed).
|
||||||
|
# Actually, /pool response usually has 'name' and 'size' (total), 'allocated' (used), 'free' (available).
|
||||||
|
|
||||||
|
# Fallback/Safe extraction
|
||||||
|
name = pool.get('name', 'Unknown')
|
||||||
|
|
||||||
|
# Some versions use 'size', 'allocated', 'free' directly
|
||||||
|
total_size = pool.get('size', 0)
|
||||||
|
used_size = pool.get('allocated', 0)
|
||||||
|
free_size = pool.get('free', 0)
|
||||||
|
|
||||||
|
# Calculate percentage if not present (sometimes it is not)
|
||||||
|
if total_size > 0:
|
||||||
|
percent_used = (used_size / total_size) * 100
|
||||||
|
else:
|
||||||
|
percent_used = 0.0
|
||||||
|
|
||||||
|
processed_data.append({
|
||||||
|
"pool_name": name,
|
||||||
|
"total_size_bytes": total_size,
|
||||||
|
"used_size_bytes": used_size,
|
||||||
|
"free_size_bytes": free_size,
|
||||||
|
"percent_used": round(percent_used, 2)
|
||||||
|
})
|
||||||
|
return processed_data
|
||||||
|
|
||||||
|
def format_output(data, output_format):
|
||||||
|
if output_format == 'json':
|
||||||
|
print(json.dumps(data, indent=4))
|
||||||
|
elif output_format == 'csv':
|
||||||
|
writer = csv.DictWriter(sys.stdout, fieldnames=["pool_name", "total_size_bytes", "used_size_bytes", "free_size_bytes", "percent_used"])
|
||||||
|
writer.writeheader()
|
||||||
|
writer.writerows(data)
|
||||||
|
else:
|
||||||
|
# Default text/human-readable
|
||||||
|
print(f"{'Pool Name':<20} {'Total Size':<15} {'Used Size':<15} {'Free Size':<15} {'% Used':<10}")
|
||||||
|
print("-" * 75)
|
||||||
|
for item in data:
|
||||||
|
print(f"{item['pool_name']:<20} {item['total_size_bytes']:<15} {item['used_size_bytes']:<15} {item['free_size_bytes']:<15} {item['percent_used']:<10}%")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="TrueNAS Scale Storage Tracker")
|
||||||
|
parser.add_argument("--format", choices=['json', 'csv', 'text'], default='text', help="Output format (json, csv, text)")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
try:
|
||||||
|
raw_data = fetch_pool_data()
|
||||||
|
processed_data = process_pool_data(raw_data)
|
||||||
|
format_output(processed_data, args.format)
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"Configuration Error: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An unexpected error occurred: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Binary file not shown.
@@ -0,0 +1,71 @@
|
|||||||
|
import unittest
|
||||||
|
from unittest.mock import patch, MagicMock
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Add src to python path to import tracker
|
||||||
|
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
||||||
|
|
||||||
|
import tracker
|
||||||
|
|
||||||
|
class TestTrueNASStorageTracker(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.mock_pool_data = [
|
||||||
|
{
|
||||||
|
"name": "tank",
|
||||||
|
"size": 10995116277760, # 10 TB
|
||||||
|
"allocated": 5497558138880, # 5 TB
|
||||||
|
"free": 5497558138880 # 5 TB
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "boot-pool",
|
||||||
|
"size": 256000000000, # 256 GB
|
||||||
|
"allocated": 64000000000, # 64 GB
|
||||||
|
"free": 192000000000 # 192 GB
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
@patch('tracker.requests.get')
|
||||||
|
@patch('tracker.os.getenv')
|
||||||
|
def test_fetch_pool_data_success(self, mock_getenv, mock_get):
|
||||||
|
# Configure mocks
|
||||||
|
mock_getenv.side_effect = lambda key: {
|
||||||
|
'TRUENAS_API_URL': 'http://mock-url',
|
||||||
|
'TRUENAS_API_KEY': 'mock-key'
|
||||||
|
}.get(key)
|
||||||
|
|
||||||
|
mock_response = MagicMock()
|
||||||
|
mock_response.json.return_value = self.mock_pool_data
|
||||||
|
mock_response.raise_for_status.return_value = None
|
||||||
|
mock_get.return_value = mock_response
|
||||||
|
|
||||||
|
# Call function
|
||||||
|
data = tracker.fetch_pool_data()
|
||||||
|
|
||||||
|
# Assertions
|
||||||
|
self.assertEqual(data, self.mock_pool_data)
|
||||||
|
mock_get.assert_called_once()
|
||||||
|
|
||||||
|
def test_process_pool_data(self):
|
||||||
|
processed = tracker.process_pool_data(self.mock_pool_data)
|
||||||
|
|
||||||
|
self.assertEqual(len(processed), 2)
|
||||||
|
|
||||||
|
# Check first pool
|
||||||
|
self.assertEqual(processed[0]['pool_name'], "tank")
|
||||||
|
self.assertEqual(processed[0]['total_size_bytes'], 10995116277760)
|
||||||
|
self.assertEqual(processed[0]['percent_used'], 50.0)
|
||||||
|
|
||||||
|
# Check second pool
|
||||||
|
self.assertEqual(processed[1]['pool_name'], "boot-pool")
|
||||||
|
self.assertEqual(processed[1]['percent_used'], 25.0)
|
||||||
|
|
||||||
|
@patch('tracker.os.getenv')
|
||||||
|
def test_missing_env_vars(self, mock_getenv):
|
||||||
|
mock_getenv.return_value = None
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
tracker.fetch_pool_data()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user