sychev-lab-mcp-server 1.0.6 → 1.0.7
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/dist/client.d.ts +109 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +143 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +29 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +312 -0
- package/dist/index.js.map +1 -0
- package/dist/server-http.d.ts +9 -0
- package/dist/server-http.d.ts.map +1 -0
- package/dist/server-http.js +204 -0
- package/dist/server-http.js.map +1 -0
- package/dist/tools.d.ts +184 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +376 -0
- package/dist/tools.js.map +1 -0
- package/package.json +1 -1
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP Client for Sychev Lab API
|
|
3
|
+
*/
|
|
4
|
+
export interface Product {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
price: number;
|
|
9
|
+
currency: string;
|
|
10
|
+
images: string[];
|
|
11
|
+
category: string;
|
|
12
|
+
tags: string[];
|
|
13
|
+
slug?: string;
|
|
14
|
+
thumbnail?: string;
|
|
15
|
+
type?: string;
|
|
16
|
+
featured?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface Article {
|
|
19
|
+
id: string;
|
|
20
|
+
title: string;
|
|
21
|
+
content: string;
|
|
22
|
+
author: string;
|
|
23
|
+
published_at: string;
|
|
24
|
+
tags: string[];
|
|
25
|
+
slug?: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
thumbnail?: string;
|
|
28
|
+
type?: string;
|
|
29
|
+
featured?: boolean;
|
|
30
|
+
path?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface Tutorial {
|
|
33
|
+
id: string;
|
|
34
|
+
title: string;
|
|
35
|
+
content: string;
|
|
36
|
+
author: string;
|
|
37
|
+
published_at: string;
|
|
38
|
+
tags: string[];
|
|
39
|
+
difficulty: string;
|
|
40
|
+
duration: string;
|
|
41
|
+
slug?: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
thumbnail?: string;
|
|
44
|
+
type?: string;
|
|
45
|
+
featured?: boolean;
|
|
46
|
+
path?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface Category {
|
|
49
|
+
id: string;
|
|
50
|
+
name: string;
|
|
51
|
+
slug: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
parent?: string;
|
|
54
|
+
children?: Category[];
|
|
55
|
+
}
|
|
56
|
+
export interface CheckoutSession {
|
|
57
|
+
sessionId: string;
|
|
58
|
+
url: string;
|
|
59
|
+
}
|
|
60
|
+
export interface CheckoutItem {
|
|
61
|
+
id: string;
|
|
62
|
+
title: string;
|
|
63
|
+
price: number;
|
|
64
|
+
quantity?: number;
|
|
65
|
+
}
|
|
66
|
+
declare class ApiClient {
|
|
67
|
+
private baseUrl;
|
|
68
|
+
constructor(baseUrl: string);
|
|
69
|
+
private fetch;
|
|
70
|
+
private post;
|
|
71
|
+
getProducts(): Promise<Product[]>;
|
|
72
|
+
getProduct(id: string, lang?: string): Promise<Product>;
|
|
73
|
+
getArticles(): Promise<Article[]>;
|
|
74
|
+
getArticle(id: string, lang: string): Promise<Article>;
|
|
75
|
+
getTutorials(): Promise<Tutorial[]>;
|
|
76
|
+
getTutorial(id: string, lang: string): Promise<Tutorial>;
|
|
77
|
+
getCategories(): Promise<{
|
|
78
|
+
categories: Category[];
|
|
79
|
+
}>;
|
|
80
|
+
searchProductsByCategory(searchTerm: string, limit?: number): Promise<Product[]>;
|
|
81
|
+
createCheckout(items: CheckoutItem[], options?: {
|
|
82
|
+
guestEmail?: string;
|
|
83
|
+
locale?: string;
|
|
84
|
+
}): Promise<CheckoutSession>;
|
|
85
|
+
createCheckoutSecure(productId: string, quantity?: number, options?: {
|
|
86
|
+
guestEmail?: string;
|
|
87
|
+
guestName?: string;
|
|
88
|
+
locale?: string;
|
|
89
|
+
}): Promise<CheckoutSession & {
|
|
90
|
+
product: {
|
|
91
|
+
id: string;
|
|
92
|
+
name: string;
|
|
93
|
+
price: number;
|
|
94
|
+
currency: string;
|
|
95
|
+
type: string;
|
|
96
|
+
};
|
|
97
|
+
}>;
|
|
98
|
+
registerUser(email: string, password: string, displayName?: string): Promise<{
|
|
99
|
+
message: string;
|
|
100
|
+
user: {
|
|
101
|
+
uid: string;
|
|
102
|
+
email: string;
|
|
103
|
+
displayName?: string;
|
|
104
|
+
};
|
|
105
|
+
}>;
|
|
106
|
+
}
|
|
107
|
+
export declare const apiClient: ApiClient;
|
|
108
|
+
export {};
|
|
109
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,WAAW,OAAO;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,OAAO;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,cAAM,SAAS;IACX,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,MAAM;YAIb,KAAK;YAWL,IAAI;IAmBZ,WAAW,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAIjC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,GAAE,MAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAK7D,WAAW,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAIjC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKtD,YAAY,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAInC,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAKxD,aAAa,IAAI,OAAO,CAAC;QAAE,UAAU,EAAE,QAAQ,EAAE,CAAA;KAAE,CAAC;IAKpD,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IA0DpF,cAAc,CAChB,KAAK,EAAE,YAAY,EAAE,EACrB,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GACnD,OAAO,CAAC,eAAe,CAAC;IAWrB,oBAAoB,CACtB,SAAS,EAAE,MAAM,EACjB,QAAQ,GAAE,MAAU,EACpB,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GACvE,OAAO,CAAC,eAAe,GAAG;QAAE,OAAO,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IAahH,YAAY,CACd,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CAO9F;AAED,eAAO,MAAM,SAAS,WAAgC,CAAC"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP Client for Sychev Lab API
|
|
3
|
+
*/
|
|
4
|
+
import { config } from './config.js';
|
|
5
|
+
class ApiClient {
|
|
6
|
+
baseUrl;
|
|
7
|
+
constructor(baseUrl) {
|
|
8
|
+
this.baseUrl = baseUrl.replace(/\/$/, '');
|
|
9
|
+
}
|
|
10
|
+
async fetch(endpoint) {
|
|
11
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
12
|
+
const response = await fetch(url);
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
throw new Error(`API error: ${response.status} ${response.statusText}`);
|
|
15
|
+
}
|
|
16
|
+
return response.json();
|
|
17
|
+
}
|
|
18
|
+
async post(endpoint, body) {
|
|
19
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
20
|
+
const response = await fetch(url, {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
headers: {
|
|
23
|
+
'Content-Type': 'application/json',
|
|
24
|
+
},
|
|
25
|
+
body: JSON.stringify(body),
|
|
26
|
+
});
|
|
27
|
+
if (!response.ok) {
|
|
28
|
+
const error = await response.json().catch(() => ({ message: 'Unknown error' }));
|
|
29
|
+
throw new Error(error.message || `API error: ${response.status}`);
|
|
30
|
+
}
|
|
31
|
+
return response.json();
|
|
32
|
+
}
|
|
33
|
+
// Products
|
|
34
|
+
async getProducts() {
|
|
35
|
+
return this.fetch('/api/lab/products/index.json');
|
|
36
|
+
}
|
|
37
|
+
async getProduct(id, lang = 'es') {
|
|
38
|
+
return this.fetch(`/api/lab/products/${id}.${lang}.json`);
|
|
39
|
+
}
|
|
40
|
+
// Articles
|
|
41
|
+
async getArticles() {
|
|
42
|
+
return this.fetch('/api/lab/index.articles.json');
|
|
43
|
+
}
|
|
44
|
+
async getArticle(id, lang) {
|
|
45
|
+
return this.fetch(`/api/lab/articles/${id}.${lang}.json`);
|
|
46
|
+
}
|
|
47
|
+
// Tutorials
|
|
48
|
+
async getTutorials() {
|
|
49
|
+
return this.fetch('/api/lab/index.tutorials.json');
|
|
50
|
+
}
|
|
51
|
+
async getTutorial(id, lang) {
|
|
52
|
+
return this.fetch(`/api/lab/tutorials/${id}.${lang}.json`);
|
|
53
|
+
}
|
|
54
|
+
// Categories
|
|
55
|
+
async getCategories() {
|
|
56
|
+
return this.fetch('/api/lab/categories.json');
|
|
57
|
+
}
|
|
58
|
+
// Search products by category (fuzzy search)
|
|
59
|
+
async searchProductsByCategory(searchTerm, limit = 20) {
|
|
60
|
+
const response = await this.getProducts();
|
|
61
|
+
// Handle case where API returns an object with products or data property
|
|
62
|
+
const products = Array.isArray(response) ? response :
|
|
63
|
+
(response && typeof response === 'object' && 'products' in response) ?
|
|
64
|
+
response.products :
|
|
65
|
+
(response && typeof response === 'object' && 'data' in response) ?
|
|
66
|
+
response.data : [];
|
|
67
|
+
const searchLower = searchTerm.toLowerCase();
|
|
68
|
+
const filtered = products.filter((p) => {
|
|
69
|
+
// Search in multiple fields (API uses 'title' instead of 'name')
|
|
70
|
+
const nameMatch = p.name?.toLowerCase().includes(searchLower) ||
|
|
71
|
+
p.title?.toLowerCase().includes(searchLower);
|
|
72
|
+
const descriptionMatch = p.description?.toLowerCase().includes(searchLower);
|
|
73
|
+
// category puede ser string, objeto o array - manejar todos los casos
|
|
74
|
+
let categoryValue = '';
|
|
75
|
+
if (typeof p.category === 'string') {
|
|
76
|
+
categoryValue = p.category;
|
|
77
|
+
}
|
|
78
|
+
else if (p.category && typeof p.category === 'object') {
|
|
79
|
+
// Es un objeto con propiedad name
|
|
80
|
+
categoryValue = p.category.name || '';
|
|
81
|
+
}
|
|
82
|
+
else if (Array.isArray(p.category)) {
|
|
83
|
+
categoryValue = p.category.join(' ');
|
|
84
|
+
}
|
|
85
|
+
const categoryMatch = categoryValue.toLowerCase().includes(searchLower);
|
|
86
|
+
const tagsMatch = p.tags?.some((tag) => tag.toLowerCase().includes(searchLower));
|
|
87
|
+
const slugMatch = p.slug?.toLowerCase().includes(searchLower);
|
|
88
|
+
return nameMatch || descriptionMatch || categoryMatch || tagsMatch || slugMatch;
|
|
89
|
+
});
|
|
90
|
+
// Sort by relevance (exact matches first, then partial matches)
|
|
91
|
+
const sorted = filtered.sort((a, b) => {
|
|
92
|
+
const aName = (a.name || a.title || '').toLowerCase();
|
|
93
|
+
const bName = (b.name || b.title || '').toLowerCase();
|
|
94
|
+
const aExact = aName === searchLower;
|
|
95
|
+
const bExact = bName === searchLower;
|
|
96
|
+
if (aExact && !bExact)
|
|
97
|
+
return -1;
|
|
98
|
+
if (!aExact && bExact)
|
|
99
|
+
return 1;
|
|
100
|
+
// Then prioritize name starts with search term
|
|
101
|
+
const aStarts = aName.startsWith(searchLower);
|
|
102
|
+
const bStarts = bName.startsWith(searchLower);
|
|
103
|
+
if (aStarts && !bStarts)
|
|
104
|
+
return -1;
|
|
105
|
+
if (!aStarts && bStarts)
|
|
106
|
+
return 1;
|
|
107
|
+
return 0;
|
|
108
|
+
});
|
|
109
|
+
return sorted.slice(0, limit);
|
|
110
|
+
}
|
|
111
|
+
// Checkout (legacy - mantenido para compatibilidad)
|
|
112
|
+
async createCheckout(items, options) {
|
|
113
|
+
return this.post('/api/stripe/checkout', {
|
|
114
|
+
items,
|
|
115
|
+
guestEmail: options?.guestEmail,
|
|
116
|
+
locale: options?.locale || 'es',
|
|
117
|
+
successUrl: `${this.baseUrl}/checkout/success`,
|
|
118
|
+
cancelUrl: `${this.baseUrl}/checkout/cancel`,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
// Checkout seguro - solo recibe productId, obtiene datos desde Firestore
|
|
122
|
+
async createCheckoutSecure(productId, quantity = 1, options) {
|
|
123
|
+
return this.post('/api/stripe/checkout-secure', {
|
|
124
|
+
productId,
|
|
125
|
+
quantity,
|
|
126
|
+
guestEmail: options?.guestEmail,
|
|
127
|
+
guestName: options?.guestName,
|
|
128
|
+
locale: options?.locale || 'es',
|
|
129
|
+
successUrl: `${this.baseUrl}/checkout/success`,
|
|
130
|
+
cancelUrl: `${this.baseUrl}/checkout/cancel`,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
// User Registration
|
|
134
|
+
async registerUser(email, password, displayName) {
|
|
135
|
+
return this.post('/api/register', {
|
|
136
|
+
email,
|
|
137
|
+
password,
|
|
138
|
+
displayName,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
export const apiClient = new ApiClient(config.baseUrl);
|
|
143
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for Sychev Lab MCP Server
|
|
3
|
+
*/
|
|
4
|
+
export interface ServerConfig {
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
name: string;
|
|
7
|
+
version: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const config: ServerConfig;
|
|
10
|
+
export declare function validateConfig(): void;
|
|
11
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,YAAY;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACnB;AAGD,eAAO,MAAM,MAAM,EAAE,YAIpB,CAAC;AAGF,wBAAgB,cAAc,IAAI,IAAI,CAkBrC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for Sychev Lab MCP Server
|
|
3
|
+
*/
|
|
4
|
+
// Load from environment or use defaults
|
|
5
|
+
export const config = {
|
|
6
|
+
baseUrl: process.env.SYCHEV_LAB_URL || 'https://lab.sychev.xyz',
|
|
7
|
+
name: 'sychev-lab-mcp',
|
|
8
|
+
version: '1.0.7'
|
|
9
|
+
};
|
|
10
|
+
// Validate configuration
|
|
11
|
+
export function validateConfig() {
|
|
12
|
+
if (!config.baseUrl) {
|
|
13
|
+
throw new Error('SYCHEV_LAB_URL environment variable is required');
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
new URL(config.baseUrl);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
throw new Error(`Invalid base URL: ${config.baseUrl}`);
|
|
20
|
+
}
|
|
21
|
+
// Reject localhost without explicit development flag to prevent accidental misconfiguration
|
|
22
|
+
if (config.baseUrl.includes('localhost') && process.env.SYCHEV_LAB_ENV !== 'development') {
|
|
23
|
+
console.warn(`⚠️ Warning: Using localhost as base URL without SYCHEV_LAB_ENV=development.`);
|
|
24
|
+
console.warn(` To override, set SYCHEV_LAB_URL explicitly or use SYCHEV_LAB_ENV=development.`);
|
|
25
|
+
console.warn(` Defaulting to production: https://lab.sychev.xyz`);
|
|
26
|
+
config.baseUrl = 'https://lab.sychev.xyz';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,wCAAwC;AACxC,MAAM,CAAC,MAAM,MAAM,GAAiB;IAChC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,wBAAwB;IAC/D,IAAI,EAAE,gBAAgB;IACtB,OAAO,EAAE,OAAO;CACnB,CAAC;AAEF,yBAAyB;AACzB,MAAM,UAAU,cAAc;IAC1B,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACvE,CAAC;IAED,IAAI,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACL,MAAM,IAAI,KAAK,CAAC,qBAAqB,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,4FAA4F;IAC5F,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,aAAa,EAAE,CAAC;QACvF,OAAO,CAAC,IAAI,CAAC,8EAA8E,CAAC,CAAC;QAC7F,OAAO,CAAC,IAAI,CAAC,kFAAkF,CAAC,CAAC;QACjG,OAAO,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;QACpE,MAAM,CAAC,OAAO,GAAG,wBAAwB,CAAC;IAC9C,CAAC;AACL,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Sychev Lab MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Supports both stdio (for Claude Desktop) and HTTP (for remote clients) transports
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node dist/index.js # Run with stdio transport (default)
|
|
9
|
+
* node dist/index.js --http # Run with HTTP transport
|
|
10
|
+
* node dist/index.js --http 3000 # Run HTTP on port 3000
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;GASG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Sychev Lab MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Supports both stdio (for Claude Desktop) and HTTP (for remote clients) transports
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node dist/index.js # Run with stdio transport (default)
|
|
9
|
+
* node dist/index.js --http # Run with HTTP transport
|
|
10
|
+
* node dist/index.js --http 3000 # Run HTTP on port 3000
|
|
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 { zodToJsonSchema } from 'zod-to-json-schema';
|
|
16
|
+
import { validateConfig, config } from './config.js';
|
|
17
|
+
import { GetProductSchema, SearchProductsSchema, GetArticleSchema, GetTutorialSchema, GetCategoriesSchema, RegisterUserSchema, CreateCheckoutSchema, ListProductsSchema, ListArticlesSchema, ListTutorialsSchema, getProductDetails, searchProductsByCategory, getArticle, getTutorial, getCategories, registerUser, createStripeCheckout, listProducts, listArticles, listTutorials, } from './tools.js';
|
|
18
|
+
// Tool definitions with schemas
|
|
19
|
+
const TOOLS = [
|
|
20
|
+
{
|
|
21
|
+
name: 'list_products',
|
|
22
|
+
description: 'List all available products from the catalog. Optionally filter by featured products only.',
|
|
23
|
+
inputSchema: zodToJsonSchema(ListProductsSchema),
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'get_product_details',
|
|
27
|
+
description: 'Get detailed information about a specific product including price, description, images, and specifications.',
|
|
28
|
+
inputSchema: zodToJsonSchema(GetProductSchema),
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'search_products_by_category',
|
|
32
|
+
description: 'Search products within a specific category. Returns matching products with their details.',
|
|
33
|
+
inputSchema: zodToJsonSchema(SearchProductsSchema),
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'get_categories',
|
|
37
|
+
description: 'Get all product categories available in the store.',
|
|
38
|
+
inputSchema: zodToJsonSchema(GetCategoriesSchema),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'list_articles',
|
|
42
|
+
description: 'List all available articles. Optionally filter by featured articles only.',
|
|
43
|
+
inputSchema: zodToJsonSchema(ListArticlesSchema),
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'get_article',
|
|
47
|
+
description: 'Get full content of a specific article by ID and language.',
|
|
48
|
+
inputSchema: zodToJsonSchema(GetArticleSchema),
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'list_tutorials',
|
|
52
|
+
description: 'List all available tutorials. Optionally filter by featured tutorials only.',
|
|
53
|
+
inputSchema: zodToJsonSchema(ListTutorialsSchema),
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'get_tutorial',
|
|
57
|
+
description: 'Get full content of a specific tutorial by ID and language including difficulty and duration.',
|
|
58
|
+
inputSchema: zodToJsonSchema(GetTutorialSchema),
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'register_user',
|
|
62
|
+
description: 'Register a new user account with email and password. Returns user UID on success.',
|
|
63
|
+
inputSchema: zodToJsonSchema(RegisterUserSchema),
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'create_stripe_checkout',
|
|
67
|
+
description: 'Create a Stripe checkout session for purchasing products. Provide product UUIDs and quantities; product details (name, price) are fetched automatically. Returns a URL to redirect the user to complete payment.',
|
|
68
|
+
inputSchema: zodToJsonSchema(CreateCheckoutSchema),
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
// Create MCP server
|
|
72
|
+
const server = new Server({
|
|
73
|
+
name: config.name,
|
|
74
|
+
version: config.version,
|
|
75
|
+
}, {
|
|
76
|
+
capabilities: {
|
|
77
|
+
tools: {},
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
// Handle list tools request
|
|
81
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
82
|
+
return { tools: TOOLS };
|
|
83
|
+
});
|
|
84
|
+
// Handle tool calls
|
|
85
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
86
|
+
const { name, arguments: args } = request.params;
|
|
87
|
+
try {
|
|
88
|
+
switch (name) {
|
|
89
|
+
case 'list_products':
|
|
90
|
+
return await listProducts(ListProductsSchema.parse(args));
|
|
91
|
+
case 'get_product_details':
|
|
92
|
+
return await getProductDetails(GetProductSchema.parse(args));
|
|
93
|
+
case 'search_products_by_category':
|
|
94
|
+
return await searchProductsByCategory(SearchProductsSchema.parse(args));
|
|
95
|
+
case 'get_categories':
|
|
96
|
+
return await getCategories(GetCategoriesSchema.parse(args));
|
|
97
|
+
case 'list_articles':
|
|
98
|
+
return await listArticles(ListArticlesSchema.parse(args));
|
|
99
|
+
case 'get_article':
|
|
100
|
+
return await getArticle(GetArticleSchema.parse(args));
|
|
101
|
+
case 'list_tutorials':
|
|
102
|
+
return await listTutorials(ListTutorialsSchema.parse(args));
|
|
103
|
+
case 'get_tutorial':
|
|
104
|
+
return await getTutorial(GetTutorialSchema.parse(args));
|
|
105
|
+
case 'register_user':
|
|
106
|
+
return await registerUser(RegisterUserSchema.parse(args));
|
|
107
|
+
case 'create_stripe_checkout':
|
|
108
|
+
return await createStripeCheckout(CreateCheckoutSchema.parse(args));
|
|
109
|
+
default:
|
|
110
|
+
return {
|
|
111
|
+
content: [{
|
|
112
|
+
type: 'text',
|
|
113
|
+
text: JSON.stringify({ error: `Unknown tool: ${name}` }),
|
|
114
|
+
}],
|
|
115
|
+
isError: true,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (error instanceof Error && error.name === 'ZodError') {
|
|
121
|
+
return {
|
|
122
|
+
content: [{
|
|
123
|
+
type: 'text',
|
|
124
|
+
text: JSON.stringify({
|
|
125
|
+
error: 'Invalid arguments',
|
|
126
|
+
details: error.message,
|
|
127
|
+
}, null, 2),
|
|
128
|
+
}],
|
|
129
|
+
isError: true,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
// Run server with stdio transport
|
|
136
|
+
async function runStdioServer() {
|
|
137
|
+
validateConfig();
|
|
138
|
+
const transport = new StdioServerTransport();
|
|
139
|
+
// Log to stderr so it doesn't interfere with stdio communication
|
|
140
|
+
console.error(`Sychev Lab MCP Server v${config.version}`);
|
|
141
|
+
console.error(`Connected to: ${config.baseUrl}`);
|
|
142
|
+
console.error('Running with stdio transport...');
|
|
143
|
+
await server.connect(transport);
|
|
144
|
+
}
|
|
145
|
+
// Run server with HTTP transport
|
|
146
|
+
async function runHttpServer(port) {
|
|
147
|
+
validateConfig();
|
|
148
|
+
const http = await import('http');
|
|
149
|
+
const url = await import('url');
|
|
150
|
+
// API key is optional - only used if MCP_API_KEY is set
|
|
151
|
+
const API_KEY = process.env.MCP_API_KEY;
|
|
152
|
+
const httpServer = http.createServer(async (req, res) => {
|
|
153
|
+
// CORS headers
|
|
154
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
155
|
+
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
|
156
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
157
|
+
res.setHeader('Content-Type', 'application/json');
|
|
158
|
+
if (req.method === 'OPTIONS') {
|
|
159
|
+
res.writeHead(200);
|
|
160
|
+
res.end();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// Only accept POST requests
|
|
164
|
+
if (req.method !== 'POST') {
|
|
165
|
+
res.writeHead(405);
|
|
166
|
+
res.end(JSON.stringify({ error: 'Method not allowed' }));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
// Check API key only if configured
|
|
170
|
+
if (API_KEY) {
|
|
171
|
+
const authHeader = req.headers['authorization'] || '';
|
|
172
|
+
const apiKey = authHeader.replace('Bearer ', '');
|
|
173
|
+
if (apiKey !== API_KEY) {
|
|
174
|
+
res.writeHead(401);
|
|
175
|
+
res.end(JSON.stringify({ error: 'Unauthorized' }));
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Parse URL
|
|
180
|
+
const parsedUrl = url.parse(req.url || '', true);
|
|
181
|
+
const pathname = parsedUrl.pathname;
|
|
182
|
+
// Read body
|
|
183
|
+
let body = '';
|
|
184
|
+
req.on('data', (chunk) => {
|
|
185
|
+
body += chunk.toString();
|
|
186
|
+
});
|
|
187
|
+
req.on('end', async () => {
|
|
188
|
+
try {
|
|
189
|
+
// Parse body for tool calls, empty body is ok for list
|
|
190
|
+
let requestData = {};
|
|
191
|
+
if (body.trim()) {
|
|
192
|
+
requestData = JSON.parse(body);
|
|
193
|
+
}
|
|
194
|
+
if (pathname === '/mcp/v1/tools/list') {
|
|
195
|
+
// List tools
|
|
196
|
+
res.writeHead(200);
|
|
197
|
+
res.end(JSON.stringify({ tools: TOOLS }));
|
|
198
|
+
}
|
|
199
|
+
else if (pathname === '/mcp/v1/tools/call') {
|
|
200
|
+
// Call tool
|
|
201
|
+
const { name, arguments: args } = requestData;
|
|
202
|
+
let result;
|
|
203
|
+
try {
|
|
204
|
+
switch (name) {
|
|
205
|
+
case 'list_products':
|
|
206
|
+
result = await listProducts(ListProductsSchema.parse(args));
|
|
207
|
+
break;
|
|
208
|
+
case 'get_product_details':
|
|
209
|
+
result = await getProductDetails(GetProductSchema.parse(args));
|
|
210
|
+
break;
|
|
211
|
+
case 'search_products_by_category':
|
|
212
|
+
result = await searchProductsByCategory(SearchProductsSchema.parse(args));
|
|
213
|
+
break;
|
|
214
|
+
case 'get_categories':
|
|
215
|
+
result = await getCategories(GetCategoriesSchema.parse(args));
|
|
216
|
+
break;
|
|
217
|
+
case 'list_articles':
|
|
218
|
+
result = await listArticles(ListArticlesSchema.parse(args));
|
|
219
|
+
break;
|
|
220
|
+
case 'get_article':
|
|
221
|
+
result = await getArticle(GetArticleSchema.parse(args));
|
|
222
|
+
break;
|
|
223
|
+
case 'list_tutorials':
|
|
224
|
+
result = await listTutorials(ListTutorialsSchema.parse(args));
|
|
225
|
+
break;
|
|
226
|
+
case 'get_tutorial':
|
|
227
|
+
result = await getTutorial(GetTutorialSchema.parse(args));
|
|
228
|
+
break;
|
|
229
|
+
case 'register_user':
|
|
230
|
+
result = await registerUser(RegisterUserSchema.parse(args));
|
|
231
|
+
break;
|
|
232
|
+
case 'create_stripe_checkout':
|
|
233
|
+
result = await createStripeCheckout(CreateCheckoutSchema.parse(args));
|
|
234
|
+
break;
|
|
235
|
+
default:
|
|
236
|
+
result = {
|
|
237
|
+
content: [{
|
|
238
|
+
type: 'text',
|
|
239
|
+
text: JSON.stringify({ error: `Unknown tool: ${name}` }),
|
|
240
|
+
}],
|
|
241
|
+
isError: true,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
if (error instanceof Error && error.name === 'ZodError') {
|
|
247
|
+
result = {
|
|
248
|
+
content: [{
|
|
249
|
+
type: 'text',
|
|
250
|
+
text: JSON.stringify({
|
|
251
|
+
error: 'Invalid arguments',
|
|
252
|
+
details: error.message,
|
|
253
|
+
}, null, 2),
|
|
254
|
+
}],
|
|
255
|
+
isError: true,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
throw error;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
res.writeHead(200);
|
|
263
|
+
res.end(JSON.stringify(result));
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
res.writeHead(404);
|
|
267
|
+
res.end(JSON.stringify({ error: 'Not found' }));
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
res.writeHead(500);
|
|
272
|
+
res.end(JSON.stringify({
|
|
273
|
+
error: 'Internal server error',
|
|
274
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
275
|
+
}));
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
httpServer.listen(port, () => {
|
|
280
|
+
console.log(`Sychev Lab MCP Server v${config.version}`);
|
|
281
|
+
console.log(`Connected to: ${config.baseUrl}`);
|
|
282
|
+
console.log(`HTTP transport running on port ${port}`);
|
|
283
|
+
console.log(`\nEndpoints:`);
|
|
284
|
+
console.log(` POST http://localhost:${port}/mcp/v1/tools/list`);
|
|
285
|
+
console.log(` POST http://localhost:${port}/mcp/v1/tools/call`);
|
|
286
|
+
console.log(` Content-Type: application/json`);
|
|
287
|
+
if (API_KEY) {
|
|
288
|
+
console.log(`\nAuthentication required:`);
|
|
289
|
+
console.log(` Authorization: Bearer <MCP_API_KEY>`);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
// Parse command line arguments
|
|
294
|
+
const args = process.argv.slice(2);
|
|
295
|
+
const useHttp = args.includes('--http');
|
|
296
|
+
const httpPortIndex = args.indexOf('--http');
|
|
297
|
+
const httpPort = httpPortIndex !== -1 && args[httpPortIndex + 1]
|
|
298
|
+
? parseInt(args[httpPortIndex + 1], 10)
|
|
299
|
+
: 3000;
|
|
300
|
+
if (useHttp) {
|
|
301
|
+
runHttpServer(httpPort).catch((error) => {
|
|
302
|
+
console.error('Fatal error:', error);
|
|
303
|
+
process.exit(1);
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
runStdioServer().catch((error) => {
|
|
308
|
+
console.error('Fatal error:', error);
|
|
309
|
+
process.exit(1);
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=index.js.map
|