Standalone SDK for developing Solace Agent Mesh Python tools
Project description
SAM Tool SDK
Standalone Python SDK for developing tools that run in Solace Agent Mesh (SAM).
What This Is
This package provides the minimal Python runtime and base classes needed to develop and execute Python tools for SAM. It is a self-contained extraction of the tool execution path from the full solace_agent_mesh Python package — just the pieces that matter for writing and running tools.
The Go SAM Secure Tool Runtime (STR) executes each tool as a subprocess by invoking the console script the tool's pyproject.toml registers (built on tool_cli from this SDK). This package is everything that subprocess needs.
Why It Exists
The Go implementation of SAM (solace-agent-mesh-go) originally required the full solace_agent_mesh Python package to be installed just to execute Python tools. That package pulls in dozens of dependencies (google-adk, solace-pubsubplus, onnxruntime, pydub, etc.) that are irrelevant to tool execution in a sandbox.
By extracting the ~1,500 lines of Python actually needed into this standalone SDK, we:
- Break the dependency — no need to install
solace_agent_meshor any of its heavy transitive dependencies - Simplify installation —
pip install sam-tool-sdkis all you need (only requirespydantic) - Own the contract — the Go STR and this SDK share a well-defined interface (JSON files + named pipes) without coupling to the Python framework's internals
- Speed up sandbox startup — avoids importing the full agent framework, which takes ~5 seconds in a cold sandbox
What's Included
| Module | Purpose |
|---|---|
cli.py |
tool_cli, dynamic_tool_cli, provider_cli — wrap a function or class as a console-script entry point with --schema + sandbox execution support |
dynamic_tool.py |
DynamicTool and DynamicToolProvider base classes for tool authors |
tool_result.py |
ToolResult, DataObject, DataDisposition — structured result types returned to the agent |
artifact_types.py |
Artifact dataclass + ArtifactTypeInfo helpers for declaring artifact parameters and pre-loading their content |
context_facade.py |
SandboxToolContextFacade — task_id, session_id, artifact APIs, status updates, and LLM callbacks available to tools at runtime |
tool_options.py |
tool_timeout, with_dynamic_schema, with_volume_params, VolumeParam, VolumeMount decorators for per-tool sandbox options |
config_schema.py |
ConfigSchemaField, with_config_schema — declare operator-supplied config (API keys, endpoints, defaults) rendered by the platform UI |
ipc.py |
IPCClient, IPCError — JSON-RPC client for the sandbox-to-host LLM callback channel |
tool_runner.py |
Sandbox entry point invoked by the Go STR; ordinarily reached via the tool_cli-built console script rather than directly |
Installation
pip install sam-tool-sdk
The only runtime dependency is pydantic>=2.4.
For local development against the source checkout:
pip install -e .
(run from the python/sam-tool-sdk/ directory of solace-agent-mesh-go)
Quick Start
Function-based tool with tool_cli
The canonical pattern. Author a function, wrap it with tool_cli, expose the resulting callable as a console script via pyproject.toml. STR invokes the console script directly — --schema for tool discovery, with a runner_args.json path for execution.
# greet_tool.py
from sam_tool_sdk import tool_cli, ToolResult, SandboxToolContextFacade
async def greet(name: str, excited: bool = False, ctx: SandboxToolContextFacade = None) -> ToolResult:
"""Greet someone by name.
Args:
name: who to greet
excited: add an exclamation mark
"""
if ctx is not None:
ctx.send_status(f"Greeting {name}...")
suffix = "!" if excited else "."
return ToolResult.ok(message=f"Hello, {name}{suffix}", data={"greeted": name})
cli = tool_cli(greet)
# pyproject.toml
[project]
name = "sam-tool-greet"
version = "0.1.0"
dependencies = ["sam-tool-sdk>=0.1,<0.2"]
[project.scripts]
greet = "greet_tool:cli"
The framework detects parameter type annotations (str, bool, int, Artifact, SandboxToolContextFacade, …), parses the docstring's Args: block for per-parameter descriptions, and emits a JSON Schema the LLM sees via --schema. The SandboxToolContextFacade-annotated parameter is injected at runtime; everything else becomes a tool argument.
Class-based tool (DynamicTool)
For more control over the tool's schema and behavior:
from sam_tool_sdk import DynamicTool, ToolResult
class WordCounter(DynamicTool):
@property
def tool_name(self):
return "count_words"
@property
def tool_description(self):
return "Counts the number of words in the given text."
@property
def parameters_schema(self):
return {
"type": "object",
"properties": {
"text": {"type": "string", "description": "The text to count words in"},
},
"required": ["text"],
}
async def _run_async_impl(self, args, tool_context=None, credential=None):
count = len(args["text"].split())
return ToolResult.ok(f"Found {count} words", data={"count": count}).model_dump()
Provider-based tools (DynamicToolProvider)
When you want to define multiple related tools from a single class:
from sam_tool_sdk import DynamicToolProvider
class MathTools(DynamicToolProvider):
@MathTools.register_tool
async def add(self, a: float, b: float) -> dict:
"""Add two numbers."""
return {"text": str(a + b)}
@MathTools.register_tool
async def multiply(self, a: float, b: float) -> dict:
"""Multiply two numbers."""
return {"text": str(a * b)}
def create_tools(self, tool_config=None):
return [] # All tools defined via @register_tool
Declaring operator config (config_schema)
Tools that need operator-supplied configuration (API keys, endpoints, default channels, …) declare it with @with_config_schema so the platform can render a config form, validate per-agent overrides, and enforce required fields at deploy time. The wire format matches the Go SDK's ConfigSchemaField exactly — --schema output is interchangeable between the two SDKs.
from sam_tool_sdk import ConfigSchemaField, with_config_schema
@with_config_schema([
ConfigSchemaField(
key="slack_bot_token",
type="string",
description="Slack Bot OAuth token (xoxb-...).",
required=True,
secret=True,
),
ConfigSchemaField(key="default_channel", type="string"),
ConfigSchemaField(key="api_url", type="string"),
])
async def post_message(tool_config, channel: str, text: str):
token = tool_config["slack_bot_token"]
...
Values are resolved at deploy time with precedence: agent override > toolset default > schema default > 422 if a required field is unset. Fields marked secret=True are redacted on API responses and rendered with a password input in the UI.
For DynamicTool subclasses, override the config_schema property to return a list of ConfigSchemaField(...).to_dict() entries.
Working with artifacts
Tools can receive and produce artifacts using type annotations:
from sam_tool_sdk import Artifact, ToolResult, DataObject, DataDisposition
async def summarize_file(doc: Artifact) -> ToolResult:
"""Summarize the contents of an uploaded document."""
text = doc.as_text()
summary = text[:200] + "..." # placeholder for real summarization
return ToolResult.ok(
message=f"Summarized {doc.filename}",
data={"summary": summary},
data_objects=[
DataObject(
name="summary.txt",
content=summary,
mime_type="text/plain",
disposition=DataDisposition.ARTIFACT,
description="Document summary",
)
],
)
Per-tool sandbox options
Decorate a tool_cli function (or override the matching DynamicTool properties) to declare timeouts and volume requirements that STR honors at execution time:
from sam_tool_sdk import tool_cli, tool_timeout, with_volume_params, VolumeParam, ToolResult
@tool_timeout(seconds=120)
@with_volume_params([
VolumeParam(name="workspace", mount_path="/workspace", mode="readwrite"),
])
async def transform(input_path: str, ctx=None) -> ToolResult:
"""Long-running transformation that needs scratch space."""
...
cli = tool_cli(transform)
tool_timeout overrides the per-invocation timeout; with_volume_params declares mount points STR provisions before the tool runs (paths surface via ctx.volume_mounts / ctx.get_volume_mount_path("/workspace"), keyed by mount path).
Packaging
A SAM tool ships as an AWS-Lambda-Layer-style directory: python/bin/<console-script> plus all dependencies pre-extracted under python/. STR sets PYTHONPATH=<toolDir>/python:<toolDir> when launching the console script, so the interpreter finds sam_tool_sdk (and everything else) without a virtualenv.
Build that layout with a single pip install --target invocation:
pip install --target dist/python \
--platform manylinux2014_x86_64 \
--python-version 3.13 \
--only-binary=:all: \
.
The directory then contains dist/python/bin/<your-script>, dist/python/sam_tool_sdk/, dist/python/pydantic/, and your own package. Pair it with a manifest.yaml declaring executable: python/bin/<your-script> and STR will discover, schema-probe, and invoke the tool.
The three production Python tools under tools/python/ (plotly, pdf_to_markdown, python_executor) are working references for the full layout.
Relationship to solace_agent_mesh
This SDK is a standalone extraction of the tool execution path from the solace_agent_mesh Python package. Tools written with sam_tool_sdk are wire-compatible with both the Go and Python SAM runtimes — the JSON-based communication protocol (runner_args.json, result.json, status pipe) is identical.
Key difference: parameters_schema
In solace_agent_mesh, DynamicTool.parameters_schema returns a google.genai.types.Schema object. In sam_tool_sdk, it returns a standard JSON Schema dict:
# solace_agent_mesh (old)
from google.genai import types as adk_types
@property
def parameters_schema(self):
return adk_types.Schema(
type=adk_types.Type.OBJECT,
properties={"name": adk_types.Schema(type=adk_types.Type.STRING)},
required=["name"],
)
# sam_tool_sdk (new)
@property
def parameters_schema(self):
return {
"type": "object",
"properties": {"name": {"type": "string"}},
"required": ["name"],
}
Migrating from solace_agent_mesh
- Replace imports:
from solace_agent_mesh.agent.tools.dynamic_tool import DynamicToolbecomesfrom sam_tool_sdk import DynamicTool - Change
parameters_schemato return a JSON Schema dict instead ofadk_types.Schema - Replace
ToolContextFacadeannotations withSandboxToolContextFacade(or just use the string name — both are detected)
How It Works
At discovery time, STR runs <your-console-script> --schema to learn the tool's name, description, parameter JSON Schema, declared timeout, and volume requirements. At invocation time, STR runs <your-console-script> <path-to-runner_args.json> — the console script (built by tool_cli) reads the args, imports your tool function or class, injects SandboxToolContextFacade plus any pre-loaded Artifact parameters, awaits the result, and writes result.json for STR to read back.
Full sequence for a single invocation:
- STR receives an A2A
sam_remote_tool/invokerequest over the broker. - STR pre-loads any
Artifact-typed parameters into a work directory. - STR writes
runner_args.json(arguments, artifact metadata, status pipe path, IPC socket path, output paths, …). - STR spawns
<your-console-script> /path/to/runner_args.jsonwithPYTHONPATHset to include the toolset'spython/directory. - The console script (this SDK) imports the tool, detects type annotations, injects context + artifacts, and calls the tool.
- The tool writes status updates to the named pipe (
ctx.send_status(...)) and optionally calls back to the host for LLM completions over the IPC socket (ctx.call_llm(...)). - The tool returns a
ToolResultor a dict; the SDK serialises anyDataObjectoutputs to the artifact directory and writesresult.json. - STR reads
result.jsonand the artifact directory, then publishes the A2Asam_remote_tool/response.
License
Apache-2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sam_tool_sdk-0.1.0.tar.gz.
File metadata
- Download URL: sam_tool_sdk-0.1.0.tar.gz
- Upload date:
- Size: 57.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
70cbe094ea5b3c0cbd9f4255467cf8c0057c5bc8a71b5a82bfc32977e01cfbe9
|
|
| MD5 |
0be9ec0a97858626ff42febfe4ebdbd5
|
|
| BLAKE2b-256 |
747c68bd6ca45bf8479d92a2a2f57f7320d02c22941ada087b657967bb514aa3
|
Provenance
The following attestation bundles were made for sam_tool_sdk-0.1.0.tar.gz:
Publisher:
python-sdk-release.yml on SolaceDev/solace-agent-mesh-go
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sam_tool_sdk-0.1.0.tar.gz -
Subject digest:
70cbe094ea5b3c0cbd9f4255467cf8c0057c5bc8a71b5a82bfc32977e01cfbe9 - Sigstore transparency entry: 1661038061
- Sigstore integration time:
-
Permalink:
SolaceDev/solace-agent-mesh-go@57a7b49a4d2f03a051cafbdf934c17730d831940 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/SolaceDev
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-sdk-release.yml@57a7b49a4d2f03a051cafbdf934c17730d831940 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file sam_tool_sdk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: sam_tool_sdk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 53.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
61bf6b119e2f0977c3b06e5328fa5af5321abede87d7dfbb71d3a8d14b09638c
|
|
| MD5 |
192a537bd894d6d84f334563f74885fa
|
|
| BLAKE2b-256 |
f5bf678ebb233d3e595cc0f14102cbe3e52fcb082ba9ea2f784fbfb0eb5ef8d4
|
Provenance
The following attestation bundles were made for sam_tool_sdk-0.1.0-py3-none-any.whl:
Publisher:
python-sdk-release.yml on SolaceDev/solace-agent-mesh-go
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sam_tool_sdk-0.1.0-py3-none-any.whl -
Subject digest:
61bf6b119e2f0977c3b06e5328fa5af5321abede87d7dfbb71d3a8d14b09638c - Sigstore transparency entry: 1661038289
- Sigstore integration time:
-
Permalink:
SolaceDev/solace-agent-mesh-go@57a7b49a4d2f03a051cafbdf934c17730d831940 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/SolaceDev
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-sdk-release.yml@57a7b49a4d2f03a051cafbdf934c17730d831940 -
Trigger Event:
workflow_dispatch
-
Statement type: