fixture-server.ts 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. /**
  2. * Minimal MCP server over stdio for e2e testing of the dsh-mcp-client plugin.
  3. * Registers controlled tools with predictable behavior for asserting edge cases.
  4. *
  5. * Run: node fixture-server.ts
  6. */
  7. import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
  8. import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
  9. import { z } from 'zod'
  10. const server = new McpServer(
  11. { name: 'fixture-server', version: '1.0.0' },
  12. { capabilities: { tools: { listChanged: true } } },
  13. )
  14. server.registerTool('add', {
  15. title: 'Add Tool',
  16. description: 'Adds two numbers.',
  17. inputSchema: { a: z.number().describe('First number'), b: z.number().describe('Second number') },
  18. }, async args => ({
  19. content: [{ type: 'text', text: String(args.a + args.b) }],
  20. }))
  21. server.registerTool('greet', {
  22. title: 'Greet Tool',
  23. description: 'Greets a person by name.',
  24. inputSchema: { name: z.string().describe('Name to greet') },
  25. }, async args => ({
  26. content: [{ type: 'text', text: `Hello, ${args.name}!` }],
  27. }))
  28. server.registerTool('fail', {
  29. title: 'Fail Tool',
  30. description: 'Always returns an error.',
  31. inputSchema: {},
  32. }, async () => ({
  33. content: [{ type: 'text', text: 'Something went wrong' }],
  34. isError: true,
  35. }))
  36. server.registerTool('image', {
  37. title: 'Image Tool',
  38. description: 'Returns an image content block.',
  39. inputSchema: {},
  40. }, async () => ({
  41. content: [
  42. { type: 'text', text: 'Here is an image:' },
  43. { type: 'image', data: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC', mimeType: 'image/png' },
  44. { type: 'text', text: 'End of image.' },
  45. ],
  46. }))
  47. server.registerTool('crash', {
  48. title: 'Crash Tool',
  49. description: 'Replies, then exits the server process (crash-recovery test).',
  50. inputSchema: {},
  51. }, async () => {
  52. // Exit AFTER the response flushes so the caller observes a clean result
  53. // followed by a transport close, like a real post-reply crash.
  54. setTimeout(() => process.exit(7), 25)
  55. return { content: [{ type: 'text', text: 'crashing' }] }
  56. })
  57. // Dotted name: legal in MCP, illegal in the DeepSeek function-name contract.
  58. // Exercises the bridge's normalize-and-hash public-name path end to end.
  59. server.registerTool('admin.reset', {
  60. title: 'Admin Reset Tool',
  61. description: 'Tool with a dotted name (normalization test).',
  62. inputSchema: {},
  63. }, async () => ({
  64. content: [{ type: 'text', text: 'reset done' }],
  65. }))
  66. const transport = new StdioServerTransport()
  67. await server.connect(transport)