All checks were successful
CI / ci (push) Successful in 33s
The @/ alias resolves via tsconfig but not in production where Bun runs server files directly. Use relative paths instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
191 lines
5.0 KiB
TypeScript
191 lines
5.0 KiB
TypeScript
import type { db as prodDb } from "../../../db/index.ts";
|
|
import {
|
|
createItem,
|
|
deleteItem,
|
|
getAllItems,
|
|
getItemById,
|
|
updateItem,
|
|
} from "../../services/item.service.ts";
|
|
|
|
type Db = typeof prodDb;
|
|
|
|
interface ToolResult {
|
|
content: Array<{ type: "text"; text: string }>;
|
|
}
|
|
|
|
function textResult(data: unknown): ToolResult {
|
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
}
|
|
|
|
function errorResult(message: string): ToolResult {
|
|
return {
|
|
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
};
|
|
}
|
|
|
|
export const itemToolDefinitions = [
|
|
{
|
|
name: "list_items",
|
|
description:
|
|
"List all items in the gear collection, optionally filtered by category.",
|
|
inputSchema: {
|
|
type: "object" as const,
|
|
properties: {
|
|
categoryId: {
|
|
type: "number",
|
|
description: "Filter items by category ID",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "get_item",
|
|
description: "Get a single item by its ID, including all details.",
|
|
inputSchema: {
|
|
type: "object" as const,
|
|
properties: {
|
|
id: { type: "number", description: "The item ID" },
|
|
},
|
|
required: ["id"],
|
|
},
|
|
},
|
|
{
|
|
name: "create_item",
|
|
description:
|
|
"Add a new item to the gear collection. Use this for items you've already decided on. For items you're still researching, use create_thread instead.",
|
|
inputSchema: {
|
|
type: "object" as const,
|
|
properties: {
|
|
name: { type: "string", description: "Item name" },
|
|
categoryId: { type: "number", description: "Category ID" },
|
|
weightGrams: { type: "number", description: "Weight in grams" },
|
|
priceCents: { type: "number", description: "Price in cents" },
|
|
notes: { type: "string", description: "Notes about the item" },
|
|
productUrl: { type: "string", description: "URL to the product page" },
|
|
imageFilename: {
|
|
type: "string",
|
|
description: "Filename of an uploaded image",
|
|
},
|
|
imageSourceUrl: {
|
|
type: "string",
|
|
description: "Original URL the image was fetched from",
|
|
},
|
|
},
|
|
required: ["name", "categoryId"],
|
|
},
|
|
},
|
|
{
|
|
name: "update_item",
|
|
description: "Update an existing item's fields.",
|
|
inputSchema: {
|
|
type: "object" as const,
|
|
properties: {
|
|
id: { type: "number", description: "The item ID to update" },
|
|
name: { type: "string", description: "Item name" },
|
|
categoryId: { type: "number", description: "Category ID" },
|
|
weightGrams: { type: "number", description: "Weight in grams" },
|
|
priceCents: { type: "number", description: "Price in cents" },
|
|
notes: { type: "string", description: "Notes about the item" },
|
|
productUrl: { type: "string", description: "URL to the product page" },
|
|
imageFilename: {
|
|
type: "string",
|
|
description: "Filename of an uploaded image",
|
|
},
|
|
imageSourceUrl: {
|
|
type: "string",
|
|
description: "Original URL the image was fetched from",
|
|
},
|
|
},
|
|
required: ["id"],
|
|
},
|
|
},
|
|
{
|
|
name: "delete_item",
|
|
description: "Delete an item from the gear collection by ID.",
|
|
inputSchema: {
|
|
type: "object" as const,
|
|
properties: {
|
|
id: { type: "number", description: "The item ID to delete" },
|
|
},
|
|
required: ["id"],
|
|
},
|
|
},
|
|
];
|
|
|
|
export function registerItemTools(db: Db) {
|
|
return {
|
|
list_items: async (args: { categoryId?: number }): Promise<ToolResult> => {
|
|
try {
|
|
const items = getAllItems(db);
|
|
if (args.categoryId) {
|
|
return textResult(
|
|
items.filter((i) => i.categoryId === args.categoryId),
|
|
);
|
|
}
|
|
return textResult(items);
|
|
} catch (err) {
|
|
return errorResult((err as Error).message);
|
|
}
|
|
},
|
|
|
|
get_item: async (args: { id: number }): Promise<ToolResult> => {
|
|
try {
|
|
const item = getItemById(db, args.id);
|
|
if (!item) return errorResult(`Item ${args.id} not found`);
|
|
return textResult(item);
|
|
} catch (err) {
|
|
return errorResult((err as Error).message);
|
|
}
|
|
},
|
|
|
|
create_item: async (args: {
|
|
name: string;
|
|
categoryId: number;
|
|
weightGrams?: number;
|
|
priceCents?: number;
|
|
notes?: string;
|
|
productUrl?: string;
|
|
imageFilename?: string;
|
|
imageSourceUrl?: string;
|
|
}): Promise<ToolResult> => {
|
|
try {
|
|
const item = createItem(db, args);
|
|
return textResult(item);
|
|
} catch (err) {
|
|
return errorResult((err as Error).message);
|
|
}
|
|
},
|
|
|
|
update_item: async (args: {
|
|
id: number;
|
|
name?: string;
|
|
categoryId?: number;
|
|
weightGrams?: number;
|
|
priceCents?: number;
|
|
notes?: string;
|
|
productUrl?: string;
|
|
imageFilename?: string;
|
|
imageSourceUrl?: string;
|
|
}): Promise<ToolResult> => {
|
|
try {
|
|
const { id, ...data } = args;
|
|
const item = updateItem(db, id, data);
|
|
if (!item) return errorResult(`Item ${id} not found`);
|
|
return textResult(item);
|
|
} catch (err) {
|
|
return errorResult((err as Error).message);
|
|
}
|
|
},
|
|
|
|
delete_item: async (args: { id: number }): Promise<ToolResult> => {
|
|
try {
|
|
const item = deleteItem(db, args.id);
|
|
if (!item) return errorResult(`Item ${args.id} not found`);
|
|
return textResult({ deleted: true, item });
|
|
} catch (err) {
|
|
return errorResult((err as Error).message);
|
|
}
|
|
},
|
|
};
|
|
}
|