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 +95 -0
- package/bin/swagmanager-mcp.js +25 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +141 -0
- package/dist/services/tool-registry.d.ts +37 -0
- package/dist/services/tool-registry.js +49 -0
- package/dist/tools/executor.d.ts +67 -0
- package/dist/tools/executor.js +2479 -0
- package/package.json +43 -0
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);
|
package/dist/index.d.ts
ADDED
|
@@ -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[];
|