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'],
        },
    },
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full disclosure burden. It successfully conveys the permissionless nature ('no merchant onboarding needed') and payment mechanism (wallet address), but fails to disclose critical behavioral traits like whether this creates an on-chain transaction, associated gas fees, listing immutability, or activation timing.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description consists of two highly efficient sentences. The first states the core action, while the second front-loads the most critical constraints (no onboarding) and required parameters. There is no redundant or wasted language.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the high complexity (16 parameters) and lack of output schema or annotations, the description is minimally adequate but misses opportunities to guide the agent through the tool's significant capabilities—specifically the promotional item mode (isPromo, promoQuantity) and fulfillment logistics (fulfillmentType, pickupLocations) which represent distinct usage patterns beyond basic selling.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

While the input schema has 100% description coverage, the description adds valuable context beyond the schema: it highlights the cryptocurrency nature (USDC/ETH/etc) and clarifies that sellerAddress is specifically 'for payment'. This semantic framing helps the agent understand the crypto-commerce context that raw parameter descriptions don't convey.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description provides a specific verb (Create) and resource (P2P marketplace listing) that clearly identifies the tool's function. The 'P2P marketplace' qualifier effectively distinguishes it from sibling tools like 'create_order' or 'add_item' by positioning it as a seller-side listing creation tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'Anyone can sell items — no merchant onboarding needed' provides implicit guidance about when to use this (for casual selling without formal merchant setup). However, it lacks explicit when-not-to-use guidance or differentiation from similar tools like 'add_item', and doesn't clarify the relationship to promo-specific workflows.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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