Skip to Content
MarshalSDKClient usage

Client usage

Examples below match the published SDKs:

Set EXEMPLAR_API_KEY, then:

from exemplar_harness import Harness harness = Harness.from_env()

Memory — add, search, and recall

At least one scope is required on create (user_id / userId, agent_id / agentId, session_id / sessionId, or app_id / appId).

ParamValuesNotes
search_mode / searchModehybrid · semantic · keywordOmit to use the server default (hybrid when enabled)
formatjson · json_compact · markdown · toonrecall defaults to markdown; search defaults to json
memory = harness.memory(user_id="user-123", session_id="chat-abc", app_id="my-app") memory.add("User prefers bullet-point answers.", memory_type="preference") # Prompt-ready string (server-rendered body when available) context = memory.recall( "how should I format answers?", format="markdown", search_mode="hybrid", ) # Structured hits results = memory.search( "formatting preferences", top_k=5, search_mode="hybrid", format="json", ) # Records + rendered body (toon / markdown / json_compact) envelope = memory.search_envelope("theme settings", format="toon", search_mode="hybrid") prompt_block = envelope.body # inject into the system prompt listed = memory.list(limit=20) record = memory.get(listed[0].memory_id) memory.update(record.memory_id, content="User prefers numbered lists.") memory.delete(record.memory_id)

Generic hook for any agent loop (no framework dependency):

from exemplar_harness.integrations.memory import MemoryHook memory = harness.memory(user_id="user-123", session_id="chat-abc", app_id="my-app") hook = MemoryHook(memory, recall_top_k=5, auto_add=False) user_input = "What do you know about me?" recall = hook.before_turn(user_input) # prepend to system prompt # ... run LLM ... hook.after_turn(user_input, assistant_output) # no-op unless auto_add=True

Skills — create, search, install

skills = harness.skills() record = skills.create( name="refund-policy", instructions="# Refund policy\n\nReturns within 30 days.\n\nSee [references/policy.md](references/policy.md).", description="Refund workflow", tags=["support"], files={"references/policy.md": "# Policy\n\n30-day returns.\n"}, ) items = skills.list(limit=20) fetched = skills.get("refund-policy") hits = skills.search("refund", top_k=5) # Materialize SKILL.md + supporting files for agent runtimes skills.install(".agents/skills", names=["refund-policy"])

Prefer skills.install(dest) folders (SKILL.md + files). record.instructions is the markdown body only—useful for quick editor/MCP use, not the full skill package.

HITL — approval gates

Trigger pauses with SDK helpers (hitl.ask, request_approval, framework tools). Operators approve or deny in the dashboard Marshal → HITL Approvals inbox. Blocking wait polls; there are no webhooks. See Human-in-the-loop.

from exemplar_harness import Harness, HITLTimeoutError harness = Harness.from_env(agent_id="deploy-agent") hitl = harness.hitl() try: result = hitl.ask( "Deploy build 1234 to production?", description="All checks green. Blast radius: payments service.", payload={"build": "1234", "service": "payments"}, ttl_seconds=1800, # server-side expiry timeout=1800, # local wait budget (seconds) poll_interval=5, ) except HITLTimeoutError: result = None if result and result.approved: deploy() elif result and result.state == "rejected": print(f"Rejected: {result.response.comment}")

Free-text input, single choice, and non-blocking create/cancel:

answer = hitl.ask("Which rollback strategy?", request_type="input") print(answer.response.text) choice = hitl.ask( "Pick a deployment region", request_type="select", options=["us-east-1", "eu-west-1"], ) print(choice.response.option) request = hitl.request_approval("Rotate production credentials?") status = hitl.get(request.request_id) if status.is_pending: hitl.cancel(request.request_id)

As a tool in any agent framework (wrap the sync ask call):

from langchain_core.tools import tool @tool def ask_human_approval(question: str) -> str: """Ask a human operator to approve or reject an action.""" result = harness.hitl().ask(question, timeout=900) if result.approved: return "approved" return f"denied ({result.state}): {result.response.comment or 'no comment'}"

Prompts — create, build, run

prompts = harness.prompts() record = prompts.create( name="support-summary", title="Support summary", messages=[ {"role": "system", "content": "Be concise."}, {"role": "user", "content": "Summarize topic: {{topic}}."}, ], variables=["topic"], ) result = prompts.run("support-summary", variables={"topic": "returns"}) print(result["content"]) # Local {{var}} substitution for your own agent framework (no Exemplar model call) built = prompts.build("support-summary", variables={"topic": "returns"}) # built.messages -> [{"role": "system", ...}, {"role": "user", ...}]

Publish a new version, or run inline without a stored prompt:

prompts.publish_version( "support-summary", messages=[ {"role": "system", "content": "Be concise."}, {"role": "user", "content": "Bullet summary for: {{topic}}."}, ], change_notes="Use bullets", ) inline = prompts.run_inline( messages=[ {"role": "system", "content": "Be concise."}, {"role": "user", "content": "Say hello."}, ], model="openai/gpt-4o-mini", )

Relay — policy decide / observe / evaluate

harness.relay({ surface }) enforces org Relay Control rules on runtime tool calls (allow / ask / deny). Shares the Harness API key, base URL, and agent_id / agentId. Optional EXEMPLAR_RELAY_BASE overrides the Relay API origin.

Runnable samples: python/relay/ · typescript/relay/ · catalog Live SDK examples.

Surfaces (wire header x-relay-surface): claude_sdk | adk | agno | openai_agents | langchain | langgraph | pydantic_ai | crewai | semantic_kernel | mastra (TypeScript).

AdapterPythonTypeScript
Claude Agent SDK hooksrelay.claude_hooks(...)relay.claudeHooks(...)
OpenAI Agents guardrailsrelay.openai_tool_input / openai_tool_outputrelay.openaiToolInput / openaiToolOutput
LangChain / LangGraph middlewarerelay.langchain_middleware / langgraph_middlewarerelay.langchainMiddleware / langgraphMiddleware
Mastra tool hooksrelay.mastraHooks(...)
Pydantic AI hooksrelay.pydantic_ai_hooks(...)— (Python-only)
CrewAI tool hooksrelay.crewai_hooks / crewai_register— (Python-only)
Semantic Kernel filterrelay.semantic_kernel_filter / semantic_kernel_register— (Python-only)
Agno tool hookrelay.agno_tool_hook(...)— (Python-only)
Google ADK callbacksrelay.adk_before_tool / adk_after_tool— (Python-only)
from exemplar_harness import Harness from exemplar_harness.relay import RelayDecision harness = Harness.from_env(agent_id="support-bot") relay = harness.relay(surface="langchain", source_app="my-app") # LangChain / LangGraph create_agent middleware agent = create_agent( model=..., tools=[...], middleware=[relay.langchain_middleware(session_id="sess-abc")], ) # Pydantic AI # agent = Agent(..., capabilities=[relay.pydantic_ai_hooks(session_id="sess-abc")]) # CrewAI: before, after = relay.crewai_hooks(session_id="sess-abc") # Semantic Kernel: relay.semantic_kernel_register(kernel, session_id="sess-abc") # Agno: tool_hooks=[relay.agno_tool_hook(session_id="sess-abc")] # Hook-free dry-run (CI / preflight) verdict = relay.evaluate( tool_name="shell", arguments={"command": "rm -rf /tmp/x"}, user_id="user_123", ) if verdict.decision is RelayDecision.DENY: raise RuntimeError(verdict.reason)

IDE Connect (Cursor, Claude Code, Codex, OpenCode) still uses Console Relay → Connect / exemplar-skills. SDK Relay is for in-process agent frameworks that share the same Control + Enforcement stack. See Relay and Connect surfaces.

MCP tools

harness.tools() builds framework-native MCP clients wired to the Exemplar MCP server (override with EXEMPLAR_MCP_URL). Live demos use this for Linear and other platform tools.

# Agno — pattern from examples/live/agno_demo.py from agno.agent import Agent from exemplar_harness.integrations.agno import harness_agno_post_hook tools_client = harness.tools() mcp = tools_client.for_provider("agno") await mcp.connect() try: agent = Agent( name="support-bot", model=..., # e.g. Gemini / OpenAIChat tools=[mcp], post_hooks=[ harness_agno_post_hook( harness, session_id="sess-abc", agent_id="support-bot" ) ], ) await agent.arun("Find open Linear issues about session ingest.") finally: await mcp.close()

Other providers expose either a native client (for_provider("langchain"), "claude_agent", …) or a tool schema + execute path (for_provider_tools_schema / execute_provider_tool for OpenAI, Anthropic, LiteLLM, Google GenAI, and similar). Supported names: HarnessTools.supported_providers().

Full recipes for LangChain, Agno, Google ADK, and adapter SDKs—including MCP + A2A mixes—live under Tools & MCP: Frameworks with Marshal SDK.

Session ingest

Direct ingest (no framework)

harness.ingest( "generic", session_id="sess-abc", event="turns", data={ "turns": [ { "input": "What is harness eval?", "output": "Automated judge over agent sessions.", "model": "gpt-4o", } ] }, agent_id="my-agent", source_app="my-app", )

Session helper with auto judge / session eval

Ingest alone stores the session. Set flags to queue Insights (judge) and/or Session Evals after ingest. Full console + SDK trigger guide: Evals.

session = harness.session( "sess-abc", agent_id="support-bot", source_app="my-app", auto_judge_run=True, # Insights (LLM judge) auto_session_eval=True, # per-turn Session Evals ) session.ingest( "generic", event="turns", data={"turns": [{"input": "Hello", "output": "Hi!", "model": "gpt-4o"}]}, ) # Or trigger judge later on existing sessions harness.runs.trigger(session_ids=["sess-abc"], sync=False)

Framework helpers (Python)

Attach an ingest hook so agent turns land in Marshal (and optionally queue Insights / Session Evals). Guide: Evals → Agent hooks.

pip install "exemplar-harness-sdk[agno]"
from agno.agent import Agent from agno.models.openai import OpenAIChat from exemplar_harness import Harness from exemplar_harness.integrations.agno import harness_agno_post_hook harness = Harness.from_env() agent = Agent( name="support-bot", model=OpenAIChat(id="gpt-4o"), post_hooks=[ harness_agno_post_hook( harness, session_id="sess-abc", agent_id="support-bot", source_app="my-app", auto_judge_run=True, auto_session_eval=True, ) ], ) agent.run("Summarize our refund policy.")
pip install "exemplar-harness-sdk[langchain]"
from langchain_openai import ChatOpenAI from exemplar_harness import Harness from exemplar_harness.integrations.langchain import make_langchain_callback_handler harness = Harness.from_env() handler = make_langchain_callback_handler( harness, session_id="sess-abc", chain_name="support-bot", source_app="my-app", auto_judge_run=True, auto_session_eval=True, ) llm = ChatOpenAI(model="gpt-4o-mini", callbacks=[handler]) # Pass the same handler in invoke/ainvoke config={"callbacks": [handler]}
pip install "exemplar-harness-sdk[openai]"
from openai import OpenAI from exemplar_harness import Harness from exemplar_harness.integrations.openai import HarnessOpenAICallback, sdk_chat_completion harness = Harness.from_env() callback = HarnessOpenAICallback( harness, session_id="sess-abc", agent_id="support-bot", source_app="my-app", auto_judge_run=True, auto_session_eval=True, ) client = OpenAI() sdk_chat_completion( harness, callback, client, model="gpt-4o", messages=[{"role": "user", "content": "Summarize our refund policy."}], )

Other first-class extras: [google-adk] (ingest_adk_session(..., auto_judge_run=True)), [claude-agent]. Additional frameworks (LangGraph, CrewAI, LiteLLM, …) are listed in the Python SDK README — Framework extras .

Framework helpers (TypeScript)

npm install @exemplar-dev/exemplar-harness-typescript-sdk ai
import { generateText } from "ai"; import { Harness, ExemplarVercelAISession, } from "@exemplar-dev/exemplar-harness-typescript-sdk"; const harness = Harness.fromEnv({ agentId: "support-bot" }); const session = new ExemplarVercelAISession(harness, { sessionId: "sess-abc", autoJudgeRun: true, autoSessionEval: true, }); const prompt = "Summarize our refund policy."; await generateText({ model: yourModel, prompt, onFinish: session.onFinish(prompt), });
import { Agent, run } from "@openai/agents"; import { Harness, wrapOpenAIAgentsRun, } from "@exemplar-dev/exemplar-harness-typescript-sdk/integrations/openai-agents"; const harness = Harness.fromEnv({ agentId: "support-bot" }); const tracedRun = wrapOpenAIAgentsRun(harness, run, { sessionId: "sess-abc", autoJudgeRun: true, autoSessionEval: true, }); await tracedRun(new Agent({ name: "Assistant", instructions: "Be helpful." }), prompt);

Also available: Anthropic, Claude Agent SDK, LangChain.js / LangGraph.js, Mastra, Google GenAI, LlamaIndex.TS, Portkey. Full matrix: Frameworks with Marshal SDK and the TypeScript SDK README .

Live demos

Full curated catalog: Live SDK examples · Repo: exemplar-platform-samples

git clone https://github.com/Exemplar-Dev/exemplar-platform-samples.git cd exemplar-platform-samples/python pip install -r requirements.txt && cp .env.example .env EXEMPLAR_API_KEY=... python -m platform.skills EXEMPLAR_API_KEY=... python -m platform.prompts EXEMPLAR_API_KEY=... python -m platform.memory EXEMPLAR_API_KEY=... python -m platform.hitl EXEMPLAR_API_KEY=... python -m frameworks.langchain_mcp EXEMPLAR_API_KEY=... python -m relay.evaluate EXEMPLAR_API_KEY=... python -m relay.langchain EXEMPLAR_API_KEY=... python -m relay.openai_agents
FeatureSample
Skills CRUDpython/platform/skills.py
Prompts CRUDpython/platform/prompts.py
Memory CRUDpython/platform/memory.py
HITL approvalspython/platform/hitl.py
Agno + MCPpython/frameworks/agno_mcp.py
LangChain + MCPpython/frameworks/langchain_mcp.py
Relay adapterspython/relay/ · typescript/relay/

TypeScript Relay + ingest: typescript/.

Last updated on