Improvement(sharepoint): added ability to create list items, different from create list (#1379)

* added add list items

(cherry picked from commit df6ea35d5bb975c03c7ec0c787bd915f34890ac0)

* bun run lint

* minor changes

---------

Co-authored-by: Adam Gough <adamgough@Mac.attlocal.net>
Co-authored-by: Adam Gough <adamgough@Adams-MacBook-Pro.local>
This commit is contained in:
Adam Gough
2025-09-18 14:18:58 -07:00
committed by GitHub
parent eb1e90bb7f
commit 7327b448e5
7 changed files with 210 additions and 17 deletions

View File

@@ -17,6 +17,7 @@
"google_calendar",
"google_docs",
"google_drive",
"google_forms",
"google_search",
"google_sheets",
"huggingface",

View File

@@ -183,6 +183,25 @@ Update the properties (fields) on a SharePoint list item
| --------- | ---- | ----------- |
| `item` | object | Updated SharePoint list item |
### `sharepoint_add_list_items`
Add a new item to a SharePoint list
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `siteSelector` | string | No | Select the SharePoint site |
| `siteId` | string | No | The ID of the SharePoint site \(internal use\) |
| `listId` | string | Yes | The ID of the list to add the item to |
| `listItemFields` | object | Yes | Field values for the new list item |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `item` | object | Created SharePoint list item |
## Notes

View File

@@ -16,7 +16,6 @@ export const SharepointBlock: BlockConfig<SharepointResponse> = {
bgColor: '#E0E0E0',
icon: MicrosoftSharepointIcon,
subBlocks: [
// Operation selector
{
id: 'operation',
title: 'Operation',
@@ -29,9 +28,9 @@ export const SharepointBlock: BlockConfig<SharepointResponse> = {
{ label: 'Create List', id: 'create_list' },
{ label: 'Read List', id: 'read_list' },
{ label: 'Update List', id: 'update_list' },
{ label: 'Add List Items', id: 'add_list_items' },
],
},
// Sharepoint Credentials
{
id: 'credential',
title: 'Microsoft Account',
@@ -81,6 +80,7 @@ export const SharepointBlock: BlockConfig<SharepointResponse> = {
'create_list',
'read_list',
'update_list',
'add_list_items',
],
},
},
@@ -111,7 +111,7 @@ export const SharepointBlock: BlockConfig<SharepointResponse> = {
layout: 'full',
placeholder: 'Enter list ID (GUID). Required for Update; optional for Read.',
canonicalParamId: 'listId',
condition: { field: 'operation', value: ['read_list', 'update_list'] },
condition: { field: 'operation', value: ['read_list', 'update_list', 'add_list_items'] },
},
{
@@ -178,7 +178,7 @@ export const SharepointBlock: BlockConfig<SharepointResponse> = {
layout: 'full',
placeholder: 'Enter list item fields',
canonicalParamId: 'listItemFields',
condition: { field: 'operation', value: 'update_list' },
condition: { field: 'operation', value: ['update_list', 'add_list_items'] },
},
],
tools: {
@@ -189,6 +189,7 @@ export const SharepointBlock: BlockConfig<SharepointResponse> = {
'sharepoint_create_list',
'sharepoint_get_list',
'sharepoint_update_list',
'sharepoint_add_list_items',
],
config: {
tool: (params) => {
@@ -205,6 +206,8 @@ export const SharepointBlock: BlockConfig<SharepointResponse> = {
return 'sharepoint_get_list'
case 'update_list':
return 'sharepoint_update_list'
case 'add_list_items':
return 'sharepoint_add_list_items'
default:
throw new Error(`Invalid Sharepoint operation: ${params.operation}`)
}
@@ -212,7 +215,6 @@ export const SharepointBlock: BlockConfig<SharepointResponse> = {
params: (params) => {
const { credential, siteSelector, manualSiteId, mimeType, ...rest } = params
// Use siteSelector if provided, otherwise use manualSiteId
const effectiveSiteId = (siteSelector || manualSiteId || '').trim()
const {
@@ -234,12 +236,10 @@ export const SharepointBlock: BlockConfig<SharepointResponse> = {
})
}
}
// Ensure listItemFields is an object for the tool schema
if (typeof parsedItemFields !== 'object' || parsedItemFields === null) {
parsedItemFields = undefined
}
// Sanitize item ID (required by tool)
const rawItemId = providedItemId ?? listItemId
const sanitizedItemId =
rawItemId === undefined || rawItemId === null
@@ -252,10 +252,9 @@ export const SharepointBlock: BlockConfig<SharepointResponse> = {
return undefined
}
// Debug logging for update_list param mapping
if (others.operation === 'update_list') {
if (others.operation === 'update_list' || others.operation === 'add_list_items') {
try {
logger.info('SharepointBlock update_list param check', {
logger.info('SharepointBlock list item param check', {
siteId: effectiveSiteId || undefined,
listId: (others as any)?.listId,
listTitle: (others as any)?.listTitle,
@@ -275,7 +274,6 @@ export const SharepointBlock: BlockConfig<SharepointResponse> = {
pageSize: others.pageSize ? Number.parseInt(others.pageSize as string, 10) : undefined,
mimeType: mimeType,
...others,
// Map to tool param names
itemId: sanitizedItemId,
listItemFields: parsedItemFields,
includeColumns: coerceBoolean(includeColumns),
@@ -287,26 +285,20 @@ export const SharepointBlock: BlockConfig<SharepointResponse> = {
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
credential: { type: 'string', description: 'Microsoft account credential' },
// Create Page operation inputs
pageName: { type: 'string', description: 'Page name' },
pageContent: { type: 'string', description: 'Page content' },
pageTitle: { type: 'string', description: 'Page title' },
// Read Page operation inputs
pageId: { type: 'string', description: 'Page ID' },
// List operation inputs
siteSelector: { type: 'string', description: 'Site selector' },
manualSiteId: { type: 'string', description: 'Manual site ID' },
pageSize: { type: 'number', description: 'Results per page' },
// Create List operation inputs
listDisplayName: { type: 'string', description: 'List display name' },
listDescription: { type: 'string', description: 'List description' },
listTemplate: { type: 'string', description: 'List template' },
// Read List operation inputs
listId: { type: 'string', description: 'List ID' },
listTitle: { type: 'string', description: 'List title' },
includeColumns: { type: 'boolean', description: 'Include columns in response' },
includeItems: { type: 'boolean', description: 'Include items in response' },
// Update List Item operation inputs
listItemId: { type: 'string', description: 'List item ID' },
listItemFields: { type: 'string', description: 'List item fields' },
},

View File

@@ -145,6 +145,7 @@ import { redditGetCommentsTool, redditGetPostsTool, redditHotPostsTool } from '@
import { s3GetObjectTool } from '@/tools/s3'
import { searchTool as serperSearch } from '@/tools/serper'
import {
sharepointAddListItemTool,
sharepointCreateListTool,
sharepointCreatePageTool,
sharepointGetListTool,
@@ -370,6 +371,7 @@ export const tools: Record<string, ToolConfig> = {
sharepoint_get_list: sharepointGetListTool,
sharepoint_create_list: sharepointCreateListTool,
sharepoint_update_list: sharepointUpdateListItemTool,
sharepoint_add_list_items: sharepointAddListItemTool,
// Provider chat tools
// Provider chat tools - handled separately in agent blocks
}

View File

@@ -0,0 +1,167 @@
import { createLogger } from '@/lib/logs/console/logger'
import type { SharepointAddListItemResponse, SharepointToolParams } from '@/tools/sharepoint/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('SharePointAddListItem')
export const addListItemTool: ToolConfig<SharepointToolParams, SharepointAddListItemResponse> = {
id: 'sharepoint_add_list_items',
name: 'Add SharePoint List Item',
description: 'Add a new item to a SharePoint list',
version: '1.0',
oauth: {
required: true,
provider: 'sharepoint',
additionalScopes: ['openid', 'profile', 'email', 'Sites.ReadWrite.All', 'offline_access'],
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'The access token for the SharePoint API',
},
siteSelector: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Select the SharePoint site',
},
siteId: {
type: 'string',
required: false,
visibility: 'hidden',
description: 'The ID of the SharePoint site (internal use)',
},
listId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The ID of the list to add the item to',
},
listItemFields: {
type: 'object',
required: true,
visibility: 'user-only',
description: 'Field values for the new list item',
},
},
request: {
url: (params) => {
const siteId = params.siteId || params.siteSelector || 'root'
if (!params.listId) {
throw new Error('listId must be provided')
}
const listSegment = params.listId
return `https://graph.microsoft.com/v1.0/sites/${siteId}/lists/${listSegment}/items`
},
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
Accept: 'application/json',
}),
body: (params) => {
if (!params.listItemFields || Object.keys(params.listItemFields).length === 0) {
throw new Error('listItemFields must not be empty')
}
const providedFields =
typeof params.listItemFields === 'object' &&
params.listItemFields !== null &&
'fields' in (params.listItemFields as Record<string, unknown>) &&
Object.keys(params.listItemFields as Record<string, unknown>).length === 1
? ((params.listItemFields as any).fields as Record<string, unknown>)
: (params.listItemFields as Record<string, unknown>)
if (!providedFields || Object.keys(providedFields).length === 0) {
throw new Error('No fields provided to create the SharePoint list item')
}
const readOnlyFields = new Set<string>([
'Id',
'id',
'UniqueId',
'GUID',
'ContentTypeId',
'Created',
'Modified',
'Author',
'Editor',
'CreatedBy',
'ModifiedBy',
'AuthorId',
'EditorId',
'_UIVersionString',
'Attachments',
'FileRef',
'FileDirRef',
'FileLeafRef',
])
const entries = Object.entries(providedFields)
const creatableEntries = entries.filter(([key]) => !readOnlyFields.has(key))
if (creatableEntries.length !== entries.length) {
const removed = entries.filter(([key]) => readOnlyFields.has(key)).map(([key]) => key)
logger.warn('Removed read-only SharePoint fields from create', {
removed,
})
}
if (creatableEntries.length === 0) {
const requestedKeys = Object.keys(providedFields)
throw new Error(
`All provided fields are read-only and cannot be set: ${requestedKeys.join(', ')}`
)
}
const sanitizedFields = Object.fromEntries(creatableEntries)
logger.info('Creating SharePoint list item', {
listId: params.listId,
fieldsKeys: Object.keys(sanitizedFields),
})
return {
fields: sanitizedFields,
}
},
},
transformResponse: async (response: Response, params) => {
let data: any
try {
data = await response.json()
} catch {
data = undefined
}
const itemId: string | undefined = data?.id
const fields: Record<string, unknown> | undefined = data?.fields || params?.listItemFields
return {
success: true,
output: {
item: {
id: itemId || 'unknown',
fields,
},
},
}
},
outputs: {
item: {
type: 'object',
description: 'Created SharePoint list item',
properties: {
id: { type: 'string', description: 'Item ID' },
fields: { type: 'object', description: 'Field values for the new item' },
},
},
},
}

View File

@@ -1,3 +1,4 @@
import { addListItemTool } from '@/tools/sharepoint/add_list_items'
import { createListTool } from '@/tools/sharepoint/create_list'
import { createPageTool } from '@/tools/sharepoint/create_page'
import { getListTool } from '@/tools/sharepoint/get_list'
@@ -11,3 +12,4 @@ export const sharepointGetListTool = getListTool
export const sharepointListSitesTool = listSitesTool
export const sharepointReadPageTool = readPageTool
export const sharepointUpdateListItemTool = updateListItemTool
export const sharepointAddListItemTool = addListItemTool

View File

@@ -259,6 +259,7 @@ export type SharepointResponse =
| SharepointGetListResponse
| SharepointCreateListResponse
| SharepointUpdateListItemResponse
| SharepointAddListItemResponse
export interface SharepointGetListResponse extends ToolResponse {
output: {
@@ -282,3 +283,12 @@ export interface SharepointUpdateListItemResponse extends ToolResponse {
}
}
}
export interface SharepointAddListItemResponse extends ToolResponse {
output: {
item: {
id: string
fields?: Record<string, unknown>
}
}
}