Zum Inhalt springen
Developer Preview — APIs and language features may change before 1.0

Action SDKs

Dieser Inhalt ist noch nicht in deiner Sprache verfügbar.

mashin provides SDKs in six languages for building external action services. Each SDK implements the same HTTP contract, so your action service works the same way regardless of what language it is written in.

For a guided walkthrough of building an action service, see Building Action Services.

The SDKs live in the mashin repository under sdks/. None of them is published to a package registry yet, so today you consume them from source: clone the repository and point your project at the directory for your language.

Language Module you import Source directory
Python mashin_effects sdks/python
TypeScript @mashin/effects sdks/typescript
Swift MashinEffects sdks/swift
Go effects sdks/go
Rust mashin_effects sdks/rust
Elixir MashinEffects sdks/elixir

Publishing these to PyPI, npm, hex, crates.io and the Swift package index is a launch checklist item. Until that happens, an install command on this page would 404 on the reader’s first line, so the page shows the source directory instead. The quick starts below are the code you write once the SDK is on your path.

Every action service exposes three HTTP endpoints:

Endpoint Method Purpose
/execute POST Run the action. Receives {"input": {...}, "context": {...}}, returns {"status": "completed", "output": {...}, "audit": {"duration_ms": N}}
/governance GET Return the action.s name, version, capabilities, and input/output schema
/health GET Return {"status": "healthy", "version": "0.1.0"}

The SDK handles the HTTP server, routing, request parsing, error handling, and audit timing. You implement two functions: execute (your logic) and governance (your declaration).

The distribution is mashin-effects and the import package is mashin_effects. Until it is on PyPI, install it from your clone with pip install -e sdks/python.

from mashin_effects import EffectService
class SentimentAnalyzer(EffectService):
name = "sentiment-analyzer"
version = "1.0.0"
capabilities = ["external_api_call"]
inputs = [{"name": "text", "type": "string", "required": True}]
outputs = [{"name": "sentiment", "type": "string"}, {"name": "score", "type": "number"}]
def execute(self, input, context):
text = input.get("text", "")
# Your analysis logic here
return {"sentiment": "positive", "score": 0.85}
if __name__ == "__main__":
SentimentAnalyzer().serve(port=8080)

The package is @mashin/effects. Until it is on npm, add it from your clone: "@mashin/effects": "file:../mashin-core/sdks/typescript" in your package.json dependencies.

import { EffectService } from "@mashin/effects";
class SentimentAnalyzer extends EffectService {
governance() {
return {
name: "sentiment-analyzer",
version: "1.0.0",
capabilities: ["external_api_call"],
inputs: [{ name: "text", type: "string", required: true }],
outputs: [{ name: "sentiment", type: "string" }, { name: "score", type: "number" }],
};
}
async execute(input: Record<string, any>, context: Record<string, any>) {
const text = input.text ?? "";
// Your analysis logic here
return { sentiment: "positive", score: 0.85 };
}
}
new SentimentAnalyzer().serve({ port: 8080 });

The module is declared as github.com/mashin-live/effects-go. It is not fetchable yet, so add a replace pointing at your clone:

go.mod
require github.com/mashin-live/effects-go v0.0.0
replace github.com/mashin-live/effects-go => ../mashin-core/sdks/go
package main
import effects "github.com/mashin-live/effects-go"
type SentimentAnalyzer struct{}
func (s SentimentAnalyzer) Execute(input, context map[string]any) (map[string]any, error) {
text, _ := input["text"].(string)
_ = text // Your analysis logic here
return map[string]any{"sentiment": "positive", "score": 0.85}, nil
}
func (s SentimentAnalyzer) Governance() effects.GovernanceDeclaration {
return effects.GovernanceDeclaration{
Name: "sentiment-analyzer",
Version: "1.0.0",
Capabilities: []string{"external_api_call"},
Inputs: []effects.FieldSpec{{Name: "text", Type: "string", Required: true}},
Outputs: []effects.FieldSpec{{Name: "sentiment", Type: "string"}, {Name: "score", Type: "number"}},
}
}
func main() {
effects.Serve(SentimentAnalyzer{}, effects.WithPort(8080))
}

The crate is mashin-effects, not yet on crates.io, so depend on it by path:

Cargo.toml
[dependencies]
mashin-effects = { path = "../mashin-core/sdks/rust" }
tokio = { version = "1", features = ["full"] }
async-trait = "0.1"
serde_json = "1"
use async_trait::async_trait;
use mashin_effects::{EffectService, GovernanceDeclaration, serve};
use std::collections::HashMap;
use serde_json::Value;
struct SentimentAnalyzer;
#[async_trait]
impl EffectService for SentimentAnalyzer {
async fn execute(
&self,
input: HashMap<String, Value>,
_context: HashMap<String, Value>,
) -> Result<HashMap<String, Value>, Box<dyn std::error::Error + Send + Sync>> {
let mut output = HashMap::new();
output.insert("sentiment".into(), Value::from("positive"));
output.insert("score".into(), Value::from(0.85));
Ok(output)
}
fn governance(&self) -> GovernanceDeclaration {
GovernanceDeclaration {
name: "sentiment-analyzer".into(),
version: "1.0.0".into(),
capabilities: vec!["external_api_call".into()],
inputs: vec![],
outputs: vec![],
rate_limits: None,
}
}
}
#[tokio::main]
async fn main() {
serve(SentimentAnalyzer, 8080).await.unwrap();
}

MashinEffects is a library product of the MashinSDK package in sdks/swift. It is not on the Swift package index yet, so depend on your clone by path:

Package.swift
dependencies: [
.package(path: "../mashin-core/sdks/swift"),
]
import MashinEffects
struct SentimentAnalyzer: EffectService {
func execute(input: [String: Any], context: [String: Any]) async throws -> [String: Any] {
return ["sentiment": "positive", "score": 0.85]
}
func governance() -> GovernanceDeclaration {
GovernanceDeclaration(
name: "sentiment-analyzer",
version: "1.0.0",
capabilities: ["external_api_call"],
inputs: [FieldSpec(name: "text", type: "string", required: true)],
outputs: [FieldSpec(name: "sentiment", type: "string"), FieldSpec(name: "score", type: "number")]
)
}
}
@main struct Main {
static func main() async throws {
let server = EffectServer(service: SentimentAnalyzer(), host: "0.0.0.0", port: 8080)
try await server.run()
}
}

The app is :mashin_effects, not yet on hex, so depend on it by path:

mix.exs
def deps do
[{:mashin_effects, path: "../mashin-core/sdks/elixir"}]
end
defmodule SentimentAnalyzer do
@behaviour MashinEffects.Service
@impl true
def execute(input, _context) do
{:ok, %{"sentiment" => "positive", "score" => 0.85}}
end
@impl true
def governance do
%{
"name" => "sentiment-analyzer",
"version" => "1.0.0",
"capabilities" => ["external_api_call"],
"inputs" => [%{"name" => "text", "type" => "string", "required" => true}],
"outputs" => [%{"name" => "sentiment", "type" => "string"}, %{"name" => "score", "type" => "number"}]
}
end
end
# Standalone
MashinEffects.start(SentimentAnalyzer, port: 8080)
# Or in a supervision tree
children = [
{MashinEffects, service: SentimentAnalyzer, port: 8080}
]

All SDKs expose the same endpoints, so you can test with curl:

Terminal window
# Health check
curl http://localhost:8080/health
# Governance declaration
curl http://localhost:8080/governance
# Execute
curl -X POST http://localhost:8080/execute \
-H "Content-Type: application/json" \
-d '{"input": {"text": "This product is great"}, "context": {}}'

Or use the CLI:

Terminal window
mashin action test http://localhost:8080

Beyond the six SDKs, the sdks/examples/ directory has standalone examples in Bun, Deno, Gleam, Zig, Kotlin, Ruby, and C#. These implement the HTTP contract directly without an SDK, proving that any language can participate as a mashin action service.