Skip to content
9 changes: 3 additions & 6 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -416,8 +416,8 @@ In addition, you will need to update the registries:
Your tool should export a constant with a naming convention of `{toolName}Tool`. The tool ID should follow the format `{provider}_{tool_name}`. For example:

```typescript:/apps/sim/tools/pinecone/fetch.ts
import { ToolConfig, ToolResponse } from '../types'
import { PineconeParams, PineconeResponse } from './types'
import { ToolConfig, ToolResponse } from '@/tools/types'
import { PineconeParams, PineconeResponse } from '@/tools/pinecone/types'

export const fetchTool: ToolConfig<PineconeParams, PineconeResponse> = {
id: 'pinecone_fetch', // Follow the {provider}_{tool_name} format
Expand Down Expand Up @@ -448,17 +448,14 @@ In addition, you will need to update the registries:
transformResponse: async (response: Response) => {
// Transform response
},
transformError: (error) => {
// Handle errors
},
}
```

6. **Register Your Tool:**
Update the tools registry in `/apps/sim/tools/index.ts` to include your new tool:

```typescript:/apps/sim/tools/index.ts
import { fetchTool, generateEmbeddingsTool, searchTextTool } from './pinecone'
import { fetchTool, generateEmbeddingsTool, searchTextTool } from '/@tools/pinecone'
// ... other imports

export const tools: Record<string, ToolConfig> = {
Expand Down
2 changes: 0 additions & 2 deletions apps/docs/content/docs/tools/airtable.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,6 @@ Update multiple existing records in an Airtable table
| `baseId` | string | Yes | ID of the Airtable base |
| `tableId` | string | Yes | ID or name of the table |
| `records` | json | Yes | Array of records to update, each with an `id` and a `fields` object |
| `fields` | string | No | No description |
| `fields` | string | No | No description |

#### Output

Expand Down
7 changes: 4 additions & 3 deletions apps/docs/content/docs/tools/browser_use.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,10 @@ Runs a browser automation task using BrowserUse

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `success` | boolean | Operation success status |
| `output` | json | Browser automation task results including task ID, success status, output data, and execution steps |
| `error` | string | Error message if the operation failed |
| `id` | string | Task execution identifier |
| `success` | boolean | Task completion status |
| `output` | json | Task output data |
| `steps` | json | Execution steps taken |



Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/tools/elevenlabs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Convert TTS using ElevenLabs voices

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `audioUrl` | string | Generated audio URL |
| `audioUrl` | string | The URL of the generated audio |



Expand Down
4 changes: 2 additions & 2 deletions apps/docs/content/docs/tools/file.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ Parse one or more uploaded files or files from URLs (text, PDF, CSV, images, etc

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `files` | json | Array of parsed file objects with content, metadata, and file properties |
| `combinedContent` | string | All file contents merged into a single text string |
| `files` | array | Array of parsed files |
| `combinedContent` | string | Combined content of all parsed files |



Expand Down
20 changes: 10 additions & 10 deletions apps/docs/content/docs/tools/supabase.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ Query data from a Supabase table

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `success` | boolean | Operation success status |
| `output` | object | Query operation results |
| `message` | string | Operation status message |
| `results` | array | Array of records returned from the query |

### `supabase_insert`

Expand All @@ -121,8 +121,8 @@ Insert data into a Supabase table

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `success` | boolean | Operation success status |
| `output` | object | Insert operation results |
| `message` | string | Operation status message |
| `results` | array | Array of inserted records |

### `supabase_get_row`

Expand All @@ -141,8 +141,8 @@ Get a single row from a Supabase table based on filter criteria

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `success` | boolean | Operation success status |
| `output` | object | Get row operation results |
| `message` | string | Operation status message |
| `results` | object | The row data if found, null if not found |

### `supabase_update`

Expand All @@ -162,8 +162,8 @@ Update rows in a Supabase table based on filter criteria

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `success` | boolean | Operation success status |
| `output` | object | Update operation results |
| `message` | string | Operation status message |
| `results` | array | Array of updated records |

### `supabase_delete`

Expand All @@ -182,8 +182,8 @@ Delete rows from a Supabase table based on filter criteria

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `success` | boolean | Operation success status |
| `output` | object | Delete operation results |
| `message` | string | Operation status message |
| `results` | array | Array of deleted records |



Expand Down
38 changes: 2 additions & 36 deletions apps/sim/app/api/proxy/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,42 +235,8 @@ export async function POST(request: Request) {
error: result.error || 'Unknown error',
})

if (tool.transformError) {
try {
const errorResult = tool.transformError(result)

// Handle both string and Promise return types
if (typeof errorResult === 'string') {
throw new Error(errorResult)
}
// It's a Promise, await it
const transformedError = await errorResult
// If it's a string or has an error property, use it
if (typeof transformedError === 'string') {
throw new Error(transformedError)
}
if (
transformedError &&
typeof transformedError === 'object' &&
'error' in transformedError
) {
throw new Error(transformedError.error || 'Tool returned an error')
}
// Fallback
throw new Error('Tool returned an error')
} catch (transformError) {
logger.error(`[${requestId}] Error transformation failed for ${toolId}`, {
error:
transformError instanceof Error ? transformError.message : String(transformError),
})
if (transformError instanceof Error) {
throw transformError
}
throw new Error('Tool returned an error')
}
} else {
throw new Error('Tool returned an error')
}
// Let the main executeTool handle error transformation to avoid double transformation
throw new Error(result.error || 'Tool execution failed')
}

const endTime = new Date()
Expand Down
147 changes: 147 additions & 0 deletions apps/sim/app/api/tools/jira/update/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { NextResponse } from 'next/server'
import { Logger } from '@/lib/logs/console/logger'
import { getJiraCloudId } from '@/tools/jira/utils'

export const dynamic = 'force-dynamic'

const logger = new Logger('JiraUpdateAPI')

export async function PUT(request: Request) {
try {
const {
domain,
accessToken,
issueKey,
summary,
title, // Support both summary and title for backwards compatibility
description,
status,
priority,
assignee,
cloudId: providedCloudId,
} = await request.json()

// Validate required parameters
if (!domain) {
logger.error('Missing domain in request')
return NextResponse.json({ error: 'Domain is required' }, { status: 400 })
}

if (!accessToken) {
logger.error('Missing access token in request')
return NextResponse.json({ error: 'Access token is required' }, { status: 400 })
}

if (!issueKey) {
logger.error('Missing issue key in request')
return NextResponse.json({ error: 'Issue key is required' }, { status: 400 })
}

// Use provided cloudId or fetch it if not provided
const cloudId = providedCloudId || (await getJiraCloudId(domain, accessToken))
logger.info('Using cloud ID:', cloudId)

// Build the URL using cloudId for Jira API
const url = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${issueKey}`

logger.info('Updating Jira issue at:', url)

// Map the summary from either summary or title field
const summaryValue = summary || title
const fields: Record<string, any> = {}

if (summaryValue) {
fields.summary = summaryValue
}

if (description) {
fields.description = {
type: 'doc',
version: 1,
content: [
{
type: 'paragraph',
content: [
{
type: 'text',
text: description,
},
],
},
],
}
}

if (status) {
fields.status = {
name: status,
}
}

if (priority) {
fields.priority = {
name: priority,
}
}

if (assignee) {
fields.assignee = {
id: assignee,
}
}

const body = { fields }

// Make the request to Jira API
const response = await fetch(url, {
method: 'PUT',
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})

if (!response.ok) {
const errorText = await response.text()
logger.error('Jira API error:', {
status: response.status,
statusText: response.statusText,
error: errorText,
})

return NextResponse.json(
{ error: `Jira API error: ${response.status} ${response.statusText}`, details: errorText },
{ status: response.status }
)
}

// Note: Jira update API typically returns 204 No Content on success
const responseData = response.status === 204 ? {} : await response.json()
logger.info('Successfully updated Jira issue:', issueKey)

return NextResponse.json({
success: true,
output: {
ts: new Date().toISOString(),
issueKey: responseData.key || issueKey,
summary: responseData.fields?.summary || 'Issue updated',
success: true,
},
})
} catch (error: any) {
logger.error('Error updating Jira issue:', {
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
})

return NextResponse.json(
{
error: error instanceof Error ? error.message : 'Internal server error',
success: false,
},
{ status: 500 }
)
}
}
Loading