Claude Code Plugins

Community-maintained marketplace

Feedback

create-extension

@connyay/mcp-star
0
0

Creates a new Starlark MCP extension with tools, handlers, and proper structure. Use when the user wants to add a new extension to the mcp-star server.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name create-extension
description Creates a new Starlark MCP extension with tools, handlers, and proper structure. Use when the user wants to add a new extension to the starlark-mcp server.

Create Extension

Creates a new Starlark-based MCP extension for the starlark-mcp server.

Instructions

When the user requests a new extension:

  1. Gather Requirements

    • Ask the user for the extension name (lowercase with underscores)
    • Ask what tools/capabilities the extension should provide
    • Determine if the extension needs:
      • External API access (HTTP calls)
      • Database access (SQLite, PostgreSQL)
      • File system operations
      • System commands (via exec.run())
  2. Choose Implementation Approach

    • IMPORTANT: Default to using existing Starlark modules first
    • Use exec.run() for CLI tools (docker, git, kubectl, etc.) - this is almost always the right choice
    • Use http module for REST APIs
    • Use existing sqlite or postgres modules for databases
    • Only create new Rust modules if:
      • The CLI tool doesn't exist or lacks JSON output
      • You need direct library access for performance
      • The existing modules don't provide required functionality
    • Remember: exec.run() is simple, fast to implement, and what users already have installed
  3. Create Extension File

    • Create extensions/{name}.star file
    • Follow the standard Starlark MCP extension structure
  4. Extension Structure

    # Tool handler functions
    def tool_name(params):
        """Tool description"""
        # Extract parameters
        param = params.get("param_name", "default")
    
        # Validate inputs
        if not param:
            return error_response("param_name is required")
    
        # Implement logic
        result = do_something(param)
    
        # Return MCP response
        return {
            "content": [{"type": "text", "text": result}],
        }
    
    # Helper functions
    def error_response(message):
        """Create an error response"""
        return {
            "content": [{"type": "text", "text": "Error: " + message}],
            "isError": True,
        }
    
    # Extension definition
    def describe_extension():
        """Define the extension"""
        return Extension(
            name = "extension_name",
            version = "1.0.0",
            description = "Extension description",
            tools = [
                Tool(
                    name = "tool_name",
                    description = "Tool description",
                    parameters = [
                        ToolParameter(
                            name = "param_name",
                            param_type = "string",
                            required = True,
                            description = "Parameter description",
                        ),
                    ],
                    handler = tool_name,
                ),
            ],
        )
    
  5. Available Starlark Modules The following modules are available in extension code:

    • exec - Start here for most CLI tools! Run system commands (exec.run("command", ["arg1", "arg2"]))
      • Returns: {"success": bool, "stdout": string, "stderr": string, "exit_code": int}
      • Perfect for: docker, git, kubectl, aws cli, etc.
      • Most CLIs support --format json or similar for easy parsing
    • http - Make HTTP requests (http.get(), http.post(), etc.)
    • sqlite - SQLite database operations (sqlite.query(), sqlite.list_tables(), sqlite.describe_table())
    • postgres - PostgreSQL operations (postgres.query(), postgres.execute(), postgres.list_tables(), postgres.describe_table())
    • env - Access environment variables (env.get("VAR_NAME", "default"))
    • time - Time utilities (time.now() for Unix timestamp)
    • json - JSON encoding/decoding (json.encode(), json.decode())
  6. Parameter Types Supported param_type values:

    • "string" - Text values
    • "integer" - Whole numbers
    • "number" - Decimal numbers
    • "boolean" - true/false
    • "array" - Lists
    • "object" - Dictionaries
  7. Security Considerations

    • Validate all user inputs
    • Sanitize parameters used in SQL queries or system commands
    • Use parameterized queries for database operations
    • Restrict file system access when possible
    • Declare allowed_exec in Extension() for system commands
  8. Test the Extension

    • Build the project: cargo build --release

    • Test with a simple MCP call:

      echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"tool_name","arguments":{}}}' | ./target/release/starlark-mcp
      
  9. Create Test Script (optional)

    • Create scripts/test_{extension_name}.sh for testing the extension
    • Make it executable: chmod +x scripts/test_{extension_name}.sh

Examples

Extension Using CLI Tool (Docker Example)

def list_containers(params):
    """List Docker containers"""
    all_containers = params.get("all", False)

    # Build docker command
    args = ["ps", "--format", "json", "--no-trunc"]
    if all_containers:
        args.append("--all")

    # Run docker CLI
    result = exec.run("docker", args)

    if not result["success"]:
        return error_response("Docker command failed: {}".format(result["stderr"]))

    # Parse JSON output (docker outputs one JSON object per line)
    containers = []
    for line in result["stdout"].strip().split("\n"):
        if line:
            container = json.decode(line)
            containers.append(container)

    output = "Found {} container(s):\n\n".format(len(containers))
    for c in containers:
        output += "- {} ({}): {}\n".format(
            c.get("Names", ""),
            c.get("ID", "")[:12],
            c.get("State", ""),
        )

    return {"content": [{"type": "text", "text": output}]}

def error_response(message):
    return {
        "content": [{"type": "text", "text": "Error: " + message}],
        "isError": True,
    }

def describe_extension():
    return Extension(
        name = "docker",
        version = "1.0.0",
        description = "Docker container management",
        allowed_exec = ["docker"],  # Must declare allowed commands
        tools = [
            Tool(
                name = "docker_list_containers",
                description = "List Docker containers",
                parameters = [
                    ToolParameter(
                        name = "all",
                        param_type = "boolean",
                        required = False,
                        default = "false",
                        description = "Include stopped containers",
                    ),
                ],
                handler = list_containers,
            ),
        ],
    )

Simple Extension (No External Dependencies)

def greet(params):
    """Greet a user"""
    name = params.get("name", "World")
    return {
        "content": [{"type": "text", "text": "Hello, {}!".format(name)}],
    }

def describe_extension():
    return Extension(
        name = "greeter",
        version = "1.0.0",
        description = "Simple greeting extension",
        tools = [
            Tool(
                name = "greet",
                description = "Greet a user by name",
                parameters = [
                    ToolParameter(
                        name = "name",
                        param_type = "string",
                        required = False,
                        default = "World",
                        description = "Name to greet",
                    ),
                ],
                handler = greet,
            ),
        ],
    )

Extension with HTTP Access

def fetch_data(params):
    """Fetch data from an API"""
    url = params.get("url", "")

    if not url:
        return error_response("url parameter is required")

    response = http.get(url, {})

    if response["status_code"] != 200:
        return error_response("HTTP request failed: {}".format(response["status_code"]))

    return {
        "content": [{"type": "text", "text": response["body"]}],
    }

def error_response(message):
    return {
        "content": [{"type": "text", "text": "Error: " + message}],
        "isError": True,
    }

def describe_extension():
    return Extension(
        name = "http_fetcher",
        version = "1.0.0",
        description = "Fetch data from HTTP endpoints",
        tools = [
            Tool(
                name = "fetch_data",
                description = "Fetch data from a URL",
                parameters = [
                    ToolParameter(
                        name = "url",
                        param_type = "string",
                        required = True,
                        description = "URL to fetch",
                    ),
                ],
                handler = fetch_data,
            ),
        ],
    )

Extension with Database Access

def query_db(params):
    """Query a SQLite database"""
    db_path = params.get("db_path", "")
    query = params.get("query", "")

    if not db_path or not query:
        return error_response("db_path and query are required")

    # Security: Only allow SELECT
    if not query.strip().upper().startswith("SELECT"):
        return error_response("Only SELECT queries allowed")

    rows = sqlite.query(db_path, query, [])

    output = "Found {} row(s):\n\n{}".format(len(rows), json.encode(rows))

    return {
        "content": [{"type": "text", "text": output}],
    }

def error_response(message):
    return {
        "content": [{"type": "text", "text": "Error: " + message}],
        "isError": True,
    }

def describe_extension():
    return Extension(
        name = "db_query",
        version = "1.0.0",
        description = "Query SQLite databases",
        tools = [
            Tool(
                name = "query_db",
                description = "Execute a SELECT query on a SQLite database",
                parameters = [
                    ToolParameter(
                        name = "db_path",
                        param_type = "string",
                        required = True,
                        description = "Path to SQLite database",
                    ),
                    ToolParameter(
                        name = "query",
                        param_type = "string",
                        required = True,
                        description = "SELECT query to execute",
                    ),
                ],
                handler = query_db,
            ),
        ],
    )

Reference Files

See existing extensions for more examples:

  • extensions/cat_facts.star - Simple extension with no dependencies
  • extensions/sqlite.star - Database access with multiple tools
  • extensions/postgres.star - PostgreSQL with environment configuration
  • extensions/plane.star - API integration with authentication
  • extensions/github.star - GitHub API integration

Notes

  • Extension names should be lowercase with underscores
  • Tool names are prefixed with extension name (e.g., sqlite_query)
  • All handlers must return a dict with content array
  • Use error_response() helper for consistent error handling
  • Test thoroughly before committing