@modelcontextprotocol/server-everything
Version:
MCP server that exercises all the features of the MCP protocol
69 lines (68 loc) • 2.97 kB
JavaScript
// The stdio transport manager: the server factory, served on the process's
// own stdin/stdout through the SDK's `serveStdio` entry, closed on SIGINT.
//
// `serveStdio` lets the client's first message pick the connection's
// protocol era (#4852): a 2026-07-28 client opens with `server/discover` (or
// any request carrying the per-request `_meta` envelope) and a 2025-era
// client with `initialize`. It then builds one server for that era from the
// factory and pins it to the connection. It may build and discard one more
// first, to answer a discover probe, so each instance's cleanup runs when that
// instance closes.
//
// It exports its start-up instead of running it at import (#4854), so a test
// can drive the same code in-process over a pair of streams and call the
// SIGINT handler directly, while `index.ts` still starts it the way a user
// launches the binary.
import { serveStdio, StdioServerTransport, } from "@modelcontextprotocol/server/stdio";
import { createServer } from "../server/index.js";
/**
* The main method
* - Serves the server factory over a StdioServerTransport,
* - Handles cleanup on process exit.
*
* @param stdin - The stream the server reads messages from (the process's stdin by default).
* @param stdout - The stream the server writes messages to (the process's stdout by default).
* @return {Promise<StdioSession>} The served connection and the handler registered for SIGINT.
*/
export async function main(stdin = process.stdin, stdout = process.stdout) {
// The cleanups of the instances still open
const cleanups = new Set();
const handle = serveStdio(({ era }) => {
const { server, cleanup } = createServer({ era });
cleanups.add(cleanup);
server.server.onclose = () => {
cleanups.delete(cleanup);
cleanup();
};
return server;
}, {
transport: new StdioServerTransport(stdin, stdout),
// serveStdio reports a transport that fails, including one that cannot
// start, here rather than by rejecting, so record the failure in the
// exit status for whatever supervises the process.
onerror: (error) => {
console.error("Transport error:", error);
process.exitCode = 1;
},
});
// Cleanup on exit
const shutdown = async () => {
await handle.close();
for (const cleanup of cleanups)
cleanup();
cleanups.clear();
process.exit(0);
};
process.on("SIGINT", shutdown);
return { handle, shutdown };
}
/**
* Start the stdio server as the launcher does: announce it on stderr and run
* `main()` on the process's stdio unless other streams are given. A transport
* that fails, including one that cannot start, is reported on stderr.
*/
export async function startStdioServer(stdin, stdout) {
console.error("Starting default (STDIO) server...");
await main(stdin, stdout);
}