@smartsheet/smar-mcp
Version:
A Model Context Protocol (MCP) server for interacting with the Smartsheet API. This server provides tools for searching, retrieving, and updating Smartsheet sheets through the MCP protocol.
390 lines (389 loc) • 15.7 kB
JavaScript
import { z } from "zod";
export function getSheetTools(server, api, allowDeleteTools) {
server.tool("get_sheet", "Retrieves the current state of a sheet, including rows, columns, and cells", {
sheetId: z.string().describe("The ID of the sheet to retrieve"),
include: z.string().optional().describe("Comma-separated list of elements to include (e.g., 'format,formulas')"),
pageSize: z.number().optional().describe("Number of rows to return per page"),
page: z.number().optional().describe("Page number to return"),
}, async ({ sheetId, include, pageSize, page }) => {
try {
console.info(`Getting sheet with ID: ${sheetId}`);
const sheet = await api.sheets.getSheet(sheetId, include, undefined, pageSize, page);
return {
content: [
{
type: "text",
text: JSON.stringify(sheet, null, 2)
}
]
};
}
catch (error) {
console.error(`Failed to get sheet with ID: ${sheetId}`, { error });
return {
content: [
{
type: "text",
text: `Failed to get sheet: ${error.message}`
}
],
isError: true
};
}
});
server.tool("get_sheet_by_url", "Retrieves the current state of a sheet, including rows, columns, and cells", {
url: z.string().describe("The URL of the sheet to retrieve"),
include: z.string().optional().describe("Comma-separated list of elements to include (e.g., 'format,formulas')"),
pageSize: z.number().optional().describe("Number of rows to return per page"),
page: z.number().optional().describe("Page number to return"),
}, async ({ url, include, pageSize, page }) => {
try {
console.info(`Getting sheet with URL: ${url}`);
const match = url.match(/\/sheets\/([^?\/]+)/);
const directIdToken = match ? match[1] : null;
if (!directIdToken) {
return {
content: [
{
type: "text",
text: `Failed to get sheet: Invalid URL format`
}
],
isError: true
};
}
const sheet = await api.sheets.getSheetByDirectIdToken(directIdToken, include, undefined, pageSize, page);
return {
content: [
{
type: "text",
text: JSON.stringify(sheet, null, 2)
}
]
};
}
catch (error) {
console.error(`Failed to get sheet with URL: ${url}`, { error });
return {
content: [
{
type: "text",
text: `Failed to get sheet: ${error.message}`
}
],
isError: true
};
}
});
server.tool("get_sheet_version", "Gets the current version number of a sheet", {
sheetId: z.string().describe("The ID of the sheet"),
}, async ({ sheetId }) => {
try {
console.info(`Getting version for sheet with ID: ${sheetId}`);
const version = await api.sheets.getSheetVersion(sheetId);
return {
content: [
{
type: "text",
text: JSON.stringify(version, null, 2)
}
]
};
}
catch (error) {
console.error(`Failed to get sheet version for sheet ID: ${sheetId}`, { error });
return {
content: [
{
type: "text",
text: `Failed to get sheet version: ${error.message}`
}
],
isError: true
};
}
});
// Tool: Get Cell History
server.tool("get_cell_history", "Retrieves the history of changes for a specific cell", {
sheetId: z.string().describe("The ID of the sheet"),
rowId: z.string().describe("The ID of the row"),
columnId: z.string().describe("The ID of the column"),
include: z.string().optional().describe("Optional parameter to include additional information"),
pageSize: z.number().optional().describe("Number of history entries to return per page"),
page: z.number().optional().describe("Page number to return"),
}, async ({ sheetId, rowId, columnId, include, pageSize, page }) => {
try {
console.info(`Getting history for cell at row ${rowId}, column ${columnId} in sheet ${sheetId}`);
const history = await api.sheets.getCellHistory(sheetId, rowId, columnId, include, pageSize, page);
return {
content: [
{
type: "text",
text: JSON.stringify(history, null, 2)
}
]
};
}
catch (error) {
console.error(`Failed to get cell history for row ${rowId}, column ${columnId} in sheet ${sheetId}`, { error });
return {
content: [
{
type: "text",
text: `Failed to get cell history: ${error.message}`
}
],
isError: true
};
}
});
// Tool: Get Row
server.tool("get_row", "Retrieves a specific row from a sheet", {
sheetId: z.string().describe("The ID of the sheet"),
rowId: z.string().describe("The ID of the row"),
include: z.string().optional().describe("Comma-separated list of elements to include (e.g., 'format,formulas')"),
}, async ({ sheetId, rowId, include }) => {
try {
console.info(`Getting row ${rowId} in sheet ${sheetId}`);
const row = await api.sheets.getRow(sheetId, rowId, include);
return {
content: [
{
type: "text",
text: JSON.stringify(row, null, 2)
}
]
};
}
catch (error) {
console.error(`Failed to get row ${rowId} in sheet ${sheetId}`, { error });
return {
content: [
{
type: "text",
text: `Failed to get row: ${error.message}`
}
],
isError: true
};
}
});
// Tool: Update Rows
server.tool("update_rows", "Updates rows in a sheet, including cell values, formatting, and formulae", {
sheetId: z.string().describe("The ID of the sheet"),
rows: z.array(z.object({
id: z.string().describe("Row ID"),
cells: z.array(z.object({
columnId: z.number().or(z.string()).describe("Column ID"),
value: z.any().optional().describe("Cell value"),
formula: z.string().optional().describe("Cell formula"),
format: z.string().optional().describe("Cell format"),
})).describe("Array of cell objects"),
})).describe("Array of row objects to update"),
}, async ({ sheetId, rows }) => {
try {
console.info(`Updating ${rows.length} rows in sheet ${sheetId}`);
const result = await api.sheets.updateRows(sheetId, rows);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2)
}
]
};
}
catch (error) {
console.error(`Failed to update ${rows.length} rows in sheet ${sheetId}`, { error });
return {
content: [
{
type: "text",
text: `Failed to update rows: ${error.message}`
}
],
isError: true
};
}
});
// Tool: Add Rows
server.tool("add_rows", "Adds new rows to a sheet", {
sheetId: z.string().describe("The ID of the sheet"),
rows: z.array(z.object({
toTop: z.boolean().optional().describe("Add row to the top of the sheet"),
toBottom: z.boolean().optional().describe("Add row to the bottom of the sheet"),
cells: z.array(z.object({
columnId: z.number().or(z.string()).describe("Column ID"),
value: z.any().optional().describe("Cell value"),
formula: z.string().optional().describe("Cell formula"),
format: z.string().optional().describe("Cell format"),
})).describe("Array of cell objects"),
})).describe("Array of row objects to add"),
}, async ({ sheetId, rows }) => {
try {
console.info(`Adding ${rows.length} rows to sheet ${sheetId}`);
const result = await api.sheets.addRows(sheetId, rows);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2)
}
]
};
}
catch (error) {
console.error(`Failed to add ${rows.length} rows to sheet ${sheetId}`, { error });
return {
content: [
{
type: "text",
text: `Failed to add rows: ${error.message}`
}
],
isError: true
};
}
});
// Tool: Delete Rows (conditionally registered)
if (allowDeleteTools) {
server.tool("delete_rows", "Deletes rows from a sheet", {
sheetId: z.string().describe("The ID of the sheet"),
rowIds: z.array(z.string()).describe("Array of row IDs to delete"),
ignoreRowsNotFound: z.boolean().optional().describe("If true, don't throw an error if rows are not found"),
}, async ({ sheetId, rowIds, ignoreRowsNotFound }) => {
try {
console.info(`Deleting ${rowIds.length} rows from sheet ${sheetId}`);
const result = await api.sheets.deleteRows(sheetId, rowIds, ignoreRowsNotFound);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2)
}
]
};
}
catch (error) {
console.error(`Failed to delete ${rowIds.length} rows from sheet ${sheetId}`, { error });
return {
content: [
{
type: "text",
text: `Failed to delete rows: ${error.message}`
}
],
isError: true
};
}
});
}
else {
console.warn("Delete operations are disabled. Set ALLOW_DELETE_TOOLS=true to enable them.");
}
// Tool: Get Sheet Location
server.tool("get_sheet_location", "Gets the folder ID where a sheet is located", {
sheetId: z.string().describe("The ID of the sheet"),
}, async ({ sheetId }) => {
try {
console.info(`Getting location for sheet ${sheetId}`);
const location = await api.sheets.getSheetLocation(sheetId);
return {
content: [
{
type: "text",
text: JSON.stringify(location, null, 2)
}
]
};
}
catch (error) {
console.error(`Failed to get location for sheet ${sheetId}`, { error });
return {
content: [
{
type: "text",
text: `Failed to get sheet location: ${error.message}`
}
],
isError: true
};
}
});
// Tool: Copy Sheet
server.tool("copy_sheet", "Creates a copy of the specified sheet in the same folder", {
sheetId: z.string().describe("The ID of the sheet to copy"),
destinationName: z.string().describe("Name for the sheet copy"),
destinationFolderId: z.string().optional().describe("ID of the destination folder (same as source if not specified)"),
}, async ({ sheetId, destinationName, destinationFolderId }) => {
try {
console.info(`Copying sheet ${sheetId} to "${destinationName}"`);
// If no destination folder is specified, get the current folder
if (!destinationFolderId) {
try {
const location = await api.sheets.getSheetLocation(sheetId);
destinationFolderId = location.folderId;
}
catch (error) {
console.warn("Failed to get sheet location, using default folder", { error });
}
}
const result = await api.sheets.copySheet(sheetId, destinationName, destinationFolderId);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2)
}
]
};
}
catch (error) {
console.error(`Failed to copy sheet ${sheetId} to "${destinationName}"`, { error });
return {
content: [
{
type: "text",
text: `Failed to copy sheet: ${error.message}`
}
],
isError: true
};
}
});
// Tool: Create Sheet
server.tool("create_sheet", "Creates a new sheet", {
name: z.string().describe("Name for the new sheet"),
columns: z.array(z.object({
title: z.string().describe("Column title"),
type: z.string().describe("Column type"),
primary: z.boolean().optional().describe("Whether this is the primary column"),
})).describe("Array of column objects"),
folderId: z.string().optional().describe("ID of the folder where the sheet should be created"),
}, async ({ name, columns, folderId }) => {
try {
console.info(`Creating new sheet "${name}"`);
const result = await api.sheets.createSheet(name, columns, folderId);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2)
}
]
};
}
catch (error) {
console.error(`Failed to create sheet "${name}"`, { error });
return {
content: [
{
type: "text",
text: `Failed to create sheet: ${error.message}`
}
],
isError: true
};
}
});
}