Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt

Use this file to discover all available pages before exploring further.

Building Your Own MCP Server

LaunchMyStore provides a built-in HTTP MCP server at https://api.launchmystore.io/mcp. Most users don’t need to build their own — see the MCP Overview for the standard setup.This guide is for developers who want to build a custom MCP server with specialized logic, combine multiple data sources, or run locally.

When to Build Custom

Build your own MCP server when you need to:
  • Combine LaunchMyStore data with other APIs
  • Add custom business logic before returning data
  • Run locally for development/testing
  • Create a white-labeled integration

Prerequisites

  • Node.js 18+
  • A LaunchMyStore MCP token (see the overview — generate one under Settings → AI Connector (MCP) in the admin).
  • Familiarity with TypeScript

Project Setup

mkdir lms-mcp-server
cd lms-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk
npm install -D typescript @types/node

Basic Server Structure

Create src/index.ts:
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';

const LMS_API_BASE = 'https://api.launchmystore.io/api/v1';

class LaunchMyStoreServer {
  private server: Server;
  private accessToken: string;

  constructor(accessToken: string) {
    this.accessToken = accessToken;
    this.server = new Server(
      { name: 'launchmystore-custom', version: '1.0.0' },
      { capabilities: { tools: {} } }
    );

    this.setupHandlers();
  }

  private async apiRequest(endpoint: string, options: RequestInit = {}) {
    const response = await fetch(`${LMS_API_BASE}${endpoint}`, {
      ...options,
      headers: {
        'Authorization': `Bearer ${this.accessToken}`,
        'Content-Type': 'application/json',
        ...options.headers,
      },
    });
    
    const data = await response.json();
    if (!response.ok) {
      throw new Error(`API Error: ${data.error || response.statusText}`);
    }
    return data;
  }

  private setupHandlers() {
    // List available tools
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: 'get_products',
          description: 'List products in the store',
          inputSchema: {
            type: 'object',
            properties: {
              search: { type: 'string', description: 'Search by name' },
              status: { type: 'string', enum: ['active', 'inactive'] },
              limit: { type: 'number', description: 'Max results (default 10)' },
            },
          },
        },
        {
          name: 'get_orders',
          description: 'List orders',
          inputSchema: {
            type: 'object',
            properties: {
              status: { 
                type: 'string', 
                enum: ['unpaid', 'confirmed', 'shipped', 'delivered', 'canceled'] 
              },
              range: { 
                type: 'string', 
                enum: ['today', 'this_week', 'this_month', 'lifetime'] 
              },
            },
          },
        },
        {
          name: 'get_analytics',
          description: 'Get store analytics',
          inputSchema: {
            type: 'object',
            properties: {
              range: { type: 'string', enum: ['today', 'this_week', 'this_month'] },
            },
          },
        },
      ],
    }));

    // Handle tool calls
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const { name, arguments: args } = request.params;

      try {
        let result: any;

        switch (name) {
          case 'get_products': {
            const params = new URLSearchParams();
            if (args?.search) params.set('search', String(args.search));
            if (args?.status) params.set('status', String(args.status));
            if (args?.limit) params.set('limit', String(args.limit));
            result = await this.apiRequest(`/products.json?${params}`);
            break;
          }

          case 'get_orders': {
            const params = new URLSearchParams();
            if (args?.status) params.set('status', String(args.status));
            if (args?.range) params.set('range', String(args.range));
            result = await this.apiRequest(`/orders.json?${params}`);
            break;
          }

          case 'get_analytics': {
            const params = new URLSearchParams();
            if (args?.range) params.set('range', String(args.range));
            result = await this.apiRequest(`/analytics.json?${params}`);
            break;
          }

          default:
            throw new Error(`Unknown tool: ${name}`);
        }

        return {
          content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
        };
      } catch (error: any) {
        return {
          content: [{ type: 'text', text: `Error: ${error.message}` }],
          isError: true,
        };
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error('LaunchMyStore MCP server running');
  }
}

// Start server
const accessToken = process.env.LMS_ACCESS_TOKEN;
if (!accessToken) {
  console.error('LMS_ACCESS_TOKEN environment variable required');
  process.exit(1);
}

new LaunchMyStoreServer(accessToken).run().catch(console.error);

Build and Run

npx tsc
LMS_ACCESS_TOKEN=your_token node dist/index.js

Claude Desktop Configuration

For custom MCP servers using stdio transport:
{
  "mcpServers": {
    "launchmystore-custom": {
      "command": "node",
      "args": ["/path/to/your/dist/index.js"],
      "env": {
        "LMS_ACCESS_TOKEN": "your_api_token"
      }
    }
  }
}

Adding Custom Logic

You can add business logic that the built-in server doesn’t have:
case 'get_low_stock_products': {
  // Fetch products and filter by stock
  const products = await this.apiRequest('/products.json?limit=100');
  const lowStock = products.data.filter(
    (p: any) => p.stock < 10 && p.stockStatus === 'inStock'
  );
  
  // Add custom recommendations
  const result = {
    products: lowStock,
    summary: `${lowStock.length} products with low stock`,
    urgentReorders: lowStock.filter((p: any) => p.stock < 3),
  };
  
  return {
    content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
  };
}

Testing

Test your server locally with the MCP inspector:
npm install -g @modelcontextprotocol/inspector
mcp-inspector node dist/index.js

Publishing

Package your server for distribution:
{
  "name": "my-lms-mcp-server",
  "version": "1.0.0",
  "bin": {
    "my-lms-mcp": "./dist/index.js"
  },
  "files": ["dist"]
}
npm publish

Built-in vs Custom

FeatureBuilt-in HTTP ServerCustom Server
SetupJust add token to configBuild & deploy code
Tools50+ pre-built toolsDefine your own
LogicStandard API responsesCustom business logic
MaintenanceAutomatic updatesYou maintain it
Combine APIsLaunchMyStore onlyAny data source
For most use cases, the built-in MCP server is recommended.

Resources