mcp-zenskar 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.
@@ -0,0 +1,26 @@
1
+ // Simple response processor for Zenskar API responses
2
+ export default class ResponseProcessor {
3
+ constructor() {
4
+ // Minimal configuration
5
+ this.maxResponseLength = 50000;
6
+ }
7
+
8
+ processResponse(responseData, toolName) {
9
+ try {
10
+ // Convert response to string if needed
11
+ let response = typeof responseData === 'string'
12
+ ? responseData
13
+ : JSON.stringify(responseData, null, 2);
14
+
15
+ // Simple truncation if too long
16
+ if (response.length > this.maxResponseLength) {
17
+ response = response.substring(0, this.maxResponseLength) + '\n\n[Response truncated due to length]';
18
+ }
19
+
20
+ return response;
21
+ } catch (error) {
22
+ console.error('Response processing error:', error);
23
+ return 'Error processing response';
24
+ }
25
+ }
26
+ }
package/src/server.js ADDED
@@ -0,0 +1,229 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { z } from 'zod';
8
+ import { fileURLToPath } from 'url';
9
+
10
+ // Get __dirname equivalent for ES modules
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+
14
+ // Import the response processor
15
+ import ResponseProcessor from './response-processor.js';
16
+
17
+ // Load the configuration
18
+ const configPath = path.join(__dirname, 'mcp-config.json');
19
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
20
+
21
+ // Logger configuration
22
+ const logger = {
23
+ info: (message, data) => console.error(`[INFO] ${message}`, data || ''),
24
+ error: (message, data) => console.error(`[ERROR] ${message}`, data || ''),
25
+ warn: (message, data) => console.error(`[WARN] ${message}`, data || ''),
26
+ debug: (message, data) => console.error(`[DEBUG] ${message}`, data || '')
27
+ };
28
+
29
+ // User context validation schema
30
+ const userContextSchema = z.object({
31
+ organization: z.string().describe('Organization ID for multi-tenant API access'),
32
+ authorization: z.string().describe('Bearer token for API authentication'),
33
+ }).describe('Required authentication context for Zenskar API');
34
+
35
+ class ZenskarMcpServer {
36
+ constructor() {
37
+ this.server = new McpServer({
38
+ name: config.server.name,
39
+ version: "1.0.0"
40
+ });
41
+
42
+ this.responseProcessor = new ResponseProcessor();
43
+ this.setupTools();
44
+ }
45
+
46
+ setupTools() {
47
+ // Register each tool from the configuration
48
+ config.tools.forEach(tool => {
49
+ this.server.registerTool(tool.name, {
50
+ description: tool.description,
51
+ inputSchema: this.generateInputSchema(tool)
52
+ }, async (args) => {
53
+ try {
54
+ return await this.executeTool(tool, args);
55
+ } catch (error) {
56
+ logger.error(`Tool execution failed for ${tool.name}:`, error.message);
57
+ return {
58
+ content: [
59
+ {
60
+ type: "text",
61
+ text: `Error: ${error.message}`
62
+ }
63
+ ]
64
+ };
65
+ }
66
+ });
67
+ });
68
+ }
69
+
70
+ generateInputSchema(tool) {
71
+ const schemaObj = {
72
+ organization: z.string().describe("Organization ID for multi-tenant API access (required)"),
73
+ authorization: z.string().describe("Bearer token for API authentication (required)")
74
+ };
75
+
76
+ // Add tool-specific arguments
77
+ tool.args.forEach(arg => {
78
+ let schema;
79
+ switch(arg.type) {
80
+ case 'string':
81
+ schema = z.string();
82
+ break;
83
+ case 'integer':
84
+ case 'number':
85
+ schema = z.number();
86
+ break;
87
+ case 'boolean':
88
+ schema = z.boolean();
89
+ break;
90
+ default:
91
+ schema = z.string();
92
+ }
93
+
94
+ schema = schema.describe(arg.description);
95
+
96
+ if (!arg.required) {
97
+ schema = schema.optional();
98
+ }
99
+
100
+ schemaObj[arg.name] = schema;
101
+ });
102
+
103
+ return schemaObj;
104
+ }
105
+
106
+ async executeTool(tool, args) {
107
+ // Extract authentication from arguments
108
+ const { organization, authorization, ...toolArgs } = args;
109
+
110
+ // Validate required authentication
111
+ if (!organization) {
112
+ throw new Error('Organization ID is required for API access');
113
+ }
114
+ if (!authorization) {
115
+ throw new Error('Authorization token is required for API access');
116
+ }
117
+
118
+ logger.info(`[${tool.name}] Executing with organization: ${organization.substring(0, 10)}...`);
119
+
120
+ // Build headers
121
+ const headers = {
122
+ 'Content-Type': 'application/json',
123
+ 'Accept': 'application/json',
124
+ 'organisation': organization, // Note: API uses 'organisation' not 'organization'
125
+ 'Authorization': authorization.startsWith('Bearer ') ? authorization : `Bearer ${authorization}`
126
+ };
127
+
128
+ // Build URL from requestTemplate
129
+ let url = `${config.server.baseUrl}${tool.requestTemplate.url}`;
130
+ const method = tool.requestTemplate.method;
131
+
132
+ // Add path parameters
133
+ tool.args.forEach(arg => {
134
+ if (arg.position === 'path' && toolArgs[arg.name]) {
135
+ url = url.replace(`{${arg.name}}`, toolArgs[arg.name]);
136
+ }
137
+ });
138
+
139
+ // Add query parameters
140
+ const queryParams = new URLSearchParams();
141
+ tool.args.forEach(arg => {
142
+ if (arg.position === 'query' && toolArgs[arg.name] !== undefined) {
143
+ queryParams.append(arg.name, toolArgs[arg.name]);
144
+ }
145
+ });
146
+
147
+ if (queryParams.toString()) {
148
+ url += `?${queryParams.toString()}`;
149
+ }
150
+
151
+ // Prepare request body for POST/PUT/PATCH requests
152
+ let body = null;
153
+ if (['POST', 'PUT', 'PATCH'].includes(method.toUpperCase())) {
154
+ const bodyArgs = {};
155
+ tool.args.forEach(arg => {
156
+ if (arg.position === 'body' && toolArgs[arg.name] !== undefined) {
157
+ bodyArgs[arg.name] = toolArgs[arg.name];
158
+ }
159
+ });
160
+
161
+ if (Object.keys(bodyArgs).length > 0) {
162
+ body = JSON.stringify(bodyArgs);
163
+ }
164
+ }
165
+
166
+ logger.debug(`[${tool.name}] Making ${method} request to: ${url}`);
167
+
168
+ try {
169
+ const response = await fetch(url, {
170
+ method: method,
171
+ headers,
172
+ body
173
+ });
174
+
175
+ const responseText = await response.text();
176
+
177
+ if (!response.ok) {
178
+ logger.error(`[${tool.name}] HTTP ${response.status}: ${responseText}`);
179
+ throw new Error(`HTTP ${response.status}: ${responseText}`);
180
+ }
181
+
182
+ // Process response
183
+ let responseData;
184
+ try {
185
+ responseData = JSON.parse(responseText);
186
+ } catch {
187
+ responseData = responseText;
188
+ }
189
+
190
+ // Use response processor for formatting
191
+ const processedResponse = this.responseProcessor.processResponse(responseData, tool.name);
192
+
193
+ logger.info(`[${tool.name}] Request successful`);
194
+
195
+ return {
196
+ content: [
197
+ {
198
+ type: "text",
199
+ text: processedResponse
200
+ }
201
+ ]
202
+ };
203
+
204
+ } catch (error) {
205
+ const errorMessage = `Error executing ${tool.name}: ${error.message}\n\nThis might be due to:\n- Invalid parameters\n- API rate limiting\n- Network connectivity issues\n- Authentication problems (check organization ID and Bearer token)\n\nPlease verify your credentials and try again.`;
206
+
207
+ logger.error(`[${tool.name}] Request failed: ${error.message}`);
208
+
209
+ throw new Error(errorMessage);
210
+ }
211
+ }
212
+
213
+ async run() {
214
+ const transport = new StdioServerTransport();
215
+ await this.server.connect(transport);
216
+ logger.info(`${config.server.name} MCP server running`);
217
+ }
218
+ }
219
+
220
+ // Start the server
221
+ if (import.meta.url === `file://${process.argv[1]}`) {
222
+ const server = new ZenskarMcpServer();
223
+ server.run().catch(error => {
224
+ logger.error('Failed to start server:', error);
225
+ process.exit(1);
226
+ });
227
+ }
228
+
229
+ export default ZenskarMcpServer;