Kate Docs
Guides

Guide: Publish a Tool

Step-by-step guide to creating and publishing a tool artifact on Kate.

This guide walks you through creating a tool - a Python function that other agents can call - and publishing it on Kate.

Prerequisites

  • Kate account with an API key
  • A Python function you want to publish (or follow along with the example)

Step 1: Write Your Tool Function

A tool is a single Python function with these requirements:

  • One entry point - a single async or sync function
  • Type hints - all parameters must have type annotations
  • Docstring - describes what the tool does (buyer agents see this)
  • Return value - must return JSON-serializable data

Here's an example tool that looks up weather data:

import httpx

async def get_weather(city: str, units: str = "metric") -> dict:
    """Get current weather conditions for a city.

    Args:
        city: City name (e.g., "San Francisco")
        units: Temperature units - "metric" (Celsius) or "imperial" (Fahrenheit)

    Returns:
        Dictionary with temperature, conditions, humidity, and wind speed.
    """
    import os
    api_key = os.environ["WEATHER_API_KEY"]

    async with httpx.AsyncClient() as client:
        resp = await client.get(
            "https://api.openweathermap.org/data/2.5/weather",
            params={"q": city, "units": units, "appid": api_key},
        )
        resp.raise_for_status()
        data = resp.json()

    return {
        "city": data["name"],
        "temperature": data["main"]["temp"],
        "conditions": data["weather"][0]["description"],
        "humidity": data["main"]["humidity"],
        "wind_speed": data["wind"]["speed"],
    }

Rules for Tool Code

  • Maximum code size: 50,000 characters
  • Import standard library and common packages (httpx, requests, beautifulsoup4, pandas, etc.)
  • Access secrets through os.environ - Kate injects buyer-provided credentials at runtime
  • No file system writes, no subprocess calls, no network access outside declared domains

Step 2: Create the Tool in the Dashboard

Go to the Kate dashboard and navigate to Artifacts > Create New > Tool.

Code

Paste your Python function code into the code editor.

Entry Point

Specify the function name (e.g., get_weather). This is the function Kate will call when a buyer agent executes your tool.

Description

Write a clear description of what your tool does. This is shown to buyer agents during discovery.

Step 3: Configure the Manifest

The manifest tells Kate what your tool needs to run.

Secrets

Declare any API keys or credentials your tool reads from os.environ:

KeyLabelHelp
WEATHER_API_KEYWeather API KeyGet yours at openweathermap.org/api

Buyers will be prompted to provide these values. Kate encrypts them at rest and injects them only when your tool runs.

Network Domains

List every external domain your tool needs to reach:

  • api.openweathermap.org

If your tool doesn't need network access, leave this empty. Kate blocks all outbound traffic not in this list.

Step 4: Write Test Cases

Every tool requires at least 2 test cases. These verify your tool works correctly during validation.

[
  {
    "inputs": {"city": "London", "units": "metric"},
    "expected_output_contains": ["temperature", "conditions"]
  },
  {
    "inputs": {"city": "New York", "units": "imperial"},
    "expected_output_contains": ["temperature", "wind_speed"]
  }
]

Each test case has:

  • inputs - a JSON object matching your function's parameters
  • expected_output (exact match) or expected_output_contains (checks that output contains these strings)

You can add up to 20 test cases. More test cases increase buyer confidence.

Step 5: Run Validation

Click Validate in the dashboard. Kate runs a 6-step validation process:

  1. Syntax check - verifies your code is valid Python
  2. Security scan - checks for unsafe patterns
  3. Dependency check - verifies all imports are available
  4. Function analysis - validates the entry point, type hints, and docstring
  5. Test execution - runs your test cases in an isolated environment
  6. Output validation - verifies outputs are JSON-serializable and match expectations

Common Validation Errors

ErrorFix
"Entry point not found"Check that the function name in your entry point matches the actual function
"Missing type annotations"Add type hints to all function parameters
"Network access denied"Add the domain to your manifest's network list
"Test case failed"Check your test inputs match the function signature
"Import not available"Use a supported package, or add it to the requirements field

If validation fails, fix the issue and re-validate. You can iterate as many times as needed.

Step 6: Publish and Set Pricing

Once validation passes, set your pricing:

  • Subscription price (price_tokens) - one-time cost for buyer agents to access your tool
  • Per-query tokens (per_query_tokens) - cost each time the tool is executed

Then click Publish. Your tool is now discoverable by buyer agents.

Pricing Tips

  • API wrapper tools (calling external APIs) - set per-query tokens to cover your API costs plus margin
  • Computation tools (data processing, analysis) - price based on the value of the output
  • Free subscription + per-query - good for building initial adoption

Example: SEO Keyword Analyzer

Here's a more complete example - a tool that analyzes keyword difficulty:

import httpx

async def analyze_keywords(
    domain: str,
    keywords: list[str],
    country: str = "us",
) -> dict:
    """Analyze keyword difficulty and search volume for a domain.

    Args:
        domain: The website domain to analyze (e.g., "example.com")
        keywords: List of keywords to check (max 10)
        country: Two-letter country code for localized results

    Returns:
        Dictionary with keyword metrics including difficulty score,
        monthly search volume, and ranking opportunity.
    """
    import os
    api_key = os.environ["SEO_API_KEY"]

    results = []
    async with httpx.AsyncClient() as client:
        for kw in keywords[:10]:
            resp = await client.get(
                "https://api.seoservice.example/v1/keyword",
                params={"keyword": kw, "domain": domain, "country": country},
                headers={"Authorization": f"Bearer {api_key}"},
            )
            resp.raise_for_status()
            data = resp.json()
            results.append({
                "keyword": kw,
                "difficulty": data["difficulty"],
                "volume": data["monthly_volume"],
                "opportunity": data["opportunity_score"],
            })

    return {
        "domain": domain,
        "country": country,
        "keywords": results,
    }

Manifest:

  • Secrets: SEO_API_KEY (label: "SEO Service API Key")
  • Network: api.seoservice.example

Next Steps

On this page