swagmanager-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # @swagmanager/mcp
2
+
3
+ MCP (Model Context Protocol) server for SwagManager — manage inventory, orders, analytics, customers, and more from Claude Code or Claude Desktop.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ npm install -g @swagmanager/mcp
9
+ ```
10
+
11
+ ### Environment Variables
12
+
13
+ Create a `.env` file or set these environment variables:
14
+
15
+ ```
16
+ SUPABASE_URL=https://your-project.supabase.co
17
+ SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
18
+ DEFAULT_STORE_ID=your-store-uuid
19
+ ```
20
+
21
+ ### Claude Code
22
+
23
+ Add to `~/.claude/settings.json`:
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "swagmanager": {
29
+ "command": "swagmanager-mcp",
30
+ "env": {
31
+ "SUPABASE_URL": "https://your-project.supabase.co",
32
+ "SUPABASE_SERVICE_ROLE_KEY": "your-key",
33
+ "DEFAULT_STORE_ID": "your-store-uuid"
34
+ }
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ ### Claude Desktop
41
+
42
+ Add to your Claude Desktop MCP config:
43
+
44
+ ```json
45
+ {
46
+ "mcpServers": {
47
+ "swagmanager": {
48
+ "command": "npx",
49
+ "args": ["@swagmanager/mcp"],
50
+ "env": {
51
+ "SUPABASE_URL": "https://your-project.supabase.co",
52
+ "SUPABASE_SERVICE_ROLE_KEY": "your-key",
53
+ "DEFAULT_STORE_ID": "your-store-uuid"
54
+ }
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ ## Tools
61
+
62
+ Tools are loaded dynamically from the `ai_tool_registry` database table. The default set includes:
63
+
64
+ | Tool | Description |
65
+ |------|-------------|
66
+ | `analytics` | Sales analytics with flexible date ranges |
67
+ | `inventory` | Adjust quantities, set stock, transfer between locations |
68
+ | `inventory_query` | Query inventory summary, velocity, by location |
69
+ | `inventory_audit` | Start, count, complete inventory audits |
70
+ | `orders` | Find orders, get details, purchase orders |
71
+ | `purchase_orders` | Create, approve, receive, cancel purchase orders |
72
+ | `transfers` | Transfer inventory between locations |
73
+ | `products` | Find, create, update products and pricing |
74
+ | `customers` | Find, create, update customers |
75
+ | `collections` | Manage product collections |
76
+ | `suppliers` | Find and list suppliers |
77
+ | `locations` | Find store locations |
78
+ | `email` | Send emails, manage inbox |
79
+ | `alerts` | Low stock and pending order alerts |
80
+ | `documents` | Generate COAs and documents |
81
+ | `audit_trail` | View audit logs |
82
+
83
+ ## Development
84
+
85
+ ```bash
86
+ git clone https://github.com/floradistro/whale-mcp.git
87
+ cd whale-mcp
88
+ npm install
89
+ cp .env.example .env # fill in your credentials
90
+ npm run dev
91
+ ```
92
+
93
+ ## License
94
+
95
+ MIT
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * SwagManager MCP Server CLI
5
+ *
6
+ * Usage:
7
+ * npx @swagmanager/mcp
8
+ * swagmanager-mcp
9
+ *
10
+ * Environment variables:
11
+ * SUPABASE_URL - Your Supabase project URL
12
+ * SUPABASE_SERVICE_ROLE_KEY - Your Supabase service role key
13
+ * STORE_ID - Default store ID (optional)
14
+ * SWAG_API_KEY - API key for auth (future)
15
+ */
16
+
17
+ import { fileURLToPath } from "url";
18
+ import { dirname, join } from "path";
19
+
20
+ const __filename = fileURLToPath(import.meta.url);
21
+ const __dirname = dirname(__filename);
22
+
23
+ // Import and run the server
24
+ const serverPath = join(__dirname, "..", "dist", "index.js");
25
+ await import(serverPath);
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SwagManager MCP Server
4
+ *
5
+ * Standalone MCP server for managing inventory, orders, analytics,
6
+ * customers, products, and more from any MCP client (Claude Code,
7
+ * Claude Desktop, Cursor, etc.)
8
+ *
9
+ * Connects to your Supabase backend. Tools are loaded dynamically
10
+ * from the ai_tool_registry table.
11
+ */
12
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SwagManager MCP Server
4
+ *
5
+ * Standalone MCP server for managing inventory, orders, analytics,
6
+ * customers, products, and more from any MCP client (Claude Code,
7
+ * Claude Desktop, Cursor, etc.)
8
+ *
9
+ * Connects to your Supabase backend. Tools are loaded dynamically
10
+ * from the ai_tool_registry table.
11
+ */
12
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
13
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
14
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
15
+ import { createClient } from "@supabase/supabase-js";
16
+ import { executeTool, getImplementedTools } from "./tools/executor.js";
17
+ // ============================================================================
18
+ // CONFIGURATION
19
+ // ============================================================================
20
+ const SUPABASE_URL = process.env.SUPABASE_URL || "";
21
+ const SUPABASE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY || "";
22
+ const STORE_ID = process.env.STORE_ID || "";
23
+ if (!SUPABASE_URL || !SUPABASE_KEY) {
24
+ console.error("Error: SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY environment variables required");
25
+ console.error("");
26
+ console.error("Set them in your MCP client config:");
27
+ console.error(' "env": {');
28
+ console.error(' "SUPABASE_URL": "https://your-project.supabase.co",');
29
+ console.error(' "SUPABASE_SERVICE_ROLE_KEY": "your-key-here"');
30
+ console.error(" }");
31
+ process.exit(1);
32
+ }
33
+ const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
34
+ // Session ID for tracing — links all tool calls in one conversation
35
+ const SESSION_ID = crypto.randomUUID();
36
+ let toolDefinitions = [];
37
+ let toolsLoadedAt = 0;
38
+ const TOOL_CACHE_TTL = 60_000; // 1 minute
39
+ async function loadToolDefinitions(force = false) {
40
+ if (!force && toolDefinitions.length > 0 && Date.now() - toolsLoadedAt < TOOL_CACHE_TTL) {
41
+ return toolDefinitions;
42
+ }
43
+ try {
44
+ const { data, error } = await supabase
45
+ .from("ai_tool_registry")
46
+ .select("name, description, definition")
47
+ .eq("is_active", true);
48
+ if (error) {
49
+ console.error("[MCP] Failed to load tools from registry:", error.message);
50
+ return toolDefinitions; // Return stale cache on error
51
+ }
52
+ toolDefinitions = (data || []).map(t => ({
53
+ name: t.name,
54
+ description: t.description || t.definition?.description || `Execute ${t.name}`,
55
+ inputSchema: t.definition?.input_schema || { type: "object", properties: {} }
56
+ }));
57
+ toolsLoadedAt = Date.now();
58
+ return toolDefinitions;
59
+ }
60
+ catch (err) {
61
+ console.error("[MCP] Error loading tool definitions:", err);
62
+ return toolDefinitions;
63
+ }
64
+ }
65
+ // ============================================================================
66
+ // MCP SERVER
67
+ // ============================================================================
68
+ const server = new Server({ name: "swagmanager", version: "1.0.0" }, { capabilities: { tools: {} } });
69
+ // List available tools
70
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
71
+ const tools = await loadToolDefinitions();
72
+ console.error(`[MCP] Returning ${tools.length} tools`);
73
+ return {
74
+ tools: tools.map(t => ({
75
+ name: t.name,
76
+ description: t.description,
77
+ inputSchema: t.inputSchema,
78
+ })),
79
+ };
80
+ });
81
+ // Execute a tool
82
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
83
+ const toolName = request.params.name;
84
+ const toolArgs = (request.params.arguments || {});
85
+ console.error(`[MCP] Executing: ${toolName}`);
86
+ // Validate tool is implemented
87
+ const implementedTools = getImplementedTools();
88
+ if (!implementedTools.includes(toolName)) {
89
+ return {
90
+ content: [{
91
+ type: "text",
92
+ text: JSON.stringify({ error: `Tool "${toolName}" not implemented` }),
93
+ }],
94
+ isError: true,
95
+ };
96
+ }
97
+ // Execute with telemetry context
98
+ const result = await executeTool(supabase, toolName, toolArgs, STORE_ID || undefined, {
99
+ source: "mcp",
100
+ requestId: SESSION_ID
101
+ });
102
+ if (result.success) {
103
+ return {
104
+ content: [{
105
+ type: "text",
106
+ text: typeof result.data === "string"
107
+ ? result.data
108
+ : JSON.stringify(result.data, null, 2),
109
+ }],
110
+ };
111
+ }
112
+ else {
113
+ return {
114
+ content: [{
115
+ type: "text",
116
+ text: JSON.stringify({ error: result.error }),
117
+ }],
118
+ isError: true,
119
+ };
120
+ }
121
+ });
122
+ // ============================================================================
123
+ // STARTUP
124
+ // ============================================================================
125
+ async function main() {
126
+ console.error("[MCP] SwagManager MCP Server v1.0.0");
127
+ console.error(`[MCP] Supabase: ${SUPABASE_URL}`);
128
+ console.error(`[MCP] Store: ${STORE_ID || "(default)"}`);
129
+ console.error(`[MCP] Session: ${SESSION_ID}`);
130
+ // Pre-load tools
131
+ const tools = await loadToolDefinitions(true);
132
+ console.error(`[MCP] Loaded ${tools.length} tools`);
133
+ // Connect via stdio
134
+ const transport = new StdioServerTransport();
135
+ await server.connect(transport);
136
+ console.error("[MCP] Ready");
137
+ }
138
+ main().catch((err) => {
139
+ console.error("[MCP] Fatal:", err);
140
+ process.exit(1);
141
+ });
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Tool Registry
3
+ *
4
+ * Loads tools from Supabase ai_tool_registry.
5
+ * Used by the agent server to know which tools are available.
6
+ */
7
+ export interface ToolRegistryEntry {
8
+ id: string;
9
+ name: string;
10
+ category: string;
11
+ description: string | null;
12
+ definition: {
13
+ name: string;
14
+ description: string;
15
+ input_schema: {
16
+ type: "object";
17
+ properties: Record<string, any>;
18
+ required?: string[];
19
+ };
20
+ };
21
+ rpc_function: string | null;
22
+ edge_function: string | null;
23
+ requires_store_id: boolean;
24
+ requires_user_id: boolean;
25
+ is_read_only: boolean;
26
+ is_active: boolean;
27
+ tool_mode: string;
28
+ }
29
+ export interface ClientToolMetadata {
30
+ id: string;
31
+ name: string;
32
+ description: string;
33
+ category: string;
34
+ }
35
+ export declare function invalidateToolCache(): void;
36
+ export declare function loadToolRegistry(supabaseUrl: string, supabaseKey: string, forceRefresh?: boolean): Promise<ToolRegistryEntry[]>;
37
+ export declare function getToolMetadata(tools: ToolRegistryEntry[]): ClientToolMetadata[];
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Tool Registry
3
+ *
4
+ * Loads tools from Supabase ai_tool_registry.
5
+ * Used by the agent server to know which tools are available.
6
+ */
7
+ import { createClient } from "@supabase/supabase-js";
8
+ // ============================================================================
9
+ // TOOL REGISTRY LOADER
10
+ // ============================================================================
11
+ let cachedTools = null;
12
+ let cacheTimestamp = 0;
13
+ const CACHE_TTL = 60000; // 1 minute cache
14
+ export function invalidateToolCache() {
15
+ cachedTools = null;
16
+ cacheTimestamp = 0;
17
+ }
18
+ export async function loadToolRegistry(supabaseUrl, supabaseKey, forceRefresh = false) {
19
+ // Return cached if fresh
20
+ if (!forceRefresh && cachedTools && Date.now() - cacheTimestamp < CACHE_TTL) {
21
+ return cachedTools;
22
+ }
23
+ const supabase = createClient(supabaseUrl, supabaseKey);
24
+ const { data, error } = await supabase
25
+ .from("ai_tool_registry")
26
+ .select("*")
27
+ .eq("is_active", true)
28
+ .order("category", { ascending: true });
29
+ if (error) {
30
+ console.error("[ToolRegistry] Failed to load tools:", error.message);
31
+ return cachedTools || [];
32
+ }
33
+ cachedTools = data;
34
+ cacheTimestamp = Date.now();
35
+ // Import dynamically to avoid circular dependency
36
+ const { getImplementedTools } = await import("../tools/executor.js");
37
+ const implementedTools = getImplementedTools();
38
+ const implementedCount = cachedTools.filter(t => implementedTools.includes(t.name)).length;
39
+ console.log(`[ToolRegistry] Loaded ${cachedTools.length} tools from registry (${implementedCount} implemented locally)`);
40
+ return cachedTools;
41
+ }
42
+ export function getToolMetadata(tools) {
43
+ return tools.map(t => ({
44
+ id: t.name,
45
+ name: t.definition?.name || t.name,
46
+ description: t.description || t.definition?.description || "",
47
+ category: t.category
48
+ }));
49
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Consolidated Tool Executor
3
+ *
4
+ * Following Anthropic's best practices for tool design:
5
+ * - "More tools don't always lead to better outcomes"
6
+ * - "Claude Code uses about a dozen tools"
7
+ * - "Consolidate multi-step operations into single tool calls"
8
+ *
9
+ * 39 tools → 12 consolidated tools:
10
+ *
11
+ * 1. inventory - manage inventory (adjust, set, transfer, bulk operations)
12
+ * 2. inventory_query - query inventory (summary, velocity, by_location, in_stock)
13
+ * 3. inventory_audit - audit workflow (start, count, complete, summary)
14
+ * 4. collections - manage collections (find, create, get_theme, set_theme, set_icon)
15
+ * 5. customers - manage customers (find, create, update)
16
+ * 6. products - manage products (find, create, update, pricing)
17
+ * 7. analytics - analytics & intelligence (summary, by_location, detailed, discover, employee, customers, products, inventory_intelligence, marketing, fraud, employee_performance, behavior, full)
18
+ * 8. locations - find/list locations
19
+ * 9. orders - manage orders (find, get, create)
20
+ * 10. suppliers - find suppliers
21
+ * 11. email - unified email (send, send_template, list, get, templates)
22
+ * 12. documents - document generation
23
+ * 13. alerts - system alerts
24
+ * 14. audit_trail - audit logs
25
+ */
26
+ import { SupabaseClient } from "@supabase/supabase-js";
27
+ export declare enum ToolErrorType {
28
+ RECOVERABLE = "recoverable",// Retry with same input (transient failure)
29
+ PERMANENT = "permanent",// Don't retry (invalid input, business logic error)
30
+ RATE_LIMIT = "rate_limit",// Exponential backoff needed
31
+ AUTH = "auth",// User permission issue
32
+ VALIDATION = "validation",// Bad input from AI
33
+ NOT_FOUND = "not_found"
34
+ }
35
+ export interface ToolResult {
36
+ success: boolean;
37
+ data?: unknown;
38
+ error?: string;
39
+ errorType?: ToolErrorType;
40
+ retryable?: boolean;
41
+ timedOut?: boolean;
42
+ }
43
+ export type SpanKind = "CLIENT" | "SERVER" | "INTERNAL" | "PRODUCER" | "CONSUMER";
44
+ export interface ExecutionContext {
45
+ source: "claude_code" | "swag_manager" | "api" | "edge_function" | "mcp" | "test";
46
+ userId?: string;
47
+ traceId?: string;
48
+ spanId?: string;
49
+ parentSpanId?: string;
50
+ traceFlags?: number;
51
+ requestId?: string;
52
+ parentId?: string;
53
+ serviceName?: string;
54
+ serviceVersion?: string;
55
+ agentId?: string;
56
+ agentName?: string;
57
+ conversationId?: string;
58
+ turnNumber?: number;
59
+ model?: string;
60
+ inputTokens?: number;
61
+ outputTokens?: number;
62
+ totalCost?: number;
63
+ costBefore?: number;
64
+ turnCost?: number;
65
+ }
66
+ export declare function executeTool(supabase: SupabaseClient, toolName: string, args: Record<string, unknown>, storeId?: string, context?: ExecutionContext): Promise<ToolResult>;
67
+ export declare function getImplementedTools(): string[];