Install
$ agentstack add skill-impertio-studio-speckle-claude-skill-package-speckle-syntax-automate ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
speckle-syntax-automate
Quick Reference
Architecture: Functions vs Automations
| Concept | What It Is | Created By | Scope | |---------|-----------|------------|-------| | Function | Reusable code template (GitHub repo) | Developer | Global -- listed in Function Library | | Automation | Configured instance of a Function | Project owner / admin | Bound to one project + model |
One Function can be deployed as many Automations across different projects, each with different input configurations.
Trigger Mechanism
The ONLY trigger is version creation -- when a new version (commit) is published to a model. There is NO manual trigger, NO scheduled trigger, and NO webhook-based trigger for Automate functions.
Execution Flow
1. User publishes new version to a model
2. Speckle Server detects version creation event
3. Server finds all Automations bound to that model
4. For each Automation, Speckle spins up a Docker container
5. Function code executes with AutomationContext
6. Results (pass/fail, annotations, files) attach to the version
7. Results visible in web interface and 3D viewer
SDK Packages
| Language | Package | Target | |----------|---------|--------| | Python | specklepy (includes speckle_automate) | Python 3.10+ | | C# | Speckle.Automate.Sdk (NuGet) | .NET 8.0 |
Three Outcome States
| State | Cause | Meaning | |-------|-------|---------| | Success | mark_run_success() called | Model meets all criteria | | Failed | mark_run_failed() called | Model does NOT meet criteria (intentional) | | Exception | Unhandled exception thrown | Code error or platform error |
Critical Warnings
ALWAYS explicitly call mark_run_success() or mark_run_failed() before the function returns. If neither is called and no exception occurs, the run status is ambiguous.
NEVER pass execute_automate_function a function invocation -- pass the function reference WITHOUT parentheses and the FunctionInputs CLASS (not an instance).
NEVER use plain str for sensitive inputs (API keys, tokens). ALWAYS use SecretStr (Python) or [Secret] attribute (C#) to prevent exposure in logs and UI.
NEVER pass an empty list to attach_error_to_objects() -- it produces silent failures with no visible annotations. ALWAYS verify the list contains valid object IDs before calling.
NEVER use .Result or .Wait() in C# Automate functions -- the function is async. ALWAYS use await for all async operations.
NEVER use complex nested types in FunctionInputs -- the Speckle UI renders inputs as a flat form. Keep inputs as simple scalar types (str, int, float, bool).
Python: Function Structure
Entry Point Pattern
Every Python Automate function MUST follow this exact structure:
from speckle_automate import (
AutomateBase,
AutomationContext,
execute_automate_function,
)
from pydantic import Field, SecretStr
class FunctionInputs(AutomateBase):
"""Define inputs with Pydantic Field() -- generates the UI form."""
param_name: str = Field(title="Human Title", description="Explanation")
def automate_function(
automate_context: AutomationContext,
function_inputs: FunctionInputs,
) -> None:
# 1. Receive the version data
base = automate_context.receive_version()
# 2. Process/validate the data
# ... your logic here ...
# 3. Report results
automate_context.mark_run_success("All checks passed.")
# Entry point -- MUST be at module level
if __name__ == "__main__":
execute_automate_function(automate_function, FunctionInputs)
FunctionInputs (Python -- Pydantic)
AutomateBase inherits from Pydantic's BaseModel. All Pydantic features work:
class FunctionInputs(AutomateBase):
# Required string
category_filter: str = Field(
title="Category Filter",
description="Only check objects in this category"
)
# Numeric with constraints and default
max_count: int = Field(
default=100,
title="Maximum Object Count",
ge=1, le=10000
)
# Secret value (encrypted, hidden in logs/UI)
api_key: SecretStr = Field(title="External API Key")
# Boolean toggle with default
strict_mode: bool = Field(
default=False,
title="Strict Mode",
description="Fail on warnings too"
)
Rules:
- ALWAYS add
titleanddescriptionto every Field -- without them the Speckle UI shows raw field names - ALWAYS provide
defaultvalues for optional inputs -- inputs without defaults are treated as required - ALWAYS use
SecretStrfor sensitive values (API keys, tokens, credentials) - The JSON Schema is auto-generated from the class and rendered as a form in Speckle
AutomationContext API (Python)
| Method | Signature | Purpose | |--------|-----------|---------| | receive_version() | () -> Base | Retrieve the root Base object for the triggering version | | attach_error_to_objects() | (category: str, object_ids: List[str], message: str) -> None | Attach error annotations visible in the 3D viewer | | attach_info_to_objects() | (category: str, object_ids: List[str], message: str) -> None | Attach informational annotations (non-error) | | mark_run_failed() | (message: str) -> None | Mark run as intentionally failed | | mark_run_success() | (message: str) -> None | Mark run as successful | | store_file_result() | (file_path: str) -> None | Upload a file artifact to the run results | | set_context_view() | () -> None | Configure 3D viewer URL for the run result |
Object Traversal Pattern
Flatten the received object hierarchy to inspect individual objects:
from specklepy.objects import Base
from typing import Iterable
def flatten_base(base: Base) -> Iterable[Base]:
"""Recursively flatten a Base object hierarchy."""
elements = getattr(base, "elements", getattr(base, "@elements", None))
if elements is not None:
for element in elements:
yield from flatten_base(element)
yield base
This function:
- Uses
getattrwith fallback to handle bothelementsand@elementsnaming - Yields descendants before the parent (depth-first, bottom-up)
- Returns
Iterable[Base]for lazy evaluation (memory-efficient for large models) - Is included in the official Python template as
flatten.py
C#: Function Structure
Project Setup
net8.0
Entry Point (Program.cs)
using Microsoft.Extensions.DependencyInjection;
using Speckle.Automate.Sdk;
var serviceCollection = new ServiceCollection();
serviceCollection.AddAutomateSdk();
serviceCollection.AddSingleton();
var serviceProvider = serviceCollection.BuildServiceProvider();
var runner = serviceProvider.GetRequiredService();
var function = serviceProvider.GetRequiredService();
return await runner.Main(args, function.Run);
The runner.Main() call:
- Parses command-line arguments
- Deserializes function inputs as
FunctionInputs - Creates the
IAutomationContext - Invokes
function.Runwith both
FunctionInputs (C# -- DataAnnotations)
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
public readonly struct FunctionInputs
{
[Required]
public string SpeckleTypeToCount { get; init; }
[DefaultValue(10)]
[Range(1, 100)]
[Required]
public int SpeckleTypeTargetCount { get; init; }
[Required]
[Secret]
public string ExternalServiceKey { get; init; }
}
Rules:
- ALWAYS use
readonly structfor immutability - ALWAYS use
System.ComponentModel.DataAnnotationsattributes for validation - ALWAYS use
[Secret]attribute for sensitive values (NOTSecretStr) - ALWAYS use
{ get; init; }properties
AutomateFunction Class
using Speckle.Automate.Sdk;
using Speckle.Sdk.Models.Extensions;
public class AutomateFunction
{
public async Task Run(
IAutomationContext automationContext,
FunctionInputs functionInputs
)
{
var rootObject = await automationContext.ReceiveVersion();
var allObjects = rootObject.Flatten().ToList();
// ... validation logic ...
automationContext.MarkRunSuccess("All checks passed.");
}
}
IAutomationContext API (C#)
| Method | Signature | Purpose | |--------|-----------|---------| | ReceiveVersion() | async Task | Retrieve the root Base object | | AttachErrorToObjects() | (string category, IList objectIds, string message) | Attach error annotations | | MarkRunFailed() | (string message) | Mark run as intentionally failed | | MarkRunSuccess() | (string message) | Mark run as successful | | StoreFileResult() | (string filePath) | Upload a file artifact | | SetContextView() | () | Configure viewer context |
Python vs C# Comparison
| Aspect | Python | C# | |--------|--------|-----| | Input base class | AutomateBase (Pydantic BaseModel) | readonly struct with DataAnnotations | | Secret fields | SecretStr | [Secret] attribute | | Context type | AutomationContext (concrete class) | IAutomationContext (interface) | | Entry point | execute_automate_function(fn, InputsClass) | DI container + IAutomationRunner.Main() | | Object flattening | Manual flatten_base() generator | Flatten() extension method | | Async model | Synchronous | Full async/await | | Package | specklepy (includes automate) | Speckle.Automate.Sdk (separate NuGet) | | Target runtime | Python 3.10+ | .NET 8.0 |
Deployment Workflow
1. Create function via Speckle wizard (Automations tab)
2. Template repo cloned to your GitHub account
3. Modify function code locally
4. Push changes to GitHub
5. Create a GitHub Release
6. GitHub Actions builds Docker image and registers function
7. Function appears in Function Library
8. Users create Automations from the function
ALWAYS create a GitHub Release to publish -- pushing code alone does NOT make the function appear in the Function Library.
NEVER modify or remove SPECKLE_FUNCTION_ID and SPECKLE_FUNCTION_TOKEN environment variables in the auto-generated .github/workflows/main.yml -- removing them breaks the deployment pipeline.
Reference Links
- [references/methods.md](references/methods.md) -- Complete API signatures for AutomationContext (Python) and IAutomationContext (C#)
- [references/examples.md](references/examples.md) -- Working code examples for both Python and C#
- [references/anti-patterns.md](references/anti-patterns.md) -- Common mistakes with explanations
Official Sources
- https://docs.speckle.systems/developers/automate/introduction
- https://docs.speckle.systems/developers/automate/quickstart
- https://docs.speckle.systems/developers/automate/create-function
- https://github.com/specklesystems/speckleautomatepython_example
- https://github.com/specklesystems/SpeckleAutomateDotnetExample
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Impertio-Studio
- Source: Impertio-Studio/Speckle-Claude-Skill-Package
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.