Skip to main content
Glama

basecamp_update_todo

Modify Basecamp todo items by updating titles, assigning people, or editing content using partial operations to reduce token usage.

Instructions

Update a todo item. Use partial content operations when possible to save on token usage.

HTML rules for content:

  • Allowed tags: div, span, h1, br, strong, em, strike, a (with an href attribute), pre, ol, ul, li, blockquote, bc-attachment (with sgid attribute).

  • Try to be semantic despite the limitations of tags. Use double to create paragraph spacing

  • To mention people:

  • To consume less tokens, existing tags can be rewritten by dropping any attributes/inner content and just leave the "sgid" and "caption" attributes, without loosing any information

  • You can highlight parts of the content in this format ... valid colors are:

    • red: 255, 229, 229

    • yellow: 250, 247, 133

    • green: 228, 248, 226

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
bucket_idYesBasecamp resource identifier
todo_idYes
titleNoNew todo title
assignee_idsNoArray of person IDs to assign to this todo
contentNoIf provided, replaces entire HTML content. Cannot be used with content_append, content_prepend, or search_replace.
content_appendNoText to append to the end of current content. Cannot be used with content.
content_prependNoText to prepend to the beginning of current content. Cannot be used with content.
search_replaceNoArray of search-replace operations to apply to current content. Cannot be used with content.

Implementation Reference

  • The handler function for 'basecamp_update_todo' tool. It validates inputs, fetches the current todo state, applies content operations for partial updates to description, computes new title and assignees, calls the Basecamp todos.update API, and returns success or error message.
    async (params) => { try { // Validate at least one operation is provided validateContentOperations(params, ["title", "assignee_ids"]); const client = await initializeBasecampClient(); // Fetch current todo to get existing values const currentResponse = await client.todos.get({ params: { bucketId: params.bucket_id, todoId: params.todo_id, }, }); if (currentResponse.status !== 200 || !currentResponse.body) { throw new Error( `Failed to fetch current todo: ${currentResponse.status}`, ); } // Determine final title (maps to Basecamp's content field) const finalTitle = params.title ?? currentResponse.body.content; // Determine final content (maps to Basecamp's description field) let finalContent: string | undefined; const hasPartialOps = params.content_append || params.content_prepend || params.search_replace; if (hasPartialOps) { const currentContent = currentResponse.body.description || ""; const result = applyContentOperations(currentContent, params); if (result === undefined) { throw new Error("Content operations resulted in undefined content"); } finalContent = result; } else if (params.content !== undefined) { // Full content replacement finalContent = params.content; } const response = await client.todos.update({ params: { bucketId: params.bucket_id, todoId: params.todo_id, }, body: { content: finalTitle, ...(finalContent !== undefined ? { description: finalContent } : {}), ...(params.assignee_ids !== undefined ? { assignee_ids: params.assignee_ids } : {}), }, }); if (response.status !== 200 || !response.body) { throw new Error("Failed to update todo"); } return { content: [ { type: "text", text: `Todo updated!\n\nID: ${response.body.id}\nContent: ${response.body.content}`, }, ], }; } catch (error) { return { content: [{ type: "text", text: handleBasecampError(error) }], }; } },
  • The input schema and metadata (title, description, annotations) for the 'basecamp_update_todo' tool, defining parameters like bucket_id, todo_id, title, assignee_ids, and content operations.
    { title: "Update Basecamp Todo", description: `Update a todo item. Use partial content operations when possible to save on token usage. ${htmlRules}`, inputSchema: { bucket_id: BasecampIdSchema, todo_id: BasecampIdSchema, title: z.string().optional().describe("New todo title"), assignee_ids: z .array(BasecampIdSchema) .optional() .describe("Array of person IDs to assign to this todo"), ...ContentOperationFields, }, annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, },
  • Registers the 'basecamp_update_todo' tool with the MCP server, providing name, schema, annotations, and handler function.
    server.registerTool( "basecamp_update_todo", { title: "Update Basecamp Todo", description: `Update a todo item. Use partial content operations when possible to save on token usage. ${htmlRules}`, inputSchema: { bucket_id: BasecampIdSchema, todo_id: BasecampIdSchema, title: z.string().optional().describe("New todo title"), assignee_ids: z .array(BasecampIdSchema) .optional() .describe("Array of person IDs to assign to this todo"), ...ContentOperationFields, }, annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, }, async (params) => { try { // Validate at least one operation is provided validateContentOperations(params, ["title", "assignee_ids"]); const client = await initializeBasecampClient(); // Fetch current todo to get existing values const currentResponse = await client.todos.get({ params: { bucketId: params.bucket_id, todoId: params.todo_id, }, }); if (currentResponse.status !== 200 || !currentResponse.body) { throw new Error( `Failed to fetch current todo: ${currentResponse.status}`, ); } // Determine final title (maps to Basecamp's content field) const finalTitle = params.title ?? currentResponse.body.content; // Determine final content (maps to Basecamp's description field) let finalContent: string | undefined; const hasPartialOps = params.content_append || params.content_prepend || params.search_replace; if (hasPartialOps) { const currentContent = currentResponse.body.description || ""; const result = applyContentOperations(currentContent, params); if (result === undefined) { throw new Error("Content operations resulted in undefined content"); } finalContent = result; } else if (params.content !== undefined) { // Full content replacement finalContent = params.content; } const response = await client.todos.update({ params: { bucketId: params.bucket_id, todoId: params.todo_id, }, body: { content: finalTitle, ...(finalContent !== undefined ? { description: finalContent } : {}), ...(params.assignee_ids !== undefined ? { assignee_ids: params.assignee_ids } : {}), }, }); if (response.status !== 200 || !response.body) { throw new Error("Failed to update todo"); } return { content: [ { type: "text", text: `Todo updated!\n\nID: ${response.body.id}\nContent: ${response.body.content}`, }, ], }; } catch (error) { return { content: [{ type: "text", text: handleBasecampError(error) }], }; } }, );

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/stefanoverna/basecamp-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server