MCP Server Mode
Scriptling can run as an MCP (Model Context Protocol) server, enabling AI assistants to use your tools and execute scripts.
MCP Server Overview
When running in MCP server mode, Scriptling provides:
- Custom MCP Tools: Root-level legacy
.toml+.pytools or decorated.pytools from--mcp-tools - Script Execution Tool: Allow AI to execute Scriptling code directly (
--mcp-exec-script) - Resources: Recursively scanned files served by URI from
--mcp-resources; a path containing{var}is a template only when it ends in.py - Prompts: Root-level static
.md/.txtor dynamic.toml+.pyprompts from--mcp-prompts - HTTP reload notifications: Supported file-change and signal reloads mutate the live HTTP server and emit
listChangednotifications, subject to the limitations below
--mcp-tools, --mcp-exec-script, or an app bundle with serve = ["mcp"] activates MCP. Resource and prompt directories are additive; --mcp-resources or --mcp-prompts alone does not mount a working MCP endpoint.
Security: Use
--disable-lib subprocessto remove shell access.--allowed-pathsrestricts participating library filesystem APIs; it is not a complete sandbox.
Starting an MCP Server
Basic MCP Server with Custom Tools
# Start HTTP server with MCP tools
scriptling --server :8000 --mcp-tools ./tools setup.pyWith Script Execution Enabled
# Enable script execution tool (allows AI to run code)
scriptling --server :8000 --mcp-exec-script setup.pyCombined Setup
# Both custom tools and script execution
scriptling --server :8000 --mcp-tools ./tools --mcp-exec-script setup.py
# With authentication
scriptling --server :8000 --mcp-tools ./tools --mcp-exec-script \
--bearer-token my-secret-token setup.py
# With filesystem restrictions
scriptling --server :8000 --mcp-tools ./tools --mcp-exec-script \
--allowed-paths "/tmp/sandbox,./data" setup.pyChoosing the transport: HTTP vs stdio
The MCP tool flags (--mcp-tools and/or --mcp-exec-script) enable the MCP
server; the presence of --server selects the transport:
- With
--server— MCP is served over HTTP at/mcp(as shown above). - Without
--server— MCP is served over stdio (stdin/stdout, newline-delimited JSON-RPC 2.0), the transport MCP hosts use to launch a server as a subprocess.
This mirrors --json-rpc, which serves over stdio by default and over HTTP at
/json-rpc when combined with --server.
# Serve tools over stdio
scriptling --mcp-tools ./tools
# Or the built-in script execution tool
scriptling --mcp-exec-scriptLogging goes to stderr in stdio mode so it never corrupts the protocol stream on stdout, exactly like
--json-rpc. Some MCP hosts (e.g. the MCP Inspector) forward every stderr line to the client before the connection is fully established, which can cause errors. Use--log-format nullto suppress all log output when this is an issue.
Configure it in an MCP host (e.g. Claude Desktop) as a command rather than a URL:
{
"mcpServers": {
"scriptling": {
"command": "scriptling",
"args": ["--mcp-tools", "/absolute/path/to/tools"]
}
}
}Test it by piping requests in:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| scriptling --mcp-exec-scriptMCP Options
| Flag | Environment Variable | Description | Default |
|---|---|---|---|
--mcp-tools |
SCRIPTLING_MCP_TOOLS |
Root directory of legacy or decorated MCP tools | (disabled) |
--mcp-resources |
SCRIPTLING_MCP_RESOURCES |
Recursive resource tree; optional _stem.toml metadata |
(disabled) |
--mcp-prompts |
SCRIPTLING_MCP_PROMPTS |
Root-level static or dynamic prompts | (disabled) |
--mcp-exec-script |
SCRIPTLING_MCP_EXEC_SCRIPT |
Enable MCP script execution tool | false |
Tools, the execution tool, or an app bundle declaring MCP activates the server. Resource and prompt flags only add content to an activated MCP server. Add --server <addr> to serve over HTTP at /mcp; without it, the activated server runs over stdio.
Script Execution Tool
The --mcp-exec-script flag enables a built-in MCP tool that allows AI assistants to execute Scriptling code directly.
Enabling Script Execution
scriptling --server :8000 --mcp-exec-script setup.pyTool Details
- Name:
execute_script - Description: Execute Scriptling code and return the result
- Parameters:
code(string, required): Scriptling code to execute
Return Behavior
The execute_script tool captures output in the following order of priority:
- Explicit return functions - If you use
tool.return_string(),tool.return_object(), etc., that value is returned - Script return value - If the script returns a value (e.g., calling a function that returns a string), it’s automatically captured and returned
- Print output - Output from
print()statements is captured and returned if no other return method is used
# Example: Direct return value
def greet():
return "Hello, World!"
greet() # Returns: "Hello, World!"
# Example: Print output
print("Hello, World!") # Returns: "Hello, World!"
# Example: Explicit return (stops execution)
import scriptling.mcp.tool as tool
tool.return_string("Hello, World!") # Returns immediatelyFor structured data (JSON), use tool.return_object(data). For complex types returned directly (dict, list), they’re automatically converted to JSON.
Example: Using the Tool
# Call the execute_script tool via MCP
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
When you need to return JSON instead of text:
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")Discovering Available Functions
The tool description instructs AI assistants to use help(topic) to discover available functions:
# Get help on built-in functions
help('builtins')
# Get help on string methods
help('str')
# Get help on a library
import json
help('json')Custom MCP Tools
Custom tools are loaded from the directory specified by --mcp-tools. Two formats are supported:
- Legacy pair:
toolname.tomlmetadata plustoolname.pyimplementation - Decorated tool: a
.pyfile whose evaluated@mcp.tooldecorators provide registration and metadata
Directory Structure
./tools/
hello.toml # Legacy metadata for "hello"
hello.py # Legacy implementation
decorated.py # One or more @mcp.tool-decorated functionsStarting with Custom Tools
scriptling --server :8000 --mcp-tools ./tools setup.pyFor details on creating custom tools, see Writing MCP Tools.
Conditional Tool Registration
The MCP scanner evaluates each .py file to find @mcp.tool decorators —
it doesn’t just parse the AST. This means decorators inside a conditional
only fire when the condition is true, so you can gate tools behind CLI flags
or environment checks.
sys.argv is available in tool evaluators and contains extra args passed
after -- on the command line:
# tools/files.py
import scriptling.runtime.mcp as mcp
import sys
@mcp.tool("Read a file", params={"path": "File path"})
def read_file(path):
return "contents"
if "--allow-write" in getattr(sys, "argv", []):
@mcp.tool("Write a file", params={"path": "File path", "content": "Content"})
def write_file(path, content):
return "wrote " + path
@mcp.tool("Delete a file", params={"path": "File path"})
def delete_file(path):
return "deleted " + path# Read-only — only read_file is visible
scriptling --mcp-tools tools
# Read + write — all three tools are visible
scriptling --mcp-tools tools -- --allow-writeThe same pattern works in app bundles:
scriptling --package . -- --allow-writeThe same CLI library setup is available while evaluating tool, resource, and prompt scripts. In particular, os.getenv() reads the live process environment and os.environ() is the snapshot captured when the library was registered; .env is loaded before command setup. These are host-process values, not an MCP sandbox.
sys.argv is set once at startup and is the same at scan time and request
time, so the condition is consistent across all tool invocations.
MCP Endpoints
When running as an MCP server, the following endpoints are available:
List 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 Tool
# Native tool (appears in tools/list)
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}
}
}'
# Script execution tool
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":"execute_script",
"arguments":{"code":"print(2 + 2)"}
}
}'Search Discoverable Tools
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":"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":5,
"method":"tools/call",
"params":{
"name":"execute_tool",
"arguments":{"name":"add","arguments":{"a":5,"b":3}}
}
}'Resources and Prompts
As well as tools, the MCP server exposes resources and prompts. They are defined as files in folders — no Go code, no startup registration — the same model as tools.
- Resources (
--mcp-resources): a file is served verbatim at the URI formed from its path (first directory = scheme, rest = URI). A path containing a{var}segment and ending in.pyis a template whose script runs with the extracted variable. An optional_stem.tomlsibling provides a human-readable name, description, and MIME type; without it the URI is used as the name. Files starting with_are metadata, never served. - Prompts (
--mcp-prompts):name.mdfor a static prompt (one user message), orname.toml+name.pyfor a dynamic, arg-driven prompt. The dynamic.tomluses the same format as a tool’s.toml—descriptionplus an array of tables — but[[arguments]]instead of[[parameters]], and prompt arguments are string-only (notype).
A built-in scriptling://script/{name} resource template (tool source code,
when --mcp-tools is set) and a write_script prompt are always available.
For a walkthrough of creating resources and prompts — including the shared
.toml format — see the Building an MCP Resources & Prompts Server tutorial.
The tool metadata reference
covers the .toml fields prompts share. To read resources and prompts from a
server in a script, see the scriptling.mcp client.
Live Reload and Change Notifications
HTTP MCP watches only each configured root directory, not nested directories. A root-level .toml or .py change triggers a debounced in-place reload and emits notifications/tools/listChanged, notifications/resources/listChanged, and notifications/prompts/listChanged. Static resource changes such as .md or .txt do not trigger reload automatically.
Stdio MCP does not currently consume watcher events or send reload notifications. Signal-triggered reload is also limited: SIGHUP/SIGUSR1 is handled only when --mcp-tools is set, and only one reload signal is consumed before shutdown. Restart the process when those limits matter.
Security Considerations
Script Execution Tool
The --mcp-exec-script flag allows AI assistants to execute arbitrary code. Consider:
- Use
--allowed-pathsto restrict participating library filesystem I/O; it is not a complete sandbox - Use
--disable-lib subprocessto remove shell access - Use
--bearer-tokento require authentication, or register script middleware for per-user keys (see Per-User Keys). The token wraps every endpoint including/mcpand the built-in/health; middleware guards/mcpand script routes but not the built-in/healthor static routes (a route claimingGET /healthreplaces the built-in — see Health Checks). - Run in a sandboxed environment for untrusted AI systems
# Restricted configuration for script execution
scriptling --server :8000 --mcp-exec-script \
--allowed-paths "/tmp/sandbox" \
--disable-lib subprocess \
--bearer-token secure-token setup.pyClients of this authenticated endpoint must send the bearer header:
curl -X POST http://127.0.0.1:8000/mcp \
-H "Authorization: Bearer secure-token" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Custom Tools
Custom tools run with the same permissions as the server process. Validate inputs and restrict file access as needed.
Complete Example
Full MCP Server Setup
# Start server with both custom tools and script execution
scriptling --server :8000 \
--mcp-tools ./tools \
--mcp-exec-script \
--bearer-token my-secret-token \
--allowed-paths "/tmp/sandbox,./data" \
--disable-lib subprocess \
setup.pyThis configuration:
- Loads custom tools from
./tools/ - Enables the script execution tool
- Requires bearer token authentication
- Restricts participating library file operations to
/tmp/sandboxand./data - Disables the subprocess library
See Also
- Basic Usage - Running scripts, interactive mode, and lint mode
- Command Line Options - Every flag and configuration file setting
- HTTP Server Mode - HTTP server without MCP
- Writing MCP Tools - Creating custom MCP tools
- MCP Library - MCP library reference
- MCP Tool Library - Tool implementation API