@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.
@@ -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
+ }