simpleStatelessStreamableHttp.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import { McpServer } from '../../server/mcp.js';
  2. import { StreamableHTTPServerTransport } from '../../server/streamableHttp.js';
  3. import * as z from 'zod/v4';
  4. import { createMcpExpressApp } from '../../server/express.js';
  5. const getServer = () => {
  6. // Create an MCP server with implementation details
  7. const server = new McpServer({
  8. name: 'stateless-streamable-http-server',
  9. version: '1.0.0'
  10. }, { capabilities: { logging: {} } });
  11. // Register a simple prompt
  12. server.registerPrompt('greeting-template', {
  13. description: 'A simple greeting prompt template',
  14. argsSchema: {
  15. name: z.string().describe('Name to include in greeting')
  16. }
  17. }, async ({ name }) => {
  18. return {
  19. messages: [
  20. {
  21. role: 'user',
  22. content: {
  23. type: 'text',
  24. text: `Please greet ${name} in a friendly manner.`
  25. }
  26. }
  27. ]
  28. };
  29. });
  30. // Register a tool specifically for testing resumability
  31. server.registerTool('start-notification-stream', {
  32. description: 'Starts sending periodic notifications for testing resumability',
  33. inputSchema: {
  34. interval: z.number().describe('Interval in milliseconds between notifications').default(100),
  35. count: z.number().describe('Number of notifications to send (0 for 100)').default(10)
  36. }
  37. }, async ({ interval, count }, extra) => {
  38. const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
  39. let counter = 0;
  40. while (count === 0 || counter < count) {
  41. counter++;
  42. try {
  43. await server.sendLoggingMessage({
  44. level: 'info',
  45. data: `Periodic notification #${counter} at ${new Date().toISOString()}`
  46. }, extra.sessionId);
  47. }
  48. catch (error) {
  49. console.error('Error sending notification:', error);
  50. }
  51. // Wait for the specified interval
  52. await sleep(interval);
  53. }
  54. return {
  55. content: [
  56. {
  57. type: 'text',
  58. text: `Started sending periodic notifications every ${interval}ms`
  59. }
  60. ]
  61. };
  62. });
  63. // Create a simple resource at a fixed URI
  64. server.registerResource('greeting-resource', 'https://example.com/greetings/default', { mimeType: 'text/plain' }, async () => {
  65. return {
  66. contents: [
  67. {
  68. uri: 'https://example.com/greetings/default',
  69. text: 'Hello, world!'
  70. }
  71. ]
  72. };
  73. });
  74. return server;
  75. };
  76. const app = createMcpExpressApp();
  77. app.post('/mcp', async (req, res) => {
  78. const server = getServer();
  79. try {
  80. const transport = new StreamableHTTPServerTransport({
  81. sessionIdGenerator: undefined
  82. });
  83. await server.connect(transport);
  84. await transport.handleRequest(req, res, req.body);
  85. res.on('close', () => {
  86. console.log('Request closed');
  87. transport.close();
  88. server.close();
  89. });
  90. }
  91. catch (error) {
  92. console.error('Error handling MCP request:', error);
  93. if (!res.headersSent) {
  94. res.status(500).json({
  95. jsonrpc: '2.0',
  96. error: {
  97. code: -32603,
  98. message: 'Internal server error'
  99. },
  100. id: null
  101. });
  102. }
  103. }
  104. });
  105. app.get('/mcp', async (req, res) => {
  106. console.log('Received GET MCP request');
  107. res.writeHead(405).end(JSON.stringify({
  108. jsonrpc: '2.0',
  109. error: {
  110. code: -32000,
  111. message: 'Method not allowed.'
  112. },
  113. id: null
  114. }));
  115. });
  116. app.delete('/mcp', async (req, res) => {
  117. console.log('Received DELETE MCP request');
  118. res.writeHead(405).end(JSON.stringify({
  119. jsonrpc: '2.0',
  120. error: {
  121. code: -32000,
  122. message: 'Method not allowed.'
  123. },
  124. id: null
  125. }));
  126. });
  127. // Start the server
  128. const PORT = 3000;
  129. app.listen(PORT, error => {
  130. if (error) {
  131. console.error('Failed to start server:', error);
  132. process.exit(1);
  133. }
  134. console.log(`MCP Stateless Streamable HTTP Server listening on port ${PORT}`);
  135. });
  136. // Handle server shutdown
  137. process.on('SIGINT', async () => {
  138. console.log('Shutting down server...');
  139. process.exit(0);
  140. });
  141. //# sourceMappingURL=simpleStatelessStreamableHttp.js.map