@modelcontextprotocol/sdk 1.18.2 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (332) hide show
  1. package/README.md +1010 -824
  2. package/dist/cjs/cli.js +35 -37
  3. package/dist/cjs/cli.js.map +1 -1
  4. package/dist/cjs/client/auth.d.ts +12 -12
  5. package/dist/cjs/client/auth.d.ts.map +1 -1
  6. package/dist/cjs/client/auth.js +81 -93
  7. package/dist/cjs/client/auth.js.map +1 -1
  8. package/dist/cjs/client/index.d.ts +186 -123
  9. package/dist/cjs/client/index.d.ts.map +1 -1
  10. package/dist/cjs/client/index.js +40 -41
  11. package/dist/cjs/client/index.js.map +1 -1
  12. package/dist/cjs/client/middleware.d.ts +2 -2
  13. package/dist/cjs/client/middleware.d.ts.map +1 -1
  14. package/dist/cjs/client/middleware.js +22 -27
  15. package/dist/cjs/client/middleware.js.map +1 -1
  16. package/dist/cjs/client/sse.d.ts +4 -4
  17. package/dist/cjs/client/sse.d.ts.map +1 -1
  18. package/dist/cjs/client/sse.js +34 -21
  19. package/dist/cjs/client/sse.js.map +1 -1
  20. package/dist/cjs/client/stdio.d.ts +4 -4
  21. package/dist/cjs/client/stdio.d.ts.map +1 -1
  22. package/dist/cjs/client/stdio.js +32 -32
  23. package/dist/cjs/client/stdio.js.map +1 -1
  24. package/dist/cjs/client/streamableHttp.d.ts +7 -6
  25. package/dist/cjs/client/streamableHttp.d.ts.map +1 -1
  26. package/dist/cjs/client/streamableHttp.js +55 -38
  27. package/dist/cjs/client/streamableHttp.js.map +1 -1
  28. package/dist/cjs/client/websocket.d.ts +2 -2
  29. package/dist/cjs/client/websocket.d.ts.map +1 -1
  30. package/dist/cjs/client/websocket.js +5 -7
  31. package/dist/cjs/client/websocket.js.map +1 -1
  32. package/dist/cjs/examples/client/multipleClientsParallel.js +2 -2
  33. package/dist/cjs/examples/client/multipleClientsParallel.js.map +1 -1
  34. package/dist/cjs/examples/client/parallelToolCallsClient.js +6 -5
  35. package/dist/cjs/examples/client/parallelToolCallsClient.js.map +1 -1
  36. package/dist/cjs/examples/client/simpleOAuthClient.js +15 -13
  37. package/dist/cjs/examples/client/simpleOAuthClient.js.map +1 -1
  38. package/dist/cjs/examples/client/simpleStreamableHttp.js +15 -11
  39. package/dist/cjs/examples/client/simpleStreamableHttp.js.map +1 -1
  40. package/dist/cjs/examples/client/streamableHttpWithSseFallbackClient.js +2 -2
  41. package/dist/cjs/examples/client/streamableHttpWithSseFallbackClient.js.map +1 -1
  42. package/dist/cjs/examples/server/demoInMemoryOAuthProvider.d.ts +1 -1
  43. package/dist/cjs/examples/server/demoInMemoryOAuthProvider.d.ts.map +1 -1
  44. package/dist/cjs/examples/server/demoInMemoryOAuthProvider.js +18 -16
  45. package/dist/cjs/examples/server/demoInMemoryOAuthProvider.js.map +1 -1
  46. package/dist/cjs/examples/server/jsonResponseStreamableHttp.js +18 -18
  47. package/dist/cjs/examples/server/jsonResponseStreamableHttp.js.map +1 -1
  48. package/dist/cjs/examples/server/mcpServerOutputSchema.js +19 -17
  49. package/dist/cjs/examples/server/mcpServerOutputSchema.js.map +1 -1
  50. package/dist/cjs/examples/server/simpleSseServer.js +8 -8
  51. package/dist/cjs/examples/server/simpleSseServer.js.map +1 -1
  52. package/dist/cjs/examples/server/simpleStatelessStreamableHttp.js +22 -22
  53. package/dist/cjs/examples/server/simpleStatelessStreamableHttp.js.map +1 -1
  54. package/dist/cjs/examples/server/simpleStreamableHttp.js +78 -78
  55. package/dist/cjs/examples/server/simpleStreamableHttp.js.map +1 -1
  56. package/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.js +18 -18
  57. package/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.js.map +1 -1
  58. package/dist/cjs/examples/server/standaloneSseWithGetStreamableHttp.js +8 -8
  59. package/dist/cjs/examples/server/standaloneSseWithGetStreamableHttp.js.map +1 -1
  60. package/dist/cjs/examples/server/toolWithSampleServer.js +19 -19
  61. package/dist/cjs/examples/server/toolWithSampleServer.js.map +1 -1
  62. package/dist/cjs/examples/shared/inMemoryEventStore.d.ts.map +1 -1
  63. package/dist/cjs/examples/shared/inMemoryEventStore.js.map +1 -1
  64. package/dist/cjs/inMemory.d.ts +3 -3
  65. package/dist/cjs/inMemory.d.ts.map +1 -1
  66. package/dist/cjs/inMemory.js +1 -1
  67. package/dist/cjs/inMemory.js.map +1 -1
  68. package/dist/cjs/server/auth/clients.d.ts +2 -2
  69. package/dist/cjs/server/auth/clients.d.ts.map +1 -1
  70. package/dist/cjs/server/auth/errors.d.ts +1 -1
  71. package/dist/cjs/server/auth/errors.d.ts.map +1 -1
  72. package/dist/cjs/server/auth/errors.js +17 -17
  73. package/dist/cjs/server/auth/errors.js.map +1 -1
  74. package/dist/cjs/server/auth/handlers/authorize.d.ts +3 -3
  75. package/dist/cjs/server/auth/handlers/authorize.d.ts.map +1 -1
  76. package/dist/cjs/server/auth/handlers/authorize.js +21 -18
  77. package/dist/cjs/server/auth/handlers/authorize.js.map +1 -1
  78. package/dist/cjs/server/auth/handlers/metadata.d.ts +2 -2
  79. package/dist/cjs/server/auth/handlers/metadata.js +1 -1
  80. package/dist/cjs/server/auth/handlers/metadata.js.map +1 -1
  81. package/dist/cjs/server/auth/handlers/register.d.ts +4 -4
  82. package/dist/cjs/server/auth/handlers/register.d.ts.map +1 -1
  83. package/dist/cjs/server/auth/handlers/register.js +7 -9
  84. package/dist/cjs/server/auth/handlers/register.js.map +1 -1
  85. package/dist/cjs/server/auth/handlers/revoke.d.ts +4 -4
  86. package/dist/cjs/server/auth/handlers/revoke.d.ts.map +1 -1
  87. package/dist/cjs/server/auth/handlers/revoke.js +9 -9
  88. package/dist/cjs/server/auth/handlers/revoke.js.map +1 -1
  89. package/dist/cjs/server/auth/handlers/token.d.ts +3 -3
  90. package/dist/cjs/server/auth/handlers/token.d.ts.map +1 -1
  91. package/dist/cjs/server/auth/handlers/token.js +14 -14
  92. package/dist/cjs/server/auth/handlers/token.js.map +1 -1
  93. package/dist/cjs/server/auth/middleware/allowedMethods.d.ts +1 -1
  94. package/dist/cjs/server/auth/middleware/allowedMethods.d.ts.map +1 -1
  95. package/dist/cjs/server/auth/middleware/allowedMethods.js +1 -3
  96. package/dist/cjs/server/auth/middleware/allowedMethods.js.map +1 -1
  97. package/dist/cjs/server/auth/middleware/bearerAuth.d.ts +4 -4
  98. package/dist/cjs/server/auth/middleware/bearerAuth.d.ts.map +1 -1
  99. package/dist/cjs/server/auth/middleware/bearerAuth.js +7 -7
  100. package/dist/cjs/server/auth/middleware/bearerAuth.js.map +1 -1
  101. package/dist/cjs/server/auth/middleware/clientAuth.d.ts +4 -4
  102. package/dist/cjs/server/auth/middleware/clientAuth.d.ts.map +1 -1
  103. package/dist/cjs/server/auth/middleware/clientAuth.js +6 -6
  104. package/dist/cjs/server/auth/middleware/clientAuth.js.map +1 -1
  105. package/dist/cjs/server/auth/provider.d.ts +4 -4
  106. package/dist/cjs/server/auth/provider.d.ts.map +1 -1
  107. package/dist/cjs/server/auth/providers/proxyProvider.d.ts +10 -10
  108. package/dist/cjs/server/auth/providers/proxyProvider.d.ts.map +1 -1
  109. package/dist/cjs/server/auth/providers/proxyProvider.js +34 -34
  110. package/dist/cjs/server/auth/providers/proxyProvider.js.map +1 -1
  111. package/dist/cjs/server/auth/router.d.ts +11 -11
  112. package/dist/cjs/server/auth/router.d.ts.map +1 -1
  113. package/dist/cjs/server/auth/router.js +16 -18
  114. package/dist/cjs/server/auth/router.js.map +1 -1
  115. package/dist/cjs/server/auth/types.d.ts +1 -1
  116. package/dist/cjs/server/auth/types.d.ts.map +1 -1
  117. package/dist/cjs/server/completable.d.ts +5 -5
  118. package/dist/cjs/server/completable.d.ts.map +1 -1
  119. package/dist/cjs/server/completable.js +5 -5
  120. package/dist/cjs/server/completable.js.map +1 -1
  121. package/dist/cjs/server/index.d.ts +9 -9
  122. package/dist/cjs/server/index.d.ts.map +1 -1
  123. package/dist/cjs/server/index.js +38 -42
  124. package/dist/cjs/server/index.js.map +1 -1
  125. package/dist/cjs/server/mcp.d.ts +8 -8
  126. package/dist/cjs/server/mcp.d.ts.map +1 -1
  127. package/dist/cjs/server/mcp.js +87 -82
  128. package/dist/cjs/server/mcp.js.map +1 -1
  129. package/dist/cjs/server/sse.d.ts +4 -4
  130. package/dist/cjs/server/sse.d.ts.map +1 -1
  131. package/dist/cjs/server/sse.js +16 -15
  132. package/dist/cjs/server/sse.js.map +1 -1
  133. package/dist/cjs/server/stdio.d.ts +3 -3
  134. package/dist/cjs/server/stdio.d.ts.map +1 -1
  135. package/dist/cjs/server/stdio.js +7 -7
  136. package/dist/cjs/server/stdio.js.map +1 -1
  137. package/dist/cjs/server/streamableHttp.d.ts +5 -5
  138. package/dist/cjs/server/streamableHttp.d.ts.map +1 -1
  139. package/dist/cjs/server/streamableHttp.js +63 -64
  140. package/dist/cjs/server/streamableHttp.js.map +1 -1
  141. package/dist/cjs/shared/auth-utils.d.ts.map +1 -1
  142. package/dist/cjs/shared/auth-utils.js +3 -3
  143. package/dist/cjs/shared/auth-utils.js.map +1 -1
  144. package/dist/cjs/shared/auth.d.ts +1 -1
  145. package/dist/cjs/shared/auth.d.ts.map +1 -1
  146. package/dist/cjs/shared/auth.js +42 -46
  147. package/dist/cjs/shared/auth.js.map +1 -1
  148. package/dist/cjs/shared/metadataUtils.d.ts +1 -1
  149. package/dist/cjs/shared/metadataUtils.js.map +1 -1
  150. package/dist/cjs/shared/protocol.d.ts +6 -6
  151. package/dist/cjs/shared/protocol.d.ts.map +1 -1
  152. package/dist/cjs/shared/protocol.js +42 -43
  153. package/dist/cjs/shared/protocol.js.map +1 -1
  154. package/dist/cjs/shared/stdio.d.ts +1 -1
  155. package/dist/cjs/shared/stdio.d.ts.map +1 -1
  156. package/dist/cjs/shared/stdio.js +3 -3
  157. package/dist/cjs/shared/stdio.js.map +1 -1
  158. package/dist/cjs/shared/transport.d.ts +1 -1
  159. package/dist/cjs/shared/transport.d.ts.map +1 -1
  160. package/dist/cjs/shared/uriTemplate.d.ts.map +1 -1
  161. package/dist/cjs/shared/uriTemplate.js +69 -71
  162. package/dist/cjs/shared/uriTemplate.js.map +1 -1
  163. package/dist/cjs/types.d.ts +9650 -4790
  164. package/dist/cjs/types.d.ts.map +1 -1
  165. package/dist/cjs/types.js +199 -234
  166. package/dist/cjs/types.js.map +1 -1
  167. package/dist/esm/cli.js +45 -47
  168. package/dist/esm/cli.js.map +1 -1
  169. package/dist/esm/client/auth.d.ts +12 -12
  170. package/dist/esm/client/auth.d.ts.map +1 -1
  171. package/dist/esm/client/auth.js +87 -99
  172. package/dist/esm/client/auth.js.map +1 -1
  173. package/dist/esm/client/index.d.ts +186 -123
  174. package/dist/esm/client/index.d.ts.map +1 -1
  175. package/dist/esm/client/index.js +43 -44
  176. package/dist/esm/client/index.js.map +1 -1
  177. package/dist/esm/client/middleware.d.ts +2 -2
  178. package/dist/esm/client/middleware.d.ts.map +1 -1
  179. package/dist/esm/client/middleware.js +23 -28
  180. package/dist/esm/client/middleware.js.map +1 -1
  181. package/dist/esm/client/sse.d.ts +4 -4
  182. package/dist/esm/client/sse.d.ts.map +1 -1
  183. package/dist/esm/client/sse.js +37 -24
  184. package/dist/esm/client/sse.js.map +1 -1
  185. package/dist/esm/client/stdio.d.ts +4 -4
  186. package/dist/esm/client/stdio.d.ts.map +1 -1
  187. package/dist/esm/client/stdio.js +36 -36
  188. package/dist/esm/client/stdio.js.map +1 -1
  189. package/dist/esm/client/streamableHttp.d.ts +7 -6
  190. package/dist/esm/client/streamableHttp.d.ts.map +1 -1
  191. package/dist/esm/client/streamableHttp.js +58 -41
  192. package/dist/esm/client/streamableHttp.js.map +1 -1
  193. package/dist/esm/client/websocket.d.ts +2 -2
  194. package/dist/esm/client/websocket.d.ts.map +1 -1
  195. package/dist/esm/client/websocket.js +6 -8
  196. package/dist/esm/client/websocket.js.map +1 -1
  197. package/dist/esm/examples/client/multipleClientsParallel.js +3 -3
  198. package/dist/esm/examples/client/multipleClientsParallel.js.map +1 -1
  199. package/dist/esm/examples/client/parallelToolCallsClient.js +7 -6
  200. package/dist/esm/examples/client/parallelToolCallsClient.js.map +1 -1
  201. package/dist/esm/examples/client/simpleOAuthClient.js +15 -13
  202. package/dist/esm/examples/client/simpleOAuthClient.js.map +1 -1
  203. package/dist/esm/examples/client/simpleStreamableHttp.js +17 -13
  204. package/dist/esm/examples/client/simpleStreamableHttp.js.map +1 -1
  205. package/dist/esm/examples/client/streamableHttpWithSseFallbackClient.js +3 -3
  206. package/dist/esm/examples/client/streamableHttpWithSseFallbackClient.js.map +1 -1
  207. package/dist/esm/examples/server/demoInMemoryOAuthProvider.d.ts +1 -1
  208. package/dist/esm/examples/server/demoInMemoryOAuthProvider.d.ts.map +1 -1
  209. package/dist/esm/examples/server/demoInMemoryOAuthProvider.js +19 -17
  210. package/dist/esm/examples/server/demoInMemoryOAuthProvider.js.map +1 -1
  211. package/dist/esm/examples/server/jsonResponseStreamableHttp.js +18 -18
  212. package/dist/esm/examples/server/jsonResponseStreamableHttp.js.map +1 -1
  213. package/dist/esm/examples/server/mcpServerOutputSchema.js +22 -20
  214. package/dist/esm/examples/server/mcpServerOutputSchema.js.map +1 -1
  215. package/dist/esm/examples/server/simpleSseServer.js +8 -8
  216. package/dist/esm/examples/server/simpleSseServer.js.map +1 -1
  217. package/dist/esm/examples/server/simpleStatelessStreamableHttp.js +22 -22
  218. package/dist/esm/examples/server/simpleStatelessStreamableHttp.js.map +1 -1
  219. package/dist/esm/examples/server/simpleStreamableHttp.js +78 -78
  220. package/dist/esm/examples/server/simpleStreamableHttp.js.map +1 -1
  221. package/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.js +19 -19
  222. package/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.js.map +1 -1
  223. package/dist/esm/examples/server/standaloneSseWithGetStreamableHttp.js +8 -8
  224. package/dist/esm/examples/server/standaloneSseWithGetStreamableHttp.js.map +1 -1
  225. package/dist/esm/examples/server/toolWithSampleServer.js +22 -22
  226. package/dist/esm/examples/server/toolWithSampleServer.js.map +1 -1
  227. package/dist/esm/examples/shared/inMemoryEventStore.d.ts.map +1 -1
  228. package/dist/esm/examples/shared/inMemoryEventStore.js.map +1 -1
  229. package/dist/esm/inMemory.d.ts +3 -3
  230. package/dist/esm/inMemory.d.ts.map +1 -1
  231. package/dist/esm/inMemory.js +1 -1
  232. package/dist/esm/inMemory.js.map +1 -1
  233. package/dist/esm/server/auth/clients.d.ts +2 -2
  234. package/dist/esm/server/auth/clients.d.ts.map +1 -1
  235. package/dist/esm/server/auth/errors.d.ts +1 -1
  236. package/dist/esm/server/auth/errors.d.ts.map +1 -1
  237. package/dist/esm/server/auth/errors.js +17 -17
  238. package/dist/esm/server/auth/errors.js.map +1 -1
  239. package/dist/esm/server/auth/handlers/authorize.d.ts +3 -3
  240. package/dist/esm/server/auth/handlers/authorize.d.ts.map +1 -1
  241. package/dist/esm/server/auth/handlers/authorize.js +26 -23
  242. package/dist/esm/server/auth/handlers/authorize.js.map +1 -1
  243. package/dist/esm/server/auth/handlers/metadata.d.ts +2 -2
  244. package/dist/esm/server/auth/handlers/metadata.js +3 -3
  245. package/dist/esm/server/auth/handlers/metadata.js.map +1 -1
  246. package/dist/esm/server/auth/handlers/register.d.ts +4 -4
  247. package/dist/esm/server/auth/handlers/register.d.ts.map +1 -1
  248. package/dist/esm/server/auth/handlers/register.js +12 -14
  249. package/dist/esm/server/auth/handlers/register.js.map +1 -1
  250. package/dist/esm/server/auth/handlers/revoke.d.ts +4 -4
  251. package/dist/esm/server/auth/handlers/revoke.d.ts.map +1 -1
  252. package/dist/esm/server/auth/handlers/revoke.js +16 -16
  253. package/dist/esm/server/auth/handlers/revoke.js.map +1 -1
  254. package/dist/esm/server/auth/handlers/token.d.ts +3 -3
  255. package/dist/esm/server/auth/handlers/token.d.ts.map +1 -1
  256. package/dist/esm/server/auth/handlers/token.js +22 -22
  257. package/dist/esm/server/auth/handlers/token.js.map +1 -1
  258. package/dist/esm/server/auth/middleware/allowedMethods.d.ts +1 -1
  259. package/dist/esm/server/auth/middleware/allowedMethods.d.ts.map +1 -1
  260. package/dist/esm/server/auth/middleware/allowedMethods.js +2 -4
  261. package/dist/esm/server/auth/middleware/allowedMethods.js.map +1 -1
  262. package/dist/esm/server/auth/middleware/bearerAuth.d.ts +4 -4
  263. package/dist/esm/server/auth/middleware/bearerAuth.d.ts.map +1 -1
  264. package/dist/esm/server/auth/middleware/bearerAuth.js +8 -8
  265. package/dist/esm/server/auth/middleware/bearerAuth.js.map +1 -1
  266. package/dist/esm/server/auth/middleware/clientAuth.d.ts +4 -4
  267. package/dist/esm/server/auth/middleware/clientAuth.d.ts.map +1 -1
  268. package/dist/esm/server/auth/middleware/clientAuth.js +8 -8
  269. package/dist/esm/server/auth/middleware/clientAuth.js.map +1 -1
  270. package/dist/esm/server/auth/provider.d.ts +4 -4
  271. package/dist/esm/server/auth/provider.d.ts.map +1 -1
  272. package/dist/esm/server/auth/providers/proxyProvider.d.ts +10 -10
  273. package/dist/esm/server/auth/providers/proxyProvider.d.ts.map +1 -1
  274. package/dist/esm/server/auth/providers/proxyProvider.js +36 -36
  275. package/dist/esm/server/auth/providers/proxyProvider.js.map +1 -1
  276. package/dist/esm/server/auth/router.d.ts +11 -11
  277. package/dist/esm/server/auth/router.d.ts.map +1 -1
  278. package/dist/esm/server/auth/router.js +22 -24
  279. package/dist/esm/server/auth/router.js.map +1 -1
  280. package/dist/esm/server/auth/types.d.ts +1 -1
  281. package/dist/esm/server/auth/types.d.ts.map +1 -1
  282. package/dist/esm/server/completable.d.ts +5 -5
  283. package/dist/esm/server/completable.d.ts.map +1 -1
  284. package/dist/esm/server/completable.js +6 -6
  285. package/dist/esm/server/completable.js.map +1 -1
  286. package/dist/esm/server/index.d.ts +9 -9
  287. package/dist/esm/server/index.d.ts.map +1 -1
  288. package/dist/esm/server/index.js +41 -45
  289. package/dist/esm/server/index.js.map +1 -1
  290. package/dist/esm/server/mcp.d.ts +8 -8
  291. package/dist/esm/server/mcp.d.ts.map +1 -1
  292. package/dist/esm/server/mcp.js +93 -88
  293. package/dist/esm/server/mcp.js.map +1 -1
  294. package/dist/esm/server/sse.d.ts +4 -4
  295. package/dist/esm/server/sse.d.ts.map +1 -1
  296. package/dist/esm/server/sse.js +20 -19
  297. package/dist/esm/server/sse.js.map +1 -1
  298. package/dist/esm/server/stdio.d.ts +3 -3
  299. package/dist/esm/server/stdio.d.ts.map +1 -1
  300. package/dist/esm/server/stdio.js +9 -9
  301. package/dist/esm/server/stdio.js.map +1 -1
  302. package/dist/esm/server/streamableHttp.d.ts +5 -5
  303. package/dist/esm/server/streamableHttp.d.ts.map +1 -1
  304. package/dist/esm/server/streamableHttp.js +67 -68
  305. package/dist/esm/server/streamableHttp.js.map +1 -1
  306. package/dist/esm/shared/auth-utils.d.ts.map +1 -1
  307. package/dist/esm/shared/auth-utils.js +3 -3
  308. package/dist/esm/shared/auth-utils.js.map +1 -1
  309. package/dist/esm/shared/auth.d.ts +1 -1
  310. package/dist/esm/shared/auth.d.ts.map +1 -1
  311. package/dist/esm/shared/auth.js +43 -47
  312. package/dist/esm/shared/auth.js.map +1 -1
  313. package/dist/esm/shared/metadataUtils.d.ts +1 -1
  314. package/dist/esm/shared/metadataUtils.js.map +1 -1
  315. package/dist/esm/shared/protocol.d.ts +6 -6
  316. package/dist/esm/shared/protocol.d.ts.map +1 -1
  317. package/dist/esm/shared/protocol.js +43 -44
  318. package/dist/esm/shared/protocol.js.map +1 -1
  319. package/dist/esm/shared/stdio.d.ts +1 -1
  320. package/dist/esm/shared/stdio.d.ts.map +1 -1
  321. package/dist/esm/shared/stdio.js +4 -4
  322. package/dist/esm/shared/stdio.js.map +1 -1
  323. package/dist/esm/shared/transport.d.ts +1 -1
  324. package/dist/esm/shared/transport.d.ts.map +1 -1
  325. package/dist/esm/shared/uriTemplate.d.ts.map +1 -1
  326. package/dist/esm/shared/uriTemplate.js +69 -71
  327. package/dist/esm/shared/uriTemplate.js.map +1 -1
  328. package/dist/esm/types.d.ts +9650 -4790
  329. package/dist/esm/types.d.ts.map +1 -1
  330. package/dist/esm/types.js +197 -232
  331. package/dist/esm/types.js.map +1 -1
  332. package/package.json +101 -98
package/README.md CHANGED
@@ -1,43 +1,46 @@
1
1
  # MCP TypeScript SDK ![NPM Version](https://img.shields.io/npm/v/%40modelcontextprotocol%2Fsdk) ![MIT licensed](https://img.shields.io/npm/l/%40modelcontextprotocol%2Fsdk)
2
2
 
3
- ## Table of Contents
3
+ <details>
4
+ <summary>Table of Contents</summary>
4
5
 
5
6
  - [Overview](#overview)
6
7
  - [Installation](#installation)
7
8
  - [Quickstart](#quick-start)
8
9
  - [What is MCP?](#what-is-mcp)
9
10
  - [Core Concepts](#core-concepts)
10
- - [Server](#server)
11
- - [Resources](#resources)
12
- - [Tools](#tools)
13
- - [Prompts](#prompts)
14
- - [Completions](#completions)
15
- - [Sampling](#sampling)
11
+ - [Server](#server)
12
+ - [Resources](#resources)
13
+ - [Tools](#tools)
14
+ - [Prompts](#prompts)
15
+ - [Completions](#completions)
16
+ - [Sampling](#sampling)
16
17
  - [Running Your Server](#running-your-server)
17
- - [stdio](#stdio)
18
- - [Streamable HTTP](#streamable-http)
19
- - [Testing and Debugging](#testing-and-debugging)
18
+ - [stdio](#stdio)
19
+ - [Streamable HTTP](#streamable-http)
20
+ - [Testing and Debugging](#testing-and-debugging)
20
21
  - [Examples](#examples)
21
- - [Echo Server](#echo-server)
22
- - [SQLite Explorer](#sqlite-explorer)
22
+ - [Echo Server](#echo-server)
23
+ - [SQLite Explorer](#sqlite-explorer)
23
24
  - [Advanced Usage](#advanced-usage)
24
- - [Dynamic Servers](#dynamic-servers)
25
- - [Low-Level Server](#low-level-server)
26
- - [Writing MCP Clients](#writing-mcp-clients)
27
- - [Proxy Authorization Requests Upstream](#proxy-authorization-requests-upstream)
28
- - [Backwards Compatibility](#backwards-compatibility)
25
+ - [Dynamic Servers](#dynamic-servers)
26
+ - [Low-Level Server](#low-level-server)
27
+ - [Writing MCP Clients](#writing-mcp-clients)
28
+ - [Proxy Authorization Requests Upstream](#proxy-authorization-requests-upstream)
29
+ - [Backwards Compatibility](#backwards-compatibility)
29
30
  - [Documentation](#documentation)
30
31
  - [Contributing](#contributing)
31
32
  - [License](#license)
32
33
 
34
+ </details>
35
+
33
36
  ## Overview
34
37
 
35
- The Model Context Protocol allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction. This TypeScript SDK implements the full MCP specification, making it easy to:
38
+ The Model Context Protocol allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction. This TypeScript SDK implements
39
+ [the full MCP specification](https://modelcontextprotocol.io/specification/latest), making it easy to:
36
40
 
37
- - Build MCP clients that can connect to any MCP server
38
41
  - Create MCP servers that expose resources, prompts and tools
42
+ - Build MCP clients that can connect to any MCP server
39
43
  - Use standard transports like stdio and Streamable HTTP
40
- - Handle all MCP protocol messages and lifecycle events
41
44
 
42
45
  ## Installation
43
46
 
@@ -45,64 +48,96 @@ The Model Context Protocol allows applications to provide context for LLMs in a
45
48
  npm install @modelcontextprotocol/sdk
46
49
  ```
47
50
 
48
- > ⚠️ MCP requires Node.js v18.x or higher to work fine.
49
-
50
51
  ## Quick Start
51
52
 
52
- Let's create a simple MCP server that exposes a calculator tool and some data:
53
+ Let's create a simple MCP server that exposes a calculator tool and some data. Save the following as `server.ts`:
53
54
 
54
55
  ```typescript
55
- import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
56
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
57
- import { z } from "zod";
56
+ import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
57
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
58
+ import express from 'express';
59
+ import { z } from 'zod';
58
60
 
59
61
  // Create an MCP server
60
62
  const server = new McpServer({
61
- name: "demo-server",
62
- version: "1.0.0"
63
+ name: 'demo-server',
64
+ version: '1.0.0'
63
65
  });
64
66
 
65
67
  // Add an addition tool
66
- server.registerTool("add",
67
- {
68
- title: "Addition Tool",
69
- description: "Add two numbers",
70
- inputSchema: { a: z.number(), b: z.number() }
71
- },
72
- async ({ a, b }) => ({
73
- content: [{ type: "text", text: String(a + b) }]
74
- })
68
+ server.registerTool(
69
+ 'add',
70
+ {
71
+ title: 'Addition Tool',
72
+ description: 'Add two numbers',
73
+ inputSchema: { a: z.number(), b: z.number() },
74
+ outputSchema: { result: z.number() }
75
+ },
76
+ async ({ a, b }) => {
77
+ const output = { result: a + b };
78
+ return {
79
+ content: [{ type: 'text', text: JSON.stringify(output) }],
80
+ structuredContent: output
81
+ };
82
+ }
75
83
  );
76
84
 
77
85
  // Add a dynamic greeting resource
78
86
  server.registerResource(
79
- "greeting",
80
- new ResourceTemplate("greeting://{name}", { list: undefined }),
81
- {
82
- title: "Greeting Resource", // Display name for UI
83
- description: "Dynamic greeting generator"
84
- },
85
- async (uri, { name }) => ({
86
- contents: [{
87
- uri: uri.href,
88
- text: `Hello, ${name}!`
89
- }]
90
- })
87
+ 'greeting',
88
+ new ResourceTemplate('greeting://{name}', { list: undefined }),
89
+ {
90
+ title: 'Greeting Resource', // Display name for UI
91
+ description: 'Dynamic greeting generator'
92
+ },
93
+ async (uri, { name }) => ({
94
+ contents: [
95
+ {
96
+ uri: uri.href,
97
+ text: `Hello, ${name}!`
98
+ }
99
+ ]
100
+ })
91
101
  );
92
102
 
93
- // Start receiving messages on stdin and sending messages on stdout
94
- const transport = new StdioServerTransport();
95
- await server.connect(transport);
103
+ // Set up Express and HTTP transport
104
+ const app = express();
105
+ app.use(express.json());
106
+
107
+ app.post('/mcp', async (req, res) => {
108
+ // Create a new transport for each request to prevent request ID collisions
109
+ const transport = new StreamableHTTPServerTransport({
110
+ sessionIdGenerator: undefined,
111
+ enableJsonResponse: true
112
+ });
113
+
114
+ res.on('close', () => {
115
+ transport.close();
116
+ });
117
+
118
+ await server.connect(transport);
119
+ await transport.handleRequest(req, res, req.body);
120
+ });
121
+
122
+ const port = parseInt(process.env.PORT || '3000');
123
+ app.listen(port, () => {
124
+ console.log(`Demo MCP Server running on http://localhost:${port}/mcp`);
125
+ }).on('error', error => {
126
+ console.error('Server error:', error);
127
+ process.exit(1);
128
+ });
96
129
  ```
97
130
 
98
- ## What is MCP?
131
+ Install the deps with `npm install @modelcontextprotocol/sdk express zod@3`, and run with `npx -y tsx server.ts`.
132
+
133
+ You can connect to it using any MCP client that supports streamable http, such as:
99
134
 
100
- The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. Think of it like a web API, but specifically designed for LLM interactions. MCP servers can:
135
+ - [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector): `npx @modelcontextprotocol/inspector` and connect to the streamable HTTP URL `http://localhost:3000/mcp`
136
+ - [Claude Code](https://docs.claude.com/en/docs/claude-code/mcp): `claude mcp add --transport http my-server http://localhost:3000/mcp`
137
+ - [VS Code](https://code.visualstudio.com/docs/copilot/customization/mcp-servers): `code --add-mcp "{\"name\":\"my-server\",\"type\":\"http\",\"url\":\"http://localhost:3000/mcp\"}"`
138
+ - [Cursor](https://cursor.com/docs/context/mcp): Click [this deeplink](cursor://anysphere.cursor-deeplink/mcp/install?name=my-server&config=eyJ1cmwiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAvbWNwIn0%3D)
101
139
 
102
- - Expose data through **Resources** (think of these sort of like GET endpoints; they are used to load information into the LLM's context)
103
- - Provide functionality through **Tools** (sort of like POST endpoints; they are used to execute code or otherwise produce a side effect)
104
- - Define interaction patterns through **Prompts** (reusable templates for LLM interactions)
105
- - And more!
140
+ Then try asking your agent to add two numbers using its new tool!
106
141
 
107
142
  ## Core Concepts
108
143
 
@@ -112,212 +147,249 @@ The McpServer is your core interface to the MCP protocol. It handles connection
112
147
 
113
148
  ```typescript
114
149
  const server = new McpServer({
115
- name: "my-app",
116
- version: "1.0.0"
150
+ name: 'my-app',
151
+ version: '1.0.0'
117
152
  });
118
153
  ```
119
154
 
120
- ### Resources
155
+ ### Tools
121
156
 
122
- Resources are how you expose data to LLMs. They're similar to GET endpoints in a REST API - they provide data but shouldn't perform significant computation or have side effects:
157
+ [Tools](https://modelcontextprotocol.io/specification/latest/server/tools) let LLMs take actions through your server. Tools can perform computation, fetch data and have side effects. Tools should be designed to be model-controlled - i.e. AI models will decide which tools to call,
158
+ and the arguments.
123
159
 
124
160
  ```typescript
125
- // Static resource
126
- server.registerResource(
127
- "config",
128
- "config://app",
129
- {
130
- title: "Application Config",
131
- description: "Application configuration data",
132
- mimeType: "text/plain"
133
- },
134
- async (uri) => ({
135
- contents: [{
136
- uri: uri.href,
137
- text: "App configuration here"
138
- }]
139
- })
161
+ // Simple tool with parameters
162
+ server.registerTool(
163
+ 'calculate-bmi',
164
+ {
165
+ title: 'BMI Calculator',
166
+ description: 'Calculate Body Mass Index',
167
+ inputSchema: {
168
+ weightKg: z.number(),
169
+ heightM: z.number()
170
+ },
171
+ outputSchema: { bmi: z.number() }
172
+ },
173
+ async ({ weightKg, heightM }) => {
174
+ const output = { bmi: weightKg / (heightM * heightM) };
175
+ return {
176
+ content: [
177
+ {
178
+ type: 'text',
179
+ text: JSON.stringify(output)
180
+ }
181
+ ],
182
+ structuredContent: output
183
+ };
184
+ }
140
185
  );
141
186
 
142
- // Dynamic resource with parameters
143
- server.registerResource(
144
- "user-profile",
145
- new ResourceTemplate("users://{userId}/profile", { list: undefined }),
146
- {
147
- title: "User Profile",
148
- description: "User profile information"
149
- },
150
- async (uri, { userId }) => ({
151
- contents: [{
152
- uri: uri.href,
153
- text: `Profile data for user ${userId}`
154
- }]
155
- })
187
+ // Async tool with external API call
188
+ server.registerTool(
189
+ 'fetch-weather',
190
+ {
191
+ title: 'Weather Fetcher',
192
+ description: 'Get weather data for a city',
193
+ inputSchema: { city: z.string() },
194
+ outputSchema: { temperature: z.number(), conditions: z.string() }
195
+ },
196
+ async ({ city }) => {
197
+ const response = await fetch(`https://api.weather.com/${city}`);
198
+ const data = await response.json();
199
+ const output = { temperature: data.temp, conditions: data.conditions };
200
+ return {
201
+ content: [{ type: 'text', text: JSON.stringify(output) }],
202
+ structuredContent: output
203
+ };
204
+ }
156
205
  );
157
206
 
158
- // Resource with context-aware completion
159
- server.registerResource(
160
- "repository",
161
- new ResourceTemplate("github://repos/{owner}/{repo}", {
162
- list: undefined,
163
- complete: {
164
- // Provide intelligent completions based on previously resolved parameters
165
- repo: (value, context) => {
166
- if (context?.arguments?.["owner"] === "org1") {
167
- return ["project1", "project2", "project3"].filter(r => r.startsWith(value));
207
+ // Tool that returns ResourceLinks
208
+ server.registerTool(
209
+ 'list-files',
210
+ {
211
+ title: 'List Files',
212
+ description: 'List project files',
213
+ inputSchema: { pattern: z.string() },
214
+ outputSchema: {
215
+ count: z.number(),
216
+ files: z.array(z.object({ name: z.string(), uri: z.string() }))
168
217
  }
169
- return ["default-repo"].filter(r => r.startsWith(value));
170
- }
218
+ },
219
+ async ({ pattern }) => {
220
+ const output = {
221
+ count: 2,
222
+ files: [
223
+ { name: 'README.md', uri: 'file:///project/README.md' },
224
+ { name: 'index.ts', uri: 'file:///project/src/index.ts' }
225
+ ]
226
+ };
227
+ return {
228
+ content: [
229
+ { type: 'text', text: JSON.stringify(output) },
230
+ // ResourceLinks let tools return references without file content
231
+ {
232
+ type: 'resource_link',
233
+ uri: 'file:///project/README.md',
234
+ name: 'README.md',
235
+ mimeType: 'text/markdown',
236
+ description: 'A README file'
237
+ },
238
+ {
239
+ type: 'resource_link',
240
+ uri: 'file:///project/src/index.ts',
241
+ name: 'index.ts',
242
+ mimeType: 'text/typescript',
243
+ description: 'An index file'
244
+ }
245
+ ],
246
+ structuredContent: output
247
+ };
171
248
  }
172
- }),
173
- {
174
- title: "GitHub Repository",
175
- description: "Repository information"
176
- },
177
- async (uri, { owner, repo }) => ({
178
- contents: [{
179
- uri: uri.href,
180
- text: `Repository: ${owner}/${repo}`
181
- }]
182
- })
183
249
  );
184
250
  ```
185
251
 
186
- ### Tools
252
+ #### ResourceLinks
253
+
254
+ Tools can return `ResourceLink` objects to reference resources without embedding their full content. This can be helpful for performance when dealing with large files or many resources - clients can then selectively read only the resources they need using the provided URIs.
187
255
 
188
- Tools let LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects:
256
+ ### Resources
257
+
258
+ [Resources](https://modelcontextprotocol.io/specification/latest/server/resources) can also expose data to LLMs, but unlike tools shouldn't perform significant computation or have side effects.
259
+
260
+ Resources are designed to be used in an application-driven way, meaning MCP client applications can decide how to expose them. For example, a client could expose a resource picker to the human, or could expose them to the model directly.
189
261
 
190
262
  ```typescript
191
- // Simple tool with parameters
192
- server.registerTool(
193
- "calculate-bmi",
194
- {
195
- title: "BMI Calculator",
196
- description: "Calculate Body Mass Index",
197
- inputSchema: {
198
- weightKg: z.number(),
199
- heightM: z.number()
200
- }
201
- },
202
- async ({ weightKg, heightM }) => ({
203
- content: [{
204
- type: "text",
205
- text: String(weightKg / (heightM * heightM))
206
- }]
207
- })
263
+ // Static resource
264
+ server.registerResource(
265
+ 'config',
266
+ 'config://app',
267
+ {
268
+ title: 'Application Config',
269
+ description: 'Application configuration data',
270
+ mimeType: 'text/plain'
271
+ },
272
+ async uri => ({
273
+ contents: [
274
+ {
275
+ uri: uri.href,
276
+ text: 'App configuration here'
277
+ }
278
+ ]
279
+ })
208
280
  );
209
281
 
210
- // Async tool with external API call
211
- server.registerTool(
212
- "fetch-weather",
213
- {
214
- title: "Weather Fetcher",
215
- description: "Get weather data for a city",
216
- inputSchema: { city: z.string() }
217
- },
218
- async ({ city }) => {
219
- const response = await fetch(`https://api.weather.com/${city}`);
220
- const data = await response.text();
221
- return {
222
- content: [{ type: "text", text: data }]
223
- };
224
- }
282
+ // Dynamic resource with parameters
283
+ server.registerResource(
284
+ 'user-profile',
285
+ new ResourceTemplate('users://{userId}/profile', { list: undefined }),
286
+ {
287
+ title: 'User Profile',
288
+ description: 'User profile information'
289
+ },
290
+ async (uri, { userId }) => ({
291
+ contents: [
292
+ {
293
+ uri: uri.href,
294
+ text: `Profile data for user ${userId}`
295
+ }
296
+ ]
297
+ })
225
298
  );
226
299
 
227
- // Tool that returns ResourceLinks
228
- server.registerTool(
229
- "list-files",
230
- {
231
- title: "List Files",
232
- description: "List project files",
233
- inputSchema: { pattern: z.string() }
234
- },
235
- async ({ pattern }) => ({
236
- content: [
237
- { type: "text", text: `Found files matching "${pattern}":` },
238
- // ResourceLinks let tools return references without file content
239
- {
240
- type: "resource_link",
241
- uri: "file:///project/README.md",
242
- name: "README.md",
243
- mimeType: "text/markdown",
244
- description: 'A README file'
245
- },
246
- {
247
- type: "resource_link",
248
- uri: "file:///project/src/index.ts",
249
- name: "index.ts",
250
- mimeType: "text/typescript",
251
- description: 'An index file'
252
- }
253
- ]
254
- })
300
+ // Resource with context-aware completion
301
+ server.registerResource(
302
+ 'repository',
303
+ new ResourceTemplate('github://repos/{owner}/{repo}', {
304
+ list: undefined,
305
+ complete: {
306
+ // Provide intelligent completions based on previously resolved parameters
307
+ repo: (value, context) => {
308
+ if (context?.arguments?.['owner'] === 'org1') {
309
+ return ['project1', 'project2', 'project3'].filter(r => r.startsWith(value));
310
+ }
311
+ return ['default-repo'].filter(r => r.startsWith(value));
312
+ }
313
+ }
314
+ }),
315
+ {
316
+ title: 'GitHub Repository',
317
+ description: 'Repository information'
318
+ },
319
+ async (uri, { owner, repo }) => ({
320
+ contents: [
321
+ {
322
+ uri: uri.href,
323
+ text: `Repository: ${owner}/${repo}`
324
+ }
325
+ ]
326
+ })
255
327
  );
256
328
  ```
257
329
 
258
- #### ResourceLinks
259
-
260
- Tools can return `ResourceLink` objects to reference resources without embedding their full content. This is essential for performance when dealing with large files or many resources - clients can then selectively read only the resources they need using the provided URIs.
261
-
262
330
  ### Prompts
263
331
 
264
- Prompts are reusable templates that help LLMs interact with your server effectively:
332
+ [Prompts](https://modelcontextprotocol.io/specification/latest/server/prompts) are reusable templates that help humans prompt models to interact with your server. They're designed to be user-driven, and might appear as slash commands in a chat interface.
265
333
 
266
334
  ```typescript
267
- import { completable } from "@modelcontextprotocol/sdk/server/completable.js";
335
+ import { completable } from '@modelcontextprotocol/sdk/server/completable.js';
268
336
 
269
337
  server.registerPrompt(
270
- "review-code",
271
- {
272
- title: "Code Review",
273
- description: "Review code for best practices and potential issues",
274
- argsSchema: { code: z.string() }
275
- },
276
- ({ code }) => ({
277
- messages: [{
278
- role: "user",
279
- content: {
280
- type: "text",
281
- text: `Please review this code:\n\n${code}`
282
- }
283
- }]
284
- })
338
+ 'review-code',
339
+ {
340
+ title: 'Code Review',
341
+ description: 'Review code for best practices and potential issues',
342
+ argsSchema: { code: z.string() }
343
+ },
344
+ ({ code }) => ({
345
+ messages: [
346
+ {
347
+ role: 'user',
348
+ content: {
349
+ type: 'text',
350
+ text: `Please review this code:\n\n${code}`
351
+ }
352
+ }
353
+ ]
354
+ })
285
355
  );
286
356
 
287
357
  // Prompt with context-aware completion
288
358
  server.registerPrompt(
289
- "team-greeting",
290
- {
291
- title: "Team Greeting",
292
- description: "Generate a greeting for team members",
293
- argsSchema: {
294
- department: completable(z.string(), (value) => {
295
- // Department suggestions
296
- return ["engineering", "sales", "marketing", "support"].filter(d => d.startsWith(value));
297
- }),
298
- name: completable(z.string(), (value, context) => {
299
- // Name suggestions based on selected department
300
- const department = context?.arguments?.["department"];
301
- if (department === "engineering") {
302
- return ["Alice", "Bob", "Charlie"].filter(n => n.startsWith(value));
303
- } else if (department === "sales") {
304
- return ["David", "Eve", "Frank"].filter(n => n.startsWith(value));
305
- } else if (department === "marketing") {
306
- return ["Grace", "Henry", "Iris"].filter(n => n.startsWith(value));
359
+ 'team-greeting',
360
+ {
361
+ title: 'Team Greeting',
362
+ description: 'Generate a greeting for team members',
363
+ argsSchema: {
364
+ department: completable(z.string(), value => {
365
+ // Department suggestions
366
+ return ['engineering', 'sales', 'marketing', 'support'].filter(d => d.startsWith(value));
367
+ }),
368
+ name: completable(z.string(), (value, context) => {
369
+ // Name suggestions based on selected department
370
+ const department = context?.arguments?.['department'];
371
+ if (department === 'engineering') {
372
+ return ['Alice', 'Bob', 'Charlie'].filter(n => n.startsWith(value));
373
+ } else if (department === 'sales') {
374
+ return ['David', 'Eve', 'Frank'].filter(n => n.startsWith(value));
375
+ } else if (department === 'marketing') {
376
+ return ['Grace', 'Henry', 'Iris'].filter(n => n.startsWith(value));
377
+ }
378
+ return ['Guest'].filter(n => n.startsWith(value));
379
+ })
307
380
  }
308
- return ["Guest"].filter(n => n.startsWith(value));
309
- })
310
- }
311
- },
312
- ({ department, name }) => ({
313
- messages: [{
314
- role: "assistant",
315
- content: {
316
- type: "text",
317
- text: `Hello ${name}, welcome to the ${department} team!`
318
- }
319
- }]
320
- })
381
+ },
382
+ ({ department, name }) => ({
383
+ messages: [
384
+ {
385
+ role: 'assistant',
386
+ content: {
387
+ type: 'text',
388
+ text: `Hello ${name}, welcome to the ${department} team!`
389
+ }
390
+ }
391
+ ]
392
+ })
321
393
  );
322
394
  ```
323
395
 
@@ -330,32 +402,33 @@ MCP supports argument completions to help users fill in prompt arguments and res
330
402
  ```typescript
331
403
  // Request completions for any argument
332
404
  const result = await client.complete({
333
- ref: {
334
- type: "ref/prompt", // or "ref/resource"
335
- name: "example" // or uri: "template://..."
336
- },
337
- argument: {
338
- name: "argumentName",
339
- value: "partial" // What the user has typed so far
340
- },
341
- context: { // Optional: Include previously resolved arguments
342
- arguments: {
343
- previousArg: "value"
405
+ ref: {
406
+ type: 'ref/prompt', // or "ref/resource"
407
+ name: 'example' // or uri: "template://..."
408
+ },
409
+ argument: {
410
+ name: 'argumentName',
411
+ value: 'partial' // What the user has typed so far
412
+ },
413
+ context: {
414
+ // Optional: Include previously resolved arguments
415
+ arguments: {
416
+ previousArg: 'value'
417
+ }
344
418
  }
345
- }
346
419
  });
347
-
348
420
  ```
349
421
 
350
422
  ### Display Names and Metadata
351
423
 
352
- All resources, tools, and prompts support an optional `title` field for better UI presentation. The `title` is used as a display name, while `name` remains the unique identifier.
424
+ All resources, tools, and prompts support an optional `title` field for better UI presentation. The `title` is used as a display name (e.g. 'Create a new issue'), while `name` remains the unique identifier (e.g. `create_issue`).
353
425
 
354
426
  **Note:** The `register*` methods (`registerTool`, `registerPrompt`, `registerResource`) are the recommended approach for new code. The older methods (`tool`, `prompt`, `resource`) remain available for backwards compatibility.
355
427
 
356
428
  #### Title Precedence for Tools
357
429
 
358
430
  For tools specifically, there are two ways to specify a title:
431
+
359
432
  - `title` field in the tool configuration
360
433
  - `annotations.title` field (when using the older `tool()` method with annotations)
361
434
 
@@ -363,23 +436,32 @@ The precedence order is: `title` → `annotations.title` → `name`
363
436
 
364
437
  ```typescript
365
438
  // Using registerTool (recommended)
366
- server.registerTool("my_tool", {
367
- title: "My Tool", // This title takes precedence
368
- annotations: {
369
- title: "Annotation Title" // This is ignored if title is set
370
- }
371
- }, handler);
439
+ server.registerTool(
440
+ 'my_tool',
441
+ {
442
+ title: 'My Tool', // This title takes precedence
443
+ annotations: {
444
+ title: 'Annotation Title' // This is ignored if title is set
445
+ }
446
+ },
447
+ handler
448
+ );
372
449
 
373
450
  // Using tool with annotations (older API)
374
- server.tool("my_tool", "description", {
375
- title: "Annotation Title" // This is used as title
376
- }, handler);
451
+ server.tool(
452
+ 'my_tool',
453
+ 'description',
454
+ {
455
+ title: 'Annotation Title' // This is used as title
456
+ },
457
+ handler
458
+ );
377
459
  ```
378
460
 
379
461
  When building clients, use the provided utility to get the appropriate display name:
380
462
 
381
463
  ```typescript
382
- import { getDisplayName } from "@modelcontextprotocol/sdk/shared/metadataUtils.js";
464
+ import { getDisplayName } from '@modelcontextprotocol/sdk/shared/metadataUtils.js';
383
465
 
384
466
  // Automatically handles the precedence: title → annotations.title → name
385
467
  const displayName = getDisplayName(tool);
@@ -390,102 +472,174 @@ const displayName = getDisplayName(tool);
390
472
  MCP servers can request LLM completions from connected clients that support sampling.
391
473
 
392
474
  ```typescript
393
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
394
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
395
- import { z } from "zod";
475
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
476
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
477
+ import express from 'express';
478
+ import { z } from 'zod';
396
479
 
397
480
  const mcpServer = new McpServer({
398
- name: "tools-with-sample-server",
399
- version: "1.0.0",
481
+ name: 'tools-with-sample-server',
482
+ version: '1.0.0'
400
483
  });
401
484
 
402
485
  // Tool that uses LLM sampling to summarize any text
403
486
  mcpServer.registerTool(
404
- "summarize",
405
- {
406
- description: "Summarize any text using an LLM",
407
- inputSchema: {
408
- text: z.string().describe("Text to summarize"),
409
- },
410
- },
411
- async ({ text }) => {
412
- // Call the LLM through MCP sampling
413
- const response = await mcpServer.server.createMessage({
414
- messages: [
415
- {
416
- role: "user",
417
- content: {
418
- type: "text",
419
- text: `Please summarize the following text concisely:\n\n${text}`,
420
- },
487
+ 'summarize',
488
+ {
489
+ title: 'Text Summarizer',
490
+ description: 'Summarize any text using an LLM',
491
+ inputSchema: {
492
+ text: z.string().describe('Text to summarize')
421
493
  },
422
- ],
423
- maxTokens: 500,
494
+ outputSchema: { summary: z.string() }
495
+ },
496
+ async ({ text }) => {
497
+ // Call the LLM through MCP sampling
498
+ const response = await mcpServer.server.createMessage({
499
+ messages: [
500
+ {
501
+ role: 'user',
502
+ content: {
503
+ type: 'text',
504
+ text: `Please summarize the following text concisely:\n\n${text}`
505
+ }
506
+ }
507
+ ],
508
+ maxTokens: 500
509
+ });
510
+
511
+ const summary = response.content.type === 'text' ? response.content.text : 'Unable to generate summary';
512
+ const output = { summary };
513
+ return {
514
+ content: [{ type: 'text', text: JSON.stringify(output) }],
515
+ structuredContent: output
516
+ };
517
+ }
518
+ );
519
+
520
+ const app = express();
521
+ app.use(express.json());
522
+
523
+ app.post('/mcp', async (req, res) => {
524
+ const transport = new StreamableHTTPServerTransport({
525
+ sessionIdGenerator: undefined,
526
+ enableJsonResponse: true
424
527
  });
425
528
 
426
- return {
427
- content: [
428
- {
429
- type: "text",
430
- text: response.content.type === "text" ? response.content.text : "Unable to generate summary",
431
- },
432
- ],
433
- };
434
- }
435
- );
529
+ res.on('close', () => {
530
+ transport.close();
531
+ });
436
532
 
437
- async function main() {
438
- const transport = new StdioServerTransport();
439
- await mcpServer.connect(transport);
440
- console.error("MCP server is running...");
441
- }
533
+ await mcpServer.connect(transport);
534
+ await transport.handleRequest(req, res, req.body);
535
+ });
442
536
 
443
- main().catch((error) => {
444
- console.error("Server error:", error);
445
- process.exit(1);
537
+ const port = parseInt(process.env.PORT || '3000');
538
+ app.listen(port, () => {
539
+ console.log(`MCP Server running on http://localhost:${port}/mcp`);
540
+ }).on('error', error => {
541
+ console.error('Server error:', error);
542
+ process.exit(1);
446
543
  });
447
544
  ```
448
545
 
449
-
450
546
  ## Running Your Server
451
547
 
452
548
  MCP servers in TypeScript need to be connected to a transport to communicate with clients. How you start the server depends on the choice of transport:
453
549
 
454
- ### stdio
550
+ ### Streamable HTTP
551
+
552
+ For remote servers, use the Streamable HTTP transport.
553
+
554
+ #### Without Session Management (Recommended)
455
555
 
456
- For command-line tools and direct integrations:
556
+ For most use cases where session management isn't needed:
457
557
 
458
558
  ```typescript
459
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
460
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
559
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
560
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
561
+ import express from 'express';
562
+ import { z } from 'zod';
563
+
564
+ const app = express();
565
+ app.use(express.json());
461
566
 
567
+ // Create the MCP server once (can be reused across requests)
462
568
  const server = new McpServer({
463
- name: "example-server",
464
- version: "1.0.0"
569
+ name: 'example-server',
570
+ version: '1.0.0'
465
571
  });
466
572
 
467
- // ... set up server resources, tools, and prompts ...
573
+ // Set up your tools, resources, and prompts
574
+ server.registerTool(
575
+ 'echo',
576
+ {
577
+ title: 'Echo Tool',
578
+ description: 'Echoes back the provided message',
579
+ inputSchema: { message: z.string() },
580
+ outputSchema: { echo: z.string() }
581
+ },
582
+ async ({ message }) => {
583
+ const output = { echo: `Tool echo: ${message}` };
584
+ return {
585
+ content: [{ type: 'text', text: JSON.stringify(output) }],
586
+ structuredContent: output
587
+ };
588
+ }
589
+ );
468
590
 
469
- const transport = new StdioServerTransport();
470
- await server.connect(transport);
471
- ```
591
+ app.post('/mcp', async (req, res) => {
592
+ // In stateless mode, create a new transport for each request to prevent
593
+ // request ID collisions. Different clients may use the same JSON-RPC request IDs,
594
+ // which would cause responses to be routed to the wrong HTTP connections if
595
+ // the transport state is shared.
472
596
 
473
- ### Streamable HTTP
597
+ try {
598
+ const transport = new StreamableHTTPServerTransport({
599
+ sessionIdGenerator: undefined,
600
+ enableJsonResponse: true
601
+ });
602
+
603
+ res.on('close', () => {
604
+ transport.close();
605
+ });
606
+
607
+ await server.connect(transport);
608
+ await transport.handleRequest(req, res, req.body);
609
+ } catch (error) {
610
+ console.error('Error handling MCP request:', error);
611
+ if (!res.headersSent) {
612
+ res.status(500).json({
613
+ jsonrpc: '2.0',
614
+ error: {
615
+ code: -32603,
616
+ message: 'Internal server error'
617
+ },
618
+ id: null
619
+ });
620
+ }
621
+ }
622
+ });
474
623
 
475
- For remote servers, set up a Streamable HTTP transport that handles both client requests and server-to-client notifications.
624
+ const port = parseInt(process.env.PORT || '3000');
625
+ app.listen(port, () => {
626
+ console.log(`MCP Server running on http://localhost:${port}/mcp`);
627
+ }).on('error', error => {
628
+ console.error('Server error:', error);
629
+ process.exit(1);
630
+ });
631
+ ```
476
632
 
477
633
  #### With Session Management
478
634
 
479
- In some cases, servers need to be stateful. This is achieved by [session management](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#session-management).
635
+ In some cases, servers need stateful sessions. This can be achieved by [session management](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#session-management) in the MCP protocol.
480
636
 
481
637
  ```typescript
482
- import express from "express";
483
- import { randomUUID } from "node:crypto";
484
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
485
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
486
- import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js"
487
-
488
-
638
+ import express from 'express';
639
+ import { randomUUID } from 'node:crypto';
640
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
641
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
642
+ import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
489
643
 
490
644
  const app = express();
491
645
  app.use(express.json());
@@ -495,69 +649,69 @@ const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
495
649
 
496
650
  // Handle POST requests for client-to-server communication
497
651
  app.post('/mcp', async (req, res) => {
498
- // Check for existing session ID
499
- const sessionId = req.headers['mcp-session-id'] as string | undefined;
500
- let transport: StreamableHTTPServerTransport;
501
-
502
- if (sessionId && transports[sessionId]) {
503
- // Reuse existing transport
504
- transport = transports[sessionId];
505
- } else if (!sessionId && isInitializeRequest(req.body)) {
506
- // New initialization request
507
- transport = new StreamableHTTPServerTransport({
508
- sessionIdGenerator: () => randomUUID(),
509
- onsessioninitialized: (sessionId) => {
510
- // Store the transport by session ID
511
- transports[sessionId] = transport;
512
- },
513
- // DNS rebinding protection is disabled by default for backwards compatibility. If you are running this server
514
- // locally, make sure to set:
515
- // enableDnsRebindingProtection: true,
516
- // allowedHosts: ['127.0.0.1'],
517
- });
518
-
519
- // Clean up transport when closed
520
- transport.onclose = () => {
521
- if (transport.sessionId) {
522
- delete transports[transport.sessionId];
523
- }
524
- };
525
- const server = new McpServer({
526
- name: "example-server",
527
- version: "1.0.0"
528
- });
652
+ // Check for existing session ID
653
+ const sessionId = req.headers['mcp-session-id'] as string | undefined;
654
+ let transport: StreamableHTTPServerTransport;
655
+
656
+ if (sessionId && transports[sessionId]) {
657
+ // Reuse existing transport
658
+ transport = transports[sessionId];
659
+ } else if (!sessionId && isInitializeRequest(req.body)) {
660
+ // New initialization request
661
+ transport = new StreamableHTTPServerTransport({
662
+ sessionIdGenerator: () => randomUUID(),
663
+ onsessioninitialized: sessionId => {
664
+ // Store the transport by session ID
665
+ transports[sessionId] = transport;
666
+ }
667
+ // DNS rebinding protection is disabled by default for backwards compatibility. If you are running this server
668
+ // locally, make sure to set:
669
+ // enableDnsRebindingProtection: true,
670
+ // allowedHosts: ['127.0.0.1'],
671
+ });
672
+
673
+ // Clean up transport when closed
674
+ transport.onclose = () => {
675
+ if (transport.sessionId) {
676
+ delete transports[transport.sessionId];
677
+ }
678
+ };
679
+ const server = new McpServer({
680
+ name: 'example-server',
681
+ version: '1.0.0'
682
+ });
529
683
 
530
- // ... set up server resources, tools, and prompts ...
684
+ // ... set up server resources, tools, and prompts ...
531
685
 
532
- // Connect to the MCP server
533
- await server.connect(transport);
534
- } else {
535
- // Invalid request
536
- res.status(400).json({
537
- jsonrpc: '2.0',
538
- error: {
539
- code: -32000,
540
- message: 'Bad Request: No valid session ID provided',
541
- },
542
- id: null,
543
- });
544
- return;
545
- }
686
+ // Connect to the MCP server
687
+ await server.connect(transport);
688
+ } else {
689
+ // Invalid request
690
+ res.status(400).json({
691
+ jsonrpc: '2.0',
692
+ error: {
693
+ code: -32000,
694
+ message: 'Bad Request: No valid session ID provided'
695
+ },
696
+ id: null
697
+ });
698
+ return;
699
+ }
546
700
 
547
- // Handle the request
548
- await transport.handleRequest(req, res, req.body);
701
+ // Handle the request
702
+ await transport.handleRequest(req, res, req.body);
549
703
  });
550
704
 
551
705
  // Reusable handler for GET and DELETE requests
552
706
  const handleSessionRequest = async (req: express.Request, res: express.Response) => {
553
- const sessionId = req.headers['mcp-session-id'] as string | undefined;
554
- if (!sessionId || !transports[sessionId]) {
555
- res.status(400).send('Invalid or missing session ID');
556
- return;
557
- }
558
-
559
- const transport = transports[sessionId];
560
- await transport.handleRequest(req, res);
707
+ const sessionId = req.headers['mcp-session-id'] as string | undefined;
708
+ if (!sessionId || !transports[sessionId]) {
709
+ res.status(400).send('Invalid or missing session ID');
710
+ return;
711
+ }
712
+
713
+ const transport = transports[sessionId];
714
+ await transport.handleRequest(req, res);
561
715
  };
562
716
 
563
717
  // Handle GET requests for server-to-client notifications via SSE
@@ -569,10 +723,6 @@ app.delete('/mcp', handleSessionRequest);
569
723
  app.listen(3000);
570
724
  ```
571
725
 
572
- > [!TIP]
573
- > When using this in a remote environment, make sure to allow the header parameter `mcp-session-id` in CORS. Otherwise, it may result in a `Bad Request: No valid session ID provided` error. Read the following section for examples.
574
-
575
-
576
726
  #### CORS Configuration for Browser-Based Clients
577
727
 
578
728
  If you'd like your server to be accessible by browser-based MCP clients, you'll need to configure CORS headers. The `Mcp-Session-Id` header must be exposed for browser clients to access it:
@@ -581,109 +731,22 @@ If you'd like your server to be accessible by browser-based MCP clients, you'll
581
731
  import cors from 'cors';
582
732
 
583
733
  // Add CORS middleware before your MCP routes
584
- app.use(cors({
585
- origin: '*', // Configure appropriately for production, for example:
586
- // origin: ['https://your-remote-domain.com', 'https://your-other-remote-domain.com'],
587
- exposedHeaders: ['Mcp-Session-Id'],
588
- allowedHeaders: ['Content-Type', 'mcp-session-id'],
589
- }));
734
+ app.use(
735
+ cors({
736
+ origin: '*', // Configure appropriately for production, for example:
737
+ // origin: ['https://your-remote-domain.com', 'https://your-other-remote-domain.com'],
738
+ exposedHeaders: ['Mcp-Session-Id'],
739
+ allowedHeaders: ['Content-Type', 'mcp-session-id']
740
+ })
741
+ );
590
742
  ```
591
743
 
592
744
  This configuration is necessary because:
745
+
593
746
  - The MCP streamable HTTP transport uses the `Mcp-Session-Id` header for session management
594
747
  - Browsers restrict access to response headers unless explicitly exposed via CORS
595
748
  - Without this configuration, browser-based clients won't be able to read the session ID from initialization responses
596
749
 
597
- #### Without Session Management (Stateless)
598
-
599
- For simpler use cases where session management isn't needed:
600
-
601
- ```typescript
602
- const app = express();
603
- app.use(express.json());
604
-
605
- app.post('/mcp', async (req: Request, res: Response) => {
606
- // In stateless mode, create a new instance of transport and server for each request
607
- // to ensure complete isolation. A single instance would cause request ID collisions
608
- // when multiple clients connect concurrently.
609
-
610
- try {
611
- const server = getServer();
612
- const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({
613
- sessionIdGenerator: undefined,
614
- });
615
- res.on('close', () => {
616
- console.log('Request closed');
617
- transport.close();
618
- server.close();
619
- });
620
- await server.connect(transport);
621
- await transport.handleRequest(req, res, req.body);
622
- } catch (error) {
623
- console.error('Error handling MCP request:', error);
624
- if (!res.headersSent) {
625
- res.status(500).json({
626
- jsonrpc: '2.0',
627
- error: {
628
- code: -32603,
629
- message: 'Internal server error',
630
- },
631
- id: null,
632
- });
633
- }
634
- }
635
- });
636
-
637
- // SSE notifications not supported in stateless mode
638
- app.get('/mcp', async (req: Request, res: Response) => {
639
- console.log('Received GET MCP request');
640
- res.writeHead(405).end(JSON.stringify({
641
- jsonrpc: "2.0",
642
- error: {
643
- code: -32000,
644
- message: "Method not allowed."
645
- },
646
- id: null
647
- }));
648
- });
649
-
650
- // Session termination not needed in stateless mode
651
- app.delete('/mcp', async (req: Request, res: Response) => {
652
- console.log('Received DELETE MCP request');
653
- res.writeHead(405).end(JSON.stringify({
654
- jsonrpc: "2.0",
655
- error: {
656
- code: -32000,
657
- message: "Method not allowed."
658
- },
659
- id: null
660
- }));
661
- });
662
-
663
-
664
- // Start the server
665
- const PORT = 3000;
666
- setupServer().then(() => {
667
- app.listen(PORT, (error) => {
668
- if (error) {
669
- console.error('Failed to start server:', error);
670
- process.exit(1);
671
- }
672
- console.log(`MCP Stateless Streamable HTTP Server listening on port ${PORT}`);
673
- });
674
- }).catch(error => {
675
- console.error('Failed to set up the server:', error);
676
- process.exit(1);
677
- });
678
-
679
- ```
680
-
681
- This stateless approach is useful for:
682
-
683
- - Simple API wrappers
684
- - RESTful scenarios where each request is independent
685
- - Horizontally scaled deployments without shared session state
686
-
687
750
  #### DNS Rebinding Protection
688
751
 
689
752
  The Streamable HTTP transport includes DNS rebinding protection to prevent security vulnerabilities. By default, this protection is **disabled** for backwards compatibility.
@@ -700,6 +763,25 @@ const transport = new StreamableHTTPServerTransport({
700
763
  });
701
764
  ```
702
765
 
766
+ ### stdio
767
+
768
+ For local integrations spawned by another process, you can use the stdio transport:
769
+
770
+ ```typescript
771
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
772
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
773
+
774
+ const server = new McpServer({
775
+ name: 'example-server',
776
+ version: '1.0.0'
777
+ });
778
+
779
+ // ... set up server resources, tools, and prompts ...
780
+
781
+ const transport = new StdioServerTransport();
782
+ await server.connect(transport);
783
+ ```
784
+
703
785
  ### Testing and Debugging
704
786
 
705
787
  To test your server, you can use the [MCP Inspector](https://github.com/modelcontextprotocol/inspector). See its README for more information.
@@ -711,57 +793,66 @@ To test your server, you can use the [MCP Inspector](https://github.com/modelcon
711
793
  A simple server demonstrating resources, tools, and prompts:
712
794
 
713
795
  ```typescript
714
- import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
715
- import { z } from "zod";
796
+ import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
797
+ import { z } from 'zod';
716
798
 
717
799
  const server = new McpServer({
718
- name: "echo-server",
719
- version: "1.0.0"
800
+ name: 'echo-server',
801
+ version: '1.0.0'
720
802
  });
721
803
 
722
- server.registerResource(
723
- "echo",
724
- new ResourceTemplate("echo://{message}", { list: undefined }),
725
- {
726
- title: "Echo Resource",
727
- description: "Echoes back messages as resources"
728
- },
729
- async (uri, { message }) => ({
730
- contents: [{
731
- uri: uri.href,
732
- text: `Resource echo: ${message}`
733
- }]
734
- })
804
+ server.registerTool(
805
+ 'echo',
806
+ {
807
+ title: 'Echo Tool',
808
+ description: 'Echoes back the provided message',
809
+ inputSchema: { message: z.string() },
810
+ outputSchema: { echo: z.string() }
811
+ },
812
+ async ({ message }) => {
813
+ const output = { echo: `Tool echo: ${message}` };
814
+ return {
815
+ content: [{ type: 'text', text: JSON.stringify(output) }],
816
+ structuredContent: output
817
+ };
818
+ }
735
819
  );
736
820
 
737
- server.registerTool(
738
- "echo",
739
- {
740
- title: "Echo Tool",
741
- description: "Echoes back the provided message",
742
- inputSchema: { message: z.string() }
743
- },
744
- async ({ message }) => ({
745
- content: [{ type: "text", text: `Tool echo: ${message}` }]
746
- })
821
+ server.registerResource(
822
+ 'echo',
823
+ new ResourceTemplate('echo://{message}', { list: undefined }),
824
+ {
825
+ title: 'Echo Resource',
826
+ description: 'Echoes back messages as resources'
827
+ },
828
+ async (uri, { message }) => ({
829
+ contents: [
830
+ {
831
+ uri: uri.href,
832
+ text: `Resource echo: ${message}`
833
+ }
834
+ ]
835
+ })
747
836
  );
748
837
 
749
838
  server.registerPrompt(
750
- "echo",
751
- {
752
- title: "Echo Prompt",
753
- description: "Creates a prompt to process a message",
754
- argsSchema: { message: z.string() }
755
- },
756
- ({ message }) => ({
757
- messages: [{
758
- role: "user",
759
- content: {
760
- type: "text",
761
- text: `Please process this message: ${message}`
762
- }
763
- }]
764
- })
839
+ 'echo',
840
+ {
841
+ title: 'Echo Prompt',
842
+ description: 'Creates a prompt to process a message',
843
+ argsSchema: { message: z.string() }
844
+ },
845
+ ({ message }) => ({
846
+ messages: [
847
+ {
848
+ role: 'user',
849
+ content: {
850
+ type: 'text',
851
+ text: `Please process this message: ${message}`
852
+ }
853
+ }
854
+ ]
855
+ })
765
856
  );
766
857
  ```
767
858
 
@@ -770,81 +861,91 @@ server.registerPrompt(
770
861
  A more complex example showing database integration:
771
862
 
772
863
  ```typescript
773
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
774
- import sqlite3 from "sqlite3";
775
- import { promisify } from "util";
776
- import { z } from "zod";
864
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
865
+ import sqlite3 from 'sqlite3';
866
+ import { promisify } from 'util';
867
+ import { z } from 'zod';
777
868
 
778
869
  const server = new McpServer({
779
- name: "sqlite-explorer",
780
- version: "1.0.0"
870
+ name: 'sqlite-explorer',
871
+ version: '1.0.0'
781
872
  });
782
873
 
783
874
  // Helper to create DB connection
784
875
  const getDb = () => {
785
- const db = new sqlite3.Database("database.db");
786
- return {
787
- all: promisify<string, any[]>(db.all.bind(db)),
788
- close: promisify(db.close.bind(db))
789
- };
876
+ const db = new sqlite3.Database('database.db');
877
+ return {
878
+ all: promisify<string, any[]>(db.all.bind(db)),
879
+ close: promisify(db.close.bind(db))
880
+ };
790
881
  };
791
882
 
792
883
  server.registerResource(
793
- "schema",
794
- "schema://main",
795
- {
796
- title: "Database Schema",
797
- description: "SQLite database schema",
798
- mimeType: "text/plain"
799
- },
800
- async (uri) => {
801
- const db = getDb();
802
- try {
803
- const tables = await db.all(
804
- "SELECT sql FROM sqlite_master WHERE type='table'"
805
- );
806
- return {
807
- contents: [{
808
- uri: uri.href,
809
- text: tables.map((t: {sql: string}) => t.sql).join("\n")
810
- }]
811
- };
812
- } finally {
813
- await db.close();
884
+ 'schema',
885
+ 'schema://main',
886
+ {
887
+ title: 'Database Schema',
888
+ description: 'SQLite database schema',
889
+ mimeType: 'text/plain'
890
+ },
891
+ async uri => {
892
+ const db = getDb();
893
+ try {
894
+ const tables = await db.all("SELECT sql FROM sqlite_master WHERE type='table'");
895
+ return {
896
+ contents: [
897
+ {
898
+ uri: uri.href,
899
+ text: tables.map((t: { sql: string }) => t.sql).join('\n')
900
+ }
901
+ ]
902
+ };
903
+ } finally {
904
+ await db.close();
905
+ }
814
906
  }
815
- }
816
907
  );
817
908
 
818
909
  server.registerTool(
819
- "query",
820
- {
821
- title: "SQL Query",
822
- description: "Execute SQL queries on the database",
823
- inputSchema: { sql: z.string() }
824
- },
825
- async ({ sql }) => {
826
- const db = getDb();
827
- try {
828
- const results = await db.all(sql);
829
- return {
830
- content: [{
831
- type: "text",
832
- text: JSON.stringify(results, null, 2)
833
- }]
834
- };
835
- } catch (err: unknown) {
836
- const error = err as Error;
837
- return {
838
- content: [{
839
- type: "text",
840
- text: `Error: ${error.message}`
841
- }],
842
- isError: true
843
- };
844
- } finally {
845
- await db.close();
910
+ 'query',
911
+ {
912
+ title: 'SQL Query',
913
+ description: 'Execute SQL queries on the database',
914
+ inputSchema: { sql: z.string() },
915
+ outputSchema: {
916
+ rows: z.array(z.record(z.any())),
917
+ rowCount: z.number()
918
+ }
919
+ },
920
+ async ({ sql }) => {
921
+ const db = getDb();
922
+ try {
923
+ const results = await db.all(sql);
924
+ const output = { rows: results, rowCount: results.length };
925
+ return {
926
+ content: [
927
+ {
928
+ type: 'text',
929
+ text: JSON.stringify(output, null, 2)
930
+ }
931
+ ],
932
+ structuredContent: output
933
+ };
934
+ } catch (err: unknown) {
935
+ const error = err as Error;
936
+ return {
937
+ content: [
938
+ {
939
+ type: 'text',
940
+ text: `Error: ${error.message}`
941
+ }
942
+ ],
943
+ isError: true
944
+ };
945
+ } finally {
946
+ await db.close();
947
+ }
846
948
  }
847
- }
848
949
  );
849
950
  ```
850
951
 
@@ -854,62 +955,122 @@ server.registerTool(
854
955
 
855
956
  If you want to offer an initial set of tools/prompts/resources, but later add additional ones based on user action or external state change, you can add/update/remove them _after_ the Server is connected. This will automatically emit the corresponding `listChanged` notifications:
856
957
 
857
- ```ts
858
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
859
- import { z } from "zod";
958
+ ```typescript
959
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
960
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
961
+ import express from 'express';
962
+ import { z } from 'zod';
860
963
 
861
964
  const server = new McpServer({
862
- name: "Dynamic Example",
863
- version: "1.0.0"
965
+ name: 'Dynamic Example',
966
+ version: '1.0.0'
864
967
  });
865
968
 
866
- const listMessageTool = server.tool(
867
- "listMessages",
868
- { channel: z.string() },
869
- async ({ channel }) => ({
870
- content: [{ type: "text", text: await listMessages(channel) }]
871
- })
969
+ const listMessageTool = server.registerTool(
970
+ 'listMessages',
971
+ {
972
+ title: 'List Messages',
973
+ description: 'List messages in a channel',
974
+ inputSchema: { channel: z.string() },
975
+ outputSchema: { messages: z.array(z.string()) }
976
+ },
977
+ async ({ channel }) => {
978
+ const messages = await listMessages(channel);
979
+ const output = { messages };
980
+ return {
981
+ content: [{ type: 'text', text: JSON.stringify(output) }],
982
+ structuredContent: output
983
+ };
984
+ }
872
985
  );
873
986
 
874
- const putMessageTool = server.tool(
875
- "putMessage",
876
- { channel: z.string(), message: z.string() },
877
- async ({ channel, message }) => ({
878
- content: [{ type: "text", text: await putMessage(channel, message) }]
879
- })
987
+ const putMessageTool = server.registerTool(
988
+ 'putMessage',
989
+ {
990
+ title: 'Put Message',
991
+ description: 'Send a message to a channel',
992
+ inputSchema: { channel: z.string(), message: z.string() },
993
+ outputSchema: { success: z.boolean() }
994
+ },
995
+ async ({ channel, message }) => {
996
+ await putMessage(channel, message);
997
+ const output = { success: true };
998
+ return {
999
+ content: [{ type: 'text', text: JSON.stringify(output) }],
1000
+ structuredContent: output
1001
+ };
1002
+ }
880
1003
  );
881
1004
  // Until we upgrade auth, `putMessage` is disabled (won't show up in listTools)
882
- putMessageTool.disable()
883
-
884
- const upgradeAuthTool = server.tool(
885
- "upgradeAuth",
886
- { permission: z.enum(["write", "admin"])},
887
- // Any mutations here will automatically emit `listChanged` notifications
888
- async ({ permission }) => {
889
- const { ok, err, previous } = await upgradeAuthAndStoreToken(permission)
890
- if (!ok) return {content: [{ type: "text", text: `Error: ${err}` }]}
891
-
892
- // If we previously had read-only access, 'putMessage' is now available
893
- if (previous === "read") {
894
- putMessageTool.enable()
895
- }
1005
+ putMessageTool.disable();
1006
+
1007
+ const upgradeAuthTool = server.registerTool(
1008
+ 'upgradeAuth',
1009
+ {
1010
+ title: 'Upgrade Authorization',
1011
+ description: 'Upgrade user authorization level',
1012
+ inputSchema: { permission: z.enum(['write', 'admin']) },
1013
+ outputSchema: {
1014
+ success: z.boolean(),
1015
+ newPermission: z.string()
1016
+ }
1017
+ },
1018
+ // Any mutations here will automatically emit `listChanged` notifications
1019
+ async ({ permission }) => {
1020
+ const { ok, err, previous } = await upgradeAuthAndStoreToken(permission);
1021
+ if (!ok) {
1022
+ return {
1023
+ content: [{ type: 'text', text: `Error: ${err}` }],
1024
+ isError: true
1025
+ };
1026
+ }
896
1027
 
897
- if (permission === 'write') {
898
- // If we've just upgraded to 'write' permissions, we can still call 'upgradeAuth'
899
- // but can only upgrade to 'admin'.
900
- upgradeAuthTool.update({
901
- paramsSchema: { permission: z.enum(["admin"]) }, // change validation rules
902
- })
903
- } else {
904
- // If we're now an admin, we no longer have anywhere to upgrade to, so fully remove that tool
905
- upgradeAuthTool.remove()
1028
+ // If we previously had read-only access, 'putMessage' is now available
1029
+ if (previous === 'read') {
1030
+ putMessageTool.enable();
1031
+ }
1032
+
1033
+ if (permission === 'write') {
1034
+ // If we've just upgraded to 'write' permissions, we can still call 'upgradeAuth'
1035
+ // but can only upgrade to 'admin'.
1036
+ upgradeAuthTool.update({
1037
+ paramsSchema: { permission: z.enum(['admin']) } // change validation rules
1038
+ });
1039
+ } else {
1040
+ // If we're now an admin, we no longer have anywhere to upgrade to, so fully remove that tool
1041
+ upgradeAuthTool.remove();
1042
+ }
1043
+
1044
+ const output = { success: true, newPermission: permission };
1045
+ return {
1046
+ content: [{ type: 'text', text: JSON.stringify(output) }],
1047
+ structuredContent: output
1048
+ };
906
1049
  }
907
- }
908
- )
1050
+ );
909
1051
 
910
- // Connect as normal
911
- const transport = new StdioServerTransport();
912
- await server.connect(transport);
1052
+ // Connect with HTTP transport
1053
+ const app = express();
1054
+ app.use(express.json());
1055
+
1056
+ app.post('/mcp', async (req, res) => {
1057
+ const transport = new StreamableHTTPServerTransport({
1058
+ sessionIdGenerator: undefined,
1059
+ enableJsonResponse: true
1060
+ });
1061
+
1062
+ res.on('close', () => {
1063
+ transport.close();
1064
+ });
1065
+
1066
+ await server.connect(transport);
1067
+ await transport.handleRequest(req, res, req.body);
1068
+ });
1069
+
1070
+ const port = parseInt(process.env.PORT || '3000');
1071
+ app.listen(port, () => {
1072
+ console.log(`MCP Server running on http://localhost:${port}/mcp`);
1073
+ });
913
1074
  ```
914
1075
 
915
1076
  ### Improving Network Efficiency with Notification Debouncing
@@ -918,8 +1079,8 @@ When performing bulk updates that trigger notifications (e.g., enabling or disab
918
1079
 
919
1080
  This feature coalesces multiple, rapid calls for the same notification type into a single message. For example, if you disable five tools in a row, only one `notifications/tools/list_changed` message will be sent instead of five.
920
1081
 
921
- > [!IMPORTANT]
922
- > This feature is designed for "simple" notifications that do not carry unique data in their parameters. To prevent silent data loss, debouncing is **automatically bypassed** for any notification that contains a `params` object or a `relatedRequestId`. Such notifications will always be sent immediately.
1082
+ > [!IMPORTANT] This feature is designed for "simple" notifications that do not carry unique data in their parameters. To prevent silent data loss, debouncing is **automatically bypassed** for any notification that contains a `params` object or a `relatedRequestId`. Such
1083
+ > notifications will always be sent immediately.
923
1084
 
924
1085
  This is an opt-in feature configured during server initialization.
925
1086
 
@@ -954,53 +1115,56 @@ server.registerTool("tool3", ...).disable();
954
1115
  For more control, you can use the low-level Server class directly:
955
1116
 
956
1117
  ```typescript
957
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
958
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
959
- import {
960
- ListPromptsRequestSchema,
961
- GetPromptRequestSchema
962
- } from "@modelcontextprotocol/sdk/types.js";
1118
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
1119
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
1120
+ import { ListPromptsRequestSchema, GetPromptRequestSchema } from '@modelcontextprotocol/sdk/types.js';
963
1121
 
964
1122
  const server = new Server(
965
- {
966
- name: "example-server",
967
- version: "1.0.0"
968
- },
969
- {
970
- capabilities: {
971
- prompts: {}
1123
+ {
1124
+ name: 'example-server',
1125
+ version: '1.0.0'
1126
+ },
1127
+ {
1128
+ capabilities: {
1129
+ prompts: {}
1130
+ }
972
1131
  }
973
- }
974
1132
  );
975
1133
 
976
1134
  server.setRequestHandler(ListPromptsRequestSchema, async () => {
977
- return {
978
- prompts: [{
979
- name: "example-prompt",
980
- description: "An example prompt template",
981
- arguments: [{
982
- name: "arg1",
983
- description: "Example argument",
984
- required: true
985
- }]
986
- }]
987
- };
1135
+ return {
1136
+ prompts: [
1137
+ {
1138
+ name: 'example-prompt',
1139
+ description: 'An example prompt template',
1140
+ arguments: [
1141
+ {
1142
+ name: 'arg1',
1143
+ description: 'Example argument',
1144
+ required: true
1145
+ }
1146
+ ]
1147
+ }
1148
+ ]
1149
+ };
988
1150
  });
989
1151
 
990
- server.setRequestHandler(GetPromptRequestSchema, async (request) => {
991
- if (request.params.name !== "example-prompt") {
992
- throw new Error("Unknown prompt");
993
- }
994
- return {
995
- description: "Example prompt",
996
- messages: [{
997
- role: "user",
998
- content: {
999
- type: "text",
1000
- text: "Example prompt text"
1001
- }
1002
- }]
1003
- };
1152
+ server.setRequestHandler(GetPromptRequestSchema, async request => {
1153
+ if (request.params.name !== 'example-prompt') {
1154
+ throw new Error('Unknown prompt');
1155
+ }
1156
+ return {
1157
+ description: 'Example prompt',
1158
+ messages: [
1159
+ {
1160
+ role: 'user',
1161
+ content: {
1162
+ type: 'text',
1163
+ text: 'Example prompt text'
1164
+ }
1165
+ }
1166
+ ]
1167
+ };
1004
1168
  });
1005
1169
 
1006
1170
  const transport = new StdioServerTransport();
@@ -1013,73 +1177,98 @@ MCP servers can request additional information from users through the elicitatio
1013
1177
 
1014
1178
  ```typescript
1015
1179
  // Server-side: Restaurant booking tool that asks for alternatives
1016
- server.tool(
1017
- "book-restaurant",
1018
- {
1019
- restaurant: z.string(),
1020
- date: z.string(),
1021
- partySize: z.number()
1022
- },
1023
- async ({ restaurant, date, partySize }) => {
1024
- // Check availability
1025
- const available = await checkAvailability(restaurant, date, partySize);
1026
-
1027
- if (!available) {
1028
- // Ask user if they want to try alternative dates
1029
- const result = await server.server.elicitInput({
1030
- message: `No tables available at ${restaurant} on ${date}. Would you like to check alternative dates?`,
1031
- requestedSchema: {
1032
- type: "object",
1033
- properties: {
1034
- checkAlternatives: {
1035
- type: "boolean",
1036
- title: "Check alternative dates",
1037
- description: "Would you like me to check other dates?"
1038
- },
1039
- flexibleDates: {
1040
- type: "string",
1041
- title: "Date flexibility",
1042
- description: "How flexible are your dates?",
1043
- enum: ["next_day", "same_week", "next_week"],
1044
- enumNames: ["Next day", "Same week", "Next week"]
1180
+ server.registerTool(
1181
+ 'book-restaurant',
1182
+ {
1183
+ title: 'Book Restaurant',
1184
+ description: 'Book a table at a restaurant',
1185
+ inputSchema: {
1186
+ restaurant: z.string(),
1187
+ date: z.string(),
1188
+ partySize: z.number()
1189
+ },
1190
+ outputSchema: {
1191
+ success: z.boolean(),
1192
+ booking: z
1193
+ .object({
1194
+ restaurant: z.string(),
1195
+ date: z.string(),
1196
+ partySize: z.number()
1197
+ })
1198
+ .optional(),
1199
+ alternatives: z.array(z.string()).optional()
1200
+ }
1201
+ },
1202
+ async ({ restaurant, date, partySize }) => {
1203
+ // Check availability
1204
+ const available = await checkAvailability(restaurant, date, partySize);
1205
+
1206
+ if (!available) {
1207
+ // Ask user if they want to try alternative dates
1208
+ const result = await server.server.elicitInput({
1209
+ message: `No tables available at ${restaurant} on ${date}. Would you like to check alternative dates?`,
1210
+ requestedSchema: {
1211
+ type: 'object',
1212
+ properties: {
1213
+ checkAlternatives: {
1214
+ type: 'boolean',
1215
+ title: 'Check alternative dates',
1216
+ description: 'Would you like me to check other dates?'
1217
+ },
1218
+ flexibleDates: {
1219
+ type: 'string',
1220
+ title: 'Date flexibility',
1221
+ description: 'How flexible are your dates?',
1222
+ enum: ['next_day', 'same_week', 'next_week'],
1223
+ enumNames: ['Next day', 'Same week', 'Next week']
1224
+ }
1225
+ },
1226
+ required: ['checkAlternatives']
1227
+ }
1228
+ });
1229
+
1230
+ if (result.action === 'accept' && result.content?.checkAlternatives) {
1231
+ const alternatives = await findAlternatives(restaurant, date, partySize, result.content.flexibleDates as string);
1232
+ const output = { success: false, alternatives };
1233
+ return {
1234
+ content: [
1235
+ {
1236
+ type: 'text',
1237
+ text: JSON.stringify(output)
1238
+ }
1239
+ ],
1240
+ structuredContent: output
1241
+ };
1045
1242
  }
1046
- },
1047
- required: ["checkAlternatives"]
1243
+
1244
+ const output = { success: false };
1245
+ return {
1246
+ content: [
1247
+ {
1248
+ type: 'text',
1249
+ text: JSON.stringify(output)
1250
+ }
1251
+ ],
1252
+ structuredContent: output
1253
+ };
1048
1254
  }
1049
- });
1050
-
1051
- if (result.action === "accept" && result.content?.checkAlternatives) {
1052
- const alternatives = await findAlternatives(
1053
- restaurant,
1054
- date,
1055
- partySize,
1056
- result.content.flexibleDates as string
1057
- );
1255
+
1256
+ // Book the table
1257
+ await makeBooking(restaurant, date, partySize);
1258
+ const output = {
1259
+ success: true,
1260
+ booking: { restaurant, date, partySize }
1261
+ };
1058
1262
  return {
1059
- content: [{
1060
- type: "text",
1061
- text: `Found these alternatives: ${alternatives.join(", ")}`
1062
- }]
1263
+ content: [
1264
+ {
1265
+ type: 'text',
1266
+ text: JSON.stringify(output)
1267
+ }
1268
+ ],
1269
+ structuredContent: output
1063
1270
  };
1064
- }
1065
-
1066
- return {
1067
- content: [{
1068
- type: "text",
1069
- text: "No booking made. Original date not available."
1070
- }]
1071
- };
1072
1271
  }
1073
-
1074
- // Book the table
1075
- await makeBooking(restaurant, date, partySize);
1076
- return {
1077
- content: [{
1078
- type: "text",
1079
- text: `Booked table for ${partySize} at ${restaurant} on ${date}`
1080
- }]
1081
- };
1082
- }
1083
1272
  );
1084
1273
  ```
1085
1274
 
@@ -1087,24 +1276,24 @@ Client-side: Handle elicitation requests
1087
1276
 
1088
1277
  ```typescript
1089
1278
  // This is a placeholder - implement based on your UI framework
1090
- async function getInputFromUser(message: string, schema: any): Promise<{
1091
- action: "accept" | "decline" | "cancel";
1092
- data?: Record<string, any>;
1279
+ async function getInputFromUser(
1280
+ message: string,
1281
+ schema: any
1282
+ ): Promise<{
1283
+ action: 'accept' | 'decline' | 'cancel';
1284
+ data?: Record<string, any>;
1093
1285
  }> {
1094
- // This should be implemented depending on the app
1095
- throw new Error("getInputFromUser must be implemented for your platform");
1286
+ // This should be implemented depending on the app
1287
+ throw new Error('getInputFromUser must be implemented for your platform');
1096
1288
  }
1097
1289
 
1098
- client.setRequestHandler(ElicitRequestSchema, async (request) => {
1099
- const userResponse = await getInputFromUser(
1100
- request.params.message,
1101
- request.params.requestedSchema
1102
- );
1103
-
1104
- return {
1105
- action: userResponse.action,
1106
- content: userResponse.action === "accept" ? userResponse.data : undefined
1107
- };
1290
+ client.setRequestHandler(ElicitRequestSchema, async request => {
1291
+ const userResponse = await getInputFromUser(request.params.message, request.params.requestedSchema);
1292
+
1293
+ return {
1294
+ action: userResponse.action,
1295
+ content: userResponse.action === 'accept' ? userResponse.data : undefined
1296
+ };
1108
1297
  });
1109
1298
  ```
1110
1299
 
@@ -1115,20 +1304,18 @@ client.setRequestHandler(ElicitRequestSchema, async (request) => {
1115
1304
  The SDK provides a high-level client interface:
1116
1305
 
1117
1306
  ```typescript
1118
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
1119
- import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
1307
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
1308
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
1120
1309
 
1121
1310
  const transport = new StdioClientTransport({
1122
- command: "node",
1123
- args: ["server.js"]
1311
+ command: 'node',
1312
+ args: ['server.js']
1124
1313
  });
1125
1314
 
1126
- const client = new Client(
1127
- {
1128
- name: "example-client",
1129
- version: "1.0.0"
1130
- }
1131
- );
1315
+ const client = new Client({
1316
+ name: 'example-client',
1317
+ version: '1.0.0'
1318
+ });
1132
1319
 
1133
1320
  await client.connect(transport);
1134
1321
 
@@ -1137,10 +1324,10 @@ const prompts = await client.listPrompts();
1137
1324
 
1138
1325
  // Get a prompt
1139
1326
  const prompt = await client.getPrompt({
1140
- name: "example-prompt",
1141
- arguments: {
1142
- arg1: "value"
1143
- }
1327
+ name: 'example-prompt',
1328
+ arguments: {
1329
+ arg1: 'value'
1330
+ }
1144
1331
  });
1145
1332
 
1146
1333
  // List resources
@@ -1148,17 +1335,16 @@ const resources = await client.listResources();
1148
1335
 
1149
1336
  // Read a resource
1150
1337
  const resource = await client.readResource({
1151
- uri: "file:///example.txt"
1338
+ uri: 'file:///example.txt'
1152
1339
  });
1153
1340
 
1154
1341
  // Call a tool
1155
1342
  const result = await client.callTool({
1156
- name: "example-tool",
1157
- arguments: {
1158
- arg1: "value"
1159
- }
1343
+ name: 'example-tool',
1344
+ arguments: {
1345
+ arg1: 'value'
1346
+ }
1160
1347
  });
1161
-
1162
1348
  ```
1163
1349
 
1164
1350
  ### Proxy Authorization Requests Upstream
@@ -1174,31 +1360,33 @@ const app = express();
1174
1360
 
1175
1361
  const proxyProvider = new ProxyOAuthServerProvider({
1176
1362
  endpoints: {
1177
- authorizationUrl: "https://auth.external.com/oauth2/v1/authorize",
1178
- tokenUrl: "https://auth.external.com/oauth2/v1/token",
1179
- revocationUrl: "https://auth.external.com/oauth2/v1/revoke",
1363
+ authorizationUrl: 'https://auth.external.com/oauth2/v1/authorize',
1364
+ tokenUrl: 'https://auth.external.com/oauth2/v1/token',
1365
+ revocationUrl: 'https://auth.external.com/oauth2/v1/revoke'
1180
1366
  },
1181
- verifyAccessToken: async (token) => {
1367
+ verifyAccessToken: async token => {
1182
1368
  return {
1183
1369
  token,
1184
- clientId: "123",
1185
- scopes: ["openid", "email", "profile"],
1186
- }
1370
+ clientId: '123',
1371
+ scopes: ['openid', 'email', 'profile']
1372
+ };
1187
1373
  },
1188
- getClient: async (client_id) => {
1374
+ getClient: async client_id => {
1189
1375
  return {
1190
1376
  client_id,
1191
- redirect_uris: ["http://localhost:3000/callback"],
1192
- }
1377
+ redirect_uris: ['http://localhost:3000/callback']
1378
+ };
1193
1379
  }
1194
- })
1195
-
1196
- app.use(mcpAuthRouter({
1197
- provider: proxyProvider,
1198
- issuerUrl: new URL("http://auth.external.com"),
1199
- baseUrl: new URL("http://mcp.example.com"),
1200
- serviceDocumentationUrl: new URL("https://docs.example.com/"),
1201
- }))
1380
+ });
1381
+
1382
+ app.use(
1383
+ mcpAuthRouter({
1384
+ provider: proxyProvider,
1385
+ issuerUrl: new URL('http://auth.external.com'),
1386
+ baseUrl: new URL('http://mcp.example.com'),
1387
+ serviceDocumentationUrl: new URL('https://docs.example.com/')
1388
+ })
1389
+ );
1202
1390
  ```
1203
1391
 
1204
1392
  This setup allows you to:
@@ -1218,31 +1406,29 @@ Clients and servers with StreamableHttp transport can maintain [backwards compat
1218
1406
  For clients that need to work with both Streamable HTTP and older SSE servers:
1219
1407
 
1220
1408
  ```typescript
1221
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
1222
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
1223
- import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
1224
- let client: Client|undefined = undefined
1409
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
1410
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
1411
+ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
1412
+ let client: Client | undefined = undefined;
1225
1413
  const baseUrl = new URL(url);
1226
1414
  try {
1227
- client = new Client({
1228
- name: 'streamable-http-client',
1229
- version: '1.0.0'
1230
- });
1231
- const transport = new StreamableHTTPClientTransport(
1232
- new URL(baseUrl)
1233
- );
1234
- await client.connect(transport);
1235
- console.log("Connected using Streamable HTTP transport");
1415
+ client = new Client({
1416
+ name: 'streamable-http-client',
1417
+ version: '1.0.0'
1418
+ });
1419
+ const transport = new StreamableHTTPClientTransport(new URL(baseUrl));
1420
+ await client.connect(transport);
1421
+ console.log('Connected using Streamable HTTP transport');
1236
1422
  } catch (error) {
1237
- // If that fails with a 4xx error, try the older SSE transport
1238
- console.log("Streamable HTTP connection failed, falling back to SSE transport");
1239
- client = new Client({
1240
- name: 'sse-client',
1241
- version: '1.0.0'
1242
- });
1243
- const sseTransport = new SSEClientTransport(baseUrl);
1244
- await client.connect(sseTransport);
1245
- console.log("Connected using SSE transport");
1423
+ // If that fails with a 4xx error, try the older SSE transport
1424
+ console.log('Streamable HTTP connection failed, falling back to SSE transport');
1425
+ client = new Client({
1426
+ name: 'sse-client',
1427
+ version: '1.0.0'
1428
+ });
1429
+ const sseTransport = new SSEClientTransport(baseUrl);
1430
+ await client.connect(sseTransport);
1431
+ console.log('Connected using SSE transport');
1246
1432
  }
1247
1433
  ```
1248
1434
 
@@ -1251,14 +1437,14 @@ try {
1251
1437
  For servers that need to support both Streamable HTTP and older clients:
1252
1438
 
1253
1439
  ```typescript
1254
- import express from "express";
1255
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
1256
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
1257
- import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
1440
+ import express from 'express';
1441
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1442
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
1443
+ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
1258
1444
 
1259
1445
  const server = new McpServer({
1260
- name: "backwards-compatible-server",
1261
- version: "1.0.0"
1446
+ name: 'backwards-compatible-server',
1447
+ version: '1.0.0'
1262
1448
  });
1263
1449
 
1264
1450
  // ... set up server resources, tools, and prompts ...
@@ -1268,39 +1454,39 @@ app.use(express.json());
1268
1454
 
1269
1455
  // Store transports for each session type
1270
1456
  const transports = {
1271
- streamable: {} as Record<string, StreamableHTTPServerTransport>,
1272
- sse: {} as Record<string, SSEServerTransport>
1457
+ streamable: {} as Record<string, StreamableHTTPServerTransport>,
1458
+ sse: {} as Record<string, SSEServerTransport>
1273
1459
  };
1274
1460
 
1275
1461
  // Modern Streamable HTTP endpoint
1276
1462
  app.all('/mcp', async (req, res) => {
1277
- // Handle Streamable HTTP transport for modern clients
1278
- // Implementation as shown in the "With Session Management" example
1279
- // ...
1463
+ // Handle Streamable HTTP transport for modern clients
1464
+ // Implementation as shown in the "With Session Management" example
1465
+ // ...
1280
1466
  });
1281
1467
 
1282
1468
  // Legacy SSE endpoint for older clients
1283
1469
  app.get('/sse', async (req, res) => {
1284
- // Create SSE transport for legacy clients
1285
- const transport = new SSEServerTransport('/messages', res);
1286
- transports.sse[transport.sessionId] = transport;
1287
-
1288
- res.on("close", () => {
1289
- delete transports.sse[transport.sessionId];
1290
- });
1291
-
1292
- await server.connect(transport);
1470
+ // Create SSE transport for legacy clients
1471
+ const transport = new SSEServerTransport('/messages', res);
1472
+ transports.sse[transport.sessionId] = transport;
1473
+
1474
+ res.on('close', () => {
1475
+ delete transports.sse[transport.sessionId];
1476
+ });
1477
+
1478
+ await server.connect(transport);
1293
1479
  });
1294
1480
 
1295
1481
  // Legacy message endpoint for older clients
1296
1482
  app.post('/messages', async (req, res) => {
1297
- const sessionId = req.query.sessionId as string;
1298
- const transport = transports.sse[sessionId];
1299
- if (transport) {
1300
- await transport.handlePostMessage(req, res, req.body);
1301
- } else {
1302
- res.status(400).send('No transport found for sessionId');
1303
- }
1483
+ const sessionId = req.query.sessionId as string;
1484
+ const transport = transports.sse[sessionId];
1485
+ if (transport) {
1486
+ await transport.handlePostMessage(req, res, req.body);
1487
+ } else {
1488
+ res.status(400).send('No transport found for sessionId');
1489
+ }
1304
1490
  });
1305
1491
 
1306
1492
  app.listen(3000);