@mctx-ai/mcp-server 0.3.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/LICENSE +21 -0
- package/dist/completion.js +214 -0
- package/dist/conversation.js +139 -0
- package/dist/index.d.ts +733 -0
- package/dist/index.js +18 -0
- package/dist/log.js +213 -0
- package/dist/progress.js +84 -0
- package/dist/sampling.js +130 -0
- package/dist/security.js +339 -0
- package/dist/server.js +876 -0
- package/dist/types.js +252 -0
- package/dist/uri.js +120 -0
- package/package.json +53 -0
- package/src/completion.js +214 -0
- package/src/conversation.js +139 -0
- package/src/index.d.ts +733 -0
- package/src/index.js +18 -0
- package/src/log.js +213 -0
- package/src/progress.js +84 -0
- package/src/sampling.js +130 -0
- package/src/security.js +339 -0
- package/src/server.js +876 -0
- package/src/types.js +252 -0
- package/src/uri.js +120 -0
package/src/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mctx-ai/mcp-server
|
|
3
|
+
*
|
|
4
|
+
* Build MCP servers with an Express-like API - no protocol knowledge required.
|
|
5
|
+
*
|
|
6
|
+
* Main package entry point that exports core functionality.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Core server factory
|
|
10
|
+
export { createServer } from "./server.js";
|
|
11
|
+
|
|
12
|
+
// Type system for tool/prompt inputs
|
|
13
|
+
export { T, buildInputSchema } from "./types.js";
|
|
14
|
+
|
|
15
|
+
// Advanced features
|
|
16
|
+
export { conversation } from "./conversation.js";
|
|
17
|
+
export { createProgress, PROGRESS_DEFAULTS } from "./progress.js";
|
|
18
|
+
export { log } from "./log.js";
|
package/src/log.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging Module
|
|
3
|
+
*
|
|
4
|
+
* Provides structured logging with RFC 5424 severity levels.
|
|
5
|
+
* Logs are buffered and sent as MCP notifications by the server.
|
|
6
|
+
*
|
|
7
|
+
* @module log
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* RFC 5424 severity levels (highest to lowest)
|
|
12
|
+
* Used for level comparison and filtering
|
|
13
|
+
*/
|
|
14
|
+
const LEVELS = {
|
|
15
|
+
emergency: 0, // System is unusable
|
|
16
|
+
alert: 1, // Action must be taken immediately
|
|
17
|
+
critical: 2, // Critical conditions
|
|
18
|
+
error: 3, // Error conditions
|
|
19
|
+
warning: 4, // Warning conditions
|
|
20
|
+
notice: 5, // Normal but significant condition
|
|
21
|
+
info: 6, // Informational messages
|
|
22
|
+
debug: 7, // Debug-level messages
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Maximum log buffer size (FIFO eviction when exceeded)
|
|
27
|
+
* @private
|
|
28
|
+
*/
|
|
29
|
+
const MAX_LOG_BUFFER_SIZE = 10000;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Log buffer - stores notifications until server flushes them
|
|
33
|
+
* @private
|
|
34
|
+
*/
|
|
35
|
+
let logBuffer = [];
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Current minimum log level (messages below this are filtered)
|
|
39
|
+
* @private
|
|
40
|
+
*/
|
|
41
|
+
let currentLogLevel = "debug"; // Default: log everything
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Creates a log notification object
|
|
45
|
+
* @private
|
|
46
|
+
* @param {string} level - Log level
|
|
47
|
+
* @param {*} data - Log data (any JSON-serializable value)
|
|
48
|
+
* @returns {Object} Log notification object
|
|
49
|
+
*/
|
|
50
|
+
function createLogNotification(level, data) {
|
|
51
|
+
// Check if we should log this level
|
|
52
|
+
if (!shouldLog(level, currentLogLevel)) {
|
|
53
|
+
return; // Don't add to buffer if below minimum level
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const notification = {
|
|
57
|
+
type: "log",
|
|
58
|
+
level,
|
|
59
|
+
data,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
logBuffer.push(notification);
|
|
63
|
+
|
|
64
|
+
// Enforce buffer size limit with FIFO eviction
|
|
65
|
+
if (logBuffer.length > MAX_LOG_BUFFER_SIZE) {
|
|
66
|
+
logBuffer.shift();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return notification;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Determines if a message should be logged based on severity levels
|
|
74
|
+
*
|
|
75
|
+
* @param {string} messageLevel - The level of the message being logged
|
|
76
|
+
* @param {string} clientLevel - The minimum level configured by client
|
|
77
|
+
* @returns {boolean} True if message should be logged
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* shouldLog('error', 'warning') // true (error >= warning)
|
|
81
|
+
* shouldLog('debug', 'info') // false (debug < info)
|
|
82
|
+
* shouldLog('info', 'info') // true (info >= info)
|
|
83
|
+
*/
|
|
84
|
+
export function shouldLog(messageLevel, clientLevel) {
|
|
85
|
+
const messageSeverity = LEVELS[messageLevel];
|
|
86
|
+
const clientSeverity = LEVELS[clientLevel];
|
|
87
|
+
|
|
88
|
+
// If either level is unknown, default to logging
|
|
89
|
+
if (messageSeverity === undefined || clientSeverity === undefined) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Lower numeric value = higher severity
|
|
94
|
+
// Message should be logged if its severity >= client's minimum severity
|
|
95
|
+
return messageSeverity <= clientSeverity;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Sets the minimum log level
|
|
100
|
+
* Messages below this level will be filtered out
|
|
101
|
+
*
|
|
102
|
+
* @param {string} level - Minimum log level (debug, info, notice, warning, error, critical, alert, emergency)
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* setLogLevel('warning'); // Only log warning and above
|
|
106
|
+
* log.debug('test'); // Won't be logged
|
|
107
|
+
* log.error('problem'); // Will be logged
|
|
108
|
+
*/
|
|
109
|
+
export function setLogLevel(level) {
|
|
110
|
+
if (LEVELS[level] === undefined) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`Invalid log level: "${level}". Must be one of: ${Object.keys(LEVELS).join(", ")}`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
currentLogLevel = level;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Gets the current log buffer
|
|
120
|
+
* Server uses this to retrieve buffered notifications
|
|
121
|
+
*
|
|
122
|
+
* @returns {Array<Object>} Array of log notification objects
|
|
123
|
+
*/
|
|
124
|
+
export function getLogBuffer() {
|
|
125
|
+
return [...logBuffer]; // Return copy to prevent external modification
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Clears the log buffer
|
|
130
|
+
* Server calls this after flushing notifications
|
|
131
|
+
*/
|
|
132
|
+
export function clearLogBuffer() {
|
|
133
|
+
logBuffer = [];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Log object with methods for each severity level
|
|
138
|
+
*
|
|
139
|
+
* Each method creates a log notification and adds it to the buffer.
|
|
140
|
+
* The server will flush these as MCP notifications.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* log.debug('Variable value:', { x: 42 });
|
|
144
|
+
* log.info('Server started on port 3000');
|
|
145
|
+
* log.warning('Rate limit approaching');
|
|
146
|
+
* log.error('Database connection failed', error);
|
|
147
|
+
* log.critical('System out of memory');
|
|
148
|
+
*/
|
|
149
|
+
export const log = {
|
|
150
|
+
/**
|
|
151
|
+
* Debug-level message (lowest severity)
|
|
152
|
+
* Used for detailed debugging information
|
|
153
|
+
*/
|
|
154
|
+
debug(data) {
|
|
155
|
+
return createLogNotification("debug", data);
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Informational message
|
|
160
|
+
* Used for general informational messages
|
|
161
|
+
*/
|
|
162
|
+
info(data) {
|
|
163
|
+
return createLogNotification("info", data);
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Notice - normal but significant condition
|
|
168
|
+
* Used for important events that are not errors
|
|
169
|
+
*/
|
|
170
|
+
notice(data) {
|
|
171
|
+
return createLogNotification("notice", data);
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Warning condition
|
|
176
|
+
* Used for warnings that don't prevent operation
|
|
177
|
+
*/
|
|
178
|
+
warning(data) {
|
|
179
|
+
return createLogNotification("warning", data);
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Error condition
|
|
184
|
+
* Used for errors that affect functionality
|
|
185
|
+
*/
|
|
186
|
+
error(data) {
|
|
187
|
+
return createLogNotification("error", data);
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Critical condition
|
|
192
|
+
* Used for critical conditions that require immediate attention
|
|
193
|
+
*/
|
|
194
|
+
critical(data) {
|
|
195
|
+
return createLogNotification("critical", data);
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Alert - action must be taken immediately
|
|
200
|
+
* Used for conditions requiring immediate operator intervention
|
|
201
|
+
*/
|
|
202
|
+
alert(data) {
|
|
203
|
+
return createLogNotification("alert", data);
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Emergency - system is unusable
|
|
208
|
+
* Used for system-wide failures
|
|
209
|
+
*/
|
|
210
|
+
emergency(data) {
|
|
211
|
+
return createLogNotification("emergency", data);
|
|
212
|
+
},
|
|
213
|
+
};
|
package/src/progress.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Progress Notifications Module
|
|
3
|
+
*
|
|
4
|
+
* Provides progress tracking for long-running generator tool handlers.
|
|
5
|
+
* Supports both determinate (with total) and indeterminate progress.
|
|
6
|
+
*
|
|
7
|
+
* @module progress
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Default configuration for generator guardrails
|
|
12
|
+
* Server will use these to prevent runaway generators
|
|
13
|
+
*/
|
|
14
|
+
export const PROGRESS_DEFAULTS = {
|
|
15
|
+
maxExecutionTime: 60000, // 60 seconds
|
|
16
|
+
maxYields: 10000,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates a progress step function for generator-based tools
|
|
21
|
+
*
|
|
22
|
+
* Returns a function that produces progress notification objects.
|
|
23
|
+
* Each call auto-increments the progress counter.
|
|
24
|
+
*
|
|
25
|
+
* @param {number} [total] - Total number of steps (optional, for determinate progress)
|
|
26
|
+
* @returns {Function} Step function that returns progress notification objects
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // Determinate progress (with known total)
|
|
30
|
+
* function* migrate({ sourceDb, targetDb }) {
|
|
31
|
+
* const tables = await getTables(sourceDb);
|
|
32
|
+
* const step = createProgress(tables.length);
|
|
33
|
+
*
|
|
34
|
+
* for (const table of tables) {
|
|
35
|
+
* yield step(); // { type: "progress", progress: 1, total: 5 }
|
|
36
|
+
* await copyTable(table, sourceDb, targetDb);
|
|
37
|
+
* }
|
|
38
|
+
*
|
|
39
|
+
* return "Migration complete";
|
|
40
|
+
* }
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* // Indeterminate progress (no total)
|
|
44
|
+
* function* processQueue({ queueUrl }) {
|
|
45
|
+
* const step = createProgress();
|
|
46
|
+
*
|
|
47
|
+
* while (hasMessages(queueUrl)) {
|
|
48
|
+
* yield step(); // { type: "progress", progress: 1 }
|
|
49
|
+
* await processMessage(queueUrl);
|
|
50
|
+
* }
|
|
51
|
+
*
|
|
52
|
+
* return "Queue processed";
|
|
53
|
+
* }
|
|
54
|
+
*/
|
|
55
|
+
export function createProgress(total) {
|
|
56
|
+
// Validate total if provided
|
|
57
|
+
if (total !== undefined && (typeof total !== "number" || total <= 0)) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
"createProgress() total must be a positive number if provided",
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let currentStep = 0;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Step function - call to generate next progress notification
|
|
67
|
+
* @returns {Object} Progress notification object
|
|
68
|
+
*/
|
|
69
|
+
return function step() {
|
|
70
|
+
currentStep++;
|
|
71
|
+
|
|
72
|
+
const notification = {
|
|
73
|
+
type: "progress",
|
|
74
|
+
progress: currentStep,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Include total only if provided (determinate progress)
|
|
78
|
+
if (total !== undefined) {
|
|
79
|
+
notification.total = total;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return notification;
|
|
83
|
+
};
|
|
84
|
+
}
|
package/src/sampling.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sampling Support Module
|
|
3
|
+
*
|
|
4
|
+
* Enables MCP servers to request LLM completions from clients.
|
|
5
|
+
* Provides the `ask` function for tools that need AI assistance.
|
|
6
|
+
*
|
|
7
|
+
* @module sampling
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Default timeout for sampling requests (30 seconds)
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_TIMEOUT = 30000;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates an ask function for a given request context
|
|
17
|
+
*
|
|
18
|
+
* The ask function allows tools to request LLM completions from the client.
|
|
19
|
+
* Returns null if the client doesn't support sampling.
|
|
20
|
+
*
|
|
21
|
+
* @param {Function} sendRequest - Callback to send MCP requests (async)
|
|
22
|
+
* @param {Object} clientCapabilities - Client capabilities from initialization
|
|
23
|
+
* @returns {Function|null} Ask function, or null if sampling not supported
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // In a tool handler
|
|
27
|
+
* function* summarize({ document, ask }) {
|
|
28
|
+
* if (!ask) {
|
|
29
|
+
* return "Client doesn't support sampling";
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* const summary = await ask("Summarize this document: " + document);
|
|
33
|
+
* return summary;
|
|
34
|
+
* }
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* // Advanced usage with options
|
|
38
|
+
* const result = await ask({
|
|
39
|
+
* messages: [
|
|
40
|
+
* { role: "user", content: { type: "text", text: "What is the capital of France?" } }
|
|
41
|
+
* ],
|
|
42
|
+
* modelPreferences: {
|
|
43
|
+
* hints: [{ name: "claude-3-5-sonnet" }]
|
|
44
|
+
* },
|
|
45
|
+
* systemPrompt: "You are a helpful geography assistant",
|
|
46
|
+
* maxTokens: 1000,
|
|
47
|
+
* });
|
|
48
|
+
*/
|
|
49
|
+
export function createAsk(sendRequest, clientCapabilities) {
|
|
50
|
+
// Validate inputs
|
|
51
|
+
if (typeof sendRequest !== "function") {
|
|
52
|
+
throw new Error("createAsk() requires sendRequest to be a function");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!clientCapabilities || typeof clientCapabilities !== "object") {
|
|
56
|
+
throw new Error("createAsk() requires clientCapabilities object");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Check if client supports sampling
|
|
60
|
+
if (!clientCapabilities.sampling) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Ask function - request LLM completion from client
|
|
66
|
+
*
|
|
67
|
+
* @param {string|Object} promptOrOptions - Simple prompt string, or options object
|
|
68
|
+
* @param {number} [timeout=30000] - Request timeout in milliseconds
|
|
69
|
+
* @returns {Promise<string>} The LLM response content
|
|
70
|
+
* @throws {Error} If request fails or times out
|
|
71
|
+
*/
|
|
72
|
+
return async function ask(promptOrOptions, timeout = DEFAULT_TIMEOUT) {
|
|
73
|
+
let requestParams;
|
|
74
|
+
|
|
75
|
+
// Simple string prompt - wrap as messages array
|
|
76
|
+
if (typeof promptOrOptions === "string") {
|
|
77
|
+
requestParams = {
|
|
78
|
+
messages: [
|
|
79
|
+
{
|
|
80
|
+
role: "user",
|
|
81
|
+
content: {
|
|
82
|
+
type: "text",
|
|
83
|
+
text: promptOrOptions,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
} else if (promptOrOptions && typeof promptOrOptions === "object") {
|
|
89
|
+
// Advanced options object
|
|
90
|
+
requestParams = { ...promptOrOptions };
|
|
91
|
+
|
|
92
|
+
// Validate required fields
|
|
93
|
+
if (!requestParams.messages || !Array.isArray(requestParams.messages)) {
|
|
94
|
+
throw new Error("ask() options must include messages array");
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
throw new Error("ask() requires a string prompt or options object");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Create timeout promise
|
|
101
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
102
|
+
setTimeout(() => {
|
|
103
|
+
reject(new Error(`Sampling request timed out after ${timeout}ms`));
|
|
104
|
+
}, timeout);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Send sampling request with timeout
|
|
108
|
+
try {
|
|
109
|
+
const responsePromise = sendRequest(
|
|
110
|
+
"sampling/createMessage",
|
|
111
|
+
requestParams,
|
|
112
|
+
);
|
|
113
|
+
const response = await Promise.race([responsePromise, timeoutPromise]);
|
|
114
|
+
|
|
115
|
+
// Extract content from response
|
|
116
|
+
if (!response || !response.content) {
|
|
117
|
+
throw new Error("Invalid sampling response: missing content");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return response.content;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
// Re-throw with better context
|
|
123
|
+
if (error.message.includes("timed out")) {
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
throw new Error(`Sampling request failed: ${error.message}`);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|