Skip to content

Commit 1bfaa0d

Browse files
committed
add db row tool + fix copilot versioning recognition
1 parent 3eef3b7 commit 1bfaa0d

File tree

13 files changed

+280
-49
lines changed

13 files changed

+280
-49
lines changed

apps/docs/content/docs/en/tools/notion.mdx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ Create a new database in Notion with custom properties
145145
| --------- | ---- | -------- | ----------- |
146146
| `parentId` | string | Yes | ID of the parent page where the database will be created |
147147
| `title` | string | Yes | Title for the new database |
148-
| `properties` | string | No | Database properties as JSON object \(optional, will create a default "Name" property if empty\) |
148+
| `properties` | json | No | Database properties as JSON object \(optional, will create a default "Name" property if empty\) |
149149

150150
#### Output
151151

@@ -157,4 +157,25 @@ Create a new database in Notion with custom properties
157157
| `created_time` | string | Creation timestamp |
158158
| `properties` | object | Database properties schema |
159159

160+
### `notion_add_database_row`
161+
162+
Add a new row to a Notion database with specified properties
163+
164+
#### Input
165+
166+
| Parameter | Type | Required | Description |
167+
| --------- | ---- | -------- | ----------- |
168+
| `databaseId` | string | Yes | ID of the database to add the row to |
169+
| `properties` | json | Yes | Row properties as JSON object matching the database schema \(e.g., \{"Name": \{"title": \[\{"text": \{"content": "Task 1"\}\}\]\}, "Status": \{"select": \{"name": "Done"\}\}\}\) |
170+
171+
#### Output
172+
173+
| Parameter | Type | Description |
174+
| --------- | ---- | ----------- |
175+
| `id` | string | Page/row ID |
176+
| `url` | string | Page/row URL |
177+
| `title` | string | Row title |
178+
| `created_time` | string | Creation timestamp |
179+
| `last_edited_time` | string | Last edit timestamp |
180+
160181

apps/sim/app/api/copilot/chat/route.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { env } from '@/lib/core/config/env'
2121
import { CopilotFiles } from '@/lib/uploads'
2222
import { createFileContent } from '@/lib/uploads/utils/file-utils'
2323
import { tools } from '@/tools/registry'
24+
import { getLatestVersionTools, stripVersionSuffix } from '@/tools/utils'
2425

2526
const logger = createLogger('CopilotChatAPI')
2627

@@ -411,11 +412,14 @@ export async function POST(req: NextRequest) {
411412
try {
412413
const { createUserToolSchema } = await import('@/tools/params')
413414

414-
integrationTools = Object.entries(tools).map(([toolId, toolConfig]) => {
415+
const latestTools = getLatestVersionTools(tools)
416+
417+
integrationTools = Object.entries(latestTools).map(([toolId, toolConfig]) => {
415418
const userSchema = createUserToolSchema(toolConfig)
419+
const strippedName = stripVersionSuffix(toolId)
416420
return {
417-
name: toolId,
418-
description: toolConfig.description || toolConfig.name || toolId,
421+
name: strippedName,
422+
description: toolConfig.description || toolConfig.name || strippedName,
419423
input_schema: userSchema,
420424
defer_loading: true, // Anthropic Advanced Tool Use
421425
...(toolConfig.oauth?.required && {

apps/sim/app/api/copilot/execute-tool/route.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils'
1717
import { REFERENCE } from '@/executor/constants'
1818
import { createEnvVarPattern } from '@/executor/utils/reference-validation'
1919
import { executeTool } from '@/tools'
20-
import { getTool } from '@/tools/utils'
20+
import { getTool, resolveToolId } from '@/tools/utils'
2121

2222
const logger = createLogger('CopilotExecuteToolAPI')
2323

@@ -86,15 +86,17 @@ export async function POST(req: NextRequest) {
8686

8787
const { toolCallId, toolName, arguments: toolArgs, workflowId } = ExecuteToolSchema.parse(body)
8888

89+
const resolvedToolName = resolveToolId(toolName)
90+
8991
logger.info(`[${tracker.requestId}] Executing tool`, {
9092
toolCallId,
9193
toolName,
94+
resolvedToolName,
9295
workflowId,
9396
hasArgs: Object.keys(toolArgs).length > 0,
9497
})
9598

96-
// Get tool config from registry
97-
const toolConfig = getTool(toolName)
99+
const toolConfig = getTool(resolvedToolName)
98100
if (!toolConfig) {
99101
// Find similar tool names to help debug
100102
const { tools: allTools } = await import('@/tools/registry')
@@ -252,7 +254,7 @@ export async function POST(req: NextRequest) {
252254
hasApiKey: !!executionParams.apiKey,
253255
})
254256

255-
const result = await executeTool(toolName, executionParams, true)
257+
const result = await executeTool(resolvedToolName, executionParams, true)
256258

257259
logger.info(`[${tracker.requestId}] Tool execution complete`, {
258260
toolName,

apps/sim/app/api/wand/route.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,11 @@ Use this context to calculate relative dates like "yesterday", "last week", "beg
243243
finalSystemPrompt += currentTimeContext
244244
}
245245

246+
if (generationType === 'json-object') {
247+
finalSystemPrompt +=
248+
'\n\nIMPORTANT: Return ONLY the raw JSON object. Do NOT wrap it in markdown code blocks (no ```json or ```). Do NOT include any explanation or text before or after the JSON. The response must start with { and end with }.'
249+
}
250+
246251
const messages: ChatMessage[] = [{ role: 'system', content: finalSystemPrompt }]
247252

248253
messages.push(...history.filter((msg) => msg.role !== 'system'))

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/tool-call/tool-call.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1790,7 +1790,8 @@ function getStateVerb(state: string): string {
17901790
* e.g., "google_calendar_list_events" -> "Google Calendar List Events"
17911791
*/
17921792
function formatToolName(name: string): string {
1793-
return name
1793+
const baseName = name.replace(/_v\d+$/, '')
1794+
return baseName
17941795
.split('_')
17951796
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
17961797
.join(' ')

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,8 @@ const generateOutputPathsWithTypes = (
303303
paths.push({ path: currentPath, type: 'array' })
304304
const subPaths = generateOutputPathsWithTypes(value.items.properties, currentPath)
305305
paths.push(...subPaths)
306-
} else if (value.type === 'object' && value.properties) {
307-
paths.push({ path: currentPath, type: 'object' })
306+
} else if ((value.type === 'object' || value.type === 'json') && value.properties) {
307+
paths.push({ path: currentPath, type: value.type })
308308
const subPaths = generateOutputPathsWithTypes(value.properties, currentPath)
309309
paths.push(...subPaths)
310310
} else {

apps/sim/blocks/blocks/notion.ts

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const NotionBlock: BlockConfig<NotionResponse> = {
2727
{ label: 'Read Database', id: 'notion_read_database' },
2828
{ label: 'Create Page', id: 'notion_create_page' },
2929
{ label: 'Create Database', id: 'notion_create_database' },
30+
{ label: 'Add Database Row', id: 'notion_add_database_row' },
3031
{ label: 'Append Content', id: 'notion_write' },
3132
{ label: 'Query Database', id: 'notion_query_database' },
3233
{ label: 'Search Workspace', id: 'notion_search' },
@@ -146,11 +147,10 @@ export const NotionBlock: BlockConfig<NotionResponse> = {
146147
},
147148
{
148149
id: 'filter',
149-
title: 'Filter (JSON)',
150-
type: 'long-input',
150+
title: 'Filter',
151+
type: 'code',
151152
placeholder: 'Enter filter conditions as JSON (optional)',
152153
condition: { field: 'operation', value: 'notion_query_database' },
153-
required: true,
154154
wandConfig: {
155155
enabled: true,
156156
prompt:
@@ -162,8 +162,8 @@ export const NotionBlock: BlockConfig<NotionResponse> = {
162162
},
163163
{
164164
id: 'sorts',
165-
title: 'Sort Criteria (JSON)',
166-
type: 'long-input',
165+
title: 'Sort Criteria',
166+
type: 'code',
167167
placeholder: 'Enter sort criteria as JSON array (optional)',
168168
condition: { field: 'operation', value: 'notion_query_database' },
169169
wandConfig: {
@@ -231,19 +231,44 @@ export const NotionBlock: BlockConfig<NotionResponse> = {
231231
},
232232
{
233233
id: 'properties',
234-
title: 'Database Properties (JSON)',
235-
type: 'long-input',
234+
title: 'Database Properties',
235+
type: 'code',
236236
placeholder: 'Enter database properties as JSON object',
237237
condition: { field: 'operation', value: 'notion_create_database' },
238238
wandConfig: {
239239
enabled: true,
240240
prompt:
241-
'Generate Notion database properties in JSON format based on the user\'s description. Properties define the schema of the database. Common types: "title" (required), "rich_text", "number", "select" (with options), "multi_select", "date", "checkbox", "url", "email", "phone_number". Example: {"Name": {"title": {}}, "Status": {"select": {"options": [{"name": "To Do"}, {"name": "Done"}]}}, "Priority": {"number": {}}}. Return ONLY valid JSON - no explanations.',
241+
'Generate Notion database properties in JSON format based on the user\'s description. Only provide the json, no escaping required. Properties define the schema of the database. Common types: "title" (required), "rich_text", "number", "select" (with options), "multi_select", "date", "checkbox", "url", "email", "phone_number". Example: {"Name": {"title": {}}, "Status": {"select": {"options": [{"name": "To Do"}, {"name": "Done"}]}}, "Priority": {"number": {}}}. Return ONLY valid JSON - no explanations.',
242242
placeholder:
243243
'Describe the columns/properties you want (e.g., "name, status dropdown, due date, priority number")...',
244244
generationType: 'json-object',
245245
},
246246
},
247+
// Add Database Row Fields
248+
{
249+
id: 'databaseId',
250+
title: 'Database ID',
251+
type: 'short-input',
252+
placeholder: 'Enter Notion database ID',
253+
condition: { field: 'operation', value: 'notion_add_database_row' },
254+
required: true,
255+
},
256+
{
257+
id: 'properties',
258+
title: 'Row Properties',
259+
type: 'code',
260+
placeholder: 'Enter row properties as JSON object',
261+
condition: { field: 'operation', value: 'notion_add_database_row' },
262+
required: true,
263+
wandConfig: {
264+
enabled: true,
265+
prompt:
266+
'Generate Notion page/row properties in JSON format based on the user\'s description. Properties must match the database schema. Common formats: Title: {"Name": {"title": [{"text": {"content": "Value"}}]}}, Text: {"Description": {"rich_text": [{"text": {"content": "Value"}}]}}, Number: {"Price": {"number": 10}}, Select: {"Status": {"select": {"name": "Done"}}}, Multi-select: {"Tags": {"multi_select": [{"name": "Tag1"}, {"name": "Tag2"}]}}, Date: {"Due": {"date": {"start": "2024-01-01"}}}, Checkbox: {"Done": {"checkbox": true}}, URL: {"Link": {"url": "https://..."}}, Email: {"Contact": {"email": "test@example.com"}}. Return ONLY valid JSON - no explanations.',
267+
placeholder:
268+
'Describe the row data (e.g., "name is Task 1, status is Done, priority is High")...',
269+
generationType: 'json-object',
270+
},
271+
},
247272
],
248273
tools: {
249274
access: [
@@ -279,18 +304,24 @@ export const NotionBlock: BlockConfig<NotionResponse> = {
279304
params: (params) => {
280305
const { credential, operation, properties, filter, sorts, ...rest } = params
281306

282-
// Parse properties from JSON string for create operations
307+
// Parse properties from JSON string for create/add operations
283308
let parsedProperties
284309
if (
285-
(operation === 'notion_create_page' || operation === 'notion_create_database') &&
310+
(operation === 'notion_create_page' ||
311+
operation === 'notion_create_database' ||
312+
operation === 'notion_add_database_row') &&
286313
properties
287314
) {
288-
try {
289-
parsedProperties = JSON.parse(properties)
290-
} catch (error) {
291-
throw new Error(
292-
`Invalid JSON for properties: ${error instanceof Error ? error.message : String(error)}`
293-
)
315+
if (typeof properties === 'string') {
316+
try {
317+
parsedProperties = JSON.parse(properties)
318+
} catch (error) {
319+
throw new Error(
320+
`Invalid JSON for properties: ${error instanceof Error ? error.message : String(error)}`
321+
)
322+
}
323+
} else {
324+
parsedProperties = properties
294325
}
295326
}
296327

@@ -384,6 +415,7 @@ export const NotionV2Block: BlockConfig<any> = {
384415
'notion_query_database_v2',
385416
'notion_search_v2',
386417
'notion_create_database_v2',
418+
'notion_add_database_row_v2',
387419
],
388420
config: {
389421
tool: createVersionedToolSelector({
@@ -418,6 +450,7 @@ export const NotionV2Block: BlockConfig<any> = {
418450
value: [
419451
'notion_create_page',
420452
'notion_create_database',
453+
'notion_add_database_row',
421454
'notion_read_database',
422455
'notion_update_page',
423456
],
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import type { NotionAddDatabaseRowParams } from '@/tools/notion/types'
2+
import type { ToolConfig } from '@/tools/types'
3+
4+
interface NotionAddDatabaseRowResponse {
5+
success: boolean
6+
output: {
7+
id: string
8+
url: string
9+
title: string
10+
created_time: string
11+
last_edited_time: string
12+
}
13+
}
14+
15+
export const notionAddDatabaseRowTool: ToolConfig<
16+
NotionAddDatabaseRowParams,
17+
NotionAddDatabaseRowResponse
18+
> = {
19+
id: 'notion_add_database_row_v2',
20+
name: 'Add Notion Database Row',
21+
description: 'Add a new row to a Notion database with specified properties',
22+
version: '1.0.0',
23+
24+
oauth: {
25+
required: true,
26+
provider: 'notion',
27+
},
28+
29+
params: {
30+
accessToken: {
31+
type: 'string',
32+
required: true,
33+
visibility: 'hidden',
34+
description: 'Notion OAuth access token',
35+
},
36+
databaseId: {
37+
type: 'string',
38+
required: true,
39+
visibility: 'user-or-llm',
40+
description: 'ID of the database to add the row to',
41+
},
42+
properties: {
43+
type: 'json',
44+
required: true,
45+
visibility: 'user-or-llm',
46+
description:
47+
'Row properties as JSON object matching the database schema (e.g., {"Name": {"title": [{"text": {"content": "Task 1"}}]}, "Status": {"select": {"name": "Done"}}})',
48+
},
49+
},
50+
51+
request: {
52+
url: () => 'https://api.notion.com/v1/pages',
53+
method: 'POST',
54+
headers: (params: NotionAddDatabaseRowParams) => {
55+
if (!params.accessToken) {
56+
throw new Error('Access token is required')
57+
}
58+
59+
return {
60+
Authorization: `Bearer ${params.accessToken}`,
61+
'Notion-Version': '2022-06-28',
62+
'Content-Type': 'application/json',
63+
}
64+
},
65+
body: (params: NotionAddDatabaseRowParams) => {
66+
return {
67+
parent: {
68+
type: 'database_id',
69+
database_id: params.databaseId,
70+
},
71+
properties: params.properties,
72+
}
73+
},
74+
},
75+
76+
transformResponse: async (response: Response) => {
77+
const data = await response.json()
78+
79+
// Extract title from properties if available
80+
let rowTitle = 'Untitled'
81+
for (const [, value] of Object.entries(data.properties || {})) {
82+
const prop = value as any
83+
if (prop.type === 'title' && prop.title?.length > 0) {
84+
rowTitle = prop.title.map((t: any) => t.plain_text || '').join('')
85+
break
86+
}
87+
}
88+
89+
return {
90+
success: true,
91+
output: {
92+
id: data.id,
93+
url: data.url,
94+
title: rowTitle,
95+
created_time: data.created_time,
96+
last_edited_time: data.last_edited_time,
97+
},
98+
}
99+
},
100+
101+
outputs: {
102+
id: { type: 'string', description: 'Page/row ID' },
103+
url: { type: 'string', description: 'Page/row URL' },
104+
title: { type: 'string', description: 'Row title' },
105+
created_time: { type: 'string', description: 'Creation timestamp' },
106+
last_edited_time: { type: 'string', description: 'Last edit timestamp' },
107+
},
108+
}

0 commit comments

Comments
 (0)