Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/heavy-moose-wish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@knocklabs/agent-toolkit": minor
---

fix: a round of tool improvements

- introduce new email layout upsert tool
- fix issues with models like gpt-4.1 and gemini-2.5
- make workflow step tools update as well as insert
- fix tool names
- unify all create or update tools to be upsert
49 changes: 49 additions & 0 deletions src/lib/tools/email-layouts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { KnockTool } from "../knock-tool.js";
type SerializedEmailLayout = {
key: string;
name: string;
htmlContent: string;
textContent: string;
};

function serializeEmailLayoutResponse(
Expand All @@ -17,6 +19,8 @@ function serializeEmailLayoutResponse(
return {
key: emailLayout.key,
name: emailLayout.name,
htmlContent: emailLayout.html_layout,
textContent: emailLayout.text_layout,
};
}

Expand Down Expand Up @@ -45,10 +49,55 @@ const listEmailLayouts = KnockTool({
},
});

const createOrUpdateEmailLayout = KnockTool({
method: "upsert_email_layout",
name: "Create or update email layout",
description: `Create or update a new email layout within the environment given. Use this tool when you need to define shared pieces of content across multiple email templates, like a header/footer. The email layout will be used to render the email template.

Here are the rules for creating an email layout:

- Every email layout must have a \`{{ content }}\` tag. This is where the content of the email will be injected.
- You must set both an HTML and text version of the email layout.
- CSS should be included in the HTML version of the email layout under <style> tags.
`,
parameters: z.object({
environment: z
.string()
.optional()
.describe(
"(string): The environment to create or update the email layout for. Defaults to `development`."
),
key: z
.string()
.describe("(string): The key of the email layout to create or update."),
name: z.string().describe("(string): The name of the email layout."),
htmlContent: z
.string()
.describe("(string): The HTML content of the email layout."),
textContent: z
.string()
.describe("(string): The text content of the email layout."),
}),
execute: (knockClient, config) => async (params) => {
const response = await knockClient.emailLayouts.upsert(params.key, {
environment: params.environment ?? config.environment ?? "development",
email_layout: {
name: params.name,
html_layout: params.htmlContent,
text_layout: params.textContent,
},
});

return serializeEmailLayoutResponse(response.email_layout);
},
});

export const emailLayouts = {
listEmailLayouts,
createOrUpdateEmailLayout,
};

export const permissions = {
read: ["listEmailLayouts"],
manage: ["createOrUpdateEmailLayout"],
};
2 changes: 1 addition & 1 deletion src/lib/tools/message-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const listMessageTypes = KnockTool({
});

const createOrUpdateMessageType = KnockTool({
method: "create_or_update_message_type",
method: "upsert_message_type",
name: "Create or update message type",
description: `
Create or update a message type. A message type is a schema that defines fields available to an editor within Knock. Message types always have at least one variant, that MUST be named "default". Use this tool when you need to create a new message type, or update an existing message type.
Expand Down
2 changes: 1 addition & 1 deletion src/lib/tools/objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const getObject = KnockTool({
});

const createOrUpdateObject = KnockTool({
method: "create_or_update_object",
method: "upsert_object",
name: "Create or update object",
description: `Create or update an object in a specific collection. Objects are used to model custom collections in Knock that are NOT users or tenants. If the object does not exist, it will be created. If the object exists, it will be updated with the provided properties. The update will always perform an upsert operation, so you do not need to provide the full properties each time.

Expand Down
10 changes: 5 additions & 5 deletions src/lib/tools/tenants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ const listTenants = KnockTool({
},
});

const setTenant = KnockTool({
method: "set_tenant",
name: "Set tenant",
const createOrUpdateTenant = KnockTool({
method: "upsert_tenant",
name: "Create or update tenant",
description: `
Creates or updates a tenant using the properties provided. Tenants in Knock are used to model organizations, teams, and other groups of users. They are a special type of object.

Expand Down Expand Up @@ -83,10 +83,10 @@ const setTenant = KnockTool({
export const tenants = {
getTenant,
listTenants,
setTenant,
createOrUpdateTenant,
};

export const permissions = {
read: ["getTenant", "listTenants"],
manage: ["setTenant"],
manage: ["createOrUpdateTenant"],
};
2 changes: 1 addition & 1 deletion src/lib/tools/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const getUser = KnockTool({
});

const createOrUpdateUser = KnockTool({
method: "create_or_update_user",
method: "upsert_user",
name: "Create or update user",
description: `
Creates a new user if they don't exist, or updates the user object for the given userId, including email, name, phone number, and any custom properties.
Expand Down
Loading