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.
@@ -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":"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"}
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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"}
@@ -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"}
@@ -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