headers.test.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import { describe, expect } from "bun:test"
  2. import { Server } from "@modelcontextprotocol/sdk/server/index.js"
  3. import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js"
  4. import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"
  5. import { LayerNode } from "@kirincode-ai/core/effect/layer-node"
  6. import { Effect } from "effect"
  7. import { testEffect } from "../lib/effect"
  8. import { MCP } from "../../src/mcp/index"
  9. const it = testEffect(LayerNode.compile(MCP.node))
  10. const serve = Effect.acquireRelease(
  11. Effect.promise(async () => {
  12. const requests: Headers[] = []
  13. const protocol = new Server({ name: "headers", version: "1.0.0" }, { capabilities: { tools: {} } })
  14. protocol.setRequestHandler(ListToolsRequestSchema, () => Promise.resolve({ tools: [] }))
  15. const transport = new WebStandardStreamableHTTPServerTransport({
  16. sessionIdGenerator: () => crypto.randomUUID(),
  17. enableJsonResponse: true,
  18. })
  19. await protocol.connect(transport)
  20. const http = Bun.serve({
  21. port: 0,
  22. fetch(request) {
  23. requests.push(new Headers(request.headers))
  24. return transport.handleRequest(request)
  25. },
  26. })
  27. return {
  28. requests,
  29. url: http.url.toString(),
  30. close: async () => {
  31. await http.stop(true)
  32. await protocol.close()
  33. },
  34. }
  35. }),
  36. (server) => Effect.promise(server.close),
  37. )
  38. describe("mcp.headers", () => {
  39. it.instance("headers are passed to transports when oauth is enabled (default)", () =>
  40. Effect.gen(function* () {
  41. const server = yield* serve
  42. const mcp = yield* MCP.Service
  43. const result = yield* mcp.add("test-server", {
  44. type: "remote",
  45. url: server.url,
  46. headers: {
  47. Authorization: "Bearer test-token",
  48. "X-Custom-Header": "custom-value",
  49. },
  50. })
  51. expect(result.status).toMatchObject({ "test-server": { status: "connected" } })
  52. expect(server.requests.length).toBeGreaterThan(0)
  53. for (const headers of server.requests) {
  54. expect(headers.get("authorization")).toBe("Bearer test-token")
  55. expect(headers.get("x-custom-header")).toBe("custom-value")
  56. }
  57. }),
  58. )
  59. it.instance("headers are passed to transports when oauth is explicitly disabled", () =>
  60. Effect.gen(function* () {
  61. const server = yield* serve
  62. const mcp = yield* MCP.Service
  63. const result = yield* mcp.add("test-server-no-oauth", {
  64. type: "remote",
  65. url: server.url,
  66. oauth: false,
  67. headers: {
  68. Authorization: "Bearer test-token",
  69. },
  70. })
  71. expect(result.status).toMatchObject({ "test-server-no-oauth": { status: "connected" } })
  72. expect(server.requests.length).toBeGreaterThan(0)
  73. for (const headers of server.requests) {
  74. expect(headers.get("authorization")).toBe("Bearer test-token")
  75. }
  76. }),
  77. )
  78. it.instance("no requestInit when headers are not provided", () =>
  79. Effect.gen(function* () {
  80. const server = yield* serve
  81. const mcp = yield* MCP.Service
  82. const result = yield* mcp.add("test-server-no-headers", {
  83. type: "remote",
  84. url: server.url,
  85. })
  86. expect(result.status).toMatchObject({ "test-server-no-headers": { status: "connected" } })
  87. expect(server.requests.length).toBeGreaterThan(0)
  88. for (const headers of server.requests) {
  89. expect(headers.has("authorization")).toBe(false)
  90. expect(headers.has("x-custom-header")).toBe(false)
  91. }
  92. }),
  93. )
  94. })