Choose and run tools with TypeSafe

Build a script that turns "Look up the Hacker News user 'pg'" into a tool call. TypeSafe's Jev selects the tool. Your code supplies the username, then Composio runs the call.

This example uses public Hacker News data, so you need no connected account. It follows the runnable TypeScript examples and Python demo.

Set up the script

Install the TypeSafe provider, then export COMPOSIO_API_KEY and TYPESAFE_API_KEY in your shell. The TypeScript version requires Node.js 24.17 or newer.

Choose, complete, and execute

Save the following script. username represents a value supplied by your application or a user form. The request asks Jev to choose among three tools, and knownArguments supplies values only for the tool they belong to.

select-tool.py
import json

from composio import Composio
from composio_typesafe import TypesafeProvider

provider = TypesafeProvider()
composio = Composio(provider=provider)
user_id = "example-user"
username = "pg"
request = f"Look up the Hacker News user '{username}'"
known_arguments = {"HACKERNEWS_GET_USER": {"username": username}}


def main():
    tool_set = composio.tools.get(
        user_id=user_id,
        tools=[
            "HACKERNEWS_GET_USER",
            "HACKERNEWS_GET_TOP_STORIES",
            "HACKERNEWS_SEARCH_POSTS",
        ],
    )
    decision = provider.decide(tool_set, request)
    print(json.dumps(decision, indent=2))

    if decision["kind"] == "abstain":
        print(f"No tool call: {decision['reason']}")
        return

    if decision["risk"] != "read_only":
        print(f"This example does not execute {decision['risk']} tools.")
        return

    if decision["kind"] == "partial":
        print("Arguments to supply:", decision["missing"])

    result = provider.execute(
        user_id,
        decision,
        arguments=known_arguments.get(decision["tool"], {}),
    )
    print(result["data"])


if __name__ == "__main__":
    main()

Run the script in the environment where you installed the packages:

python select-tool.py

The script prints the decision, the missing argument paths, and the Hacker News profile. A decision for this request typically has kind: 'partial', tool: 'HACKERNEWS_GET_USER', and missing: [['username']]. Confidence scores depend on the request and model version.

The partial call is expected: Jev can select the tool but does not generate the username. execute combines the arguments from Jev with the values your code supplies. Caller values take precedence. If a required value is still missing, execution raises TypesafeIncompleteDecisionError before calling the tool.

When you adapt this script, collect missing values from your application or ask the user for them. The suggestions field can contain uncertain model answers; treat those as candidates to review, not confirmed input.

Try an abstention

Change request to Explain what Hacker News is without using any tools, then run the script again. If Jev returns abstain, the script prints its reason and exits without executing. The model may also abstain when no tool fits or confidence is too low.

Keep API failures separate from abstentions. A timeout or invalid TypeSafe key raises an error, so it appears as a failed run instead of "No tool call."

Shortlist tools for another provider

If you already use an LLM to choose arguments, ask Jev to narrow its tool list first. This separate script ranks up to 50 Hacker News tools, keeps three, and converts them to OpenAI's tool format. It does not call OpenAI or execute a tool.

Install the OpenAI provider alongside the packages above:

uv add composio-openai
shortlist-tools.py
from composio import Composio
from composio_openai import OpenAIProvider
from composio_typesafe import TypesafeProvider

typesafe = TypesafeProvider()
composio = Composio(provider=OpenAIProvider())
request = "What are the top stories on Hacker News right now?"

raw = composio.tools.get_raw_composio_tools(toolkits=["hackernews"], limit=50)
shortlist = typesafe.shortlist_tools(raw, request, k=3)
tools = composio.tools.get(
    user_id="example-user",
    tools=[tool.slug for tool in shortlist["tools"]],
)
print(shortlist["scores"])
print(f"Prepared {len(tools)} tools for OpenAI")
python shortlist-tools.py

Pass the resulting tools to your OpenAI agent. Shortlisting selects candidates; your agent still chooses which candidate to call and supplies its arguments.