Simple agent SDK

Your agent,
ready to ship.

Any model. Built-in tools, memory, and MCP. Serve locally or publish to the Genfleet marketplace — all from one package.

pip install "genfleet-sdk[openai,serve] @ git+https://github.com/genfleet/sdk@dev"

Installation

Install the SDK with pip or uv, from GitHub: it is not on PyPI yet. Core depends only on Pydantic — extras add provider clients and the A2A server. Pin a tag or commit instead of @dev for a fixed version.

shell
# Core (pydantic only) pip install "genfleet-sdk @ git+https://github.com/genfleet/sdk@dev" # With OpenAI + A2A server pip install "genfleet-sdk[openai,serve] @ git+https://github.com/genfleet/sdk@dev" # With Anthropic + A2A server pip install "genfleet-sdk[anthropic,serve] @ git+https://github.com/genfleet/sdk@dev" # Everything pip install "genfleet-sdk[all] @ git+https://github.com/genfleet/sdk@dev"
ExtraInstallsUse for
openaiopenai>=1.0OpenAI + any OpenAI-compatible endpoint (DeepSeek, Groq, Ollama…)
anthropicanthropic>=0.25Anthropic Claude models
geminigoogle-genai>=1.0Google Gemini models
memoryredis>=5.0Redis-backed session memory
mcpmcp>=1.0MCP server connections
servefastapi, uvicorn, sse-starletteA2A HTTP server
allall of the aboveFull install
Requires Python 3.12+. Provider libraries are lazy-imported — missing extras raise a clear ImportError with install instructions.

Agent Skill

Install the genfleet-sdk skill so your coding agent understands the Agent API and can help you build agents faster.

shell
npx skills add genfleet/sdk
Build agents
Ask your agent to create an Agent() with tools, MCP servers, or memory — it uses current SDK patterns.
Debug issues
Your coding agent knows the Agent constructor, provider config rules, tool schemas, and A2A formats.
Add tools
Your coding agent uses @tool correctly with proper type hints and JSON Schema generation.
Serve & deploy
Your coding agent knows create_app() vs serve(), A2A protocol, and how to test with curl.

Quickstart

Create an Agent, give it a model and a tool, then serve it over A2A.

quickstart.py
import os from genfleet.sdk import Agent from genfleet.sdk.serve import serve def get_weather(city: str) -> str: """Gets the current weather for a city.""" return f"72°F and sunny in {city}" agent = Agent( role="You are a helpful assistant.", model={"model": "openai/gpt-4o-mini", "api_key": os.environ["OPENAI_API_KEY"]}, tools=[get_weather], ) serve(agent, name="my-agent", port=8000)
shell
# Start the server python quickstart.py # Call it curl -X POST http://localhost:8000 \ -H "Content-Type: application/json" -H 'A2A-Version: 1.0' \ -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{"message":{"messageId":"m1","role":"ROLE_USER","parts":[{"text":"What is the weather in Paris?"}]}}}'

Core Concepts

The SDK defines the contract between your agent code and the Genfleet platform.

Agent
The main class. Wires the provider, tool dispatch loop, session memory, and MCP connections automatically.
AgentProtocol
Universal agent contract. Implement run() for framework integrations that manage their own LLM calls.
ToolProtocol
Expose functions as tools the agent can call. Use @tool or pass plain functions to tools=.
@tool Decorator
Turn any function into a ToolProtocol with auto-generated JSON schema from type hints and docstrings.
Plain functions passed to Agent(tools=[...]) are auto-wrapped — @tool is optional. Use it when you want an explicit name or description.

Agent

The main developer-facing class. Implements AgentProtocol — pass it directly to serve().

ClassAgent

Wires together a provider, tool dispatch loop, optional Redis memory, and optional MCP connections. The tool loop is built-in: the agent calls tools, feeds results back to the LLM, and repeats until a text response is produced.

python
Agent( role: str, # system prompt / persona model: ModelConfig, # provider + credentials dict tools: list[Callable] = [], # plain functions — auto-wrapped mcps: list[MCPConfig] = [], # MCP server connections memory: MemoryConfig | "platform" | None = None, # episodic memory (platform store or Redis) context: str | None = None, # extra context appended to system prompt audit: AuditConfig | None = None, # structured audit logging )

ModelConfig

Provider credentials as a plain dict. Use the provider/model-name format to specify the provider explicitly.

python
# OpenAI {"model": "openai/gpt-4o-mini", "api_key": "sk-..."} # Anthropic {"model": "anthropic/claude-sonnet-4-6", "api_key": "sk-ant-..."} # Google Gemini {"model": "gemini/gemini-1.5-pro", "api_key": "..."} # Any OpenAI-compatible endpoint (DeepSeek, Groq, Ollama, …) {"model": "openai/deepseek-chat", "api_key": "sk-...", "base_url": "https://api.deepseek.com"}
Provider prefixProviderRequired extra
openai/OpenAI (+ any compatible endpoint)[openai]
anthropic/Anthropic[anthropic]
gemini/Google Gemini[gemini]
openai/ + base_urlOpenAI-compatible[openai]

MemoryConfig

Redis-backed session memory. History is loaded at the start of each run() and saved at the end. Requires pip install 'genfleet-sdk[memory] @ git+https://github.com/genfleet/sdk@dev'.

python
{ "type": "redis", "connection": "redis://localhost:6379/0", "user": "optional", "password": "optional", }

Session ID from input.metadata["session_id"]. Falls back to a per-request UUID when absent. History stored with a 24-hour TTL.

shell — passing session_id
curl -X POST http://localhost:8000 \ -H "Content-Type: application/json" -H 'A2A-Version: 1.0' \ -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{"message":{"messageId":"m1","role":"ROLE_USER","contextId":"user-123","parts":[{"text":"My name is Alice"}]}}}'

MCPConfig

Connect to MCP servers. Tool schemas are fetched on the first run() call and merged with local tools. Requires pip install 'genfleet-sdk[mcp] @ git+https://github.com/genfleet/sdk@dev'.

python
# stdio — spawns a subprocess { "type": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"], } # SSE — connects to a running HTTP server { "type": "sse", "url": "http://localhost:3001/sse", }

AuditConfig

Opt-in structured audit logging. Captures invocation lifecycle, LLM calls, tool dispatch, token usage, and errors. No overhead when audit=None (the default).

python — console backend
agent = Agent( role="You are a helpful assistant.", model={"model": "openai/gpt-4o-mini", "api_key": os.environ["OPENAI_API_KEY"]}, audit={"backend": "console", "agent_name": "my-agent"}, )
python — file backend (JSONL)
audit={"backend": "file", "file_path": "audit.jsonl"}
python — callback backend
async def my_handler(event): print(event.event_type, event.data) audit={"backend": "callback", "callback": my_handler}
KeyTypeDefaultDescription
backendstrrequired"console", "file", or "callback"
file_pathstr"audit.jsonl"Output path (file backend only)
callbackCallablerequired for callbackSync or async handler function
max_content_lengthint500Truncation limit for content in events (0 = unlimited)
eventslist[str]allFilter which event types to emit
agent_namestrNoneName tag included in every event

Audit Events

EventWhenKey data
invocation.startrun() calledmessage, session_id
llm.requestBefore LLM callmodel, message_count, tool_count
llm.responseAfter LLM respondstoken_usage, latency_ms
tool.callBefore tool dispatchtool_name, arguments
tool.resultAfter tool returnstool_name, result, latency_ms
invocation.endrun() completestotal_latency_ms, total_token_usage
invocation.errorException in run()error_type, message

Token usage (input/output/total tokens) is tracked automatically for OpenAI, Anthropic, and Gemini providers.

AgentProtocol

The universal agent contract. Use this for framework integrations that manage their own LLM calls. Agent already implements it.

ProtocolAgentProtocol

Any object with a matching run method satisfies the protocol. No inheritance needed.

protocol.py
@runtime_checkable class AgentProtocol(Protocol): def run(self, input: AgentInput) -> AsyncIterator[AgentOutput]: ...

Direct implementation

my_agent.py
from genfleet.sdk import AgentInput, AgentOutput, AgentProtocol from genfleet.sdk.serve import serve class MyAgent: async def run(self, input: AgentInput): yield AgentOutput(content=f"Hello! You said: {input.message}", done=True) assert isinstance(MyAgent(), AgentProtocol) # passes serve(MyAgent(), name="my-agent", port=8000)

ToolProtocol

The contract for tools the agent can invoke.

ProtocolToolProtocol

Any object with schema() and call() satisfies this protocol.

protocol.py
@runtime_checkable class ToolProtocol(Protocol): def schema(self) -> ToolSchema: ... async def call(self, tool_call: ToolCall) -> str: ...

Schemas

Pydantic models forming the data contract between agents, tools, and the platform.

ModelAgentInput

Input to every agent invocation.

FieldTypeDefaultDescription
messagestrrequiredThe user's message
historylist[Message][]Conversation history
tool_schemaslist[ToolSchema][]Caller-supplied tool schemas
metadatadict[str, Any]{}session_id key used by Redis memory
ModelAgentOutput

A single chunk yielded by an agent's run().

FieldTypeDefaultDescription
contentstr | NoneNoneText content of this chunk
tool_callslist[ToolCall][]Tool calls the agent wants to make
doneboolFalseWhether this is the final chunk
metadatadict[str, Any]{}Arbitrary output metadata

@tool Decorator

Turn any function into a ToolProtocol with one line. Schema auto-generated from type hints and docstring.

Decorator@tool

Returns a ToolWrapper. Works with sync and async functions.

python
from genfleet.sdk import tool # Bare — schema from type hints + docstring @tool async def search(query: str, limit: int = 10) -> str: """Searches the web for a query.""" return f"Results for {query}" # With explicit name / description @tool(name="add", description="Adds two numbers") def add_numbers(a: float, b: float) -> str: return str(a + b)

Type mappings

PythonJSON Schema
str{"type": "string"}
int{"type": "integer"}
float{"type": "number"}
bool{"type": "boolean"}
list[str]{"type": "array", "items": {"type": "string"}}
dict{"type": "object"}

ToolWrapper

The object returned by @tool. Satisfies ToolProtocol — call directly or via a ToolCall.

ClassToolWrapper

Wraps any sync or async callable with a JSON schema and ToolProtocol interface.

python
from genfleet.sdk import tool, ToolCall @tool def greet(name: str) -> str: """Says hello.""" return f"Hello, {name}!" # Via ToolCall (how the platform dispatches) result = await greet.call(ToolCall(id="1", name="greet", arguments={"name": "World"})) # Direct call result = await greet(name="World")

A2A Server

Serve any AgentProtocol as an A2A-compliant HTTP endpoint. Requires [serve] extra.

MethodPathDescription
GET/.well-known/agent-card.jsonAgent card
POST/JSON-RPC 2.0, A2A v1.0 (SendMessage, SendStreamingMessage; the 0.x tasks/send and tasks/sendSubscribe still work for one minor release)

create_app()

Functioncreate_app(agent, *, name, description) → FastAPI

Returns a FastAPI app for custom ASGI deployments.

python
from genfleet.sdk import Agent from genfleet.sdk.serve import create_app app = create_app( Agent(role="You are helpful.", model={"model": "openai/gpt-4o-mini", "api_key": "sk-..."}), name="my-agent", ) # uvicorn myfile:app --port 8000

serve()

Functionserve(agent, *, name, description, host, port)

Blocking one-liner. Calls uvicorn internally. Best for scripts and local dev.

ParameterTypeDefaultDescription
agentAgentProtocolrequiredThe agent to serve
namestr"agent"Agent name
descriptionstr""Agent description
hoststr"0.0.0.0"Bind address
portint8000Bind port

Example: OpenAI Agent

Minimal Agent with a tool using an OpenAI model.

openai_agent.py
import os from genfleet.sdk import Agent from genfleet.sdk.serve import serve def get_weather(city: str) -> str: """Gets the current weather for a city.""" return f"72°F and sunny in {city}" agent = Agent( role="You are a helpful assistant.", model={"model": "openai/gpt-4o-mini", "api_key": os.environ["OPENAI_API_KEY"]}, tools=[get_weather], ) serve(agent, name="openai-agent", port=8000)

Example: Anthropic Agent

Claude-powered agent — provider auto-detected from the claude- prefix.

anthropic_agent.py
import os from genfleet.sdk import Agent from genfleet.sdk.serve import serve def lookup(topic: str) -> str: """Looks up a fact in the knowledge base.""" return f"Here's what we know about {topic}: it's fascinating." agent = Agent( role="You are a helpful assistant.", model={"model": "anthropic/claude-sonnet-4-6", "api_key": os.environ["ANTHROPIC_API_KEY"]}, tools=[lookup], ) serve(agent, name="anthropic-agent", port=8002)

Example: Session Memory

Persistent multi-turn conversations backed by Redis.

memory_agent.py
import os from genfleet.sdk import Agent from genfleet.sdk.serve import serve agent = Agent( role="You are a friendly assistant with a long memory.", model={"model": "openai/gpt-4o-mini", "api_key": os.environ["OPENAI_API_KEY"]}, memory={ "type": "redis", "connection": os.environ.get("REDIS_URL", "redis://localhost:6379/0"), "password": os.environ.get("REDIS_PASSWORD"), }, ) serve(agent, name="memory-agent", port=8000)
shell — two-turn session test
# Turn 1 curl -X POST http://localhost:8000 \ -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{"message":{"messageId":"m1","role":"ROLE_USER","contextId":"demo","parts":[{"text":"My name is Alice"}]}}}' # Turn 2 — agent recalls the name curl -X POST http://localhost:8000 \ -d '{"jsonrpc":"2.0","id":2,"method":"SendMessage","params":{"message":{"messageId":"m2","role":"ROLE_USER","contextId":"demo","parts":[{"text":"What is my name?"}]}}}'

Example: MCP Server

Connect to an MCP server. Tool schemas are fetched on first call and merged with local tools.

mcp_agent.py
import os from genfleet.sdk import Agent from genfleet.sdk.serve import serve def read_file(path: str) -> str: """Reads a local file.""" with open(path) as f: return f.read() agent = Agent( role="You are a helpful file assistant.", model={"model": "openai/gpt-4o-mini", "api_key": os.environ["OPENAI_API_KEY"]}, tools=[read_file], # local tools mcps=[{ # MCP tools — discovered on first run() "type": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"], }], ) serve(agent, name="mcp-agent", port=8000)

Example: LangChain Agent

LangChain manages its own LLM calls — implement AgentProtocol directly.

langchain_agent.py
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage from langchain_core.tools import tool as lc_tool from langchain_openai import ChatOpenAI from genfleet.sdk import AgentInput, AgentOutput from genfleet.sdk.serve import serve @lc_tool def calculator(expression: str) -> str: """Evaluates a math expression.""" return str(eval(expression)) llm = ChatOpenAI(model="gpt-4o-mini").bind_tools([calculator]) class LangChainAgent: async def run(self, input: AgentInput): messages = [SystemMessage(content="You are a helpful assistant.")] for msg in input.history: messages.append( HumanMessage(content=msg.content) if msg.role == "user" else AIMessage(content=msg.content or "") ) messages.append(HumanMessage(content=input.message)) response = await llm.ainvoke(messages) if response.tool_calls: yield AgentOutput( tool_calls=[{"id": tc["id"], "name": tc["name"], "arguments": tc["args"]} for tc in response.tool_calls], done=False, ) else: yield AgentOutput(content=response.content, done=True) serve(LangChainAgent(), name="langchain-agent", port=8003)

Example: CrewAI Agent

CrewAI manages its own orchestration — implement AgentProtocol directly and wrap the blocking call.

crewai_agent.py
import asyncio from crewai import Agent as CrewAgent, Crew, Task from genfleet.sdk import AgentInput, AgentOutput from genfleet.sdk.serve import serve researcher = CrewAgent( role="Senior Research Analyst", goal="Provide thorough research on any topic", backstory="You are an experienced research analyst.", verbose=False, ) class CrewAIAgent: async def run(self, input: AgentInput): task = Task(description=input.message, expected_output="Clear analysis", agent=researcher) crew = Crew(agents=[researcher], tasks=[task], verbose=False) # kickoff() is synchronous — wrap to avoid blocking the event loop result = await asyncio.to_thread(crew.kickoff) yield AgentOutput(content=str(result), done=True) serve(CrewAIAgent(), name="crewai-agent", port=8004)

Publish on Genfleet (private beta)

From an empty directory to chatting with your own agent on the platform: install, write an agent with a tool, test it locally, push it, run it in your workspace, chat. Every command here was checked against the SDK and the gf CLI on 2026-09-30.

Before you start, from whoever invited you: the platform URL (below, $GENFLEET_API), read access to the genfleet/cli repository, and your account from the invitation. Signing up makes you the owner of your own workspace, and that is all gf agents push needs: you need no platform role.

1. Install

Python 3.12 or newer and uv. The SDK and the CLI are installed from GitHub; neither is on PyPI yet.

shell
# the gf CLI (log in, push agents) uv tool install git+https://github.com/genfleet/cli gf --help export GENFLEET_API=https://… # the platform URL from your invitation

2. Write an agent with a tool

An agent is a directory with a Python package inside it. The manifest, genfleet.toml, sits inside the package, next to agent.py:

layout
hello-agent/ pyproject.toml hello_agent/ __init__.py genfleet.toml # manifest: name, kind, entry agent.py # build_agent() factory + the tool __main__.py # serve it locally tests/ test_agent.py
hello-agent/pyproject.toml
[project] name = "hello-agent" version = "0.1.0" requires-python = ">=3.12" dependencies = ["genfleet-sdk[openai,serve] @ git+https://github.com/genfleet/sdk@dev"] [dependency-groups] dev = ["pytest"] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.metadata] allow-direct-references = true [tool.hatch.build.targets.wheel] packages = ["hello_agent"]
hello-agent/hello_agent/genfleet.toml
[agent] name = "hello-agent" kind = "code" description = "Says hello and counts words." entry = "hello_agent.agent:build_agent"
hello-agent/hello_agent/agent.py
import os from genfleet.sdk import Agent, tool # `openrouter/` is the SDK's OpenRouter prefix: one key, any vendor's model. # A `:free` model costs nothing; free ids rotate, so swap in another `:free` # model that supports tool calling if this one stops resolving. MODEL = "openrouter/nvidia/nemotron-3.5-lightning:free" @tool(description="Counts the words in a piece of text.") def count_words(text: str) -> str: return str(len(text.split())) def build_agent(api_key: str | None = None) -> Agent: # A factory, not a module-level Agent(): the package stays importable # without a key, so tests can build it with a dummy one. key = api_key or os.environ.get("OPENROUTER_API_KEY") if not key: raise RuntimeError("Set OPENROUTER_API_KEY (on the platform: a secret, see step 5).") return Agent( role="You are a friendly assistant. Use count_words when asked to count words.", model={"model": MODEL, "api_key": key}, tools=[count_words], )
hello-agent/hello_agent/__init__.py and __main__.py
# __init__.py from .agent import build_agent __all__ = ["build_agent"] # __main__.py from genfleet.sdk.serve import serve from .agent import build_agent if __name__ == "__main__": serve(build_agent(), name="hello-agent", port=8000)
On the platform your agent runs in a sandbox that has the SDK (with the openai, anthropic, gemini and serve extras) and the standard library — nothing else. The dependencies in your pyproject.toml are for running locally; they are not installed on the platform. Keep tools as plain Python in your package, as above.

3. Test it locally

A test that needs no network and no key:

hello-agent/tests/test_agent.py
import asyncio from genfleet.sdk import ToolCall from hello_agent.agent import build_agent, count_words def test_builds_without_a_real_key(): assert build_agent(api_key="test") is not None def test_count_words(): call = ToolCall(id="1", name="count_words", arguments={"text": "one two three"}) assert asyncio.run(count_words.call(call)) == "3"
shell
cd hello-agent uv sync uv run pytest -q # 2 passed export OPENROUTER_API_KEY=sk-or-v1-… # a free OpenRouter key works uv run python -m hello_agent # A2A server on :8000

In another shell:

shell
curl -s localhost:8000/.well-known/agent-card.json curl -s -X POST localhost:8000 -H 'Content-Type: application/json' -H 'A2A-Version: 1.0' -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{"message":{"messageId":"m1","role":"ROLE_USER","parts":[{"text":"How many words are in: the quick brown fox?"}]}}}'

4. Log in and push

gf agents push takes the agent directory (the one with pyproject.toml), finds the manifest one level in, archives the directory deterministically and uploads it. It prints the archive's digest; the platform stores the same bytes under the same digest, and the CLI refuses a mismatch.

shell
gf login --api "$GENFLEET_API" # prompts for email and password gf whoami # the account and workspace you are acting as gf agents digest hello-agent # optional: the digest, computed offline gf agents push hello-agent # ... stored sha256:<64 hex>

Pushing stores the code; it does not create an agent yet. It prints the gf agents create command to run next. You can also do both in one step (next section).

5. Run it in your workspace

First the model key. In the web app, open Secrets and create a secret named OPENROUTER_API_KEY. The agent only receives it because its spec references it (below); a secret nothing references reaches no agent.

Then create the agent from the pushed code, reading that secret:

shell
gf agents create sha256:<the digest from gf agents push> \ --name hello-agent --secret OPENROUTER_API_KEY # or push, create and start it in one go: gf agents push hello-agent --create --secret OPENROUTER_API_KEY --activate

--secret OPENROUTER_API_KEY passes a reference, not your key. The agent's spec gets "env": {"OPENROUTER_API_KEY": "$OPENROUTER_API_KEY"}, and the platform replaces it with the value of your secret of that name each time the agent starts. The key never passes through gf. With --create, the name and description come from genfleet.toml. Use --name to override the name.

Tools from the marketplace need approval first. The count_words tool above is plain Python inside your package, so it runs as soon as the agent does. A tool your manifest names under [tools] (for example "@acme/jira" = "1.x") is different. It is pinned when the agent is submitted, and it is mounted into the sandbox only after a Genfleet reviewer approves the submission. Until then the agent runs without it. For the beta, keep tools in your package.
Declare the hosts your agent calls. A hosted agent reaches the internet only through the platform's egress proxy. List the hosts it needs in [agent], for example egress = ["api.github.com", "*.googleapis.com"]. A tool declares its own under [tool]. Each entry is a hostname, optionally starting with *. and optionally ending with :port (443 by default); IP addresses are refused. For now the proxy only records where each agent goes. Once enforcement is on, it refuses hosts that no list allows.

6. Chat with it

Start the agent's sandbox (skip this if you passed --activate), then send it a message:

shell
gf agents activate hello-agent gf agents chat hello-agent "How many words are in: the quick brown fox?"

Or use the web app. Open Agents, find hello-agent and choose Activate. Then open Chat, pick hello-agent and ask it to count some words. Either way, the answer comes from your count_words tool.

Changed the code? Push again and create the agent under a new name (or delete the old one first). To offer it to other workspaces, submit it to the marketplace from the agent's page; a Genfleet reviewer approves it before it is listed.

When something goes wrong

  • gf agents push or create answers 403: your role in the workspace is member. Pushing and creating agents need owner, admin or developer. Ask the workspace owner.
  • gf agents create prints “saved but not loaded”: the agent row exists, but the engine could not load it. The message says why. Fix the cause, delete the agent in the web app, and create it again.
  • The agent will not activate — check that the secret named in spec.env exists, and that your code imports nothing beyond the SDK and the standard library (step 2).
  • An error shows an error id or trace id — include it when you ask for help; it is how we find the failure in our logs.

Where to get help

Reply to your invitation email, or contact the person who invited you. SDK bugs and questions: github.com/genfleet/sdk/issues (public, so never paste keys or private code there).

All Exports

Everything exported from genfleet.sdk.

python
from genfleet.sdk import ( # Core Agent, # Main class — declare and run agents AgentProtocol, # Protocol — implement run() for custom integrations ToolProtocol, # Protocol — implement schema() + call() AgentFactory, # Type alias — callable returning AgentProtocol # Schemas AgentInput, # Model — agent invocation input AgentOutput, # Model — single output chunk Message, # Model — conversation history entry ToolCall, # Model — tool invocation request ToolSchema, # Model — tool JSON schema TokenUsage, # Model — token counts (input, output, total) AuditEvent, # Model — structured audit event # Config TypedDicts ModelConfig, # Provider + credentials MemoryConfig, # Memory config — platform store or Redis MCPConfig, # MCP server connection config AuditConfig, # Audit backend config # Tools ToolWrapper, # Class — wraps callable as ToolProtocol tool, # Decorator — function → ToolWrapper ) # A2A serve module (requires [serve] extra) from genfleet.sdk.serve import create_app, serve