Server data from the Official MCP Registry
A local read-only MCP server for Etsy shops, active listings, and listing variation inventory.
About
A local read-only MCP server for Etsy shops, active listings, and listing variation inventory.
Security Report
Valid MCP server (2 strong, 1 medium validity signals). No known CVEs in dependencies. โ ๏ธ Package registry links to a different repository than scanned source. Imported from the Official MCP Registry. Trust signals: 5 highly-trusted packages. 1 finding(s) downgraded by scanner intelligence.
9 files analyzed ยท 1 issue found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
How to Install
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-profplum700-etsy-mcp-server": {
"args": [
"-y",
"@profplum700/etsy-mcp-server",
"--yes"
],
"command": "npx"
}
}
}Documentation
View on GitHubFrom the project's GitHub README.
Etsy API v3 Client
A modern, universal TypeScript/JavaScript client for the Etsy Open API v3 with full OAuth 2.0 PKCE authentication support. Works seamlessly in both browser and Node.js environments.
This repository is heavily based on the official Etsy OpenAPI specification (https://www.etsy.com/openapi/generated/oas/3.0.0.json), as referenced in Etsy's API documentation (https://developers.etsy.com/documentation/reference).
๐ Features
- Universal Compatibility: Works in browsers, Node.js, and Web Workers
- OAuth 2.0 PKCE Authentication: Full support for secure authentication flow
- TypeScript First: Complete type definitions with IntelliSense support
- Rate Limiting: Built-in request throttling to respect API limits
- Token Management: Automatic token refresh with configurable storage
- Caching: Optional response caching to improve performance
- Error Handling: Comprehensive error types for different failure scenarios
- Zero Dependencies: No external runtime dependencies
๐ฆ Installation
npm install @profplum700/etsy-v3-api-client
yarn add @profplum700/etsy-v3-api-client
pnpm add @profplum700/etsy-v3-api-client
๐ค Connect an agent to your Etsy shop
The companion @profplum700/etsy-mcp-server package runs locally over stdio and exposes read-only shop, active-listing, and listing-inventory tools. It requires Node.js 24+, your own approved Etsy developer app, and an OS credential store. It does not use a hosted endpoint.
Register http://localhost:3030/oauth/redirect as the Etsy app callback if required, then run this in a local interactive terminal:
npx --yes @profplum700/etsy-mcp-server@latest setup
Enter your keystring and shared secret in the hidden terminal prompts, authorize only shops_r and listings_r in Etsy, then choose whether to add the server to your user-level Codex MCP configuration. For another stdio client, configure npx --yes @profplum700/etsy-mcp-server@latest serve as its launch command.
You can connect multiple shops without replacing earlier profiles. Run setup --reuse-app --manual-browser to reuse the saved developer app credentials, then open the printed authorization URL in the browser profile signed in to the shop you want to add. Use status to see saved shops and use <shop name or ID> to choose which one MCP tools query. disconnect removes only the active shop; disconnect --all removes every saved profile. Use remove-codex to remove the Codex entry without disconnecting any shop.
Prompt for an agent: โSet up the local Etsy MCP server. Do not ask me to paste Etsy credentials into chat or save them in a repository or environment file. Run the setup command in an interactive local terminal so I can enter them there; request only shops_r and listings_r, verify my shop, and offer the user-level Codex connection.โ
๐ง Quick Start
Basic Setup
import { EtsyClient, AuthHelper } from '@profplum700/etsy-v3-api-client';
// Create authentication helper
const authHelper = new AuthHelper({
keystring: 'your-api-key',
redirectUri: 'https://your-app.com/callback',
scopes: ['shops_r', 'listings_r']
});
// Get authorization URL
const authUrl = await authHelper.getAuthUrl();
console.log('Visit this URL to authorize:', authUrl);
// After user authorization, exchange code for tokens
const state = await authHelper.getState();
await authHelper.setAuthorizationCode('authorization-code-from-callback', state);
const tokens = await authHelper.getAccessToken();
// Create API client
const client = new EtsyClient({
keystring: 'your-api-key',
sharedSecret: 'your-shared-secret', // REQUIRED for v3 API compliance
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
expiresAt: tokens.expires_at
});
// Make API calls
const user = await client.getUser();
console.log('User:', user);
Browser Usage
<script type="module">
import { EtsyClient, AuthHelper } from 'https://unpkg.com/@profplum700/etsy-v3-api-client/dist/browser.esm.js';
// Your code here...
</script>
Or using UMD:
<script src="https://unpkg.com/@profplum700/etsy-v3-api-client/dist/browser.umd.js"></script>
<script>
const { EtsyClient, AuthHelper } = EtsyApiClient;
// Your code here...
</script>
Node.js Usage
// CommonJS
const { EtsyClient, AuthHelper } = require('@profplum700/etsy-v3-api-client');
// ES Modules
import { EtsyClient, AuthHelper } from '@profplum700/etsy-v3-api-client';
๐ Authentication
OAuth 2.0 Flow
- Create AuthHelper with your app credentials
- Generate authorization URL for user to visit
- Handle callback with authorization code
- Exchange code for tokens
- Use tokens with EtsyClient
import { AuthHelper, ETSY_SCOPES } from '@profplum700/etsy-v3-api-client';
const authHelper = new AuthHelper({
keystring: 'your-api-key',
redirectUri: 'https://your-app.com/callback',
scopes: [
ETSY_SCOPES.SHOPS_READ,
ETSY_SCOPES.LISTINGS_READ,
ETSY_SCOPES.PROFILE_READ
]
});
// Step 1: Get authorization URL
const authUrl = await authHelper.getAuthUrl();
// Redirect user to authUrl
// Step 2: Handle callback (in your callback endpoint)
const { code, state } = getCallbackParams(); // Your implementation
const expectedState = await authHelper.getState();
if (state === expectedState) {
await authHelper.setAuthorizationCode(code, state);
const tokens = await authHelper.getAccessToken();
// Store tokens securely
}
Available Scopes
import { ETSY_SCOPES, COMMON_SCOPE_COMBINATIONS } from '@profplum700/etsy-v3-api-client';
// Individual scopes
const scopes = [
ETSY_SCOPES.SHOPS_READ,
ETSY_SCOPES.LISTINGS_WRITE,
ETSY_SCOPES.TRANSACTIONS_READ
];
// Pre-defined combinations
const readOnlyScopes = COMMON_SCOPE_COMBINATIONS.SHOP_READ_ONLY;
const managementScopes = COMMON_SCOPE_COMBINATIONS.SHOP_MANAGEMENT;
๐ช Client Usage
Creating a Client
import { EtsyClient } from '@profplum700/etsy-v3-api-client';
const client = new EtsyClient({
keystring: 'your-api-key',
sharedSecret: 'your-shared-secret', // Get this from Your Apps page on Etsy
accessToken: 'user-access-token',
refreshToken: 'user-refresh-token',
expiresAt: new Date('2024-12-31T23:59:59Z'),
// Optional configuration
rateLimiting: {
enabled: true,
maxRequestsPerSecond: 5,
maxRequestsPerDay: 5000 // Conservative local fallback; Etsy response headers report this app's actual quota
},
caching: {
enabled: true,
ttl: 300 // 5 minutes
}
});
Making API Calls
// Get current user
const user = await client.getUser();
// Get user's shops
const shops = await client.getUserShops();
// Get shop listings
const listings = await client.getListingsByShop('shop-id');
// Get specific listing
const listing = await client.getListing('listing-id');
// Search listings
const searchResults = await client.findAllListingsActive({
keywords: 'vintage',
taxonomy_id: 123,
limit: 25
});
Error Handling
import { EtsyApiError, EtsyAuthError, EtsyRateLimitError } from '@profplum700/etsy-v3-api-client';
try {
const user = await client.getUser();
} catch (error) {
if (error instanceof EtsyAuthError && error.code === 'REAUTH_REQUIRED') {
// Etsy refused the refresh token (invalid_grant): send the user through OAuth again
console.error('Re-authorisation needed');
} else if (error instanceof EtsyAuthError && error.code === 'TOKEN_SAVE_FAILED') {
// Saving rotated tokens failed; error.cause is the storage/callback error.
// The new tokens are only in memory until a retry saves them. Do not log
// error.cause as-is: a custom save callback's error may contain tokens.
console.error('Token save failed:', error.code);
} else if (error instanceof EtsyAuthError) {
// Other auth errors, e.g. TOKEN_REFRESH_FAILED or TOKEN_REFRESH_TIMEOUT
// for a transient refresh failure; the stored tokens are kept
console.error('Auth error:', error.message, error.code);
} else if (error instanceof EtsyRateLimitError) {
// Handle rate limiting
console.error('Rate limited. Retry after:', error.retryAfter);
} else if (error instanceof EtsyApiError) {
// Handle API errors
console.error('API error:', error.statusCode, error.message);
} else {
// Handle other errors
console.error('Unexpected error:', error);
}
}
๐พ Token Storage
Built-in Storage Options
The client provides several storage mechanisms:
import {
createDefaultTokenStorage,
LocalStorageTokenStorage,
SessionStorageTokenStorage,
FileTokenStorage,
MemoryTokenStorage
} from '@profplum700/etsy-v3-api-client';
// Automatic storage selection based on environment
const storage = createDefaultTokenStorage();
// Browser localStorage
const localStorage = new LocalStorageTokenStorage('etsy-tokens');
// Browser sessionStorage
const sessionStorage = new SessionStorageTokenStorage('etsy-tokens');
// Node.js file storage
const fileStorage = new FileTokenStorage('./tokens.json');
// In-memory storage (not persistent)
const memoryStorage = new MemoryTokenStorage();
// Use with client
const client = new EtsyClient(config, storage);
Custom Storage
import { TokenStorage } from '@profplum700/etsy-v3-api-client';
class CustomTokenStorage implements TokenStorage {
async save(tokens: EtsyTokens): Promise<void> {
// Your save implementation
}
async load(): Promise<EtsyTokens | null> {
// Your load implementation
}
async clear(): Promise<void> {
// Your clear implementation
}
}
Refresh Token Rotation
Etsy issues a new refresh token on every refresh, and the old one may stop working. The client saves the new tokens (token storage, refreshSave or refreshSaveAsync) before it uses them. If that save fails, the call throws EtsyAuthError with code TOKEN_SAVE_FAILED, and error.cause is the original storage or callback error. The new tokens are then held only in memory, and the next refresh retries the save without asking Etsy again. If the process exits before a save succeeds, the new refresh token is lost and Etsy may require the user to authorise again. Handle TOKEN_SAVE_FAILED: fix the storage and let a later call retry the save before the process exits.
๐ฆ Rate Limiting
The client includes built-in rate limiting to respect Etsy's API limits:
const client = new EtsyClient({
// ... other config
rateLimiting: {
enabled: true,
maxRequestsPerSecond: 5, // Conservative QPS fallback
maxRequestsPerDay: 5000, // Conservative rolling 24-hour fallback
minRequestInterval: 200 // Minimum ms between requests (5 QPS fallback)
}
});
// Check remaining requests in the rolling local window
const remaining = client.getRemainingRequests();
console.log(`${remaining} requests remaining in the rolling 24-hour window`);
rateLimiting.enabled: false turns off local pacing only (the per-second interval, the local daily count and the shared cooldown). It does not turn off 429 handling: a read that gets a 429 is still retried with backoff, and every 429 still throws EtsyRateLimitError. With pacing off, the retry options in rateLimiting (maxRetries, baseDelayMs, maxDelayMs, jitter) are ignored and the defaults apply (3 retries). A read is not retried when Etsy reports that the daily quota is used up.
Every 429 throws EtsyRateLimitError. A read that gets a 429 is retried with backoff first. A write (POST, PUT, PATCH, DELETE) is never replayed: it throws at once, with retryAfter set to Etsy's Retry-After in seconds (or the client's own cooldown when Etsy sends none). The exception is a used-up daily quota (isRetryable() is false): there retryAfter is set only when Etsy sends Retry-After.
Each attempt reads the access token just before it is sent, so a request that waited for a rate-limit slot or a backoff never sends an expired token. When Etsy answers 401 and a refresh token is held, the client refreshes the token once and resends once; a second 401 is thrown as EtsyApiError.
When building a custom transport around the exported EtsyRateLimiter, keep the reservation ID attached to its response so concurrent, out-of-order headers cannot incorrectly restore exhausted quota:
const reservationId = await rateLimiter.waitForRateLimitWithReservation();
let response: Response;
try {
response = await fetch(url);
} catch (error) {
rateLimiter.releaseRequestSlot(reservationId);
throw error;
}
rateLimiter.updateFromHeaders(response.headers, reservationId);
waitForRateLimit() remains available for source compatibility when you only need dispatch pacing. For custom transports that also feed response headers back to the limiter, use the reservation-aware method above; uncorrelated positive headers cannot safely reopen exhausted quota.
๐ Caching
Optional response caching to improve performance:
const client = new EtsyClient({
// ... other config
caching: {
enabled: true,
ttl: 300 // Cache for 5 minutes
}
});
// Clear cache when needed
await client.clearCache();
// Sign out: clears the tokens and the response cache, so cached
// answers are not served after the tokens are gone
await client.clearTokens();
Cache keys are built from the request alone, not from the Etsy account. Do not share one caching.storage between clients signed in to different Etsy accounts, or they may read each other's cached responses.
๐ Environment Support
Browser
- Modern browsers with ES2020+ support
- Web Workers
- Service Workers
Node.js
- Node.js 20+
- Full CommonJS and ES Module support
Package Exports
The package provides optimized builds for different environments:
{
"imports": {
"@profplum700/etsy-v3-api-client": "./dist/index.esm.js",
"@profplum700/etsy-v3-api-client/browser": "./dist/browser.esm.js",
"@profplum700/etsy-v3-api-client/node": "./dist/node.esm.js"
}
}
๐ API Reference
EtsyClient Methods
User Methods
getUser()- Get current user infogetUserShops(userId?)- Get user's shops
Shop Methods
getShop(shopId)- Get shop detailsgetShopByOwnerUserId(userId)- Get shop by owner user ID
Personalization Methods (New)
getListingPersonalizations(listingId)- Get personalization questions for a listingupdateListingPersonalization(shopId, listingId, params)- Create or update personalization questionsdeleteListingPersonalization(shopId, listingId)- Delete personalization
Listing Methods
getListing(listingId)- Get listing detailsgetListingsByShop(shopId, options?)- Get shop's listingsfindAllListingsActive(options?)- Search active listings
Listing Management Endpoint Support
| Category | Endpoint | Supported |
|---|---|---|
| BuyerTaxonomy | getBuyerTaxonomyNodes | Yes |
| BuyerTaxonomy | getPropertiesByBuyerTaxonomyId | No |
| SellerTaxonomy | getSellerTaxonomyNodes | Yes |
| SellerTaxonomy | getPropertiesByTaxonomyId | Yes |
| ShopListing | createDraftListing | Yes |
| ShopListing | getListingsByShop | Yes |
| ShopListing | deleteListing | Yes |
| ShopListing | getListing | Yes |
| ShopListing | findAllListingsActive | Yes |
| ShopListing | findAllActiveListingsByShop | No |
| ShopListing | getListingsByListingIds | No |
| ShopListing | getFeaturedListingsByShop | No |
| ShopListing | deleteListingProperty | No |
| ShopListing | updateListingProperty | Yes |
| ShopListing | getListingProperty | No |
| ShopListing | getListingProperties | Yes |
| ShopListing | updateListing | Yes |
| ShopListing | getListingsByShopReceipt | No |
| ShopListing | getListingsByShopReturnPolicy | No |
| ShopListing | getListingsByShopSectionId | No |
| ShopListing | getListingPersonalization | Yes |
| ShopListing | updateListingPersonalization | Yes |
| ShopListing | deleteListingPersonalization | Yes |
| ShopListing File | deleteListingFile | No |
| ShopListing File | getListingFile | No |
| ShopListing File | getAllListingFiles | No |
| ShopListing File | uploadListingFile | No |
| ShopListing Image | deleteListingImage | Yes |
| ShopListing Image | getListingImage | Yes |
| ShopListing Image | getListingImages | Yes |
| ShopListing Image | uploadListingImage | Yes |
| ShopListing Inventory | getListingInventory | Yes |
| ShopListing Inventory | updateListingInventory | Yes |
| ShopListing Offering | getListingOffering | No |
| ShopListing Product | getListingProduct | No |
| ShopListing Translation | createListingTranslation | No |
| ShopListing Translation | getListingTranslation | No |
| ShopListing Translation | updateListingTranslation | No |
| ShopListing VariationImage | getListingVariationImages | No |
| ShopListing VariationImage | updateVariationImages | No |
| ShopListing Video | deleteListingVideo | No |
| ShopListing Video | getListingVideo | No |
| ShopListing Video | getListingVideos | No |
| ShopListing Video | uploadListingVideo | No |
| Other | ping | No |
| Other | tokenScopes | No |
| Ledger Entry | getShopPaymentAccountLedgerEntry | Yes |
| Ledger Entry | getShopPaymentAccountLedgerEntries | Yes |
| Payment | getShopPaymentByReceiptId | No |
| Payment | getPaymentAccountLedgerEntry | No |
| Payment | getPayments | No |
| Shop Receipt | getShopReceipt | Yes |
| Shop Receipt | updateShopReceipt | Yes |
| Shop Receipt | getShopReceipts | Yes |
| Shop Receipt | searchAllShopReceipts | No |
| Shop Receipt Transactions | getShopReceiptTransaction | Yes |
| Shop Receipt Transactions | getShopReceiptTransactionsByListing | No |
| Shop Receipt Transactions | getShopReceiptTransactionsByReceipt | Yes |
| Shop Receipt Transactions | getShopReceiptTransactionsByShop | No |
| Review | getReviewsByListing | Yes |
| Review | getReviewsByShop | Yes |
| Shop HolidayPreferences | getShopHolidayPreferences | No |
| Shop HolidayPreferences | updateShopHolidayPreferences | No |
| Shop ProcessingProfiles | createShopShippingProfileUpgrade | No |
| Shop ProcessingProfiles | getShopShippingProfileUpgrades | Yes |
| Shop ProcessingProfiles | deleteShopShippingProfileUpgrade | No |
| Shop ProcessingProfiles | getShopShippingProfileUpgrade | No |
| Shop ProcessingProfiles | updateShopShippingProfileUpgrade | No |
| Shop ShippingProfile | getShippingCarriers | No |
| Shop ShippingProfile | createShopShippingProfile | Yes |
| Shop ShippingProfile | getShopShippingProfiles | Yes |
| Shop ShippingProfile | deleteShopShippingProfile | Yes |
| Shop ShippingProfile | getShopShippingProfile | Yes |
| Shop ShippingProfile | updateShopShippingProfile | Yes |
| Shop ShippingProfile | createShopShippingProfileDestination | Yes |
| Shop ShippingProfile | getShopShippingProfileDestinationsByShippingProfile | Yes |
| Shop ShippingProfile | deleteShopShippingProfileDestination | Yes |
| Shop ShippingProfile | updateShopShippingProfileDestination | Yes |
| Shop ShippingProfile | createShopShippingProfileUpgrade | No |
| Shop ShippingProfile | getShopShippingProfileUpgrades | Yes |
| Shop ShippingProfile | deleteShopShippingProfileUpgrade | No |
| Shop ShippingProfile | updateShopShippingProfileUpgrade | No |
| Shop | getShop | Yes |
| Shop | updateShop | Yes |
| Shop | getShopByOwnerUserId | Yes |
| Shop | findShops | No |
| Shop ProductionPartner | getShopProductionPartners | Yes |
| Shop Section | createShopSection | Yes |
| Shop Section | getShopSections | Yes |
| Shop Section | deleteShopSection | Yes |
| Shop Section | getShopSection | Yes |
| Shop Section | updateShopSection | Yes |
| Shop Return Policy | consolidateShopReturnPolicies | No |
| Shop Return Policy | createShopReturnPolicy | No |
| Shop Return Policy | getShopReturnPolicies | No |
| Shop Return Policy | deleteShopReturnPolicy | No |
| Shop Return Policy | getShopReturnPolicy | No |
| Shop Return Policy | updateShopReturnPolicy | No |
| User | getUser | Yes |
| User | getMe | No |
| UserAddress | deleteUserAddress | No |
| UserAddress | getUserAddress | No |
| UserAddress | getUserAddresses | No |
Review Methods
getReviewsByListing(listingId, options?)- Get reviews for a listinggetReviewsByShop(shopId, options?)- Get reviews for a shop
Session Methods
clearTokens()- Clear the tokens (memory, token storage andrefreshClearAsync) and the response cacheclearCache()- Clear the response cache only
Search Methods
findAllListingsActive(params)- Search listings with filters
AuthHelper Methods
getAuthUrl()- Generate authorization URLsetAuthorizationCode(code, state)- Set auth code from callbackgetAccessToken()- Exchange code for tokensgetState()- Get current state parametergetCodeVerifier()- Get PKCE code verifier
TokenManager Methods
getAccessToken()- Get current access token (refreshes if needed)refreshToken()- Manually refresh tokensgetCurrentTokens()- Get current token setisTokenExpired()- Check if token is expiredclearTokens()- Clear stored tokens
๐ ๏ธ Configuration Options
interface EtsyClientConfig {
keystring: string; // Required: Your API key
sharedSecret: string; // Required: Your shared secret (from Your Apps page)
accessToken?: string; // User's access token
refreshToken?: string; // User's refresh token
expiresAt?: Date; // Token expiration date
refreshSave?: (token, refresh, expires) => void; // Token save callback
tokenRefreshTimeoutMs?: number; // Deadline for a token refresh request (default 30000)
rateLimiting?: {
enabled?: boolean; // Local pacing only; false does not stop 429 retries
maxRequestsPerSecond?: number; // Requests per second limit
maxRequestsPerDay?: number; // Daily request limit
minRequestInterval?: number; // Minimum interval between requests (ms)
};
caching?: {
enabled?: boolean; // Enable response caching
ttl?: number; // Cache TTL in seconds
};
}
๐ Examples
Complete Authentication Flow
import { AuthHelper, EtsyClient, createDefaultTokenStorage } from '@profplum700/etsy-v3-api-client';
async function authenticateAndFetchData() {
// Step 1: Initialize authentication
const authHelper = new AuthHelper({
keystring: process.env.ETSY_API_KEY!,
redirectUri: 'http://localhost:3000/callback',
scopes: ['shops_r', 'listings_r', 'profile_r']
});
// Step 2: Get authorization URL
const authUrl = await authHelper.getAuthUrl();
console.log('Visit:', authUrl);
// Step 3: Handle callback (pseudo-code)
const { code, state } = await waitForCallback();
const expectedState = await authHelper.getState();
if (state !== expectedState) {
throw new Error('State mismatch');
}
// Step 4: Exchange for tokens
await authHelper.setAuthorizationCode(code, state);
const tokens = await authHelper.getAccessToken();
// Step 5: Create client with storage
const storage = createDefaultTokenStorage();
const client = new EtsyClient({
keystring: process.env.ETSY_API_KEY!,
...tokens
}, storage);
// Step 6: Use the API
const user = await client.getUser();
const shops = await client.getUserShops();
if (shops.length > 0) {
const listings = await client.getListingsByShop(shops[0].shop_id.toString());
console.log(`Found ${listings.length} listings`);
}
}
Simple Browser Example
Here's a complete working example for browser environments:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Etsy API Client - Browser Example</title>
</head>
<body>
<div id="app">
<h1>Etsy API Browser Example</h1>
<button id="loginBtn">Login with Etsy</button>
<div id="userInfo" style="display: none;">
<h2>User Information</h2>
<div id="userData"></div>
<button id="getShopsBtn">Get My Shops</button>
<div id="shopsData"></div>
</div>
</div>
<script type="module">
import { AuthHelper, EtsyClient, ETSY_SCOPES } from 'https://unpkg.com/@profplum700/etsy-v3-api-client@1.0.0/dist/browser.esm.js';
// Replace with your actual API key
const API_KEY = 'your-api-key-here';
const REDIRECT_URI = window.location.origin + window.location.pathname;
let authHelper;
let client;
// Initialize auth helper
function initAuth() {
authHelper = new AuthHelper({
keystring: API_KEY,
redirectUri: REDIRECT_URI,
scopes: [
ETSY_SCOPES.SHOPS_READ,
ETSY_SCOPES.LISTINGS_READ,
ETSY_SCOPES.PROFILE_READ
]
});
}
// Handle login button click
document.getElementById('loginBtn').addEventListener('click', async () => {
const authUrl = await authHelper.getAuthUrl();
window.location.href = authUrl;
});
// Handle OAuth callback
async function handleCallback() {
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const state = urlParams.get('state');
if (code && state) {
try {
const expectedState = await authHelper.getState();
if (state === expectedState) {
await authHelper.setAuthorizationCode(code, state);
const tokens = await authHelper.getAccessToken();
// Create client
client = new EtsyClient({
keystring: API_KEY,
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
expiresAt: tokens.expires_at,
rateLimiting: { enabled: true },
caching: { enabled: true, ttl: 300 }
});
// Store tokens in localStorage for persistence
localStorage.setItem('etsy_tokens', JSON.stringify(tokens));
// Clear URL parameters
window.history.replaceState({}, document.title, window.location.pathname);
// Show user info
await showUserInfo();
} else {
console.error('State mismatch');
}
} catch (error) {
console.error('Authentication failed:', error);
alert('Authentication failed. Please try again.');
}
}
}
// Show user information
async function showUserInfo() {
try {
const user = await client.getUser();
document.getElementById('loginBtn').style.display = 'none';
document.getElementById('userInfo').style.display = 'block';
document.getElementById('userData').innerHTML = `
<p><strong>User ID:</strong> ${user.user_id}</p>
<p><strong>Login Name:</strong> ${user.login_name || 'N/A'}</p>
<p><strong>Primary Email:</strong> ${user.primary_email || 'N/A'}</p>
`;
} catch (error) {
console.error('Failed to get user info:', error);
alert('Failed to get user information.');
}
}
// Get user shops
document.getElementById('getShopsBtn').addEventListener('click', async () => {
try {
const shops = await client.getUserShops();
if (shops.length > 0) {
const shopsHtml = shops.map(shop => `
<div style="border: 1px solid #ccc; padding: 10px; margin: 10px 0;">
<h3>${shop.shop_name}</h3>
<p><strong>Shop ID:</strong> ${shop.shop_id}</p>
<p><strong>Title:</strong> ${shop.title || 'N/A'}</p>
<p><strong>URL:</strong> <a href="${shop.url}" target="_blank">${shop.url}</a></p>
</div>
`).join('');
document.getElementById('shopsData').innerHTML = `
<h3>Your Shops (${shops.length})</h3>
${shopsHtml}
`;
} else {
document.getElementById('shopsData').innerHTML = '<p>No shops found.</p>';
}
} catch (error) {
console.error('Failed to get shops:', error);
alert('Failed to get shop information.');
}
});
// Check for existing tokens on page load
async function checkExistingAuth() {
const storedTokens = localStorage.getItem('etsy_tokens');
if (storedTokens) {
try {
const tokens = JSON.parse(storedTokens);
// Check if tokens are still valid
if (new Date(tokens.expires_at) > new Date()) {
client = new EtsyClient({
keystring: API_KEY,
...tokens,
rateLimiting: { enabled: true },
caching: { enabled: true, ttl: 300 }
});
await showUserInfo();
} else {
// Tokens expired, clear them
localStorage.removeItem('etsy_tokens');
}
} catch (error) {
console.error('Failed to restore session:', error);
localStorage.removeItem('etsy_tokens');
}
}
}
// Initialize the application
initAuth();
await handleCallback();
await checkExistingAuth();
</script>
</body>
</html>
This example demonstrates:
- Complete OAuth 2.0 flow in the browser
- Token persistence using localStorage
- Error handling for authentication failures
- Making API calls to get user and shop information
- Automatic session restoration on page reload
React Hook Example
import { useState, useEffect } from 'react';
import { EtsyClient, createDefaultTokenStorage } from '@profplum700/etsy-v3-api-client';
function useEtsyClient(tokens) {
const [client, setClient] = useState(null);
useEffect(() => {
if (tokens) {
const storage = createDefaultTokenStorage({ preferSession: true });
const etsyClient = new EtsyClient({
keystring: process.env.REACT_APP_ETSY_API_KEY,
...tokens,
rateLimiting: { enabled: true },
caching: { enabled: true, ttl: 300 }
}, storage);
setClient(etsyClient);
}
}, [tokens]);
return client;
}
๐ Documentation
Comprehensive documentation and examples are available in the docs/ directory:
Guides
- Getting Started - Quick start guide and basic usage
- Authentication - Complete OAuth 2.0 authentication guide
- Listing Management - Creating and managing listings
- Order Fulfillment - Processing orders and shipments
- Shipping Profiles - Managing shipping profiles
- Webhooks - Real-time event notifications
Troubleshooting
- Common Issues - Solutions to common problems
Example Applications
Complete, working example applications demonstrating various use cases:
- Simple Shop Manager - Basic CLI shop management
- Bulk Listing Updater - Batch operations on listings
- Order Fulfillment App - Automated order processing
- Inventory Sync - Real-time inventory synchronization
- Analytics Dashboard - Shop metrics and reporting
- Production Template - Production-ready deployment template
๐ค Contributing
Contributions are welcome! Please read our Contributing Guidelines for details.
- Fork the repository
- Create your feature branch:
git checkout -b feature/amazing-feature - Commit your changes:
git commit -m 'Add amazing feature' - Push to the branch:
git push origin feature/amazing-feature - Open a Pull Request
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Links
โ Support
If you have questions or need help:
- Check the API Documentation
- Search GitHub Issues
- Create a new issue if your question isn't answered
๐ท๏ธ Changelog
See CHANGELOG.md for version history and release notes.
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Rea
Freeby Morluto ยท Developer Tools
Reverse engineer anything from your terminal or agent with one CLI and MCP server.
Paperclip
Freeby Paperclipai ยท Developer Tools
Trending hip-hop artist momentum scores across four cultural dimensions.
Fetch
Freeby Modelcontextprotocol ยท Developer Tools
Web content fetching and conversion for efficient LLM usage
Git
Freeby Modelcontextprotocol ยท Developer Tools
Read, search, and manipulate Git repositories programmatically
Toleno
Freeby Toleno ยท Developer Tools
Toleno Network MCP Server โ Manage your Toleno mining account with Claude AI using natural language.
MCP Marketplace
Freeby mcp-marketplace ยท Developer Tools
Search and install MCP servers from inside your AI client.
