title: Default Tool Examples
description: Default Tools - Examples reference. 9 starter tools covering web fetch, datetime, productivity, search, AI, and messaging.
All 9 inherit Tool Studio’s default sandbox: deny-first class allowlist, no filesystem, network in strict or host-allowlist mode with the SSRF four-layer guard for the tools that fetch.
getCurrentTime 🆓
:material-clock-outline:
datetime · example · util L0
Returns the current time in ISO 8601 format. If the user specifies a city, country, or location, the agent should first map it to an IANA time zone and supply it via the timeZone parameter. If no time zone is provided, UTC is used.
**Params** `timeZone`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `timeZone` | `STRING` | | IANA time zone identifier (e.g., Asia/Seoul) |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Returns the current time formatted as ISO-8601 with timezone offset.
*
* - If a timezone is provided, the offset format (+HH:MM / -HH:MM) is used.
* - If no timezone is provided, UTC time with 'Z' is returned.
*
* JavaScript standard APIs only.
*/
const now = new Date();
// Format date/time parts in target timezone
const parts = new Intl.DateTimeFormat('en-CA', {
timeZone,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
}).formatToParts(now);
const m = {};
for (const p of parts) {
m[p.type] = p.value;
}
// Milliseconds
const ms = String(now.getMilliseconds()).padStart(3, '0');
// Local time interpreted as UTC millis
const localAsUtc = Date.UTC(
m.year,
Number(m.month) - 1,
m.day,
m.hour,
m.minute,
m.second
);
// Actual UTC millis
const actualUtc = now.getTime();
const offsetMinutes = Math.round((localAsUtc - actualUtc) / 60000);
const sign = offsetMinutes >= 0 ? '+' : '-';
const abs = Math.abs(offsetMinutes);
const hh = String(Math.floor(abs / 60)).padStart(2, '0');
const mm = String(abs % 60).padStart(2, '0');
return (
`${m.year}-${m.month}-${m.day}` +
`T${m.hour}:${m.minute}:${m.second}.${ms}` +
`${sign}${hh}:${mm}`
);
```
sendEmail 🆓
:material-email-outline:
productivity · util L0
Drafts an email and renders a **Send email** action card in chat - the user clicks to send it from their own mail app (review-then-send).
**Params** `to` · `cc` · `subject` · `body`
**Env** -
Click for full reference · params · sandbox · JS source
**More detail**
Calling this tool does **not** send anything. It returns a fenced `saip-action` block (`type: email`) that the chat renders as an **Email draft** card with a **📧 Send email** button; the button opens a prefilled `mailto:` link in the user's own mail app. This is **review-then-send** - the model only drafts, and the user reviews the fields and sends. The model must never claim the email was sent, and the From address is the user's own default mail account.
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `to` | `STRING` | | Recipient email address(es), comma-separated. Optional. |
| `cc` | `STRING` | | Cc email address(es), comma-separated. Optional. |
| `subject` | `STRING` | ✓ | Email subject line. |
| `body` | `STRING` | ✓ | Email body text. Plain text; line breaks are kept. |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
````javascript
if (subject == null || String(subject).trim() === '') throw new Error('subject required');
if (body == null || String(body).trim() === '') throw new Error('body required');
const action = { type: 'email', subject: String(subject), body: String(body) };
const toValue = to == null ? '' : String(to).trim();
const ccValue = cc == null ? '' : String(cc).trim();
if (toValue !== '') action.to = toValue;
if (ccValue !== '') action.cc = ccValue;
return 'Email ready below — review it and click the Send email button to send it from your own mail app.\n\n```saip-action-return-direct\n' + JSON.stringify(action) + '\n```';
````
addToCalendar 🆓
:material-calendar-plus:
productivity · util L0
Builds a calendar event and renders an **Add to calendar** action card in chat - a dropdown to add the event to Google Calendar, Outlook, or Yahoo Calendar, or to get a standard `.ics` file (the `.ics` opens directly in your calendar app on the desktop, or downloads in a browser).
**Params** `title` · `start` · `end` · `location` · `description`
**Env** -
Click for full reference · params · sandbox · JS source
**More detail**
Calling this tool does **not** add anything to a calendar. It returns a fenced `saip-action` block (`type: calendar`) that the chat renders as a **Calendar event** card with an **📅 Add to calendar** dropdown offering Google Calendar, Outlook, Yahoo Calendar, and an `.ics` option; on the desktop app the `.ics` opens directly in your calendar app, in a browser it downloads a standard `.ics` file client-side that the user imports. The agent must convert rough date/time input into ISO-8601 and ensure `end` is after `start`.
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `title` | `STRING` | ✓ | Event title. |
| `start` | `STRING` | ✓ | Start time as ISO-8601 (convert rough input first, e.g. 2026-01-15T10:00:00+09:00). |
| `end` | `STRING` | ✓ | End time as ISO-8601, after start. |
| `location` | `STRING` | | Optional location text. |
| `description` | `STRING` | | Optional event description. |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
````javascript
if (title == null || String(title).trim() === '') throw new Error('title required');
if (start == null || String(start).trim() === '') throw new Error('start required');
if (end == null || String(end).trim() === '') throw new Error('end required');
const s = new Date(String(start));
const e = new Date(String(end));
if (isNaN(s.getTime())) throw new Error('invalid start: ' + start);
if (isNaN(e.getTime())) throw new Error('invalid end: ' + end);
if (e <= s) throw new Error('end must be after start');
const action = { type: 'calendar', title: String(title), start: s.toISOString(), end: e.toISOString() };
const loc = location == null ? '' : String(location).trim();
const desc = description == null ? '' : String(description).trim();
if (loc !== '') action.location = loc;
if (desc !== '') action.description = desc;
return 'Event ready below — review it and click the Add to calendar button to add it to your own calendar.\n\n```saip-action-return-direct\n' + JSON.stringify(action) + '\n```';
````
showLocation 🆓
:material-map-marker:
productivity · util L0
Renders an interactive Google Map of a place directly in the chat - no API key needed.
**Params** `query` · `label`
**Env** -
Click for full reference · params · sandbox · JS source
**More detail**
Calling this tool returns a fenced `saip-action` block (`type: map`) that the chat renders as a **Location** card with a keyless embedded Google Map centered on the place, plus an **Open in Google Maps** link. It is display-only - the map loads client-side from the `query` (a place name, address, or `lat,lng`); no account, key, or server call is involved. Useful right after a geocoding or weather tool so the user can see where the result is.
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `query` | `STRING` | ✓ | Place name, address, or 'lat,lng' to show on the map. |
| `label` | `STRING` | | Optional short caption shown above the map. |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
````javascript
if (query == null || String(query).trim() === '') throw new Error('query required');
const action = { type: 'map', query: String(query).trim() };
const lbl = label == null ? '' : String(label).trim();
if (lbl !== '') action.label = lbl;
return 'Location shown on the map below.\n\n```saip-action\n' + JSON.stringify(action) + '\n```';
````
getWeather 🆓
:material-weather-partly-cloudy:
web · example · weather L3
Free public weather lookup via wttr.in (no API key). Returns a small JSON summary: { location, tempC, humidity, windSpeed, windDirection }.
**Params** `location`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `location` | `STRING` | | City / region name (e.g. 'Seoul', 'New York'). Empty = caller's IP-detected location. |
**Sandbox** - **L3** (Scoped widening) - `fetch` allowlisted to `wttr.in` (SSRF-guarded); no filesystem.
JS source
```javascript
const path = encodeURIComponent((location || '').trim().replace(/ +/g, '+'));
const resp = await fetch('https://wttr.in/' + path + '?format=j1', { maxLength: 1_000_000 });
if (!resp.ok) throw new Error('wttr.in returned ' + resp.status);
const data = resp.json();
const area = data.nearest_area && data.nearest_area[0];
const areaName = area && area.areaName && area.areaName[0] && area.areaName[0].value;
const current = data.current_condition && data.current_condition[0];
return {
location: areaName || location || null,
tempC: current && current.temp_C ? Number(current.temp_C) : null,
humidity: current && current.humidity ? Number(current.humidity) : null,
windSpeed: current && current.windspeedKmph ? current.windspeedKmph + ' km/h' : null,
windDirection: current && current.winddir16Point ? current.winddir16Point : null,
};
```
googlePseSearch 🔑 × 2
:simple-google:
web · example · search L3
Google Programmable Search Engine query (Custom Search API). Requires `GOOGLE_API_KEY` and `GOOGLE_PSE_ID` env vars.
**Params** `query` · `resultNum` · `startPage`
**Env** `GOOGLE_API_KEY` · `GOOGLE_PSE_ID`
Click for full reference · params · sandbox · JS source
**More detail**
Setup:
1. Create / open a project at console.cloud.google.com, enable 'Custom Search API', issue an API key.
2. Create a Programmable Search Engine at programmablesearchengine.google.com and copy its ID.
3. Export the values: GOOGLE_API_KEY=AIza... GOOGLE_PSE_ID=01234...
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `query` | `STRING` | ✓ | Search query string |
| `resultNum` | `INTEGER` | | Number of results to return (1-10, default 3) |
| `startPage` | `INTEGER` | | 1-based offset into the result list |
**Sandbox** - **L3** (Scoped widening) - `fetch` allowlisted to `www.googleapis.com` (SSRF-guarded); no filesystem.
JS source
```javascript
const url = 'https://www.googleapis.com/customsearch/v1'
+ '?key=' + encodeURIComponent(googleApiKey)
+ '&cx=' + encodeURIComponent(pseId)
+ '&q=' + encodeURIComponent(query)
+ '&start=' + (startPage || 1)
+ '&num=' + (resultNum || 3);
const resp = await fetch(url, { maxLength: 5_000_000 });
if (!resp.ok) {
return { success: false, status: resp.status, message: resp.text() };
}
return resp.json();
```
openaiResponseGenerator 🔑 × 1
:material-creation:
ai · example L3
Calls OpenAI's Responses API. Requires `OPENAI_API_KEY` env var.
**Params** `prompt` · `model`
**Env** `OPENAI_API_KEY`
Click for full reference · params · sandbox · JS source
**More detail**
Setup:
1. Sign in at platform.openai.com and issue an API key.
2. Export it: OPENAI_API_KEY=sk-...
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `prompt` | `STRING` | ✓ | User prompt / question |
| `model` | `STRING` | | Model id (default 'gpt-5.4-mini') |
**Sandbox** - **L3** (Scoped widening) - `fetch` allowlisted to `api.openai.com` (SSRF-guarded); no filesystem.
JS source
```javascript
const resp = await fetch('https://api.openai.com/v1/responses', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + openaiApiKey,
},
body: JSON.stringify({
model: model || 'gpt-5.4-mini',
input: prompt,
}),
maxLength: 5_000_000,
});
if (!resp.ok) return { success: false, status: resp.status, message: resp.text() };
const data = resp.json();
let content = '';
let reasoning = '';
for (const item of (data.output || [])) {
if (item.type === 'message' && item.content) {
for (const c of item.content) {
if (c && c.type === 'output_text' && c.text != null) content += c.text;
}
}
if (item.type === 'reasoning' && item.summary) {
for (const s of item.summary) if (s && s.text != null) reasoning += s.text;
}
}
return { content, reasoning };
```
sendSlackMessage 🔑 × 1
:material-pound-box-outline:
messaging · example L3
Posts a text message to a Slack channel via Incoming Webhook. Requires `SLACK_WEBHOOK_URL` env var (the full https://hooks.slack.com/services/... URL).
**Params** `text`
**Env** `SLACK_WEBHOOK_URL`
Click for full reference · params · sandbox · JS source
**More detail**
Setup:
1. Create a Slack app at api.slack.com/apps, enable 'Incoming Webhooks'.
2. Add a webhook to the target channel and copy the URL.
3. Export it: SLACK_WEBHOOK_URL=https://hooks.slack.com/services/...
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `text` | `STRING` | ✓ | Message text to post |
**Sandbox** - **L3** (Scoped widening) - `fetch` allowlisted to `hooks.slack.com` (SSRF-guarded); no filesystem.
JS source
```javascript
const resp = await fetch(slackWebhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text }),
});
if (!resp.ok) return { success: false, status: resp.status, message: resp.text() };
return { status: 'ok' };
```
These nine tools are picked so any pair plugs together - a perfect first agentic-workflow surface. Two patterns you can reproduce after Local Pass:
Three of the nine need a credential. The launcher’s Environment Variables card is the recommended place to set them; the static-variable substring is masked from console.log and from the chat tool-call trace whenever it appears as a full string in the output.