Skip to content

Latest commit

 

History

History
623 lines (468 loc) · 16.3 KB

File metadata and controls

623 lines (468 loc) · 16.3 KB

Script Tools

Run Python, Node, or shell scripts during prompt execution. Scripts validate input, compute results, and can auto-trigger MCP tools.

Why script tools? Stop copy-pasting validation logic. Define it once in a schema, let the script handle the rest. Scripts can even chain into other MCP tools automatically.

Quick Start

Create a word counter tool in 3 files:

resources/prompts/utils/word_count/
├── prompt.yaml
└── tools/
    └── counter/
        ├── tool.yaml
        ├── schema.json
        └── script.py

1. Tool config (tools/counter/tool.yaml):

id: counter
name: Word Counter
runtime: python
script: script.py
timeout: 5000

execution:
  trigger: schema_match

2. Input schema (tools/counter/schema.json):

{
  "type": "object",
  "properties": {
    "text": { "type": "string", "description": "Text to count" }
  },
  "required": ["text"]
}

3. Script (tools/counter/script.py):

#!/usr/bin/env python3
import json, sys
data = json.load(sys.stdin)
text = data.get("text", "")
print(json.dumps({"words": len(text.split()), "chars": len(text)}))

Use it:

prompt_engine(command: ">>word_count", options: {text: "Hello world"})

Result available in template as {{tool_counter}}.


How It Works

User Input → Schema Match → Script Executes → Output to Template
                              ↓
                    (optional) Auto-Execute MCP Tool
Step What Happens
1. Schema match User args compared against schema.json
2. Script runs Receives args via stdin (JSON), outputs to stdout
3. Template merge Output available as {{tool_<id>}}
4. Auto-execute If script returns auto_execute block, MCP tool is invoked

Trigger Types

Control when your script activates:

Trigger Activates When Best For
schema_match User args match schema (default) Most tools
explicit User passes tool:<id> argument Destructive/expensive ops
always Every prompt execution Logging, metrics
never Disabled WIP features

schema_match (Default)

execution:
  trigger: schema_match
  strict: false  # ANY required param triggers

Strict mode comparison:

Mode Behavior
strict: false Triggers if ANY required param present
strict: true Triggers only if ALL required params present

explicit

User must explicitly request:

>>my_prompt tool:deploy env:"production"

Config:

execution:
  trigger: explicit

Confirmation Gate

Add a confirmation step for expensive operations:

execution:
  trigger: schema_match
  confirm: true
  confirmMessage: "Run full analysis? Takes ~5 minutes."

Flow: Detection → Confirmation prompt → User approves with tool:<id> → Execution


Auto-Execute: Chain Scripts to MCP Tools

Scripts can trigger MCP tools automatically. This enables wizard workflows: validate input in a script, then invoke resource_manager, prompt_engine, etc.

Response Format

Return this JSON structure to trigger auto-execution:

{
  "valid": true,
  "auto_execute": {
    "tool": "resource_manager",
    "params": {
      "resource_type": "methodology",
      "action": "create",
      "id": "my-framework",
      "name": "My Framework"
    }
  },
  "warnings": [],
  "summary": { "phases": 5 }
}

If valid: false, auto-execute is skipped:

{
  "valid": false,
  "errors": ["Missing required field: phases", "ID must be lowercase"],
  "warnings": []
}

Template Integration

{% if tool_builder.valid %}
  {% if tool_builder_result %}
    ## Created Successfully
    {{ tool_builder_result.text }}
  {% else %}
    ## Ready to Create
    Parameters: {{ tool_builder.auto_execute.params | dump }}
  {% endif %}
{% else %}
  ## Validation Failed
  {% for error in tool_builder.errors %}- {{ error }}
  {% endfor %}
{% endif %}

Real-World Example: Methodology Builder

The create_methodology prompt uses auto-execute to create new framework methodologies.

Directory Structure

resources/prompts/examples/create_methodology/
├── prompt.yaml
├── user-message.md
└── tools/
    └── methodology_builder/
        ├── tool.yaml      # trigger: schema_match
        ├── schema.json    # Validates methodology structure
        └── script.py      # Returns auto_execute for resource_manager

The Script (Simplified)

#!/usr/bin/env python3
import json, sys

def validate(data):
    errors = []
    if not data.get("id"):
        errors.append("Missing: id")
    if len(data.get("phases", [])) < 2:
        errors.append("Need at least 2 phases")

    if errors:
        return {"valid": False, "errors": errors}

    return {
        "valid": True,
        "auto_execute": {
            "tool": "resource_manager",
            "params": {
                "resource_type": "methodology",
                "action": "create",
                **data
            }
        }
    }

print(json.dumps(validate(json.load(sys.stdin))))

Usage

Design mode — Get guidance on creating a methodology:

prompt_engine(command: ">>create_methodology", options: {
  name: "FOCUS",
  concept: "targeted problem solving"
})

Creation mode — Pass all fields, script validates and creates:

prompt_engine(command: ">>create_methodology", options: {
  "id": "focus",
  "name": "FOCUS Framework",
  "system_prompt_guidance": "Apply FOCUS:\n\n**Find**: Identify...",
  "phases": [
    {"id": "find", "name": "Find", "description": "Identify the problem"}
  ]
})

Meta-Prompt Pattern

Build "wizard" prompts that guide users through complex creation workflows. The template shows different content based on whether the script triggered.

Two-Phase UX

Phase Trigger Template Shows
Design No schema match (missing required fields) Guidance, examples, field descriptions
Validation Schema matches (all required fields present) Script runs → results, errors, or success
Auto-Execute Script returns valid: true + auto_execute MCP tool called, result shown

Template Structure

{% if not tool_builder %}
{# ═══════ DESIGN PHASE ═══════ #}
# Create a Widget

To create a widget, provide these fields:

| Field | Required | Description |
|-------|----------|-------------|
| `id` | Yes | Unique identifier (lowercase, hyphens) |
| `name` | Yes | Display name |
| `type` | No | Widget type (default: "standard") |

## Example

create_widget id:"my-widget" name:"My Widget"


{% elif tool_builder.valid %}
{# ═══════ VALIDATION PASSED ═══════ #}
  {% if tool_builder_result %}
{# ═══════ AUTO-EXECUTE SUCCEEDED ═══════ #}
## Widget Created Successfully

{{ tool_builder_result.text }}

**Next steps:**
- Test your widget: `>>my_widget`
- Edit: `resource_manager(action:"update", id:"{{tool_builder.output.id}}")`
  {% else %}
{# ═══════ READY TO CREATE ═══════ #}
## Validation Passed

Widget configuration is valid and ready to create.
  {% endif %}
{% else %}
{# ═══════ VALIDATION FAILED ═══════ #}
## Validation Errors

Fix these issues:

{% for error in tool_builder.errors %}- {{ error }}
{% endfor %}

Then retry with the corrected values.
{% endif %}

Working Examples

These meta-prompts demonstrate the pattern:

Prompt Creates Location
>>create_gate Quality gates resources/prompts/examples/create_gate/
>>create_prompt New prompts resources/prompts/examples/create_prompt/
>>create_methodology Frameworks resources/prompts/examples/create_methodology/

Template Variables

Complete reference for script tool variables available in templates:

Script Output Variables

Variable Type When Available Description
{{tool_<id>}} object After schema match Full script result object
{{tool_<id>.valid}} boolean After script runs Whether validation passed
{{tool_<id>.output}} any If script returns output Parsed output data
{{tool_<id>.errors}} string[] If valid: false Validation error messages
{{tool_<id>.warnings}} string[] Always Non-blocking warnings
{{tool_<id>.summary}} object Optional Metadata for display
{{tool_<id>.auto_execute}} object If auto-execute configured MCP tool params

Auto-Execute Variables

Variable Type When Available Description
{{tool_<id>_result}} object After auto-execute completes MCP tool response
{{tool_<id>_result.text}} string On success Response message
{{tool_<id>_result.error}} string On failure Error message

Conditional Patterns

Check if script triggered:

{% if tool_counter %}
  Script ran: {{tool_counter.output.words}} words
{% else %}
  Waiting for input...
{% endif %}

Check validation status:

{% if tool_builder.valid %}
  Ready to proceed
{% else %}
  Errors: {{tool_builder.errors | join(', ')}}
{% endif %}

Check auto-execute result:

{% if tool_builder_result %}
  Created: {{tool_builder_result.text}}
{% elif tool_builder.valid %}
  Validation passed, awaiting creation
{% endif %}

Runtime Support

Runtime Value Interpreter
Python python python3
Node.js node node
Shell shell bash / sh
Auto auto Detected from extension

Configuration Reference

tool.yaml

id: my_tool              # Unique identifier
name: My Tool            # Display name
description: Does things # Optional
runtime: python          # python | node | shell | auto
script: script.py        # Relative to tool directory
timeout: 30000           # Max execution time (ms)
enabled: true            # Set false to disable
workingDir: .            # Script working directory (relative to tool dir)

# Environment variables (explicit pass-through)
env:
  MY_VAR: "static_value"           # Static value
  API_KEY: "${API_KEY}"            # Pass-through from parent env

execution:
  trigger: schema_match  # schema_match | explicit | always | never
  strict: false          # Require ALL params (schema_match only)
  confirm: false         # Require user confirmation
  confirmMessage: "..."  # Custom confirmation text

schema.json

Standard JSON Schema. Required params trigger schema_match:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "input": { "type": "string", "description": "Main input" },
    "verbose": { "type": "boolean", "default": false }
  },
  "required": ["input"]
}

Best Practices

Do Don't
Use schema_match for most tools Use always for expensive operations
Use explicit for destructive actions Skip validation in auto-execute scripts
Keep timeouts reasonable (5-30s) Set timeout > 5 minutes
Return structured JSON Print debug output to stdout
Validate before building auto_execute Trust user input blindly

Security Model

Scripts run like npm/pip packages: you trust the author. Version-control and code-review are your safeguards.

Protections

Protection What It Does
Process isolation Separate subprocess per script
Timeout Default 30s, max 5min — kills runaway scripts
Working directory Locked to tool folder
Env filtering Only safe vars inherited (no leaked API keys)
Input validation JSON Schema checked before execution
Auto-execute whitelist Only approved MCP tools can trigger

Environment Variables

Your ANTHROPIC_API_KEY won't leak into scripts. Only safe runtime vars pass through:

Inherited Blocked (must pass explicitly)
PATH, HOME, USER, SHELL ANTHROPIC_API_KEY, OPENAI_API_KEY
NODE_ENV, PYTHONPATH, VIRTUAL_ENV AWS_SECRET_ACCESS_KEY, DATABASE_URL
LANG, LC_ALL, EDITOR, CI Any credential or secret

Need a blocked var? Pass it explicitly in tool.yaml:

env:
  DATABASE_URL: "${DATABASE_URL}"
  MY_API_KEY: "${MY_API_KEY}"

What Scripts CAN Do

Capability Example
Filesystem access Read configs, write temp files
Network requests Call APIs, fetch data
Child processes Run other scripts, shell commands
Passed env vars Use explicitly configured secrets

Full process capabilities by design. The trust boundary is who writes the script, not what the script can do.


Migration from Deprecated Config

modetrigger / confirm

Old New
mode: auto Remove (default)
mode: manual trigger: explicit
mode: confirm confirm: true

confidencestrict

Old New
confidence: 0.8 strict: false
confidence: 1.0 strict: true

Inline Script References

Execute scripts inline during template rendering using {{script:id}} syntax. Unlike auto-execute patterns, inline references resolve before template processing, making results available immediately.

Syntax

Pattern Description Example Output
{{script:id}} Full JSON output {"count": 42, "status": "ok"}
{{script:id.field}} Access specific field 42
{{script:id key='val'}} Pass inline arguments Varies by script

Basic Usage

## Analysis Results

Row count: {{script:analyzer.row_count}}
Status: {{script:analyzer.status}}

Full data: {{script:analyzer}}

Inline Arguments

Pass arguments directly in the reference:

<!-- String values (use quotes) -->
{{script:formatter text='Hello World'}}

<!-- Numbers and booleans (no quotes) -->
{{script:calculator num=42 verbose=true}}

<!-- Multiple arguments -->
{{script:analyzer file='data.csv' format='json' limit=100}}

Inline arguments override context variables with the same name.

Script Discovery

Scripts are searched in priority order:

  1. Prompt-local: resources/prompts/{category}/{prompt}/tools/{script_id}/
  2. Workspace: resources/scripts/{script_id}/

First match wins. Prompt-local scripts take priority.

Error Handling

Script execution is blocking. Errors stop template processing:

Error When Message
ScriptNotRegisteredError Script not found Lists searched paths
ScriptExecutionFailedError Script exits non-zero Includes stderr
InvalidFieldAccessError Field doesn't exist Lists available fields
InvalidScriptOutputError Field access on non-object Shows raw output

Caching

Duplicate references in the same template execute only once:

<!-- Script runs once, both show "42" -->
Count: {{script:analyzer.count}}
Again: {{script:analyzer.count}}

Cache key includes script ID, field access, and inline arguments. Different arguments = different execution.

Combining with Template Variables

Script references resolve first, then template variables:

<!-- Works: script runs, then {{name}} replaces -->
The {{script:analyzer.row_count}} rows belong to {{name}}.

<!-- Also works: context passed to script -->
Analysis for {{name}}: {{script:analyzer user='{{name}}'}}

Comparison: Auto-Execute vs Inline References

Feature Auto-Execute Inline Reference
Trigger Schema match Template pattern
Template var {{tool_<id>}} {{script:id}}
Arguments User args + context Inline + context
MCP chaining Yes (auto_execute) No
Error behavior Validation result Blocking exception
Use case Wizard workflows Data injection

Related