Claude-skill-registry create-extension
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.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/create-extension" ~/.claude/skills/majiayu000-claude-skill-registry-create-extension && rm -rf "$T"
manifest:
skills/data/create-extension/SKILL.mdsource content
Create Extension
Creates a new Starlark-based MCP extension for the starlark-mcp server.
Instructions
When the user requests a new extension:
-
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()
-
Choose Implementation Approach
- IMPORTANT: Default to using existing Starlark modules first
- Use
for CLI tools (docker, git, kubectl, etc.) - this is almost always the right choiceexec.run() - Use
module for REST APIshttp - Use existing
orsqlite
modules for databasespostgres - 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:
is simple, fast to implement, and what users already have installedexec.run()
-
Create Extension File
- Create
fileextensions/{name}.star - Follow the standard Starlark MCP extension structure
- Create
-
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, ), ], ) -
Available Starlark Modules The following modules are available in extension code:
- Start here for most CLI tools! Run system commands (exec
)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
or similar for easy parsing--format json
- Returns:
- Make HTTP requests (http
,http.get()
, etc.)http.post()
- SQLite database operations (sqlite
,sqlite.query()
,sqlite.list_tables()
)sqlite.describe_table()
- PostgreSQL operations (postgres
,postgres.query()
,postgres.execute()
,postgres.list_tables()
)postgres.describe_table()
- Access environment variables (env
)env.get("VAR_NAME", "default")
- Time utilities (time
for Unix timestamp)time.now()
- JSON encoding/decoding (json
,json.encode()
)json.decode()
-
Parameter Types Supported
values:param_type
- Text values"string"
- Whole numbers"integer"
- Decimal numbers"number"
- true/false"boolean"
- Lists"array"
- Dictionaries"object"
-
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
in Extension() for system commandsallowed_exec
-
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
-
-
Create Test Script (optional)
- Create
for testing the extensionscripts/test_{extension_name}.sh - Make it executable:
chmod +x scripts/test_{extension_name}.sh
- Create
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:
- Simple extension with no dependenciesextensions/cat_facts.star
- Database access with multiple toolsextensions/sqlite.star
- PostgreSQL with environment configurationextensions/postgres.star
- API integration with authenticationextensions/plane.star
- GitHub API integrationextensions/github.star
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
arraycontent - Use
helper for consistent error handlingerror_response() - Test thoroughly before committing