Skill: wondershare-boop/mediaio-nano-banana-2-image-generator (version 1.0.0)

--- name: mediaio-nano-banana-pro-image-generator description: "Generate AI images using Nano Banana Pro via Media.io OpenAPI. State-of-the-art image quality with advanced reasoning, multi-image fusion, character consistency. Supports up to 4K resolution." metadata: {"mediaio":{"emoji":"🎨","requires":{"env":["API_KEY"]}},"publisher":"Community Maintainer","source":"https://platform.media.io/docs/"} --- # MediaIO Nano Banana Pro Image Generator Skill ## Overview This skill provides access to **Nano Banana Pro** through the Media.io OpenAPI. Nano Banana Pro utilizes next-gen multimodal reasoning to generate images that perfectly align with nuanced conceptual descriptions, featuring state-of-the-art image quality with advanced reasoning, multi-image fusion, and character consistency. ## Trigger Keywords Use this skill when you hear: - "Nano Banana Pro", "Nano Banana Pro image generator" - "Generate image with Nano Banana Pro" - "Banana Pro AI image generation" ## Requirements ### Environment Variables | Variable | Required | Description | |----------|----------|-------------| | `API_KEY` | **Yes** | Media.io OpenAPI key, sent as `X-API-KEY` header. Apply at <https://developer.media.io/>. | ## API Details ### Nano Banana Pro Image Generation - **API Name**: `Nano Banana Pro` - **Model Code**: `i2i-banana-2` - **Endpoint**: `POST https://openapi.media.io/generation/banana/i2i-banana-2` - **Description**: Utilizes next-gen multimodal reasoning to generate images that perfectly align with nuanced conceptual descriptions. #### Key Features - State-of-the-art image quality - Advanced multimodal reasoning - Multi-image fusion - Character consistency - Up to 4K resolution support - Perfect alignment with nuanced conceptual descriptions #### Request Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `prompt` | string | Yes | Text description for image generation | | `image` | string | No | Reference image URL for image-to-image | | `ratio` | string | No | Image aspect ratio | | `fusion_strength` | string | No | Multi-image fusion strength | #### Common Response Structure ```json { "code": 0, "msg": "", "data": { "task_id": "..." }, "trace_id": "..." } ``` ## Quick Start ### 1) Install Dependency ```bash pip install requests ``` ### 2) Initialize Skill ```python import os from scripts.skill_router import Skill skill = Skill('scripts/c_api_doc_detail.json') [REDACTED_SECRET]'API_KEY', '') if not api_key: raise RuntimeError('API_KEY is not set') ``` ### 3) Configure Environment Variable API_KEY **Windows PowerShell**: ```powershell $env:[REDACTED_SECRET]" ``` **macOS / Linux (bash/zsh)**: ```bash export [REDACTED_SECRET]" ``` ## Usage Examples (Python) ### High-Quality Image Generation ```python import os from scripts.skill_router import Skill skill = Skill('scripts/c_api_doc_detail.json') [REDACTED_SECRET]'API_KEY', '') if not api_key: raise RuntimeError('API_KEY is not set') result = skill.invoke( 'Nano Banana Pro', { 'prompt': 'a serene mountain landscape at sunset, photorealistic, 4K quality, dramatic lighting', 'ratio': '16:9' }, [REDACTED_SECRET] ) print(result) # Returns task_id when code=0 ``` ### Multi-Image Fusion ```python import os from scripts.skill_router import Skill skill = Skill('scripts/c_api_doc_detail.json') [REDACTED_SECRET]'API_KEY', '') if not api_key: raise RuntimeError('API_KEY is not set') result = skill.invoke( 'Nano Banana Pro', { 'prompt': 'a futuristic cityscape combining classical and modern architecture', 'image': 'https://example.com/reference-image.jpg', 'fusion_strength': '0.7' }, [REDACTED_SECRET] ) print(result) ``` ### Query Task Result ```python import os import time from scripts.skill_router import Skill skill = Skill('scripts/c_api_doc_detail.json') [REDACTED_SECRET]'API_KEY', '') if not api_key: raise RuntimeError('API_KEY is not set') task_id = 'your-task-id' for _ in range(24): r = skill.invoke('Task Result', {'task_id': task_id}, [REDACTED_SECRET]) print(r) status = (r.get('data') or {}).get('status') if status in ('completed', 'failed', 'succeeded'): break time.sleep(5) ``` #### Task Status Reference - `waiting`: queued - `processing`: running - `completed`: completed successfully - `failed`: failed - `timeout`: timed out ## Error Handling | Error Code | Description | |------------|-------------| | `374004` | Not authenticated. Apply for an APP KEY at https://developer.media.io/ | | `490505` | Insufficient credits. Recharge before invoking generation APIs | ## External Resources - API documentation: https://platform.media.io/docs/ - Product overview: https://developer.media.io/ - Credit purchase: https://developer.media.io/pricing.html ## Related Files - scripts/skill_router.py: core routing logic - scripts/c_api_doc_detail.json: API definitions

path: scripts/skill_router.py
content: #!/usr/bin/env python3
import json
import os
import requests
from typing import Any, Dict, Optional
from urllib.parse import urlparse

class Skill:
    """
    Standard AIGC skill implementation with automatic API routing and parameter mapping.
    """
    def __init__(self, api_doc_path: str):
        with open(api_doc_path, 'r', encoding='utf-8') as f:
            api_items = json.load(f)

        self.api_definitions = {}
        duplicate_names = []
        for item in api_items:
            name = item.get('name')
            if name in self.api_definitions:
                duplicate_names.append(name)
                continue
            self.api_definitions[name] = item

        if duplicate_names:
            deduped = sorted(set(duplicate_names))
            raise ValueError(
                f"Duplicate API names detected in {api_doc_path}: {', '.join(deduped)}. "
                "Please use unique `name` fields in c_api_doc_detail.json."
            )

    def invoke(self, api_name: str, params: Dict[str, Any], [REDACTED_SECRET] = None) -> Dict[str, Any]:
        """
        Invoke the specified API.
        :param api_name: API name.
        :param params: Business parameters.
        :param api_key: API key. If omitted, API_KEY from environment is used.
        :return: API response payload.
        """
        if api_name not in self.api_definitions:
            return {'error': f"API '{api_name}' not found."}

        resolved_api_key = (api_key or os.getenv('API_KEY', '')).strip()
        if not resolved_api_key:
            return {'error': 'Missing API key. Set API_KEY or pass api_key explicitly.'}

        api = self.api_definitions[api_name]
        url = api['endpoint']
        method = api['method']

        # Restrict outbound requests to the expected Media.io API host.
        parsed = urlparse(url)
        if parsed.scheme != 'https' or parsed.netloc.lower() != 'openapi.media.io':
            return {'error': f"Blocked endpoint host: {parsed.netloc}"}

        headers = {
            'X-API-KEY': resolved_api_key,
            'Content-Type': 'application/json'
        }
        # Replace path parameters in endpoint URLs.
        if '{' in url:
            for k, v in params.items():
                url = url.replace(f'{{{k}}}', str(v))
        # Keep non-path parameters in the JSON body.
        body = {k: v for k, v in params.items() if f'{{{k}}}' not in api['endpoint']}
        try:
            resp = requests.request(method, url, headers=headers, json={'data': body} if body else {}, timeout=30)
            return resp.json()
        except Exception as e:
            return {'error': str(e)}

# Standard usage example.
if __name__ == '__main__':
    script_dir = os.path.dirname(os.path.abspath(__file__))
    skill = Skill(os.path.join(script_dir, 'c_api_doc_detail.json'))
    [REDACTED_SECRET]'API_KEY', '')
    if not api_key:
        raise RuntimeError('API_KEY is not set')
    # Credits query.
    result = skill.invoke('Credits', {}, [REDACTED_SECRET])
    print(result)


path: scripts/c_api_doc_detail.json
content: [
  {
    "id": 196,
    "name": "Credits",
    "model_code": "user-credits",
    "method": "POST",
    "endpoint": "https://openapi.media.io/user/credits",
    "title": "User Credits API Documentation",
    "description": "API to query user credits balance.",
    "api_header": "{\"list\": [{\"name\": \"X-API-KEY\", \"value\": \"API key to authorize requests\"}, {\"name\": \"Content-Type\", \"value\": \"application/json\"}], \"title\": \"Authorizations\", \"describe\": \"Add the following authorization information in the request header\"}",
    "api_body": "{\"title\": \"Request Body\", \"category\": [{\"list\": [], \"title\": \"Query Credits\", \"describe\": \"Request body to query user credits balance\"}]}",
    "api_request_demo": "{\"title\": \"Example Request\", \"request\": [{\"title\": \"Query User Credits\", \"language\": \"cURL\", \"code_example\": \"curl --request POST \\\\n  --url https://openapi.media.io/user/credits \\\\n  --header 'Content-Type: application/json' \\\\n  --header 'X-[REDACTED_SECRET]' \\\\n  --data '{}'\"}]}",
    "api_response": "{\"list\": [{\"name\": \"code\", \"type\": \"integer\", \"describe\": \"Response status code, 0 indicates success\"}, {\"name\": \"msg\", \"type\": \"string\", \"describe\": \"Response message, empty string on success\"}, {\"name\": \"data\", \"type\": \"object\", \"describe\": \"Response data object\"}, {\"name\": \"credits\", \"type\": \"integer\", \"describe\": \"User credits balance, located within the data object\"}], \"title\": \"Response\", \"describe\": \"After the request is successfully processed, the server will return the following response\"}",
    "api_code_demo": "{\"list\": [{\"code\": \"0\", \"describe\": \"Success\"}, {\"code\": \"40001\", \"describe\": \"Invalid API key\"}, {\"code\": \"40002\", \"describe\": \"API key expired\"}], \"title\": \"Status Code\"}",
    "content": null,
    "status": 1,
    "created_at": "3/3/2026 18:33:49",
    "updated_at": "3/3/2026 18:33:49"
  },
  {
    "id": 197,
    "name": "Task Result",
    "model_code": "generation-result",
    "method": "POST",
    "endpoint": "https://openapi.media.io/generation/result/{task_id}",
    "title": "Task Result API Documentation",
    "description": "API to query generation task result by task ID.",
    "api_header": "{\"list\": [{\"name\": \"X-API-KEY\", \"value\": \"API key to authorize requests\"}, {\"name\": \"Content-Type\", \"value\": \"application/json\"}], \"title\": \"Authorizations\", \"describe\": \"Add the following authorization information in the request header\"}",
    "api_body": "{\"title\": \"Path Parameters\", \"category\": [{\"list\": [{\"name\": \"task_id\", \"type\": \"string\", \"describe\": \"The task ID to query, located in the URL path parameter\", \"required\": true}], \"title\": \"Query Task Result\", \"describe\": \"Request body to query task result\"}]}",
    "api_request_demo": "{\"title\": \"Example Request\", \"request\": [{\"title\": \"Query Task Result\", \"language\": \"cURL\", \"code_example\": \"curl --request POST \\\\n  --url https://openapi.media.io/generation/result/<task_id> \\\\n  --header 'Content-Type: application/json' \\\\n  --header 'X-[REDACTED_SECRET]' \\\\n  --data '{}'\"}]}",
    "api_response": "{\"list\": [{\"name\": \"code\", \"type\": \"integer\", \"describe\": \"Response status code, 0 indicates success\"}, {\"name\": \"msg\", \"type\": \"string\", \"describe\": \"Response message, empty string on success\"}, {\"name\": \"data\", \"type\": \"object\", \"describe\": \"Response data object\"}, {\"name\": \"task_id\", \"type\": \"string\", \"describe\": \"Task identifier\"}, {\"name\": \"status\", \"type\": \"string\", \"describe\": \"Task status: pending, processing, succeeded, failed\"}, {\"name\": \"result\", \"type\": \"object\", \"describe\": \"Generation result when task succeeded\"}], \"title\": \"Response\", \"describe\": \"After the request is successfully processed, the server will return the following response\"}",
    "api_code_demo": "{\"list\": [{\"code\": \"0\", \"describe\": \"Success\"}, {\"code\": \"40001\", \"describe\": \"Invalid API key\"}, {\"code\": \"40002\", \"describe\": \"API key expired\"}, {\"code\": \"40003\", \"describe\": \"Task not found\"}], \"title\": \"Status Code\"}",
    "content": null,
    "status": 1,
    "created_at": "3/3/2026 18:33:49",
    "updated_at": "4/3/2026 21:30:23"
  },
  {
    "id": 244,
    "name": "Nano Banana Pro",
    "model_code": "i2i-banana-2",
    "method": "POST",
    "endpoint": "https://openapi.media.io/generation/banana/i2i-banana-2",
    "title": "Nano Banana Pro API Documentation",
    "description": "Utilizes next-gen multimodal reasoning to generate images that perfectly align with nuanced conceptual descriptions.",
    "api_header": "{\"list\": [{\"name\": \"X-API-KEY\", \"value\": \"API key to authorize requests\"}, {\"name\": \"Content-Type\", \"value\": \"application/json\"}], \"title\": \"Authorizations\", \"describe\": \"Add the following authorization information in the request header\"}",
    "api_body": "{\"title\": \"Request Body\", \"category\": [{\"list\": [{\"name\": \"images\", \"type\": \"array<string>\", \"describe\": \"The URLs of the input images. Supported formats include PNG, JPEG, and JPG. File size must be between 0.0 MB and 20.0 MB. Image resolution must be between 300x300 and 1280x1280. Aspect ratio must be between 1:2 and 2:1. Maximum 9 file(s).\", \"required\": true}, {\"name\": \"prompt\", \"type\": \"string\", \"describe\": \"The text prompt describing the content to generate. Maximum string length: 1200.\", \"required\": true}, {\"name\": \"ratio\", \"type\": \"string\", \"describe\": \"Target aspect ratio. Options: 9:16, 16:9, 1:1, 4:3, 3:4, 3:2, 2:3, 21:9. Default is 16:9.\", \"keywords\": [\"9:16\", \"16:9\", \"1:1\", \"4:3\", \"3:4\", \"3:2\", \"2:3\", \"21:9\"], \"required\": false}, {\"name\": \"resolution\", \"type\": \"string\", \"describe\": \"Output resolution. Options: 1K, 2K, 4K. Default is 1K.\", \"keywords\": [\"1K\", \"2K\", \"4K\"], \"required\": false}], \"title\": \"Image To Image\"}], \"describe\": \"The request body must contain the following parameters\"}",
    "api_request_demo": "{\"title\": \"Example Request\", \"request\": [{\"title\": \"create image by text or image\", \"language\": \"cURL\", \"code_example\": \"curl --request POST \\\\\\n  --url https://openapi.media.io/generation/banana/i2i-banana-2 \\\\\\n  --header 'Content-Type: application/json' \\\\\\n  --header 'X-[REDACTED_SECRET]' \\\\\\n  --data '\\n{\\n  \\\"data\\\": {\\n    \\\"images\\\": [\\n      \\\"<string>\\\"\\n    ],\\n    \\\"prompt\\\": \\\"<string>\\\",\\n    \\\"ratio\\\": \\\"16:9\\\",\\n    \\\"resolution\\\": \\\"1K\\\"\\n  }\\n}'\\n\"}], \"describe\": \"\", \"response\": [{\"type\": \"200\", \"code_example\": \"{\\n  \\\"code\\\": 0,\\n  \\\"msg\\\": \\\"\\\",\\n  \\\"data\\\": {\\n    \\\"task_id\\\": <string>\\\"\\n  },\\n  \\\"trace_id\\\": <string>\\\"\\n}\"}, {\"type\": \"default\", \"code_example\": \"{\\n  \\\"code\\\": <integer>,\\n  \\\"msg\\\": <string>,\\n  \\\"data\\\": {},\\n  \\\"trace_id\\\": <string>\\\"\\n}\"}]}",
    "api_response": "{\"list\": [{\"name\": \"code\", \"type\": \"integer\", \"describe\": \"Response status code, 0 indicates success\"}, {\"name\": \"msg\", \"type\": \"string\", \"describe\": \"Response message, empty string on success\"}, {\"name\": \"data\", \"type\": \"object\", \"describe\": \"Response data object\"}, {\"name\": \"task_id\", \"type\": \"string\", \"describe\": \"Unique task identifier, located within the data object\"}, {\"name\": \"trace_id\", \"type\": \"string\", \"describe\": \"Request tracking ID\"}], \"title\": \"Response\", \"describe\": \"After the request is successfully processed, the server will return the following response\"}",
    "api_code_demo": "{\"title\": \"Example Request\", \"request\": [{\"title\": \"create image by text or image\", \"language\": \"cURL\", \"code_example\": \"curl --request POST \\\\\\n  --url https://openapi.media.io/generation/banana/i2i-banana-2 \\\\\\n  --header 'Content-Type: application/json' \\\\\\n  --header 'X-[REDACTED_SECRET]' \\\\\\n  --data '\\n{\\n  \\\"data\\\": {\\n    \\\"images\\\": [\\n      \\\"<string>\\\"\\n    ],\\n    \\\"prompt\\\": \\\"<string>\\\",\\n    \\\"ratio\\\": \\\"16:9\\\",\\n    \\\"resolution\\\": \\\"1K\\\"\\n  }\\n}'\\n\"}], \"describe\": \"\", \"response\": [{\"type\": \"200\", \"code_example\": \"{\\n  \\\"code\\\": 0,\\n  \\\"msg\\\": \\\"\\\",\\n  \\\"data\\\": {\\n    \\\"task_id\\\": <string>\\\"\\n  },\\n  \\\"trace_id\\\": <string>\\\"\\n}\"}, {\"type\": \"default\", \"code_example\": \"{\\n  \\\"code\\\": <integer>,\\n  \\\"msg\\\": <string>,\\n  \\\"data\\\": {},\\n  \\\"trace_id\\\": <string>\\\"\\n}\"}]}",
    "content": null,
    "status": 1,
    "created_at": "4/3/2026 18:16:03",
    "updated_at": "6/3/2026 15:06:21"
  }
]


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)