Etsy MCP Server logo

Etsy MCP Server

by georgejeffers

An MCP server for interacting with the Etsy API. This service handles authentication, API calls to Etsy for managing listings, shop details, shipping profiles, and image uploads.

View on GitHub

Last updated: N/A

Etsy MCP Server

An MCP (Model Context Protocol) server for interacting with the Etsy API. This service handles authentication, API calls to Etsy for managing listings, shop details, shipping profiles, and image uploads.

Overview

This service provides a set of tools accessible via the Model Context Protocol (MCP) to interact with the Etsy V3 API. It manages the OAuth 2.0 authentication flow (including PKCE token refresh), stores tokens securely, and exposes functionalities like creating listings, fetching shop data, managing shipping profiles, and uploading images. The goal is to simplify interactions with the Etsy API for MCP-compatible clients.

This MCP server is primarily designed for development and tool integration. Please ensure you comply with Etsy's API Terms of Use.

Features

  • 🔑 OAuth2 Authentication: Handles the complete Etsy OAuth2 flow with PKCE.
  • đŸ›ī¸ Listing Management: Create, and retrieve Etsy listings.
  • â„šī¸ Shop Information: Fetch details for your Etsy shop.
  • đŸ“Ļ Shipping Profiles: List and create shipping profiles for your shop.
  • đŸ–ŧī¸ Image Uploads: Upload images and associate them with your listings.
  • 🔧 Default Shop Management: Set and get a default shop for easier multi-shop use.
  • 🔒 Secure Token Storage: Persists OAuth tokens securely.

Installation

Prerequisites

  • Node.js (version 18.0.0 or higher recommended - see package.json engines)
  • npm (usually comes with Node.js) or yarn

Manual Installation

  1. Clone the repository (replace with your repository URL if you host it on GitHub/GitLab etc.):

    git clone <your-repository-url>
    cd etsy-mcp
    

    (If you're working locally, you can skip the clone and just cd into the project directory.)

  2. Install dependencies:

    npm install
    

    Or if you use yarn:

    yarn install
    
  3. Configure Environment Variables: Copy the .env.example file to a new file named .env:

    cp .env.example .env
    

    Then, open .env and fill in your Etsy App credentials:

    ETSY_API_KEY=YOUR_ETSY_APP_API_KEY
    ETSY_CLIENT_SECRET=YOUR_ETSY_APP_CLIENT_SECRET
    # Optional: define a specific host and port for the OAuth callback redirect URI
    # ETSY_MCP_HOST=localhost
    # ETSY_MCP_OAUTH_PORT=3003
    # ETSY_MCP_REDIRECT_URI=http://localhost:3003/oauth/callback
    # Optional: define a custom path for token storage and logs
    # ETSY_MCP_TOKEN_PATH=/path/to/your/token_storage_directory
    # ETSY_MCP_LOG_PATH=/path/to/your/logs_directory
    # Optional: Your default shipping profile ID for physical items
    # SHIPPING_PROFILE_ID=YOUR_DEFAULT_SHIPPING_PROFILE_ID
    

    Note: The REDIRECT_URI in your .env file (or the default http://localhost:3003/oauth/callback) must match one of the redirect URIs configured for your Etsy App.

Usage

Run the Service

  • For production/built version:
    npm run build
    npm start
    
  • For development with auto-reload:
    npm run dev
    

This will launch the server, which listens for MCP commands, typically via standard input/output.

Use in Cursor IDE (or other MCP Clients)

To use this Etsy MCP server with an MCP client like Cursor IDE, you'll typically provide a JSON configuration.

In Cursor settings, navigate to the MCP tab, click "+ Add new global MCP server", and input:

{
    "mcpServers": {
        "Etsy MCP": {
            "command": "npm",
            "args": ["run", "dev"],
            "env": {
                "ETSY_API_KEY": "YOUR_ETSY_API_KEY_HERE",
                "ETSY_CLIENT_SECRET": "YOUR_ETSY_CLIENT_SECRET_HERE"
                // Add other ENV VARS from your .env if needed, e.g., REDIRECT_URI
                // "REDIRECT_URI": "http://localhost:3003/oauth/callback"
                // "SHIPPING_PROFILE_ID": "YOUR_DEFAULT_SHIPPING_PROFILE_ID_HERE"
            },
            "cwd": "/Users/name/Desktop/repos/ai gemini apps/etsy_api/etsy-mcp" // IMPORTANT: Update this to the correct absolute path of your project
        }
    }
}

Key points for the client configuration:

  • command & args: The example uses npm run dev for development. For a production setup, you might use npm start (which implies npm run build first if not already built).
  • env:
    • Replace YOUR_ETSY_API_KEY_HERE and YOUR_ETSY_CLIENT_SECRET_HERE with your actual credentials if you don't want to rely on the .env file being picked up by the npm run dev script's environment. It's generally better to let the server pick up credentials from its .env file. The example above is for overriding or ensuring they are set for the client's execution context.
  • cwd: Crucially, update this to the absolute path where your etsy-mcp project is located on your machine.
  • The tools array can also be defined here, but the server itself reports its capabilities, so it's often not strictly needed in the client-side static configuration if the client can dynamically fetch them.

API Reference

The service provides the following MCP tools. Parameters are based on Zod schemas defined in the server.

authenticate()

Initiates the OAuth2 authentication flow with Etsy.

  • Description: Starts the authentication process. The server will return a URL that you need to open in a browser to grant access to your Etsy account.
  • Parameters: None.
  • Returns:
{
  "content": [
    {
      "type": "text",
      "text": "Please visit this URL to authorize the application: https://www.etsy.com/oauth/connect?response_type=code&client_id=YOUR_API_KEY...\nComplete the process in your browser. You can then use other tools."
    }
  ]
}

set_default_shop()

Fetches your Etsy shops and sets the first one found as the default for this session.

  • Description: After successful authentication, use this tool to select a default shop. If you have multiple shops, it will automatically use the first one returned by Etsy. This default shop will be used by other tools unless a shop_id is explicitly provided to them.
  • Parameters: None.
  • Returns:
{
  "content": [
    {
      "type": "text",
      "text": "Default shop set to: YourShopName (ID: 12345678). Other tools will now use this shop unless a specific shop_id is provided."
    }
  ]
}

Or if no shops are found:

{
  "content": [
    {
      "type": "text",
      "text": "No shops found for your Etsy account."
    }
  ]
}

get_default_shop()

Gets the currently configured default Etsy shop ID and name.

  • Description: Use this to check which shop is currently set as the default.
  • Parameters: None.
  • Returns:
{
  "content": [
    {
      "type": "text",
      "text": "Current default shop: YourShopName (ID: 12345678)"
    }
  ]
}

Or if no default shop is set:

{
  "content": [
    {
      "type": "text",
      "text": "No default shop is currently set. Please run the `set_default_shop` tool after authenticating."
    }
  ]
}

get_listings(shop_id?: number)

Get all active listings for a shop.

  • Parameters:
    • shop_id (number, optional): The ID of the shop. If not provided, uses the default shop.
  • Returns:
{
  "content": [
    {
      "type": "text",
      "text": "{\n  \"count\": 1,\n  \"results\": [\n    {\n      \"listing_id\": 123456789,\n      \"user_id\": 98765432,\n      \"shop_id\": 12345678,\n      \"title\": \"Handmade Ceramic Mug\",\n      \"description\": \"A beautiful handmade ceramic mug...\",\n      \"state\": \"active\",\n      \"price\": { \"amount\": 2500, \"divisor\": 100, \"currency_code\": \"USD\" },\n      \"quantity\": 5,\n      /* ... other listing fields ... */\n    }\n  ]\n}"
    }
  ]
}

get_shop_details(shop_id?: number)

Get details for a specific shop.

  • Parameters:
    • shop_id (number, optional): The ID of the shop. If not provided, uses the default shop.
  • Returns:
{
  "content": [
    {
      "type": "text",
      "text": "{\n  \"shop_id\": 12345678,\n  \"shop_name\": \"YourShopName\",\n  \"user_id\": 98765432,\n  \"title\": \"Unique Handmade Goods\",\n  \"status\": \"active\",\n  /* ... other shop fields ... */\n}"
    }
  ]
}

create_listing(shop_id?: number, listing_data: object)

Creates a new Etsy listing.

  • Description: For physical items, ensure you have a shipping_profile_id. To add images, create the listing first, then use the upload_listing_image tool with the returned listing_id.
  • Parameters:
    • shop_id (number, optional): The ID of the shop. If not provided, uses the default shop.
    • listing_data (object):
      • title (string): Listing title.
      • description (string): Listing description.
      • price (number): Listing price (e.g., 25.99).
      • quantity (number): Available quantity.
      • who_made (string): e.g., 'i_did', 'collective', 'someone_else'.
      • when_made (string): e.g., 'made_to_order', '2020_2024', '1950_1959'.
      • taxonomy_id (number): The numeric ID of the listing's category.
      • shipping_profile_id (number, optional): The numeric ID of the shipping profile. Required if type is 'physical'.
      • type (enum: "physical", "digital", "download"): Listing type.
  • Returns:
{
  "content": [
    {
      "type": "text",
      "text": "{\n  \"listing_id\": 123456790,\n  \"user_id\": 98765432,\n  \"shop_id\": 12345678,\n  \"title\": \"New Awesome Product\",\n  /* ... other fields of the newly created listing ... */\n}"
    }
  ]
}

list_shop_shipping_profiles(shop_id?: number)

Lists all shipping profiles for a given shop.

  • Parameters:
    • shop_id (number, optional): The ID of the shop. If not provided, uses the default shop.
  • Returns:
{
  "content": [
    {
      "type": "text",
      "text": "{\n  \"count\": 1,\n  \"results\": [\n    {\n      \"shipping_profile_id\": 7654321,\n      \"title\": \"Standard US Shipping\",\n      \"origin_country_iso\": \"US\",\n      /* ... other shipping profile fields ... */\n    }\n  ]\n}"
    }
  ]
}

create_shop_shipping_profile(shop_id?: number, title: string, origin_country_iso: string, primary_cost: number, secondary_cost: number, min_processing_time: number, max_processing_time: number, destination_country_iso?: string, destination_region?: string)

Creates a new shipping profile for a shop.

  • Parameters:
    • shop_id (number, optional): The ID of the shop. If not provided, uses the default shop.
    • title (string): A title for the shipping profile (e.g., 'US Standard').
    • origin_country_iso (string, length 2): ISO code of the origin country (e.g., 'US').
    • primary_cost (number, min 0): Cost of shipping to this destination alone.
    • secondary_cost (number, min 0): Cost of shipping with another item.
    • min_processing_time (integer, min 1): Minimum days to process the order.
    • max_processing_time (integer, min 1): Maximum days to process the order.
    • destination_country_iso (string, length 2, optional): ISO code for a specific destination country.
    • destination_region (enum: "eu", "non_eu", "none", optional): A specific destination region. (Note: Either destination_country_iso OR destination_region must be provided, but not both.)
  • Returns:
{
  "content": [
    {
      "type": "text",
      "text": "{\n  \"shipping_profile_id\": 7654322,\n  \"title\": \"New Express Profile\",\n  /* ... other fields of the newly created shipping profile ... */\n}"
    }
  ]
}

upload_listing_image(shop_id?: number, listing_id: number, file_name: string, image_name?: string)

Uploads an image from a predefined local server directory and associates it with an Etsy listing.

  • Description: After creating a listing, use this tool to upload images. Images must be placed in the public/uploads/listing_images directory on the server (relative to the project root).
  • Parameters:
    • shop_id (number, optional): Shop ID. Uses default if not provided.
    • listing_id (integer): The ID of the listing to add the image to.
    • file_name (string): The name of the image file (e.g., 'my_image.jpg') located in the server's public/uploads/listing_images directory.
    • image_name (string, optional): The desired filename for the image on Etsy. Defaults to file_name if not provided.
  • Returns:
{
  "content": [
    {
      "type": "text",
      "text": "{\n  \"listing_image_id\": 987654321,\n  \"listing_id\": 123456790,\n  \"hex_code\": \"f1e2d3\",\n  \"url_75x75\": \"https://.../75x75.jpg\",\n  /* ... other image details ... */\n}"
    }
  ]
}

How It Works

  1. Client Request: An MCP client sends a tool call request to this server (e.g., via stdio when run locally).
  2. Authentication Check: For tools requiring authentication, the server checks for a valid OAuth token using TokenStorage.
  3. OAuth Flow (if needed): a. If no valid token exists, the authenticate tool is typically invoked by the client (or instructed by the server). b. The EtsyMCPServer calls the OAuthServer to generate an Etsy authorization URL (using PKCE). c. The user opens this URL in a browser and authorizes the application. d. Etsy redirects to the OAuthServer's callback endpoint with an authorization code. e. The OAuthServer exchanges the code for an access token and refresh token using the PKCE code verifier. f. Tokens (including user_id and potentially default shop_id and shop_name) are saved by TokenStorage.
  4. API Call: The EtsyMCPServer uses the EtsyApiClient (which holds the access token) to make the requested API call to the Etsy V3 API.
  5. Token Refresh: If an access token is expired, getValidAccessToken() in EtsyMCPServer attempts to use the refresh token to get a new access token.
  6. Response: The response from the Etsy API is processed and returned to the MCP client in the standard MCP format.

Troubleshooting

  • Authentication Errors / Invalid Grant:
    • Ensure your ETSY_API_KEY, ETSY_CLIENT_SECRET in .env are correct.
    • Verify that the REDIRECT_URI used by the server (default http://localhost:3003/oauth/callback or as set in .env) exactly matches one of the "Callback URLs" configured in your Etsy App settings.
    • Stale tokens: Try clearing tokens.json from your token storage path (see TokenStorage.ts or your ETSY_MCP_TOKEN_PATH env var) and re-authenticating.
  • EADDRINUSE for OAuth Server: This means the port (default 3003) for the OAuth callback server is already in use. Stop the other process or configure a different ETSY_MCP_OAUTH_PORT in your .env file and update your Etsy App's redirect URI accordingly.
  • File Not Found for Image Upload: Ensure the file_name provided to upload_listing_image exists within the public/uploads/listing_images/ directory relative to your project root.
  • Log Files: Check the log files generated in the logs directory (or ETSY_MCP_LOG_PATH) for detailed error messages. Each run creates a timestamped log file.

License

MIT License (Please create a LICENSE file with the MIT License text if you choose this license, or specify another.)

Sponsors