Skip to main content
Glama
baskcart

W3Ship MCP Server

by baskcart

create_listing

Create peer-to-peer marketplace listings for selling items using cryptocurrency payments. Specify title, price, currency, and wallet address without merchant onboarding requirements.

Instructions

Create a P2P marketplace listing. Anyone can sell items — no merchant onboarding needed. Specify title, price, currency (USDC/ETH/etc), and your wallet address for payment.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
titleYesItem title (e.g. "PS5 Console")
descriptionYesItem description
priceYesPrice amount (e.g. 200). Set to 0 for promotional items.
currencyNoPayment currency (USDC, ETH, DAI, etc). Default: USDC
categoryNoCategory: electronics, clothing, collectibles, home, sports, gifts, books, promotional, other
sellerAddressNoSeller wallet address for payment. Uses W3SHIP_PUBLIC_KEY if not provided.
sellerNameNoOptional display name for the seller
conditionNoItem condition: new, like_new, used, refurbished. Default: new
quantityNoHow many available. Default: 1
shipsToNoCountries that can be shipped to (e.g. ["US", "CA"]). Default: ["US"]
expiresInDaysNoListing expires after N days. Default: 30
isPromoNoSet to true for promotional/free items. Price auto-sets to 0, category to "promotional".
shippingCostNoFor promo items: shipping cost the buyer pays (e.g. 8.99). Default: 0
promoQuantityNoFor promo items: how many are available to claim (e.g. 500). Default: 100
fulfillmentTypeNoFulfillment method: "ship" (mail only), "pickup" (in-store only), "both" (customer chooses). Default: "ship". Set to "pickup" for zero-cost promo distribution.
pickupLocationsNoPickup locations for in-store fulfillment. Required when fulfillmentType is "pickup" or "both".

Implementation Reference

  • Handler for 'create_listing' tool which validates input, verifies the seller, and makes a POST request to the W3Ship API.
    case 'create_listing': {
        const {
            title, description: desc, price, currency: cur,
            category: cat, sellerAddress: seller, sellerName,
            condition: cond, quantity: qty, shipsTo, expiresInDays,
            isPromo, shippingCost, promoQuantity,
            fulfillmentType, pickupLocations
        } = args as any;
    
        const sellerAddr = seller || CONFIGURED_KEY;
        if (!sellerAddr) {
            return {
                content: [{ type: 'text', text: 'Error: Seller wallet address required. Set W3SHIP_PUBLIC_KEY or provide sellerAddress.' }],
                isError: true,
            };
        }
    
        // Seller verification — check if wallet has a registered identity
        try {
            const verifyRes = await fetch(`${W3SHIP_API}/api/listing/verify-seller?publicKey=${encodeURIComponent(sellerAddr)}`);
            const verifyData = await verifyRes.json() as any;
            if (!verifyRes.ok || !verifyData.verified) {
                return {
                    content: [{
                        type: 'text',
                        text: JSON.stringify({
                            error: 'Seller not verified',
                            message: 'Your wallet address is not registered in the W3Ship identity ledger. To sell on the marketplace, register your identity first at w3ship.com or link your address via Dah.mx.',
                            publicKey: sellerAddr.substring(0, 20) + '...',
                            registerUrl: 'https://w3ship.com/setup-mcp',
                        }, null, 2)
                    }],
                    isError: true,
                };
            }
        } catch (e: any) {
            // If verification service is down, log warning but allow (fail-open for demo)
            console.warn(`[Listing] Seller verification failed (allowing): ${e.message}`);
        }
    
        const listingId = `LST-${Date.now().toString(36).toUpperCase()}-${Math.random().toString(36).substring(2, 6).toUpperCase()}`;
        const expDays = expiresInDays || 30;
        const expiresAt = new Date(Date.now() + expDays * 24 * 60 * 60 * 1000).toISOString();
        const currency = (cur || 'USDC').toUpperCase();
        const category = (cat || 'other').toLowerCase();
        const condition = cond || 'new';
        const quantity = qty || 1;
        const ships = shipsTo || ['US'];
    
        try {
            const createRes = await fetch(`${W3SHIP_API}/api/listing`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    id: listingId, title, description: desc,
                    price: isPromo ? 0 : price,
                    currency, category: isPromo ? 'promotional' : category,
                    sellerAddress: sellerAddr,
                    sellerName, condition, quantity: isPromo ? (promoQuantity || quantity) : quantity,
                    shipsTo: ships, expiresAt,
                    isPromo: isPromo || undefined,
                    shippingCost: isPromo ? (shippingCost || 0) : undefined,
                    promoQuantity: isPromo ? (promoQuantity || quantity || 100) : undefined,
                    fulfillmentType: fulfillmentType || undefined,
                    pickupLocations: pickupLocations || undefined,
                }),
            });
            const createData = await createRes.json() as any;
            if (!createRes.ok) {
                return { content: [{ type: 'text', text: `Error creating listing: ${createData.error || 'Unknown error'}` }], isError: true };
            }
    
            if (isPromo) {
                const ft = fulfillmentType || (pickupLocations?.length ? 'pickup' : 'ship');
                const pickupMsg = ft !== 'ship' && pickupLocations?.length
                    ? ` Pickup available at: ${pickupLocations.map((l: any) => l.name).join(', ')}.`
                    : '';
                const shipMsg = ft !== 'pickup'
                    ? ` Shipping: ${shippingCost || 0} ${currency}.`
                    : '';
                return {
                    content: [{
                        type: 'text',
                        text: JSON.stringify({
                            listing: {
                                id: listingId, title, type: 'PROMOTIONAL',
                                fulfillment: ft,
                                ...(ft !== 'pickup' ? { shippingCost: `${shippingCost || 0} ${currency}` } : {}),
                                ...(pickupLocations?.length ? { pickupLocations: pickupLocations.map((l: any) => ({ id: l.id, name: l.name, address: l.address })) } : {}),
                                totalAvailable: promoQuantity || quantity || 100, expiresAt,
                            },
                            message: `🎁 Promotional listing created! "${title}" is FREE.${shipMsg}${pickupMsg} ${promoQuantity || quantity || 100} available. One per wallet.`,
                            claimWith: `Customers use claim_promo(listingId: "${listingId}"${ft !== 'ship' ? ', fulfillmentChoice: "pickup"' : ''}) to claim.`,
                        }, null, 2)
                    }]
                };
            }
    
            return {
                content: [{
                    type: 'text',
                    text: JSON.stringify({
                        listing: { id: listingId, title, price: `${price} ${currency}`, category, condition, quantity, shipsTo: ships, expiresAt, payTo: sellerAddr },
                        message: `Listing created! Share listing ID "${listingId}" with buyers. Payment goes to ${sellerAddr}. Stored persistently in DynamoDB.`,
                        addToCart: `Buyers can use add_item with productOffering.id = "${listingId}" to add this to their cart.`,
                    }, null, 2)
                }]
            };
        } catch (e: any) {
            return { content: [{ type: 'text', text: `Error creating listing: ${e.message}` }], isError: true };
        }
    }
  • Definition and input schema for the 'create_listing' tool.
        name: 'create_listing',
        description: 'Create a P2P marketplace listing. Anyone can sell items — no merchant onboarding needed. Specify title, price, currency (USDC/ETH/etc), and your wallet address for payment.',
        inputSchema: {
            type: 'object',
            properties: {
                title: { type: 'string', description: 'Item title (e.g. "PS5 Console")' },
                description: { type: 'string', description: 'Item description' },
                price: { type: 'number', description: 'Price amount (e.g. 200). Set to 0 for promotional items.' },
                currency: { type: 'string', description: 'Payment currency (USDC, ETH, DAI, etc). Default: USDC' },
                category: { type: 'string', description: 'Category: electronics, clothing, collectibles, home, sports, gifts, books, promotional, other' },
                sellerAddress: { type: 'string', description: 'Seller wallet address for payment. Uses W3SHIP_PUBLIC_KEY if not provided.' },
                sellerName: { type: 'string', description: 'Optional display name for the seller' },
                condition: { type: 'string', description: 'Item condition: new, like_new, used, refurbished. Default: new' },
                quantity: { type: 'number', description: 'How many available. Default: 1' },
                shipsTo: { type: 'array', items: { type: 'string' }, description: 'Countries that can be shipped to (e.g. ["US", "CA"]). Default: ["US"]' },
                expiresInDays: { type: 'number', description: 'Listing expires after N days. Default: 30' },
                isPromo: { type: 'boolean', description: 'Set to true for promotional/free items. Price auto-sets to 0, category to "promotional".' },
                shippingCost: { type: 'number', description: 'For promo items: shipping cost the buyer pays (e.g. 8.99). Default: 0' },
                promoQuantity: { type: 'number', description: 'For promo items: how many are available to claim (e.g. 500). Default: 100' },
                fulfillmentType: { type: 'string', description: 'Fulfillment method: "ship" (mail only), "pickup" (in-store only), "both" (customer chooses). Default: "ship". Set to "pickup" for zero-cost promo distribution.' },
                pickupLocations: {
                    type: 'array',
                    items: {
                        type: 'object',
                        properties: {
                            id: { type: 'string', description: 'Unique location ID (e.g. "qbm")' },
                            name: { type: 'string', description: 'Location name (e.g. "Quaker Bridge Mall")' },
                            address: { type: 'string', description: 'Full street address' },
                            city: { type: 'string' },
                            state: { type: 'string' },
                            hours: { type: 'string', description: 'Operating hours (e.g. "Mon-Sat 10am-9pm")' },
                            instructions: { type: 'string', description: 'Pickup instructions (e.g. "Ask at VR kiosk near entrance")' },
                        },
                        required: ['id', 'name', 'address'],
                    },
                    description: 'Pickup locations for in-store fulfillment. Required when fulfillmentType is "pickup" or "both".',
                },
            },
            required: ['title', 'description', 'price'],
        },
    },

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/baskcart/w3ship-mcp-server'

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