mcp-ts-template 1.1.6
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 +201 -0
- package/README.md +233 -0
- package/dist/config/index.d.ts +73 -0
- package/dist/config/index.js +125 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +162 -0
- package/dist/mcp-client/client.d.ts +36 -0
- package/dist/mcp-client/client.js +276 -0
- package/dist/mcp-client/configLoader.d.ts +75 -0
- package/dist/mcp-client/configLoader.js +203 -0
- package/dist/mcp-client/index.d.ts +10 -0
- package/dist/mcp-client/index.js +14 -0
- package/dist/mcp-client/transport.d.ts +34 -0
- package/dist/mcp-client/transport.js +183 -0
- package/dist/mcp-server/resources/echoResource/echoResourceLogic.d.ts +38 -0
- package/dist/mcp-server/resources/echoResource/echoResourceLogic.js +40 -0
- package/dist/mcp-server/resources/echoResource/index.d.ts +5 -0
- package/dist/mcp-server/resources/echoResource/index.js +5 -0
- package/dist/mcp-server/resources/echoResource/registration.d.ts +12 -0
- package/dist/mcp-server/resources/echoResource/registration.js +122 -0
- package/dist/mcp-server/server.d.ts +27 -0
- package/dist/mcp-server/server.js +176 -0
- package/dist/mcp-server/tools/echoTool/echoToolLogic.d.ts +68 -0
- package/dist/mcp-server/tools/echoTool/echoToolLogic.js +73 -0
- package/dist/mcp-server/tools/echoTool/index.d.ts +5 -0
- package/dist/mcp-server/tools/echoTool/index.js +5 -0
- package/dist/mcp-server/tools/echoTool/registration.d.ts +12 -0
- package/dist/mcp-server/tools/echoTool/registration.js +86 -0
- package/dist/mcp-server/transports/authentication/authMiddleware.d.ts +57 -0
- package/dist/mcp-server/transports/authentication/authMiddleware.js +145 -0
- package/dist/mcp-server/transports/httpTransport.d.ts +23 -0
- package/dist/mcp-server/transports/httpTransport.js +411 -0
- package/dist/mcp-server/transports/stdioTransport.d.ts +40 -0
- package/dist/mcp-server/transports/stdioTransport.js +70 -0
- package/dist/types-global/errors.d.ts +73 -0
- package/dist/types-global/errors.js +66 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.js +12 -0
- package/dist/utils/internal/errorHandler.d.ts +90 -0
- package/dist/utils/internal/errorHandler.js +247 -0
- package/dist/utils/internal/index.d.ts +3 -0
- package/dist/utils/internal/index.js +3 -0
- package/dist/utils/internal/logger.d.ts +50 -0
- package/dist/utils/internal/logger.js +267 -0
- package/dist/utils/internal/requestContext.d.ts +47 -0
- package/dist/utils/internal/requestContext.js +48 -0
- package/dist/utils/metrics/index.d.ts +1 -0
- package/dist/utils/metrics/index.js +1 -0
- package/dist/utils/metrics/tokenCounter.d.ts +27 -0
- package/dist/utils/metrics/tokenCounter.js +124 -0
- package/dist/utils/parsing/dateParser.d.ts +27 -0
- package/dist/utils/parsing/dateParser.js +62 -0
- package/dist/utils/parsing/index.d.ts +2 -0
- package/dist/utils/parsing/index.js +2 -0
- package/dist/utils/parsing/jsonParser.d.ts +46 -0
- package/dist/utils/parsing/jsonParser.js +79 -0
- package/dist/utils/security/idGenerator.d.ts +93 -0
- package/dist/utils/security/idGenerator.js +147 -0
- package/dist/utils/security/index.d.ts +3 -0
- package/dist/utils/security/index.js +3 -0
- package/dist/utils/security/rateLimiter.d.ts +92 -0
- package/dist/utils/security/rateLimiter.js +171 -0
- package/dist/utils/security/sanitization.d.ts +180 -0
- package/dist/utils/security/sanitization.js +372 -0
- package/package.json +79 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
|
|
3
|
+
// Import utils from the main barrel file (ErrorHandler, logger, requestContextService from ../../../utils/internal/*)
|
|
4
|
+
import { ErrorHandler, logger, requestContextService } from '../../../utils/index.js';
|
|
5
|
+
// Import logic, schema, and type from the dedicated logic file
|
|
6
|
+
import { processEchoResource } from './echoResourceLogic.js'; // Removed querySchema import
|
|
7
|
+
// Type inference for UnsubscribeRequest removed as it's not handled
|
|
8
|
+
/**
|
|
9
|
+
* Registers the 'echo' resource and its handlers with the provided MCP server instance.
|
|
10
|
+
* This includes defining the resource template, metadata, query schema, examples,
|
|
11
|
+
* and the core request handling logic. Error handling is integrated using ErrorHandler.
|
|
12
|
+
*
|
|
13
|
+
* @function registerEchoResource
|
|
14
|
+
* @param {McpServer} server - The MCP server instance to register the resource with.
|
|
15
|
+
* @returns {Promise<void>} A promise that resolves when the resource registration is complete.
|
|
16
|
+
* @throws {McpError} Throws an McpError if the registration process fails critically.
|
|
17
|
+
*/
|
|
18
|
+
export const registerEchoResource = async (server) => {
|
|
19
|
+
const resourceName = "echo-resource"; // Internal identifier for the resource
|
|
20
|
+
// Create registration context using the service
|
|
21
|
+
const registrationContext = requestContextService.createRequestContext({
|
|
22
|
+
operation: 'RegisterEchoResource',
|
|
23
|
+
resourceName: resourceName,
|
|
24
|
+
module: 'EchoResourceRegistration'
|
|
25
|
+
});
|
|
26
|
+
logger.info(`Registering resource: ${resourceName}`, registrationContext);
|
|
27
|
+
// Use ErrorHandler to wrap the entire registration process for robustness
|
|
28
|
+
await ErrorHandler.tryCatch(async () => {
|
|
29
|
+
// Define the resource template structure (URI pattern and basic operations)
|
|
30
|
+
const template = new ResourceTemplate("echo://{message}", // URI template using RFC 6570 syntax
|
|
31
|
+
{
|
|
32
|
+
// --- List Operation ---
|
|
33
|
+
// Provides a list of example or discoverable resource URIs.
|
|
34
|
+
list: async () => ({
|
|
35
|
+
resources: [{
|
|
36
|
+
uri: "echo://hello", // Example static URI
|
|
37
|
+
name: "Default Echo Message",
|
|
38
|
+
description: "A simple echo resource example using a default message."
|
|
39
|
+
}]
|
|
40
|
+
}),
|
|
41
|
+
// --- Complete Operation ---
|
|
42
|
+
// (Optional) Provides suggestions or completions based on partial input.
|
|
43
|
+
// Not implemented for this simple resource.
|
|
44
|
+
});
|
|
45
|
+
logger.debug(`Resource template created for ${resourceName}`, registrationContext);
|
|
46
|
+
// Register the resource, its template, metadata, and handler with the server
|
|
47
|
+
// This implicitly handles 'resources/read' based on the template match.
|
|
48
|
+
server.resource(resourceName, // The unique name for this resource registration
|
|
49
|
+
template, // The ResourceTemplate defined above
|
|
50
|
+
// --- Resource Metadata ---
|
|
51
|
+
{
|
|
52
|
+
name: "Echo Message", // User-friendly name
|
|
53
|
+
description: "A simple echo resource that returns a message, optionally specified in the URI.",
|
|
54
|
+
mimeType: "application/json", // Default MIME type for responses
|
|
55
|
+
// --- Query Schema ---
|
|
56
|
+
// Removed querySchema as path variable is used via template
|
|
57
|
+
// --- Examples ---
|
|
58
|
+
// Provides illustrative examples for clients
|
|
59
|
+
examples: [
|
|
60
|
+
{
|
|
61
|
+
name: "Basic echo",
|
|
62
|
+
uri: "echo://hello",
|
|
63
|
+
description: "Get a default welcome message."
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "Custom echo",
|
|
67
|
+
uri: "echo://custom-message-here",
|
|
68
|
+
description: "Get a response echoing 'custom-message-here'."
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
// --- Resource Handler (for resources/read implicitly) ---
|
|
73
|
+
// The core logic executed when a request matches the resource template.
|
|
74
|
+
async (uri, params) => {
|
|
75
|
+
// Create handler context using the service
|
|
76
|
+
const handlerContext = requestContextService.createRequestContext({
|
|
77
|
+
parentContext: registrationContext, // Link to the registration context if needed
|
|
78
|
+
operation: 'HandleEchoResourceRequest',
|
|
79
|
+
resourceName: resourceName,
|
|
80
|
+
uri: uri.href,
|
|
81
|
+
params: params // Include relevant request details
|
|
82
|
+
});
|
|
83
|
+
logger.debug("Handling echo resource request", handlerContext);
|
|
84
|
+
// Wrap the handler logic in tryCatch for robust error handling
|
|
85
|
+
return await ErrorHandler.tryCatch(async () => {
|
|
86
|
+
// Delegate the core processing logic, passing the context
|
|
87
|
+
const responseData = processEchoResource(uri, params, handlerContext);
|
|
88
|
+
logger.debug("Echo resource processed successfully", handlerContext);
|
|
89
|
+
// Return the response in the standardized format expected by the MCP SDK
|
|
90
|
+
// Correctly omits `type: "text"` as per 2025-03-26 spec
|
|
91
|
+
return {
|
|
92
|
+
contents: [{
|
|
93
|
+
uri: uri.href, // Echo back the requested URI
|
|
94
|
+
blob: Buffer.from(JSON.stringify(responseData)).toString('base64'), // Return Base64 encoded JSON object
|
|
95
|
+
mimeType: "application/json" // Specify the content type
|
|
96
|
+
}]
|
|
97
|
+
};
|
|
98
|
+
}, {
|
|
99
|
+
// Configuration for the error handler specific to this request
|
|
100
|
+
operation: 'processing echo resource handler',
|
|
101
|
+
context: handlerContext, // Pass handler-specific context
|
|
102
|
+
input: { uri: uri.href, params }, // Log input on error
|
|
103
|
+
// Provide a custom error mapping for more specific error reporting
|
|
104
|
+
errorMapper: (error) => new McpError(// Add type 'unknown' to error parameter
|
|
105
|
+
BaseErrorCode.INTERNAL_ERROR, // Map internal errors
|
|
106
|
+
`Error processing echo resource request for URI '${uri.href}': ${error instanceof Error ? error.message : 'Unknown error'}`, { ...handlerContext } // Include context in the McpError
|
|
107
|
+
)
|
|
108
|
+
});
|
|
109
|
+
}); // End of server.resource call
|
|
110
|
+
logger.info(`Resource registered successfully: ${resourceName}`, registrationContext);
|
|
111
|
+
}, {
|
|
112
|
+
// Configuration for the error handler wrapping the entire registration
|
|
113
|
+
operation: `registering resource ${resourceName}`,
|
|
114
|
+
context: registrationContext, // Context for registration-level errors
|
|
115
|
+
errorCode: BaseErrorCode.INTERNAL_ERROR, // Default error code for registration failure
|
|
116
|
+
// Custom error mapping for registration failures
|
|
117
|
+
errorMapper: (error) => new McpError(// Add type 'unknown' to error parameter
|
|
118
|
+
error instanceof McpError ? error.code : BaseErrorCode.INTERNAL_ERROR, `Failed to register resource '${resourceName}': ${error instanceof Error ? error.message : 'Unknown error'}`, { ...registrationContext } // Include context in the McpError
|
|
119
|
+
),
|
|
120
|
+
critical: true // Mark registration failure as critical to halt startup
|
|
121
|
+
}); // End of ErrorHandler.tryCatch for registration
|
|
122
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main entry point for the MCP (Model Context Protocol) server.
|
|
3
|
+
* This file orchestrates the server's lifecycle:
|
|
4
|
+
* 1. Initializes the core McpServer instance with its identity and capabilities.
|
|
5
|
+
* 2. Registers available resources and tools, making them discoverable and usable by clients.
|
|
6
|
+
* 3. Selects and starts the appropriate communication transport (stdio or Streamable HTTP)
|
|
7
|
+
* based on configuration.
|
|
8
|
+
* 4. Handles top-level error management during startup.
|
|
9
|
+
*
|
|
10
|
+
* MCP Specification References:
|
|
11
|
+
* - Lifecycle: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/lifecycle.mdx
|
|
12
|
+
* - Overview (Capabilities): https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/index.mdx
|
|
13
|
+
* - Transports: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/transports.mdx
|
|
14
|
+
*/
|
|
15
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
16
|
+
/**
|
|
17
|
+
* Main application entry point. Initializes and starts the MCP server.
|
|
18
|
+
*
|
|
19
|
+
* MCP Spec Relevance:
|
|
20
|
+
* - Orchestrates the server startup sequence, culminating in a server ready to accept
|
|
21
|
+
* connections and process MCP messages according to the chosen transport's rules.
|
|
22
|
+
* - Implements top-level error handling for critical startup failures, ensuring the
|
|
23
|
+
* process exits appropriately if it cannot initialize correctly.
|
|
24
|
+
*
|
|
25
|
+
* @returns {Promise<void | McpServer>} Resolves upon successful startup (void for http, McpServer for stdio). Rejects on critical failure.
|
|
26
|
+
*/
|
|
27
|
+
export declare function initializeAndStartServer(): Promise<void | McpServer>;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main entry point for the MCP (Model Context Protocol) server.
|
|
3
|
+
* This file orchestrates the server's lifecycle:
|
|
4
|
+
* 1. Initializes the core McpServer instance with its identity and capabilities.
|
|
5
|
+
* 2. Registers available resources and tools, making them discoverable and usable by clients.
|
|
6
|
+
* 3. Selects and starts the appropriate communication transport (stdio or Streamable HTTP)
|
|
7
|
+
* based on configuration.
|
|
8
|
+
* 4. Handles top-level error management during startup.
|
|
9
|
+
*
|
|
10
|
+
* MCP Specification References:
|
|
11
|
+
* - Lifecycle: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/lifecycle.mdx
|
|
12
|
+
* - Overview (Capabilities): https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/index.mdx
|
|
13
|
+
* - Transports: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/transports.mdx
|
|
14
|
+
*/
|
|
15
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
16
|
+
// Import validated configuration and environment details.
|
|
17
|
+
import { config, environment } from '../config/index.js';
|
|
18
|
+
// Import core utilities: ErrorHandler, logger, requestContextService.
|
|
19
|
+
import { ErrorHandler, logger, requestContextService } from '../utils/index.js';
|
|
20
|
+
// Import registration functions for specific resources and tools.
|
|
21
|
+
import { registerEchoResource } from './resources/echoResource/index.js';
|
|
22
|
+
import { registerEchoTool } from './tools/echoTool/index.js';
|
|
23
|
+
// Import transport setup functions.
|
|
24
|
+
import { startHttpTransport } from './transports/httpTransport.js';
|
|
25
|
+
import { connectStdioTransport } from './transports/stdioTransport.js';
|
|
26
|
+
/**
|
|
27
|
+
* Creates and configures a new instance of the McpServer.
|
|
28
|
+
*
|
|
29
|
+
* This function is central to defining the server's identity and functionality
|
|
30
|
+
* as presented to connecting clients during the MCP initialization phase.
|
|
31
|
+
*
|
|
32
|
+
* MCP Spec Relevance:
|
|
33
|
+
* - Server Identity (`serverInfo`): The `name` and `version` provided here are part
|
|
34
|
+
* of the `ServerInformation` object returned in the `InitializeResult` message,
|
|
35
|
+
* allowing clients to identify the server they are connected to.
|
|
36
|
+
* - Capabilities Declaration: The `capabilities` object declares the features this
|
|
37
|
+
* server supports, enabling clients to tailor their interactions.
|
|
38
|
+
* - `logging: {}`: Indicates the server can receive `logging/setLevel` requests
|
|
39
|
+
* and may send `notifications/message` log messages (handled by the logger utility).
|
|
40
|
+
* - `resources: { listChanged: true }`: Signals that the server supports dynamic
|
|
41
|
+
* resource lists and will send `notifications/resources/list_changed` if the
|
|
42
|
+
* available resources change after initialization.
|
|
43
|
+
* - `tools: { listChanged: true }`: Signals support for dynamic tool lists and
|
|
44
|
+
* `notifications/tools/list_changed`.
|
|
45
|
+
* - Resource/Tool Registration: This function calls specific registration functions
|
|
46
|
+
* (e.g., `registerEchoResource`) which use SDK methods (`server.resource`, `server.tool`)
|
|
47
|
+
* to make capabilities available for discovery (`resources/list`, `tools/list`) and
|
|
48
|
+
* invocation (`resources/read`, `tools/call`).
|
|
49
|
+
*
|
|
50
|
+
* Design Note: This factory function is used to create server instances. For the 'stdio'
|
|
51
|
+
* transport, it's called once. For the 'http' transport, it's passed to `startHttpTransport`
|
|
52
|
+
* and called *per session* to ensure session isolation.
|
|
53
|
+
*
|
|
54
|
+
* @returns {Promise<McpServer>} A promise resolving with the configured McpServer instance.
|
|
55
|
+
* @throws {Error} If any resource or tool registration fails.
|
|
56
|
+
*/
|
|
57
|
+
async function createMcpServerInstance() {
|
|
58
|
+
const context = { operation: 'createMcpServerInstance' };
|
|
59
|
+
logger.info('Initializing MCP server instance', context);
|
|
60
|
+
// Configure the request context service (used for correlating logs/errors).
|
|
61
|
+
requestContextService.configure({
|
|
62
|
+
appName: config.mcpServerName,
|
|
63
|
+
appVersion: config.mcpServerVersion,
|
|
64
|
+
environment,
|
|
65
|
+
});
|
|
66
|
+
// Instantiate the core McpServer using the SDK.
|
|
67
|
+
// Provide server identity (name, version) and declare supported capabilities.
|
|
68
|
+
logger.debug('Instantiating McpServer with capabilities', { ...context, serverInfo: { name: config.mcpServerName, version: config.mcpServerVersion }, capabilities: { logging: {}, resources: { listChanged: true }, tools: { listChanged: true } } });
|
|
69
|
+
const server = new McpServer({ name: config.mcpServerName, version: config.mcpServerVersion }, // ServerInformation part of InitializeResult
|
|
70
|
+
{ capabilities: { logging: {}, resources: { listChanged: true }, tools: { listChanged: true } } } // Declared capabilities
|
|
71
|
+
);
|
|
72
|
+
try {
|
|
73
|
+
// Register all defined resources and tools. These calls populate the server's
|
|
74
|
+
// internal registry, making them available via MCP methods like 'tools/list'.
|
|
75
|
+
logger.debug('Registering resources and tools...', context);
|
|
76
|
+
await registerEchoResource(server); // Example resource registration
|
|
77
|
+
await registerEchoTool(server); // Example tool registration
|
|
78
|
+
// Add calls to register other resources/tools here.
|
|
79
|
+
logger.info('Resources and tools registered successfully', context);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
// Registration is critical; log and re-throw errors.
|
|
83
|
+
logger.error('Failed to register resources/tools', {
|
|
84
|
+
...context,
|
|
85
|
+
error: err instanceof Error ? err.message : String(err),
|
|
86
|
+
stack: err instanceof Error ? err.stack : undefined, // Include stack for debugging
|
|
87
|
+
});
|
|
88
|
+
throw err; // Propagate error to prevent server starting with incomplete capabilities.
|
|
89
|
+
}
|
|
90
|
+
return server;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Selects, sets up, and starts the appropriate MCP transport layer based on configuration.
|
|
94
|
+
* This function acts as the bridge between the core server logic and the communication channel.
|
|
95
|
+
*
|
|
96
|
+
* MCP Spec Relevance:
|
|
97
|
+
* - Transport Selection: Reads `config.mcpTransportType` ('stdio' or 'http') to determine
|
|
98
|
+
* which transport mechanism defined in the MCP specification to use.
|
|
99
|
+
* - Transport Connection: Calls dedicated functions (`connectStdioTransport` or `startHttpTransport`)
|
|
100
|
+
* which handle the specifics of establishing communication according to the chosen
|
|
101
|
+
* transport's rules (e.g., stdin/stdout handling for 'stdio', HTTP server setup and
|
|
102
|
+
* endpoint handling for 'http').
|
|
103
|
+
* - Server Instance Lifecycle:
|
|
104
|
+
* - For 'stdio', creates a single `McpServer` instance for the lifetime of the process.
|
|
105
|
+
* - For 'http', passes the `createMcpServerInstance` factory function to `startHttpTransport`,
|
|
106
|
+
* allowing the HTTP transport to create a new, isolated server instance for each client session,
|
|
107
|
+
* aligning with the stateful session management described in the Streamable HTTP spec.
|
|
108
|
+
*
|
|
109
|
+
* @returns {Promise<McpServer | void>} Resolves with the McpServer instance for 'stdio', or void for 'http'.
|
|
110
|
+
* @throws {Error} If the configured transport type is unsupported or if transport setup fails.
|
|
111
|
+
*/
|
|
112
|
+
async function startTransport() {
|
|
113
|
+
// Determine the transport type from the validated configuration.
|
|
114
|
+
const transportType = config.mcpTransportType;
|
|
115
|
+
const context = { operation: 'startTransport', transport: transportType };
|
|
116
|
+
logger.info(`Starting transport: ${transportType}`, context);
|
|
117
|
+
// --- HTTP Transport Setup ---
|
|
118
|
+
if (transportType === 'http') {
|
|
119
|
+
logger.debug('Delegating to startHttpTransport...', context);
|
|
120
|
+
// For HTTP, the transport layer manages its own lifecycle and potentially multiple sessions.
|
|
121
|
+
// We pass the factory function to allow the HTTP transport to create server instances as needed (per session).
|
|
122
|
+
await startHttpTransport(createMcpServerInstance, context);
|
|
123
|
+
// The HTTP server runs indefinitely, listening for connections, so this function returns void.
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// --- Stdio Transport Setup ---
|
|
127
|
+
if (transportType === 'stdio') {
|
|
128
|
+
logger.debug('Creating single McpServer instance for stdio transport...', context);
|
|
129
|
+
// For stdio, there's typically one persistent connection managed by a parent process.
|
|
130
|
+
// Create a single McpServer instance for the entire process lifetime.
|
|
131
|
+
const server = await createMcpServerInstance();
|
|
132
|
+
logger.debug('Delegating to connectStdioTransport...', context);
|
|
133
|
+
// Connect the server instance to the stdio transport handler.
|
|
134
|
+
await connectStdioTransport(server, context);
|
|
135
|
+
// Return the server instance; the caller (main entry point) might hold onto it.
|
|
136
|
+
return server;
|
|
137
|
+
}
|
|
138
|
+
// --- Unsupported Transport ---
|
|
139
|
+
// This case should theoretically not be reached due to config validation, but acts as a safeguard.
|
|
140
|
+
logger.fatal(`Unsupported transport type configured: ${transportType}`, context);
|
|
141
|
+
throw new Error(`Unsupported transport type: ${transportType}. Must be 'stdio' or 'http'.`);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Main application entry point. Initializes and starts the MCP server.
|
|
145
|
+
*
|
|
146
|
+
* MCP Spec Relevance:
|
|
147
|
+
* - Orchestrates the server startup sequence, culminating in a server ready to accept
|
|
148
|
+
* connections and process MCP messages according to the chosen transport's rules.
|
|
149
|
+
* - Implements top-level error handling for critical startup failures, ensuring the
|
|
150
|
+
* process exits appropriately if it cannot initialize correctly.
|
|
151
|
+
*
|
|
152
|
+
* @returns {Promise<void | McpServer>} Resolves upon successful startup (void for http, McpServer for stdio). Rejects on critical failure.
|
|
153
|
+
*/
|
|
154
|
+
export async function initializeAndStartServer() {
|
|
155
|
+
const context = { operation: 'initializeAndStartServer' };
|
|
156
|
+
logger.info('MCP Server initialization sequence started.', context);
|
|
157
|
+
try {
|
|
158
|
+
// Initiate the transport setup based on configuration.
|
|
159
|
+
const result = await startTransport();
|
|
160
|
+
logger.info('MCP Server initialization sequence completed successfully.', context);
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
// Catch any errors that occurred during server instance creation or transport setup.
|
|
165
|
+
logger.fatal('Critical error during MCP server initialization.', {
|
|
166
|
+
...context,
|
|
167
|
+
error: err instanceof Error ? err.message : String(err),
|
|
168
|
+
stack: err instanceof Error ? err.stack : undefined,
|
|
169
|
+
});
|
|
170
|
+
// Use the centralized error handler for consistent critical error reporting.
|
|
171
|
+
ErrorHandler.handleError(err, { ...context, critical: true });
|
|
172
|
+
// Exit the process with a non-zero code to indicate failure.
|
|
173
|
+
logger.info('Exiting process due to critical initialization error.', context);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type RequestContext } from "../../../utils/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Defines the valid formatting modes for the echo tool operation.
|
|
5
|
+
* - `standard`: Echo the message as is.
|
|
6
|
+
* - `uppercase`: Convert the message to uppercase.
|
|
7
|
+
* - `lowercase`: Convert the message to lowercase.
|
|
8
|
+
*/
|
|
9
|
+
export declare const ECHO_MODES: readonly ["standard", "uppercase", "lowercase"];
|
|
10
|
+
/**
|
|
11
|
+
* Zod schema defining the input parameters for the `echo_message` tool.
|
|
12
|
+
* Includes validation rules and descriptions for each parameter.
|
|
13
|
+
*/
|
|
14
|
+
export declare const EchoToolInputSchema: z.ZodObject<{
|
|
15
|
+
/** The message to be echoed back. Must be between 1 and 1000 characters. */
|
|
16
|
+
message: z.ZodString;
|
|
17
|
+
/** Specifies how the message should be formatted. Defaults to 'standard'. */
|
|
18
|
+
mode: z.ZodDefault<z.ZodOptional<z.ZodEnum<["standard", "uppercase", "lowercase"]>>>;
|
|
19
|
+
/** The number of times the formatted message should be repeated. Defaults to 1, max 10. */
|
|
20
|
+
repeat: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
21
|
+
/** Whether to include an ISO 8601 timestamp in the response. Defaults to true. */
|
|
22
|
+
timestamp: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
23
|
+
}, "strip", z.ZodTypeAny, {
|
|
24
|
+
repeat: number;
|
|
25
|
+
message: string;
|
|
26
|
+
timestamp: boolean;
|
|
27
|
+
mode: "standard" | "uppercase" | "lowercase";
|
|
28
|
+
}, {
|
|
29
|
+
message: string;
|
|
30
|
+
repeat?: number | undefined;
|
|
31
|
+
timestamp?: boolean | undefined;
|
|
32
|
+
mode?: "standard" | "uppercase" | "lowercase" | undefined;
|
|
33
|
+
}>;
|
|
34
|
+
/**
|
|
35
|
+
* TypeScript type inferred from `EchoToolInputSchema`.
|
|
36
|
+
* Represents the validated input parameters for the echo tool.
|
|
37
|
+
* @typedef {z.infer<typeof EchoToolInputSchema>} EchoToolInput
|
|
38
|
+
*/
|
|
39
|
+
export type EchoToolInput = z.infer<typeof EchoToolInputSchema>;
|
|
40
|
+
/**
|
|
41
|
+
* Defines the structure of the JSON payload returned by the `echo_message` tool handler.
|
|
42
|
+
* This object is JSON-stringified and placed within the `text` field of the
|
|
43
|
+
* `CallToolResult`'s `content` array.
|
|
44
|
+
*/
|
|
45
|
+
export interface EchoToolResponse {
|
|
46
|
+
/** The original message provided in the input. */
|
|
47
|
+
originalMessage: string;
|
|
48
|
+
/** The message after applying the specified formatting mode. */
|
|
49
|
+
formattedMessage: string;
|
|
50
|
+
/** The formatted message repeated the specified number of times, joined by spaces. */
|
|
51
|
+
repeatedMessage: string;
|
|
52
|
+
/** The formatting mode that was applied ('standard', 'uppercase', or 'lowercase'). */
|
|
53
|
+
mode: typeof ECHO_MODES[number];
|
|
54
|
+
/** The number of times the message was repeated. */
|
|
55
|
+
repeatCount: number;
|
|
56
|
+
/** Optional ISO 8601 timestamp indicating when the response was generated. Included if `timestamp` input was true. */
|
|
57
|
+
timestamp?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Processes the core logic for the echo tool.
|
|
61
|
+
* Formats and repeats the message based on the provided parameters.
|
|
62
|
+
*
|
|
63
|
+
* @function processEchoMessage
|
|
64
|
+
* @param {EchoToolInput} params - The validated input parameters for the echo tool.
|
|
65
|
+
* @param {RequestContext} context - The request context for logging and tracing.
|
|
66
|
+
* @returns {EchoToolResponse} The processed response data, including original message, formatted/repeated message, and optional timestamp.
|
|
67
|
+
*/
|
|
68
|
+
export declare const processEchoMessage: (params: EchoToolInput, context: RequestContext) => EchoToolResponse;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { z } from 'zod'; // Import z here
|
|
2
|
+
// Import utils from the main barrel file (logger from ../../../utils/internal/logger.js, RequestContext from ../../../utils/internal/requestContext.js)
|
|
3
|
+
import { logger } from "../../../utils/index.js";
|
|
4
|
+
// --- Schema and Type Definitions (Moved from types.ts) ---
|
|
5
|
+
/**
|
|
6
|
+
* Defines the valid formatting modes for the echo tool operation.
|
|
7
|
+
* - `standard`: Echo the message as is.
|
|
8
|
+
* - `uppercase`: Convert the message to uppercase.
|
|
9
|
+
* - `lowercase`: Convert the message to lowercase.
|
|
10
|
+
*/
|
|
11
|
+
export const ECHO_MODES = ['standard', 'uppercase', 'lowercase'];
|
|
12
|
+
/**
|
|
13
|
+
* Zod schema defining the input parameters for the `echo_message` tool.
|
|
14
|
+
* Includes validation rules and descriptions for each parameter.
|
|
15
|
+
*/
|
|
16
|
+
export const EchoToolInputSchema = z.object({
|
|
17
|
+
/** The message to be echoed back. Must be between 1 and 1000 characters. */
|
|
18
|
+
message: z.string().min(1, "Message cannot be empty").max(1000, "Message cannot exceed 1000 characters").describe('The message to echo back (1-1000 characters)'),
|
|
19
|
+
/** Specifies how the message should be formatted. Defaults to 'standard'. */
|
|
20
|
+
mode: z.enum(ECHO_MODES).optional().default('standard').describe('How to format the echoed message: standard (as-is), uppercase, or lowercase'),
|
|
21
|
+
/** The number of times the formatted message should be repeated. Defaults to 1, max 10. */
|
|
22
|
+
repeat: z.number().int().min(1).max(10).optional().default(1).describe('Number of times to repeat the message (1-10)'),
|
|
23
|
+
/** Whether to include an ISO 8601 timestamp in the response. Defaults to true. */
|
|
24
|
+
timestamp: z.boolean().optional().default(true).describe('Whether to include a timestamp in the response')
|
|
25
|
+
}).describe('Defines the input arguments for the echo_message tool.');
|
|
26
|
+
// --- Core Logic Function ---
|
|
27
|
+
/**
|
|
28
|
+
* Processes the core logic for the echo tool.
|
|
29
|
+
* Formats and repeats the message based on the provided parameters.
|
|
30
|
+
*
|
|
31
|
+
* @function processEchoMessage
|
|
32
|
+
* @param {EchoToolInput} params - The validated input parameters for the echo tool.
|
|
33
|
+
* @param {RequestContext} context - The request context for logging and tracing.
|
|
34
|
+
* @returns {EchoToolResponse} The processed response data, including original message, formatted/repeated message, and optional timestamp.
|
|
35
|
+
*/
|
|
36
|
+
export const processEchoMessage = (params, context // Add context parameter
|
|
37
|
+
) => {
|
|
38
|
+
// Use the passed context for logging
|
|
39
|
+
logger.debug("Processing echo message logic", { ...context, inputMessage: params.message, mode: params.mode });
|
|
40
|
+
// Process the message according to the requested mode
|
|
41
|
+
let formattedMessage = params.message;
|
|
42
|
+
switch (params.mode) {
|
|
43
|
+
case 'uppercase':
|
|
44
|
+
formattedMessage = params.message.toUpperCase();
|
|
45
|
+
break;
|
|
46
|
+
case 'lowercase':
|
|
47
|
+
formattedMessage = params.message.toLowerCase();
|
|
48
|
+
break;
|
|
49
|
+
// 'standard' mode keeps the message as-is
|
|
50
|
+
}
|
|
51
|
+
// Repeat the message the specified number of times, ensuring it's within bounds
|
|
52
|
+
// Default repeat value is handled by the Zod schema
|
|
53
|
+
const safeRepeatCount = Math.min(params.repeat, 10);
|
|
54
|
+
const repeatedMessage = Array(safeRepeatCount)
|
|
55
|
+
.fill(formattedMessage)
|
|
56
|
+
.join(' ');
|
|
57
|
+
// Prepare the response data using the imported EchoToolResponse type
|
|
58
|
+
const response = {
|
|
59
|
+
originalMessage: params.message,
|
|
60
|
+
formattedMessage,
|
|
61
|
+
repeatedMessage,
|
|
62
|
+
// Default mode value is handled by the Zod schema
|
|
63
|
+
mode: params.mode,
|
|
64
|
+
repeatCount: safeRepeatCount
|
|
65
|
+
};
|
|
66
|
+
// Add timestamp if requested (default is true based on schema)
|
|
67
|
+
if (params.timestamp !== false) {
|
|
68
|
+
response.timestamp = new Date().toISOString();
|
|
69
|
+
}
|
|
70
|
+
// Use the passed context for logging the result
|
|
71
|
+
logger.debug("Echo message processed successfully", { ...context, response });
|
|
72
|
+
return response;
|
|
73
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
/**
|
|
3
|
+
* Registers the 'echo_message' tool and its handler with the provided MCP server instance.
|
|
4
|
+
* Defines the tool's input schema, description, and the core request handling logic.
|
|
5
|
+
* Error handling is integrated using ErrorHandler. (Asynchronous)
|
|
6
|
+
*
|
|
7
|
+
* @function registerEchoTool
|
|
8
|
+
* @param {McpServer} server - The MCP server instance to register the tool with.
|
|
9
|
+
* @returns {Promise<void>} A promise that resolves when the tool registration is complete.
|
|
10
|
+
* @throws {McpError} Throws an McpError if the registration process fails critically.
|
|
11
|
+
*/
|
|
12
|
+
export declare const registerEchoTool: (server: McpServer) => Promise<void>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Import schema and types from the logic file
|
|
2
|
+
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
|
|
3
|
+
// Import utils from the main barrel file (ErrorHandler, logger, requestContextService from ../../../utils/internal/*)
|
|
4
|
+
import { ErrorHandler, logger, requestContextService } from "../../../utils/index.js";
|
|
5
|
+
import { EchoToolInputSchema } from './echoToolLogic.js'; // Schema needed for shape extraction
|
|
6
|
+
// Import the core logic function
|
|
7
|
+
import { processEchoMessage } from './echoToolLogic.js';
|
|
8
|
+
/**
|
|
9
|
+
* Registers the 'echo_message' tool and its handler with the provided MCP server instance.
|
|
10
|
+
* Defines the tool's input schema, description, and the core request handling logic.
|
|
11
|
+
* Error handling is integrated using ErrorHandler. (Asynchronous)
|
|
12
|
+
*
|
|
13
|
+
* @function registerEchoTool
|
|
14
|
+
* @param {McpServer} server - The MCP server instance to register the tool with.
|
|
15
|
+
* @returns {Promise<void>} A promise that resolves when the tool registration is complete.
|
|
16
|
+
* @throws {McpError} Throws an McpError if the registration process fails critically.
|
|
17
|
+
*/
|
|
18
|
+
export const registerEchoTool = async (server) => {
|
|
19
|
+
const toolName = "echo_message"; // The unique identifier for the tool
|
|
20
|
+
const toolDescription = "Echoes a message back with optional formatting and repetition."; // Tool description
|
|
21
|
+
// Create registration context using the service
|
|
22
|
+
const registrationContext = requestContextService.createRequestContext({
|
|
23
|
+
operation: 'RegisterEchoTool',
|
|
24
|
+
toolName: toolName,
|
|
25
|
+
module: 'EchoToolRegistration'
|
|
26
|
+
});
|
|
27
|
+
logger.info(`Registering tool: ${toolName}`, registrationContext);
|
|
28
|
+
// Use ErrorHandler to wrap the entire registration process
|
|
29
|
+
await ErrorHandler.tryCatch(async () => {
|
|
30
|
+
// Register the tool using the 4-argument server.tool() overload (SDK v1.10.2+)
|
|
31
|
+
server.tool(toolName, toolDescription, // Argument 2: Tool Description
|
|
32
|
+
// --- Tool Input Schema (Raw Shape) ---
|
|
33
|
+
// Pass the raw shape of the Zod schema. The SDK uses this for validation.
|
|
34
|
+
EchoToolInputSchema.shape, // Argument 3: Schema Shape
|
|
35
|
+
// --- Tool Handler ---
|
|
36
|
+
// The core logic executed when the tool is called.
|
|
37
|
+
// Params are automatically validated against the provided schema shape by the SDK.
|
|
38
|
+
async (params) => {
|
|
39
|
+
// Create handler context using the service
|
|
40
|
+
const handlerContext = requestContextService.createRequestContext({
|
|
41
|
+
parentContext: registrationContext, // Link to registration context
|
|
42
|
+
operation: 'HandleEchoToolRequest',
|
|
43
|
+
toolName: toolName,
|
|
44
|
+
params: params // Include relevant request details
|
|
45
|
+
});
|
|
46
|
+
logger.debug("Handling echo tool request", handlerContext);
|
|
47
|
+
// Wrap the handler logic in tryCatch for robust error handling
|
|
48
|
+
return await ErrorHandler.tryCatch(async () => {
|
|
49
|
+
// Delegate the core processing logic, passing the context
|
|
50
|
+
const response = processEchoMessage(params, handlerContext);
|
|
51
|
+
logger.debug("Echo tool processed successfully", handlerContext);
|
|
52
|
+
// Return the response in the standard MCP tool result format
|
|
53
|
+
// as required by the SDK's server.tool method signature.
|
|
54
|
+
return {
|
|
55
|
+
content: [{
|
|
56
|
+
type: "text", // Content type is text
|
|
57
|
+
// The actual content is a JSON string representing the EchoToolResponse
|
|
58
|
+
text: JSON.stringify(response, null, 2)
|
|
59
|
+
}],
|
|
60
|
+
isError: false // Explicitly set isError to false for successful execution
|
|
61
|
+
};
|
|
62
|
+
}, {
|
|
63
|
+
// Configuration for the error handler specific to this tool call
|
|
64
|
+
operation: 'processing echo message handler',
|
|
65
|
+
context: handlerContext, // Pass handler-specific context
|
|
66
|
+
input: params, // Log input parameters on error
|
|
67
|
+
// Provide a custom error mapping for more specific error reporting
|
|
68
|
+
errorMapper: (error) => new McpError(// Add type 'unknown' to error parameter
|
|
69
|
+
// Use VALIDATION_ERROR if the error likely stems from processing invalid (though schema-valid) input
|
|
70
|
+
error instanceof McpError ? error.code : BaseErrorCode.INTERNAL_ERROR, `Error processing echo message tool: ${error instanceof Error ? error.message : 'Unknown error'}`, { ...handlerContext } // Include context in the McpError
|
|
71
|
+
)
|
|
72
|
+
});
|
|
73
|
+
}); // End of server.tool call
|
|
74
|
+
logger.info(`Tool registered successfully: ${toolName}`, registrationContext);
|
|
75
|
+
}, {
|
|
76
|
+
// Configuration for the error handler wrapping the entire registration
|
|
77
|
+
operation: `registering tool ${toolName}`,
|
|
78
|
+
context: registrationContext, // Context for registration-level errors
|
|
79
|
+
errorCode: BaseErrorCode.INTERNAL_ERROR, // Default error code for registration failure
|
|
80
|
+
// Custom error mapping for registration failures
|
|
81
|
+
errorMapper: (error) => new McpError(// Add type 'unknown' to error parameter
|
|
82
|
+
error instanceof McpError ? error.code : BaseErrorCode.INTERNAL_ERROR, `Failed to register tool '${toolName}': ${error instanceof Error ? error.message : 'Unknown error'}`, { ...registrationContext } // Include context in the McpError
|
|
83
|
+
),
|
|
84
|
+
critical: true // Mark registration failure as critical to halt startup
|
|
85
|
+
}); // End of ErrorHandler.tryCatch for registration
|
|
86
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Authentication Middleware: Bearer Token Validation (JWT).
|
|
3
|
+
*
|
|
4
|
+
* This middleware validates JSON Web Tokens (JWT) passed via the 'Authorization' header
|
|
5
|
+
* using the 'Bearer' scheme (e.g., "Authorization: Bearer <your_token>").
|
|
6
|
+
* It verifies the token's signature and expiration using the secret key defined
|
|
7
|
+
* in the configuration (MCP_AUTH_SECRET_KEY).
|
|
8
|
+
*
|
|
9
|
+
* If the token is valid, the decoded payload is attached to `req.auth` for potential
|
|
10
|
+
* use in downstream authorization logic (e.g., checking scopes or permissions).
|
|
11
|
+
* If the token is missing, invalid, or expired, it sends an HTTP 401 Unauthorized response.
|
|
12
|
+
*
|
|
13
|
+
* --- Scope and Relation to MCP Authorization Spec (2025-03-26) ---
|
|
14
|
+
* - This middleware handles the *validation* of an already obtained Bearer token,
|
|
15
|
+
* as required by Section 2.6 of the MCP Auth Spec.
|
|
16
|
+
* - It does *NOT* implement the full OAuth 2.1 authorization flows (e.g., Authorization
|
|
17
|
+
* Code Grant with PKCE), token endpoints (/token), authorization endpoints (/authorize),
|
|
18
|
+
* metadata discovery (/.well-known/oauth-authorization-server), or dynamic client
|
|
19
|
+
* registration (/register) described in the specification. It assumes the client
|
|
20
|
+
* obtained the JWT through an external process compliant with the spec or another
|
|
21
|
+
* agreed-upon mechanism.
|
|
22
|
+
* - It correctly returns HTTP 401 errors for invalid/missing tokens as per Section 2.8.
|
|
23
|
+
*
|
|
24
|
+
* --- Implementation Details & Requirements ---
|
|
25
|
+
* - Requires the 'jsonwebtoken' package (`npm install jsonwebtoken @types/jsonwebtoken`).
|
|
26
|
+
* - The `MCP_AUTH_SECRET_KEY` environment variable MUST be set to a strong, secret value
|
|
27
|
+
* in production. The middleware includes a startup check for this.
|
|
28
|
+
* - In non-production environments, if the secret key is missing, authentication checks
|
|
29
|
+
* are bypassed for development convenience (a warning is logged). THIS IS INSECURE FOR PRODUCTION.
|
|
30
|
+
* - The structure of the JWT payload (e.g., containing user ID, scopes) depends on the
|
|
31
|
+
* token issuer and is not dictated by this middleware itself, but the payload is made
|
|
32
|
+
* available on `req.auth`.
|
|
33
|
+
*
|
|
34
|
+
* @see {@link https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/authorization.mdx | MCP Authorization Specification}
|
|
35
|
+
*/
|
|
36
|
+
import { Request, Response, NextFunction } from 'express';
|
|
37
|
+
import jwt from 'jsonwebtoken';
|
|
38
|
+
declare global {
|
|
39
|
+
namespace Express {
|
|
40
|
+
interface Request {
|
|
41
|
+
/** Decoded JWT payload if authentication is successful, or a development mode indicator. */
|
|
42
|
+
auth?: jwt.JwtPayload | string | {
|
|
43
|
+
devMode: boolean;
|
|
44
|
+
warning: string;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Express middleware function for verifying JWT Bearer token authentication.
|
|
51
|
+
* Checks the `Authorization` header, verifies the token, and attaches the payload to `req.auth`.
|
|
52
|
+
*
|
|
53
|
+
* @param {Request} req - Express request object.
|
|
54
|
+
* @param {Response} res - Express response object.
|
|
55
|
+
* @param {NextFunction} next - Express next middleware function.
|
|
56
|
+
*/
|
|
57
|
+
export declare function mcpAuthMiddleware(req: Request, res: Response, next: NextFunction): void;
|