Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ build/
dist/

amazonCookies.json
wishlists.json
mocks/*_202*.html
mocks/*_202*.txt
!mcp-server-amazon.log
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,18 @@ See `~/Library/Logs/Claude/mcp-server-amazon.log`
## License

[The MIT license](./LICENSE)

## Wishlist Feature (fork)

This fork adds a local wishlist system via 6 new MCP tools:

| Tool | Description |
|---|---|
| `create-wishlist` | Create a new named wishlist |
| `add-to-wishlist` | Add a product by ASIN (auto-creates wishlist if needed) |
| `get-wishlist` | View all items in a wishlist |
| `list-wishlists` | List all wishlists with item counts and estimated totals |
| `remove-from-wishlist` | Remove an item by ASIN |
| `delete-wishlist` | Delete an entire wishlist |

Wishlists are stored locally in `wishlists.json` (gitignored). No Amazon API required — pure local JSON persistence.
113 changes: 113 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { z } from 'zod'
import { getOrdersHistory } from './orders.js'
import { getCartContent, addToCart, clearCart } from './cart.js'
import { getProductDetails, searchProducts } from './products.js'
import { createWishlist, addToWishlist, getWishlist, listWishlists, removeFromWishlist, deleteWishlist } from './wishlist.js'

// Create server instance
const server = new McpServer({
Expand Down Expand Up @@ -268,6 +269,118 @@ server.tool(
}
)

// ##################################
// Wishlist Tools
// ##################################

server.tool(
'create-wishlist',
'Create a new local wishlist to save Amazon products for later. Wishlists are stored in wishlists.json.',
{
name: z.string().min(1).describe('Name for the wishlist, e.g. "Camping Gear", "Nintendo Switch", "Tesla Accessories"'),
description: z.string().optional().describe('Optional description for the wishlist'),
},
async ({ name, description }) => {
try {
const result = createWishlist(name, description)
return { content: [{ type: 'text', text: result.success ? `✅ ${result.message}` : `❌ ${result.message}` }] }
} catch (error: any) {
return { content: [{ type: 'text', text: `❌ Error creating wishlist: ${error.message}` }] }
}
}
)

server.tool(
'add-to-wishlist',
'Add a product to a local wishlist by ASIN. Auto-creates the wishlist if it does not exist. Use this to save cart items for later.',
{
wishlistName: z.string().min(1).describe('Name of the wishlist to add the item to'),
asin: z.string().length(10).describe('10-character Amazon ASIN of the product'),
title: z.string().min(1).describe('Product title'),
price: z.string().min(1).describe('Product price, e.g. "$19.99"'),
productUrl: z.string().describe('Amazon product URL or path'),
image: z.string().optional().describe('Product image URL'),
notes: z.string().optional().describe('Optional personal notes about this item'),
},
async ({ wishlistName, asin, title, price, productUrl, image, notes }) => {
try {
const result = addToWishlist(wishlistName, { asin, title, price, productUrl, image, notes })
return { content: [{ type: 'text', text: result.success ? `✅ ${result.message}` : `❌ ${result.message}` }] }
} catch (error: any) {
return { content: [{ type: 'text', text: `❌ Error adding to wishlist: ${error.message}` }] }
}
}
)

server.tool(
'get-wishlist',
'Get all items in a specific wishlist, including titles, prices, ASINs, and product links.',
{
name: z.string().min(1).describe('Name of the wishlist to retrieve'),
},
async ({ name }) => {
try {
const result = getWishlist(name)
if (!result) {
return { content: [{ type: 'text', text: `❌ Wishlist "${name}" not found. Use list-wishlists to see all wishlists.` }] }
}
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }
} catch (error: any) {
return { content: [{ type: 'text', text: `❌ Error retrieving wishlist: ${error.message}` }] }
}
}
)

server.tool(
'list-wishlists',
'List all wishlists with their names, item counts, estimated totals, and last updated timestamps.',
{},
async () => {
try {
const result = listWishlists()
if (result.length === 0) {
return { content: [{ type: 'text', text: 'No wishlists found. Use create-wishlist or add-to-wishlist to get started.' }] }
}
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }
} catch (error: any) {
return { content: [{ type: 'text', text: `❌ Error listing wishlists: ${error.message}` }] }
}
}
)

server.tool(
'remove-from-wishlist',
'Remove a specific product from a wishlist by ASIN.',
{
wishlistName: z.string().min(1).describe('Name of the wishlist'),
asin: z.string().length(10).describe('10-character ASIN of the product to remove'),
},
async ({ wishlistName, asin }) => {
try {
const result = removeFromWishlist(wishlistName, asin)
return { content: [{ type: 'text', text: result.success ? `✅ ${result.message}` : `❌ ${result.message}` }] }
} catch (error: any) {
return { content: [{ type: 'text', text: `❌ Error removing from wishlist: ${error.message}` }] }
}
}
)

server.tool(
'delete-wishlist',
'Permanently delete an entire wishlist and all its items.',
{
name: z.string().min(1).describe('Name of the wishlist to delete'),
},
async ({ name }) => {
try {
const result = deleteWishlist(name)
return { content: [{ type: 'text', text: result.success ? `✅ ${result.message}` : `❌ ${result.message}` }] }
} catch (error: any) {
return { content: [{ type: 'text', text: `❌ Error deleting wishlist: ${error.message}` }] }
}
}
)

// Start the server
async function main() {
const transport = new StdioServerTransport()
Expand Down
199 changes: 199 additions & 0 deletions src/wishlist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import fs from 'fs'
import path from 'path'

const __dirname = new URL('.', import.meta.url).pathname
const WISHLISTS_FILE_PATH = path.join(__dirname, '..', 'wishlists.json')

// ##################################
// Types
// ##################################

export interface WishlistItem {
asin: string
title: string
price: string
productUrl: string
image?: string
notes?: string
addedAt: string
}

export interface Wishlist {
name: string
description?: string
items: WishlistItem[]
createdAt: string
updatedAt: string
}

interface WishlistStore {
wishlists: Record<string, Wishlist>
}

// ##################################
// Storage Helpers
// ##################################

function wishlistKey(name: string): string {
return name.toLowerCase().replace(/\s+/g, '-')
}

function loadWishlists(): WishlistStore {
if (fs.existsSync(WISHLISTS_FILE_PATH)) {
try {
return JSON.parse(fs.readFileSync(WISHLISTS_FILE_PATH, 'utf-8'))
} catch {
return { wishlists: {} }
}
}
return { wishlists: {} }
}

function saveWishlists(store: WishlistStore): void {
fs.writeFileSync(WISHLISTS_FILE_PATH, JSON.stringify(store, null, 2))
}

// ##################################
// Create Wishlist
// ##################################

export function createWishlist(name: string, description?: string): { success: boolean; message: string } {
const store = loadWishlists()
const key = wishlistKey(name)

if (store.wishlists[key]) {
return { success: false, message: `Wishlist "${name}" already exists.` }
}

store.wishlists[key] = {
name,
description,
items: [],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
}

saveWishlists(store)
console.error(`[INFO][create-wishlist] Created wishlist "${name}"`)
return { success: true, message: `Wishlist "${name}" created successfully.` }
}

// ##################################
// Add to Wishlist
// ##################################

export function addToWishlist(
wishlistName: string,
item: Omit<WishlistItem, 'addedAt'>,
): { success: boolean; message: string } {
const store = loadWishlists()
const key = wishlistKey(wishlistName)

// Auto-create wishlist if it doesn't exist
if (!store.wishlists[key]) {
store.wishlists[key] = {
name: wishlistName,
items: [],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
}
console.error(`[INFO][add-to-wishlist] Auto-created wishlist "${wishlistName}"`)
}

const wishlist = store.wishlists[key]

// Prevent duplicates
if (wishlist.items.some(i => i.asin === item.asin)) {
return { success: false, message: `"${item.title}" is already in wishlist "${wishlistName}".` }
}

wishlist.items.push({ ...item, addedAt: new Date().toISOString() })
wishlist.updatedAt = new Date().toISOString()

saveWishlists(store)
console.error(`[INFO][add-to-wishlist] Added ASIN ${item.asin} to wishlist "${wishlistName}"`)
return { success: true, message: `Added "${item.title}" to wishlist "${wishlistName}".` }
}

// ##################################
// Get Wishlist
// ##################################

export function getWishlist(name: string): Wishlist | null {
const store = loadWishlists()
const key = wishlistKey(name)
return store.wishlists[key] ?? null
}

// ##################################
// List Wishlists
// ##################################

export function listWishlists(): {
name: string
description?: string
itemCount: number
estimatedTotal: string
updatedAt: string
}[] {
const store = loadWishlists()

return Object.values(store.wishlists).map(wishlist => {
const total = wishlist.items.reduce((sum, item) => {
const price = parseFloat(item.price.replace(/[$,]/g, ''))
return sum + (isNaN(price) ? 0 : price)
}, 0)

return {
name: wishlist.name,
description: wishlist.description,
itemCount: wishlist.items.length,
estimatedTotal: `$${total.toFixed(2)}`,
updatedAt: wishlist.updatedAt,
}
})
}

// ##################################
// Remove from Wishlist
// ##################################

export function removeFromWishlist(wishlistName: string, asin: string): { success: boolean; message: string } {
const store = loadWishlists()
const key = wishlistKey(wishlistName)

if (!store.wishlists[key]) {
return { success: false, message: `Wishlist "${wishlistName}" not found.` }
}

const wishlist = store.wishlists[key]
const before = wishlist.items.length
wishlist.items = wishlist.items.filter(i => i.asin !== asin)

if (wishlist.items.length === before) {
return { success: false, message: `ASIN "${asin}" not found in wishlist "${wishlistName}".` }
}

wishlist.updatedAt = new Date().toISOString()
saveWishlists(store)
console.error(`[INFO][remove-from-wishlist] Removed ASIN ${asin} from wishlist "${wishlistName}"`)
return { success: true, message: `Removed item from wishlist "${wishlistName}".` }
}

// ##################################
// Delete Wishlist
// ##################################

export function deleteWishlist(name: string): { success: boolean; message: string } {
const store = loadWishlists()
const key = wishlistKey(name)

if (!store.wishlists[key]) {
return { success: false, message: `Wishlist "${name}" not found.` }
}

delete store.wishlists[key]
saveWishlists(store)
console.error(`[INFO][delete-wishlist] Deleted wishlist "${name}"`)
return { success: true, message: `Wishlist "${name}" deleted.` }
}