@artinet/sdk
Version:
A TypeScript SDK for building collaborative AI agents.
113 lines (112 loc) • 4.37 kB
JavaScript
/**
* Copyright 2026 The Artinet Project
* SPDX-License-Identifier: Apache-2.0
*/
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
import { logger } from "../../../config/index.js";
import { z } from 'zod/v4';
/**
* Configuration for mounting an in-memory MCP server.
*/
const InMemoryParamsSchema = z.object({
/** Whether to invoke the target as a factory function or constructor */
type: z.enum(['factory', 'constructor']),
/** The name of the exported factory function or constructor class */
target: z.string(),
/** The module path to dynamically import (e.g., '@modelcontextprotocol/server-everything/dist/everything.js') */
module: z.string(),
/** Optional arguments to pass to the factory function or constructor */
args: z.record(z.string(), z.unknown()).optional(),
});
/**
* Mounts an MCP server in-memory for direct integration.
* Creates a linked transport pair for client-server communication without network overhead.
* @docs https://modelcontextprotocol.io/docs/concepts/transports#in-memory
*
* @param params - {@link InMemoryParams} Configuration for the server module
* @param extract - Optional function to extract the McpServer from the imported module.
* Use when the module's export structure doesn't match standard factory/constructor patterns.
* @example
* ```typescript
* import { mountMemServer } from "@artinet/sdk/mcp/mem";
* import { Client } from "@modelcontextprotocol/sdk/client/index.js";
*
* // Mount a server using a factory function with custom extraction
* const { server, clientTransport } = await mountMemServer(
* {
* type: "factory",
* target: "createServer",
* module: "@modelcontextprotocol/server-everything/dist/everything.js",
* },
* (module) => module.createServer().server
* );
*
* // Connect a client to the in-memory server
* const client = new Client({ name: "my-client", version: "1.0.0" });
* await client.connect(clientTransport);
*
* // Use the client
* const tools = await client.listTools();
* ```
* @returns Promise containing the {@link McpServer} instance and linked transport pair
*/
export async function mountMemServer(params, extract) {
const [serverTransport, clientTransport] = InMemoryTransport.createLinkedPair();
//IFFE
const modules = (() => {
const modules = new Map();
return async (moduleName) => {
if (modules.has(moduleName)) {
return modules.get(moduleName);
}
const module = await import(moduleName).catch((error) => {
logger.error(`Failed to import module ${moduleName}`, error);
throw error;
});
if (module) {
modules.set(moduleName, module);
}
return module;
};
})();
const module = await modules(params.module).catch((error) => {
logger.error(`Failed to import module ${params.module}`, error);
throw error;
});
if (!module) {
logger.warn(`Failed to import module ${params.module}`);
throw new Error(`Failed to import module ${params.module}`);
}
let server;
if (extract) {
server = extract(module);
}
else if (params.type === 'factory') {
const factory = module[params.target];
if (!factory) {
const error = new Error(`Failed to find factory ${params.target} in module ${params.module}`);
logger.error(error.message, error);
throw error;
}
server = factory(params.args);
}
else {
const constructor = module[params.target];
if (!constructor) {
const error = new Error(`Failed to find constructor ${params.target} in module ${params.module}`);
logger.error(error.message, error);
throw error;
}
server = new constructor(params.args);
}
if (!server?.connect) {
const error = new Error(`Server ${params.target} does not have a connect method`);
logger.error(error.message, error);
throw error;
}
await server.connect(serverTransport).catch((error) => {
logger.error(`Failed to connect to server ${params.target}`, error);
throw error;
});
return { serverTransport, clientTransport, server };
}