Chapter 11: The worka-sdk
for Frontend
TL;DR
- Import the hook:
import { useTool } from '@worka-ai/sdk';
- Instantiate it in your component:
const { call, data, isLoading, error } = useTool('tool_name', target?);
- Return Values:
call(params?)
: Theasync
function you execute to run the tool.data
: The successful response from the tool.isLoading
: A boolean for the loading state.error
: The error object if the call fails.
- Targeting:
- Omit the
target
parameter to call a tool on your pack's own backend. - Provide a
target
object ({ tenant: string, name: string }
) to call a tool on another pack (e.g., a virtual pack likeworka/ui
).
- Omit the
- Best Practice: To prevent infinite re-render loops, always memoize the
target
object if you provide it.import { useMemo } from 'react';
const uiTarget = useMemo(() => ({ tenant: 'worka', name: 'ui' }), []);
const { call: openTab } = useTool('open_tab', uiTarget);
Your pack's UI does not run in a vacuum. It needs a way to communicate with its own backend, other packs, and the Worka Host itself. The @worka-ai/sdk
is the secure bridge that makes this possible.
It is the only supported way for a pack's UI to make these calls. Direct access to the host's invoke
function is blocked by the sandbox.
The useTool
Hook in Detail
The primary export of the SDK is the useTool
React hook. It handles the complexity of authentication, routing, and asynchronous state management, giving you a clean and simple API.
Let's look at a complete example:
import { useTool } from '@worka-ai/sdk';
import { useMemo } from 'react';
import { Button, Spinner, Text } from '@chakra-ui/react';
function MyComponent() {
// Target the 'system' pack
const systemTarget = useMemo(() => ({ tenant: 'worka', name: 'system' }), []);
// Get the tools to list projects
const { call: listProjects, data, isLoading, error } = useTool('list_projects', systemTarget);
if (isLoading) {
return <Spinner />;
}
if (error) {
return <Text color="red.500">{error.message}</Text>;
}
return (
<>
<Button onClick={() => listProjects()}>Load Projects</Button>
{data && <Text>Projects Loaded: {data.length}</Text>}
</>
);
}
Parameters
tool
(string, required): The name of the tool you want to call (e.g.,'list_projects'
).target
(object, optional): An object specifying thetenant
andname
of the pack you want to call.- If you omit this parameter, the call is automatically routed to your pack's own backend MCP server.
- This is required to call tools on other packs or on Worka's virtual packs (like
worka/ui
orworka/db
).
Return Value
The hook returns an object with four key properties that cover the entire lifecycle of a request:
call
: This is theasync
function you execute to trigger the tool call. You can pass a JSON object to it as an argument, which will be sent as the parameters for the tool (e.g.,call({ id: 123 })
).isLoading
: A boolean that istrue
from the momentcall
is executed until a response is received. This is perfect for showing loading spinners or disabling buttons.data
: If the tool call is successful, this property will be populated with the response from the backend.error
: If the tool call fails for any reason (e.g., network error, backend logic error), this property will contain the error object.
Best Practice: Preventing Re-Renders
In React, if you pass an object literal like { tenant: 'worka', name: 'system' }
directly as a prop or hook parameter, a new object is created on every single render. This will cause the useTool
hook to think its parameters have changed, leading to infinite loops.
To prevent this, you must wrap the target
object in a useMemo
hook. This ensures that the same object instance is passed to useTool
on every render, unless its dependencies change.
Incorrect (causes loops):
// A new object is created on every render, causing the hook to re-run
const { call } = useTool('open_tab', { tenant: 'worka', name: 'ui' });
Correct (stable):
import { useMemo } from 'react';
// The same object is used on every render
const uiTarget = useMemo(() => ({ tenant: 'worka', name: 'ui' }), []);
const { call } = useTool('open_tab', uiTarget);