> ## 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

> Create a custom MCP server for LaunchMyStore integration

# Building Your Own MCP Server

<Note>
  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](/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.
</Note>

## 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](/mcp/overview) — generate one under **Settings → AI Connector (MCP)** in the admin).
* Familiarity with TypeScript

## Project Setup

```bash theme={null}
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`:

```typescript theme={null}
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

```bash theme={null}
npx tsc
LMS_ACCESS_TOKEN=your_token node dist/index.js
```

## Claude Desktop Configuration

For custom MCP servers using stdio transport:

```json theme={null}
{
  "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:

```typescript theme={null}
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:

```bash theme={null}
npm install -g @modelcontextprotocol/inspector
mcp-inspector node dist/index.js
```

## Publishing

Package your server for distribution:

```json theme={null}
{
  "name": "my-lms-mcp-server",
  "version": "1.0.0",
  "bin": {
    "my-lms-mcp": "./dist/index.js"
  },
  "files": ["dist"]
}
```

```bash theme={null}
npm publish
```

## Built-in vs Custom

| Feature      | Built-in HTTP Server     | Custom Server         |
| ------------ | ------------------------ | --------------------- |
| Setup        | Just add token to config | Build & deploy code   |
| Tools        | 50+ pre-built tools      | Define your own       |
| Logic        | Standard API responses   | Custom business logic |
| Maintenance  | Automatic updates        | You maintain it       |
| Combine APIs | LaunchMyStore only       | Any data source       |

For most use cases, the [built-in MCP server](/mcp/overview) is recommended.

## Resources

* [MCP Specification](https://modelcontextprotocol.io/docs)
* [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
* [LaunchMyStore API Reference](/api-reference/overview)
* [Built-in MCP Tools](/mcp/tools)
