@1mcp/agent 0.1.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 +201 -0
- package/README.md +115 -0
- package/build/capabilities/capabilityManager.js +41 -0
- package/build/client.js +18 -0
- package/build/clients/clientManager.js +79 -0
- package/build/config/configManager.js +108 -0
- package/build/config/transportConfig.js +39 -0
- package/build/constants.js +31 -0
- package/build/handlers/notificationHandlers.js +60 -0
- package/build/handlers/requestHandlers.js +218 -0
- package/build/index.js +149 -0
- package/build/logger.js +98 -0
- package/build/mcpTransport.js +122 -0
- package/build/server.js +61 -0
- package/build/services/configReloadService.js +98 -0
- package/build/utils/errorHandling.js +96 -0
- package/package.json +55 -0
package/build/server.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from './constants.js';
|
|
3
|
+
import logger, { addMCPTransport } from './logger.js';
|
|
4
|
+
import { createTransports } from './config/transportConfig.js';
|
|
5
|
+
import { createClients } from './clients/clientManager.js';
|
|
6
|
+
import { collectAndRegisterCapabilities } from './capabilities/capabilityManager.js';
|
|
7
|
+
import configReloadService from './services/configReloadService.js';
|
|
8
|
+
/**
|
|
9
|
+
* Initialize and configure the MCP server
|
|
10
|
+
* @returns The configured MCP server instance
|
|
11
|
+
*/
|
|
12
|
+
async function initializeServer() {
|
|
13
|
+
try {
|
|
14
|
+
// Create the server instance
|
|
15
|
+
const server = new Server({
|
|
16
|
+
name: MCP_SERVER_NAME,
|
|
17
|
+
version: MCP_SERVER_VERSION,
|
|
18
|
+
}, {
|
|
19
|
+
capabilities: {
|
|
20
|
+
logging: {},
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
// Initialize the MCP transport for logging
|
|
24
|
+
addMCPTransport(server, MCP_SERVER_NAME);
|
|
25
|
+
logger.info('Server created and MCP transport initialized for logging');
|
|
26
|
+
return server;
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
logger.error(`Failed to initialize server: ${error}`);
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Main function to set up the MCP server
|
|
35
|
+
*/
|
|
36
|
+
async function setupServer() {
|
|
37
|
+
try {
|
|
38
|
+
// Initialize the server
|
|
39
|
+
const server = await initializeServer();
|
|
40
|
+
// Create transports from configuration
|
|
41
|
+
const transports = createTransports();
|
|
42
|
+
logger.info(`Created ${Object.keys(transports).length} transports`);
|
|
43
|
+
// Create clients for each transport
|
|
44
|
+
const clients = await createClients(transports);
|
|
45
|
+
logger.info(`Created ${Object.keys(clients).length} clients`);
|
|
46
|
+
// Collect capabilities and register handlers
|
|
47
|
+
await collectAndRegisterCapabilities(clients, server);
|
|
48
|
+
// Initialize the configuration reload service
|
|
49
|
+
configReloadService.initialize(server, transports);
|
|
50
|
+
logger.info('Configuration reload service initialized');
|
|
51
|
+
logger.info('Server setup completed successfully');
|
|
52
|
+
return server;
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
logger.error(`Failed to set up server: ${error}`);
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Set up the server and export it
|
|
60
|
+
const server = await setupServer();
|
|
61
|
+
export { server };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import logger from '../logger.js';
|
|
2
|
+
import configManager, { ConfigChangeEvent } from '../config/configManager.js';
|
|
3
|
+
import { createTransports } from '../config/transportConfig.js';
|
|
4
|
+
import { createClients } from '../clients/clientManager.js';
|
|
5
|
+
import { collectAndRegisterCapabilities } from '../capabilities/capabilityManager.js';
|
|
6
|
+
/**
|
|
7
|
+
* Service to handle dynamic configuration reloading
|
|
8
|
+
*/
|
|
9
|
+
export class ConfigReloadService {
|
|
10
|
+
static instance;
|
|
11
|
+
server = null;
|
|
12
|
+
currentTransports = {};
|
|
13
|
+
isReloading = false;
|
|
14
|
+
/**
|
|
15
|
+
* Private constructor to enforce singleton pattern
|
|
16
|
+
*/
|
|
17
|
+
constructor() { }
|
|
18
|
+
/**
|
|
19
|
+
* Get the singleton instance of the ConfigReloadService
|
|
20
|
+
* @returns The ConfigReloadService instance
|
|
21
|
+
*/
|
|
22
|
+
static getInstance() {
|
|
23
|
+
if (!ConfigReloadService.instance) {
|
|
24
|
+
ConfigReloadService.instance = new ConfigReloadService();
|
|
25
|
+
}
|
|
26
|
+
return ConfigReloadService.instance;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Initialize the service with the server instance
|
|
30
|
+
* @param server The MCP server instance
|
|
31
|
+
* @param initialTransports The initial transports
|
|
32
|
+
*/
|
|
33
|
+
initialize(server, initialTransports) {
|
|
34
|
+
this.server = server;
|
|
35
|
+
this.currentTransports = initialTransports;
|
|
36
|
+
// Set up configuration change listener
|
|
37
|
+
configManager.on(ConfigChangeEvent.TRANSPORT_CONFIG_CHANGED, this.handleConfigChange.bind(this));
|
|
38
|
+
// Start watching for configuration changes
|
|
39
|
+
configManager.startWatching();
|
|
40
|
+
logger.info('Config reload service initialized');
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Handle configuration changes
|
|
44
|
+
* @param newConfig The new transport configuration
|
|
45
|
+
*/
|
|
46
|
+
async handleConfigChange(newConfig) {
|
|
47
|
+
if (!this.server || this.isReloading) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
this.isReloading = true;
|
|
51
|
+
logger.info('Handling configuration change...');
|
|
52
|
+
try {
|
|
53
|
+
// Create new transports from updated configuration
|
|
54
|
+
const newTransports = createTransports();
|
|
55
|
+
// Disconnect and clean up old transports that are no longer in the config
|
|
56
|
+
// or have changed configuration
|
|
57
|
+
const transportKeysToRemove = Object.keys(this.currentTransports).filter((key) => !newTransports[key] ||
|
|
58
|
+
JSON.stringify(newConfig[key]) !== JSON.stringify(configManager.getTransportConfig()[key]));
|
|
59
|
+
for (const key of transportKeysToRemove) {
|
|
60
|
+
try {
|
|
61
|
+
const transport = this.currentTransports[key];
|
|
62
|
+
// Attempt to close the transport if possible
|
|
63
|
+
// Note: Transport interface might not have a standard disconnect method,
|
|
64
|
+
// but we can safely try to close it if it's a closeable transport
|
|
65
|
+
if (transport && 'close' in transport && typeof transport.close === 'function') {
|
|
66
|
+
await transport.close();
|
|
67
|
+
logger.info(`Closed transport: ${key}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
logger.error(`Error closing transport ${key}: ${error}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Create clients for the new transports
|
|
75
|
+
const newClients = await createClients(newTransports);
|
|
76
|
+
// Recollect and register capabilities with the server
|
|
77
|
+
await collectAndRegisterCapabilities(newClients, this.server);
|
|
78
|
+
// Update current transports
|
|
79
|
+
this.currentTransports = newTransports;
|
|
80
|
+
logger.info('Configuration reload completed successfully');
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
logger.error(`Failed to reload configuration: ${error}`);
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
this.isReloading = false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Stop the service and clean up resources
|
|
91
|
+
*/
|
|
92
|
+
stop() {
|
|
93
|
+
configManager.stopWatching();
|
|
94
|
+
configManager.removeAllListeners(ConfigChangeEvent.TRANSPORT_CONFIG_CHANGED);
|
|
95
|
+
logger.info('Config reload service stopped');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export default ConfigReloadService.getInstance();
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ERROR_CODES } from '../constants.js';
|
|
2
|
+
import logger from '../logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* Custom error types for the MCP agent
|
|
5
|
+
*/
|
|
6
|
+
export class MCPError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
data;
|
|
9
|
+
constructor(message, code, data) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'MCPError';
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.data = data;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class ClientNotFoundError extends MCPError {
|
|
17
|
+
constructor(clientName) {
|
|
18
|
+
super(`Client not found: ${clientName}`, ERROR_CODES.TRANSPORT_NOT_FOUND);
|
|
19
|
+
this.name = 'ClientNotFoundError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export class ClientConnectionError extends MCPError {
|
|
23
|
+
constructor(clientName, originalError) {
|
|
24
|
+
super(`Failed to connect to client: ${clientName}${originalError ? ` - ${originalError.message}` : ''}`, ERROR_CODES.TRANSPORT_NOT_FOUND, { originalError });
|
|
25
|
+
this.name = 'ClientConnectionError';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export class InvalidRequestError extends MCPError {
|
|
29
|
+
constructor(message, data) {
|
|
30
|
+
super(message, ERROR_CODES.INVALID_PARAMS, data);
|
|
31
|
+
this.name = 'InvalidRequestError';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export class ProxyError extends MCPError {
|
|
35
|
+
constructor(message, originalError) {
|
|
36
|
+
super(message, ERROR_CODES.INTERNAL_SERVER_ERROR, originalError ? { originalError: originalError.message } : undefined);
|
|
37
|
+
this.name = 'ProxyError';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Handles errors in async functions by wrapping them in a try/catch block
|
|
42
|
+
* @param fn The async function to wrap
|
|
43
|
+
* @param errorMessage The error message to log
|
|
44
|
+
* @returns A wrapped function that handles errors
|
|
45
|
+
*/
|
|
46
|
+
export function withErrorHandling(fn, errorMessage) {
|
|
47
|
+
return async (...args) => {
|
|
48
|
+
try {
|
|
49
|
+
return await fn(...args);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
logger.error(`${errorMessage}: ${error}`);
|
|
53
|
+
// Rethrow MCPErrors as is
|
|
54
|
+
if (error instanceof MCPError) {
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
// Convert other errors to ProxyError
|
|
58
|
+
throw new ProxyError(errorMessage, error instanceof Error ? error : new Error(String(error)));
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Formats an error for JSON-RPC response
|
|
64
|
+
* @param error The error to format
|
|
65
|
+
* @returns A formatted error object
|
|
66
|
+
*/
|
|
67
|
+
export function formatErrorResponse(error) {
|
|
68
|
+
if (error instanceof MCPError) {
|
|
69
|
+
return {
|
|
70
|
+
code: error.code,
|
|
71
|
+
message: error.message,
|
|
72
|
+
data: error.data,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
code: ERROR_CODES.INTERNAL_SERVER_ERROR,
|
|
77
|
+
message: error instanceof Error ? error.message : String(error),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Extracts client name and resource name from a URI
|
|
82
|
+
* @param uri The URI to parse
|
|
83
|
+
* @param separator The separator used in the URI
|
|
84
|
+
* @returns An object with clientName and resourceName
|
|
85
|
+
* @throws InvalidRequestError if the URI is invalid
|
|
86
|
+
*/
|
|
87
|
+
export function parseUri(uri, separator) {
|
|
88
|
+
const parts = uri.split(separator);
|
|
89
|
+
if (parts.length !== 2) {
|
|
90
|
+
throw new InvalidRequestError(`Invalid URI format: ${uri}`);
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
clientName: parts[0],
|
|
94
|
+
resourceName: parts[1],
|
|
95
|
+
};
|
|
96
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@1mcp/agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "One MCP server to aggregate them all - A unified Model Context Protocol server implementation",
|
|
5
|
+
"homepage": "https://1mcp.app",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/1mcp-app/agent.git"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"bin": {
|
|
16
|
+
"1mcp": "./build/index.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"build"
|
|
20
|
+
],
|
|
21
|
+
"husky": {
|
|
22
|
+
"hooks": {
|
|
23
|
+
"pre-commit": "lint-staged"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"lint-staged": {
|
|
27
|
+
"*.{js,ts}": [
|
|
28
|
+
"prettier --write"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.7.0",
|
|
33
|
+
"express": "^4.21.2",
|
|
34
|
+
"winston": "^3.17.0",
|
|
35
|
+
"winston-transport": "^4.9.0",
|
|
36
|
+
"yargs": "^17.7.2",
|
|
37
|
+
"yargs-parser": "^21.1.1"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/express": "^5.0.0",
|
|
41
|
+
"@types/node": "^22.13.10",
|
|
42
|
+
"@types/yargs": "^17.0.33",
|
|
43
|
+
"husky": "^9.1.7",
|
|
44
|
+
"lint-staged": "^15.5.0",
|
|
45
|
+
"prettier": "^3.5.3",
|
|
46
|
+
"tsc-watch": "^6.2.1",
|
|
47
|
+
"typescript": "^5.3.3"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"dev": "tsc-watch --onSuccess \"node build/index.js\"",
|
|
51
|
+
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
|
|
52
|
+
"watch": "tsc --watch",
|
|
53
|
+
"inspector": "npx @modelcontextprotocol/inspector build/index.js"
|
|
54
|
+
}
|
|
55
|
+
}
|