Your First Tool
Tools let PIE take actions during conversations. They're JavaScript functions that run in a secure sandbox.
What Tools Do
- Call external APIs
- Process and transform data
- Perform calculations
- Interact with services
Create a Tool
Let's build a weather tool that fetches current weather data.
Step 1: Get an API Key
- Sign up at WeatherAPI.com
- Copy your API key
Step 2: Create the Agent
- Go to Agents in PIE
- Click Create Agent
- Select Tool tier
Step 3: Write the Code
/**
* Weather Tool - Get current weather for any city
*/
async function handler(input, context) {
const { city } = input;
if (!city) {
return { error: true, message: 'City is required' };
}
// Get API key from secrets
const apiKey = context.secrets.WEATHER_API_KEY;
if (!apiKey) {
return { error: true, message: 'Weather API key not configured' };
}
// Make the API request using context.fetch()
const response = await context.fetch(
`https://api.weatherapi.com/v1/current.json?key=${apiKey}&q=${encodeURIComponent(city)}`
);
if (!response.ok) {
return {
error: true,
message: `Failed to fetch weather: ${response.status}`
};
}
// Parse the response
const data = JSON.parse(response.body);
// Return structured data for the AI
return {
city: data.location.name,
country: data.location.country,
temperature_c: data.current.temp_c,
temperature_f: data.current.temp_f,
condition: data.current.condition.text,
humidity: data.current.humidity,
wind_kph: data.current.wind_kph,
feels_like_c: data.current.feelslike_c,
};
}
module.exports = { handler };Step 4: Define the Manifest
{
"trigger": "auto",
"developerSecrets": {
"WEATHER_API_KEY": {
"description": "API key from weatherapi.com",
"required": true
}
},
"tool": {
"name": "get_weather",
"description": "Get current weather conditions for a city. Use when users ask about weather.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name (e.g., 'London', 'New York', 'Tokyo')"
}
},
"required": ["city"]
}
}
}Step 5: Add Your API Key
- After creating the agent, go to agent settings
- Add your
WEATHER_API_KEY
Expose Heartbeat Events When It Makes Sense
Not every tool needs event triggers. Read-only tools like this weather example usually do not need heartbeatEvents.
If your tool performs meaningful mutations, publishes content, sends messages, or creates records, add a curated event catalog to the manifest so users can build Heartbeats from those actions:
{
"heartbeatEvents": {
"events": [
{
"id": "report_created",
"displayName": "Report created",
"description": "Fires when the tool creates a new report.",
"enabled": true,
"matchers": [
{ "source": "tool", "action": "create_report" }
]
}
]
}
}PIE can suggest these events automatically when you save the plugin, but you should review the suggestions and keep only the user-facing outcomes you want to expose.
How Tools Work
- User asks: "What's the weather in Tokyo?"
- AI sees the
get_weathertool description - AI decides to call the tool with
{ city: "Tokyo" } - PIE runs your handler in a sandbox
- Your code calls the weather API
- Results return to the AI
- AI formats a response for the user
Understanding the Handler
async function handler(input, context) {
// input = parameters from the AI (e.g., { city: "Tokyo" })
// context = PIE's API
// - context.fetch(url, options) - make HTTP requests
// - context.secrets - your API keys
// - context.user - user information
return { /* data for the AI */ };
}
module.exports = { handler };The Context Object
| Property | Type | Description |
|---|---|---|
context.fetch(url, options) | Function | Make HTTP requests |
context.secrets | Object | Developer secrets (API keys) |
context.user.id | String | Current user's ID |
context.user.displayName | String | User's display name |
context.userConfig | Object | User-defined configuration values for this agent |
context.userConfig | Object | User-configurable settings (from userFields in manifest) |
context.oauth.* | Object | OAuth operations: isConnected(), fetch(), getConnectionInfo() |
context.files.* | Object | File storage: upload(), list(), get(), delete() |
context.db.query() | Function | Run read-only SQL queries against external Postgres databases |
context.tasks.* | Object | Task scheduling: create(), list(), update(), delete() |
See the full Context API reference for details on all available properties and methods.
Security Scanning
When you create or update a tool, PIE automatically runs a security scan on your code. The scan checks for common security issues like data exfiltration, credential harvesting, and code injection. Your tool must pass all 10 security criteria to receive a "Verified" badge.
You can view scan results and manually trigger re-scans from the Security tab in the Developer Portal.
See the full Security Scanning guide for details.
Tips for Good Tools
- Clear descriptions - Help the AI know when to use your tool
- Validate input - Check for required parameters
- Handle errors - Return helpful error messages
- Return structured data - Let the AI format the response
Next Steps
Need a shareable frontend or public route for your tool? Read the Public Apps and Routes guide.
Ready for the advanced stuff? Let's build an OAuth connector.