Skip to main content

Tools, execute and send

The session object and the methods that list, run and talk to tools: tools, findTools, execute, proxyExecute, send, sendStream.

2 min read
View MarkdownEdit on GitHub

Session

What create returns. A session is one user's connection to a set of servers; every method below sends that session's id, so the backend can bill, audit and scope each call.

const  = await .("user_0000", { : "brazilian" });

.;     // "ses_..."
.; // "active" | "closed" | "error"
.;    // { url, headers }: the session as a tool endpoint, what @codespar/mcp emits config for

The object create builds also carries userId, servers (the ids the preset expanded to) and createdAt (a Date), but the Session interface in @codespar/types declares only id, status and mcp; reading the other three needs a cast, and the compiled example above stops at what the type knows.

Two of the methods below, tools and findTools, are on the object but not on the Session interface in @codespar/types, so session.tools() does not type-check without a cast. The free functions of the same name reach them by duck typing, take any SessionBase, and are the typed way to call them; the compiled examples below use them.

tools

Readtools(): Promise<Tool[]>
Pythonsession.tools()

Every tool the session can call: the meta-tools plus each connected server's tools. Cached after the first call; connections refreshes the cache.

No parameters.

Example
const  = await ();
for (const  of ) .(., .);
Result: Tool[]
interface Tool {
  /** e.g. "codespar_pay" */
  name: string;
  /** Human-readable, shown to LLMs */
  description: string;
  /** JSON Schema for the tool's input */
  input_schema: Record<string, unknown>;
  /** The server that provides it; "codespar" for meta-tools */
  server: string;
}

Throws nothing on a failed refresh: connections swallows transport errors and this returns the cached list, or [] when there is none.

Related REST GET /v1/sessions/{id}/connections, which carries the tool list.

findTools

ReadfindTools(intent: string): Promise<Tool[]>
Pythonsession.find_tools(intent)

The subset of tools() whose name or description contains intent, case-insensitively. A substring match, not a search: for a ranked, catalog-wide search use discover.

ParameterTypeRequiredDescription
intentstringyesThe substring to look for in tool names and descriptions
Example
const  = await (, "pix");
Result: Tool[]
// The same Tool shape as tools(), filtered.
Tool[]

Throws nothing beyond what tools() throws.

execute

Writeexecute(toolName: string, params: Record<string, unknown>): Promise<ToolResult>
Can move moneyPythonsession.execute(tool_name, params)

Runs whatever tool you name; a payment tool moves money.

Runs one tool by name, meta-tool or catalog tool, through POST /v1/sessions/{id}/execute. The result is a ToolResult envelope either way: a tool failure and a non-2xx HTTP answer both come back as success: false with the reason in error, so a caller branches on the envelope rather than catching.

ParameterTypeRequiredDescription
toolNamestringyescodespar_pay, or a catalog tool as the server's tool name
paramsRecord<string, unknown>yesThe tool's input, matching its input_schema
Example
const  = await .("codespar_wallet", { : "balance" });
if (.) {
  .(.);
} else {
  .(.);
}
Result: ToolResult
interface ToolResult {
  success: boolean;
  data: unknown;
  error: string | null;
  /** ms */
  duration: number;
  server: string;
  tool: string;
  tool_call_id?: string;
  called_at?: string;
}

Throws a CodesparApiError with status: 0 when the request never reaches the backend, and a TimeoutError on timeout. An HTTP error status does not throw: it is { success: false, error: "<status>: <body>" }. When the tool refuses for a governed reason, data carries a coded output; the tool-result guards narrow it.

Related Meta-tools for the names and inputs, REST POST /v1/sessions/{id}/execute.

proxyExecute

WriteproxyExecute(request: ProxyRequest): Promise<ProxyResult>
Can move moneyPythonsession.proxy_execute(request)

Calls the provider's own API with the tenant's stored credentials; what it does is whatever that endpoint does.

A raw HTTP call to a connected server's upstream API, with the backend injecting the stored credential. For the endpoints a provider has that the catalog does not wrap. Never send provider keys in headers; the backend adds them.

ParameterTypeRequiredDescription
requestProxyRequestyesServer id, endpoint path on the provider, method, and optional body, query and headers
Example
const  = await .({
  : "asaas",
  : "/v3/customers",
  : "GET",
  : { : 10 },
});
.(., .);
ProxyRequest and ProxyResult
interface ProxyRequest {
  server: string;
  endpoint: string;
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
  body?: unknown;
  params?: Record<string, string | number | boolean>;
  headers?: Record<string, string>;
}

interface ProxyResult {
  status: number;
  data: unknown;
  headers: Record<string, string>;
  /** ms */
  duration: number;
  proxy_call_id?: string;
}

Throws a CodesparApiError when the CodeSpar backend answers non-2xx or cannot be reached; the upstream provider's own status comes back as a value in status. A TimeoutError on timeout.

Related REST POST /v1/sessions/{id}/proxy_execute.

send

Writesend(message: string): Promise<SendResult>
Can move moneyPythonsession.send(message)

Drives a tool-use loop that may call any tool of the session, payment tools included.

One natural-language turn. The backend runs a model tool-use loop over the session's tools and resolves when the loop finishes, with the final text and every tool call it made.

ParameterTypeRequiredDescription
messagestringyesThe user's message
Example
const  = await .("Charge R$150 via Pix and issue the NF-e");
.(.);
for (const  of .) .(., .);
Result: SendResult
interface SendResult {
  message: string;
  tool_calls: ToolCallRecord[];
  iterations: number;
}

interface ToolCallRecord {
  id: string;
  tool_name: string;
  server_id: string;
  status: "success" | "error";
  duration_ms: number;
  input: unknown;
  output: unknown;
  error_code: string | null;
}

Throws a CodesparApiError on a non-2xx answer or a transport failure, a TimeoutError when the whole turn exceeds the timeout.

Related sendStream for the same turn as events, REST POST /v1/sessions/{id}/send.

sendStream

StreamsendStream(message: string): AsyncIterable<StreamEvent>
Can move moneyPythonsession.send_stream(message)

The same loop as send(), streamed.

The same turn as send, as it happens. Sends Accept: text/event-stream to the same route and yields one typed event per SSE frame. The timeout is an idle timeout that resets on every complete frame, so a long turn that keeps producing frames does not time out.

ParameterTypeRequiredDescription
messagestringyesThe user's message
Example
for await (const  of .("Charge R$150 via Pix")) {
  if (. === "assistant_text") ..(.);
  if (. === "tool_result") .(..);
  if (. === "done") .(..);
}
StreamEvent
type StreamEvent =
  | { type: "user_message"; content: string }
  | { type: "assistant_text"; content: string; iteration: number }
  | { type: "tool_use"; id: string; name: string; input: Record<string, unknown> }
  | { type: "tool_result"; toolCall: ToolCallRecord }
  | { type: "done"; result: SendResult }
  | { type: "error"; error: string; message?: string };

Throws a CodesparApiError when the response is not 2xx or the transport fails, a TimeoutError when no complete frame arrives within the timeout. An error event is a value, not a throw.

Related send, REST POST /v1/sessions/{id}/send.

Tools, execute and send | CodeSpar