README
¶
Scriptling CLI
A command-line interface for the Scriptling programming language.
Installation
Download the appropriate binary for your platform from the releases or build from source.
Usage
Run a script file
scriptling script.py
Run from stdin
echo 'print("Hello World")' | scriptling
Interactive mode
scriptling --interactive
# or
scriptling -i
Lint mode
Lint scripts for syntax errors without executing them:
# Lint a file
scriptling --lint script.py
# Lint from stdin
echo 'x = 1 +' | scriptling --lint
# JSON output format
scriptling --lint --lint-format json script.py
Output format (text):
script.py:3: expected token COLON (error)
Output format (JSON):
{
"files_checked": 1,
"has_errors": true,
"errors": [
{
"file": "script.py",
"line": 3,
"message": "expected token COLON",
"severity": "error",
"code": "parse-error"
}
]
}
The linter exits with code 0 if no errors are found, and code 1 if any errors exist.
Command Line Options
| Flag | Environment Variable | Description | Default |
|---|---|---|---|
-i, --interactive |
- | Start interactive mode | false |
-l, --lint |
- | Lint script files without executing | false |
--lint-format |
SCRIPTLING_LINT_FORMAT |
Output format for lint (text/json) | text |
-L, --libpath |
SCRIPTLING_LIBPATH |
Additional library search directories (repeatable) | (none) |
--log-level |
SCRIPTLING_LOG_LEVEL |
Log level (trace/debug/info/warn/error) | info |
--log-format |
SCRIPTLING_LOG_FORMAT |
Log format (console/json) | console |
-S, --server |
SCRIPTLING_SERVER |
HTTP server address (host:port) | (disabled) |
--json-rpc |
SCRIPTLING_JSONRPC |
Enable JSON-RPC 2.0 server mode: stdio by default, HTTP /json-rpc with --server |
false |
--mcp-tools |
SCRIPTLING_MCP_TOOLS |
Directory containing MCP tools | (disabled) |
--bearer-token |
SCRIPTLING_BEARER_TOKEN |
Bearer token for authentication | none |
--allowed-paths |
SCRIPTLING_ALLOWED_PATHS |
Comma-separated allowed filesystem paths | (no restriction) |
--tls-cert |
SCRIPTLING_TLS_CERT |
TLS certificate file | none |
--tls-key |
SCRIPTLING_TLS_KEY |
TLS key file | none |
--tls-generate |
- | Generate self-signed certificate | false |
--secret-config |
SCRIPTLING_SECRET_CONFIG |
TOML file defining host-owned secret provider aliases for scriptling.secret |
none |
Environment Configuration
The CLI automatically loads environment variables from a .env file in the current directory (if it exists). This is useful for setting default values for flags without typing them on the command line.
Example .env file:
# Log configuration
SCRIPTLING_LOG_LEVEL=debug
SCRIPTLING_LOG_FORMAT=console
# Additional library search paths (colon-separated on Unix, semicolon on Windows)
SCRIPTLING_LIBPATH=/shared/libs
# Server configuration
SCRIPTLING_SERVER=:8000
SCRIPTLING_JSONRPC=true
SCRIPTLING_MCP_TOOLS=./tools
SCRIPTLING_BEARER_TOKEN=your-secret-token
# Filesystem restrictions
SCRIPTLING_ALLOWED_PATHS=/tmp/data,./uploads
# Secret provider aliases for scriptling.secret
SCRIPTLING_SECRET_CONFIG=./secrets.toml
Script Execution Modes
Scriptling supports three levels of filesystem access control:
| Mode | Flag | Filesystem Access | Path Restrictions |
|---|---|---|---|
| Full | (default) | All libraries | None |
| Restricted | --allowed-paths /path1,/path2 |
All libraries | Only specified paths |
| None | --allowed-paths - |
All libraries | No paths allowed |
Full Mode (default)
All libraries available, no restrictions:
scriptling script.py
Restricted Mode
All libraries available, but filesystem operations restricted to specified paths:
# Restrict to specific directories
scriptling --allowed-paths "/tmp/data,./uploads" script.py
# With relative paths
scriptling --allowed-paths "./data,../shared" script.py
# Via environment variable
SCRIPTLING_ALLOWED_PATHS="/var/www,./public" scriptling script.py
No File Access Mode
Disable all filesystem access (useful for running untrusted scripts):
scriptling --allowed-paths - script.py
All file operations (os.read_file, os.write_file, pathlib, glob, sandbox.exec_file) will be denied.
When a script tries to access a path outside the allowed directories:
import os
# This will raise an error if /etc/passwd is not in allowed paths
try:
content = os.read_file("/etc/passwd")
except Exception as e:
print(f"Access denied: {e}")
# Output: Access denied: access denied: path '/etc/passwd' is outside allowed directories
Available libraries:
- Standard libraries:
json,math,random,re,time,base64,hashlib,hmac,urllib datetime- Date and time operationsyaml,toml- YAML and TOML parsinghtml.parser- HTML parsingrequests- HTTP clientos- Environment variables and file operations (path-restricted)pathlib,glob- File system access (path-restricted)secrets- Cryptographic random number generationscriptling.secret- Host-configured secret resolution through provider aliasesscriptling.runtime- Runtime utilities including sandbox and background tasksscriptling.net.websocket- WebSocket clientscriptling.net.multicast- UDP multicast group messagingscriptling.net.unicast- UDP and TCP point-to-point messagingscriptling.net.gossip- Gossip protocol cluster membership and messagingsubprocess- Process executionwait_for- Process monitoring- AI, agent, and MCP libraries
Accessing Environment Variables in Scripts
You can access environment variables from within Scriptling scripts using the os library:
import os
# Get a specific environment variable
api_key = os.getenv("API_KEY", "default-key")
print(f"API Key: {api_key}")
# Get all environment variables
env = os.environ()
print(f"Home: {env['HOME']}")
print(f"Path: {env['PATH']}")
Host-Owned Secret Providers
Use scriptling.secret when the host application or CLI owns the provider configuration and scripts should only see aliases plus logical secret paths:
[[secrets.provider]]
provider = "vault"
alias = "prod_vault"
address = "https://vault.internal:8200"
token = "hvs.example"
cache_ttl = "5m"
[[secrets.provider]]
provider = "onepassword"
alias = "op"
address = "http://op-connect:8080"
token = "example-token"
Run a script with the config file:
scriptling --secret-config ./secrets.toml script.py
In the script:
import scriptling.secret as secret
db_password = secret.get("prod_vault", "secret/data/app", "password")
api_key = secret.get("op", "Engineering/api-key", "credential")
The script never receives provider URLs, API tokens, or other private configuration. Those stay in the host application or CLI config file.
HTTP Server Mode
Start an HTTP server with optional MCP tools:
# Start HTTP server on port 8000
scriptling --server :8000 setup.py
# With TLS (self-signed certificate)
scriptling --server :8443 --tls-generate setup.py
# With MCP tools from directory
scriptling --server :8000 --mcp-tools ./tools setup.py
# With JSON-RPC over HTTP at /json-rpc
scriptling --server :8000 --json-rpc setup.py
# With MCP tools and JSON-RPC on the same HTTP server
scriptling --server :8000 --mcp-tools ./tools --json-rpc setup.py
# With MCP script execution tool
scriptling --server :8000 --mcp-exec-script setup.py
# With both MCP tools and exec tool
scriptling --server :8000 --mcp-tools ./tools --mcp-exec-script setup.py
# With authentication
scriptling --server :8000 --bearer-token my-secret-token setup.py
# With filesystem restrictions
scriptling --server :8000 --allowed-paths "/var/www,./uploads" setup.py
JSON-RPC Server Mode
--json-rpc supports one transport at a time:
# Stdio JSON-RPC: reads stdin and writes stdout
scriptling --json-rpc setup.py
# HTTP JSON-RPC: mounts POST /json-rpc on the HTTP server
scriptling --server :8000 --json-rpc setup.py
# HTTP JSON-RPC can run alongside MCP tools and normal HTTP routes
scriptling --server :8000 --json-rpc --mcp-tools ./tools setup.py
Register handlers from the setup script exactly as you do for stdio:
import scriptling.runtime as runtime
runtime.jsonrpc.method("echo", "handlers.echo")
runtime.jsonrpc.notification("progress", "handlers.progress")
HTTP clients send a single JSON-RPC object or a batch array to /json-rpc.
Requests receive 200 application/json; notifications and all-notification
batches receive 204 No Content.
curl -X POST http://127.0.0.1:8000/json-rpc \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"echo","params":{"hello":"world"},"id":1}'
MCP Script Execution Tool
The --mcp-exec-script flag enables a built-in MCP tool that allows LLMs to execute Scriptling code directly:
# Enable script execution tool
scriptling --server :8000 --mcp-exec-script setup.py
# Combine with custom tools
scriptling --server :8000 --mcp-tools ./tools --mcp-exec-script setup.py
Tool Details:
- Name:
execute_script - Description: Execute Scriptling code and return the result. Scriptling is a Python 3-like scripting language.
- Parameters:
code(string, required): Scriptling code to execute
Return Behavior:
- Output from
print()statements is automatically captured and returned - For structured data (JSON), use
import scriptling.mcp.tooland calltool.return_object(data) - For text output, use
tool.return_string(text)
Usage Example:
# Call the tool via MCP - print() output is captured
curl -X POST http://127.0.0.1:8000/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"id":1,
"method":"tools/call",
"params":{
"name":"execute_script",
"arguments":{
"code":"for i in range(1, 21, 3):\n print(i)"
}
}
}'
Returning Structured Data:
import scriptling.mcp.tool as tool
# Return JSON object
data = {"users": ["Alice", "Bob"], "count": 2}
tool.return_object(data)
# Or return text
tool.return_string("Operation completed successfully")
The tool description instructs LLMs to use help(topic) to discover available functions and libraries:
# Get help on built-in functions
help('builtins')
# Get help on string methods
help('str')
# Get help on a library
import json
help('json')
MCP Tools
Tools consist of two files: a .toml metadata file and a .py script file.
.toml)Defines the tool's description, parameters, and registration mode:
hello.toml:
description = "Greet a person by name"
keywords = ["hello", "greet", "welcome"]
# Optional: Registration mode
# discoverable = false (default) - Native mode: tool appears in tools/list, directly callable
# discoverable = true - Discovery mode: hidden from tools/list, found via tool_search
[[parameters]]
name = "name"
type = "string"
description = "Name of the person to greet"
required = true
[[parameters]]
name = "times"
type = "int"
description = "Number of times to repeat the greeting"
Parameter Types:
Each type emits a valid JSON Schema type in the tool definition sent to the LLM.
| Type | Aliases | JSON Schema emitted | Description |
|---|---|---|---|
string |
string |
Text values | |
integer |
int |
integer |
Whole numbers |
number |
float |
number |
Integer or float |
boolean |
bool |
boolean |
True/false values |
array:string |
array of string |
Array of strings | |
array:integer |
array:int |
array of integer |
Array of whole numbers |
array:number |
array:float |
array of number |
Array of numbers |
array:boolean |
array:bool |
array of boolean |
Array of booleans |
Unknown type strings cause the tool to fail to register at server startup with a clear error message.
Parameter Fields:
| Field | Required | Description |
|---|---|---|
name |
Yes | Parameter name |
type |
Yes | Data type (see table above) |
description |
Yes | Description shown to the LLM |
required |
No | Whether the parameter must be provided (default: false) |
Registration Modes:
- Native mode (default): Tool appears in
tools/listand can be called directly - Discovery mode (
discoverable = true): Tool is hidden fromtools/list, searchable viatool_search, and callable viaexecute_tool
.py)Implements the tool logic using the scriptling.mcp.tool library:
hello.py:
import scriptling.mcp.tool as tool
# Get parameters with defaults
name = tool.get_string("name", "World")
times = tool.get_int("times", 1)
# Implement tool logic
greetings = []
for i in range(times):
greetings.append(f"Hello, {name}!")
result = "\n".join(greetings)
# Return result
tool.return_string(result)
Available Tool Functions:
# Get parameters
tool.get_string(name, default="") # Get string parameter
tool.get_int(name, default=0) # Get integer parameter
tool.get_float(name, default=0.0) # Get float parameter
tool.get_bool(name, default=False) # Get boolean parameter
tool.get_list(name, default=[]) # Get list parameter
# Return results
tool.return_string(text) # Return text result
tool.return_object(obj) # Return object as JSON
tool.return_toon(obj) # Return object as TOON format
tool.return_error(message) # Return error message
A tool that calculates the sum of two numbers:
add.toml:
description = "Calculate the sum of two numbers"
keywords = ["math", "add", "sum", "calculate"]
discoverable = true # Hidden from tools/list, searchable
[[parameters]]
name = "a"
type = "int"
description = "First number"
required = true
[[parameters]]
name = "b"
type = "int"
description = "Second number"
required = true
add.py:
import scriptling.mcp.tool as tool
a = tool.get_int("a")
b = tool.get_int("b")
result = a + b
tool.return_string(f"{a} + {b} = {result}")
Testing Tools
# Start server with MCP tools
scriptling --server :8000 --mcp-tools ./tools setup.py &
# List available tools
curl -X POST http://127.0.0.1:8000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# Call a native tool directly
curl -X POST http://127.0.0.1:8000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"hello","arguments":{"name":"Alice","times":2}}}'
# Search for discoverable tools
curl -X POST http://127.0.0.1:8000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"tool_search","arguments":{"query":"math"}}}'
# Execute a discoverable tool
curl -X POST http://127.0.0.1:8000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"execute_tool","arguments":{"name":"add","arguments":{"a":5,"b":3}}}}'
Help
scriptling --help
Building
The CLI tool uses Task for building. Install Task first:
# macOS
brew install go-task/tap/go-task
# Or download from https://taskfile.dev/
Build for current platform
task build
Build for all platforms
task build-all
Install locally
task install
Features
- File execution: Run Scriptling scripts from files
- Stdin execution: Pipe scripts to stdin
- Interactive mode: REPL-like interactive execution
- Lint mode: Check scripts for syntax errors without execution
- HTTP Server: Start HTTP server with custom routes via
--server - MCP Server: Serve tools via Model Context Protocol with
--mcp-tools - Path restrictions: Restrict filesystem access with
--allowed-paths - Custom libraries: Load libraries from the script directory automatically, with optional extra paths via
--libpath - Environment configuration: Auto-load settings from
.envfile - Configurable logging: Set log level with
--log-level(debug, info, warn, error) - Cross-platform: Built for Linux, macOS, and Windows on AMD64 and ARM64
- Minimal size: Optimized with stripped binaries (~7MB)
Libraries
The CLI includes all standard libraries plus external libraries:
datetime,json,math,random,re,time,base64,hashlib,hmac,urllibrequests- HTTP client libraryscriptling.net.websocket- WebSocket clientscriptling.net.multicast- UDP multicast group messagingscriptling.net.unicast- UDP and TCP point-to-point messagingscriptling.net.gossip- Gossip protocol cluster membership and messagingsubprocess- Process execution library
Library Loading
Scriptling automatically searches for libraries relative to the running script, matching Python's behaviour. For interactive mode or stdin, the current working directory is used instead.
# Libraries in ./myproject/ are found automatically
scriptling ./myproject/script.py
# Interactive mode: libraries in cwd are found automatically
scriptling --interactive
Use --libpath (repeatable) to add extra search directories. The script directory (or cwd) is always searched first:
# Search script dir first, then /shared/libs
scriptling --libpath /shared/libs script.py
# Multiple extra directories
scriptling --libpath /shared/libs --libpath /company/libs script.py
# Via environment variable (space-separated list)
SCRIPTLING_LIBPATH=/shared/libs scriptling script.py
Libraries can be organised using Python-style folder structure:
myproject/
script.py
utils.py # import utils
knot/
__init__.py # (optional) package initialization
groups.py # import knot.groups
roles.py # import knot.roles
# In script.py — no --libpath needed, same directory is searched automatically
import utils # Loads from myproject/utils.py
import knot.groups # Loads from myproject/knot/groups.py
Loading Priority:
For nested imports like knot.groups, the loader checks:
dir/knot/groups.py(folder structure - preferred)dir/knot.groups.py(flat file - legacy fallback)
Documentation
¶
There is no documentation for this package.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package pluginpack bridges fetcher plugins into the pack scheme registry: every loaded plugin that advertises the fetch capability gets its schemes registered, so knot://libs as a --package value (or as a script source) resolves to a bundle whose files are fetched on demand over the plugin protocol.
|
Package pluginpack bridges fetcher plugins into the pack scheme registry: every loaded plugin that advertises the fetch capability gets its schemes registered, so knot://libs as a --package value (or as a script source) resolves to a bundle whose files are fetched on demand over the plugin protocol. |