@sylphai/adal-cli-linux-x64 0.1.0-beta.91 → 0.1.1-beta.20

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 (180) hide show
  1. package/adal-cli.js +565 -543
  2. package/backend/adal-backend/_internal/MarkupSafe-3.0.2.dist-info/INSTALLER +1 -0
  3. package/backend/adal-backend/_internal/MarkupSafe-3.0.2.dist-info/LICENSE.txt +28 -0
  4. package/backend/adal-backend/_internal/MarkupSafe-3.0.2.dist-info/METADATA +92 -0
  5. package/backend/adal-backend/_internal/MarkupSafe-3.0.2.dist-info/RECORD +12 -0
  6. package/backend/adal-backend/_internal/MarkupSafe-3.0.2.dist-info/WHEEL +6 -0
  7. package/backend/adal-backend/_internal/MarkupSafe-3.0.2.dist-info/top_level.txt +1 -0
  8. package/backend/adal-backend/_internal/aiohttp/_http_parser.cpython-312-x86_64-linux-gnu.so +0 -0
  9. package/backend/adal-backend/_internal/aiohttp/_http_writer.cpython-312-x86_64-linux-gnu.so +0 -0
  10. package/backend/adal-backend/_internal/aiohttp/_websocket/mask.cpython-312-x86_64-linux-gnu.so +0 -0
  11. package/backend/adal-backend/_internal/aiohttp/_websocket/reader_c.cpython-312-x86_64-linux-gnu.so +0 -0
  12. package/backend/adal-backend/_internal/attrs-25.3.0.dist-info/INSTALLER +1 -0
  13. package/backend/adal-backend/_internal/attrs-25.3.0.dist-info/METADATA +232 -0
  14. package/backend/adal-backend/_internal/attrs-25.3.0.dist-info/RECORD +36 -0
  15. package/backend/adal-backend/_internal/attrs-25.3.0.dist-info/WHEEL +4 -0
  16. package/backend/adal-backend/_internal/attrs-25.3.0.dist-info/licenses/LICENSE +21 -0
  17. package/backend/adal-backend/_internal/base_library.zip +0 -0
  18. package/backend/adal-backend/_internal/certifi/cacert.pem +4738 -0
  19. package/backend/adal-backend/_internal/certifi/py.typed +0 -0
  20. package/backend/adal-backend/_internal/charset_normalizer/md.cpython-312-x86_64-linux-gnu.so +0 -0
  21. package/backend/adal-backend/_internal/charset_normalizer/md__mypyc.cpython-312-x86_64-linux-gnu.so +0 -0
  22. package/backend/adal-backend/_internal/click-8.2.1.dist-info/INSTALLER +1 -0
  23. package/backend/adal-backend/_internal/click-8.2.1.dist-info/METADATA +82 -0
  24. package/backend/adal-backend/_internal/click-8.2.1.dist-info/RECORD +22 -0
  25. package/backend/adal-backend/_internal/click-8.2.1.dist-info/WHEEL +4 -0
  26. package/backend/adal-backend/_internal/click-8.2.1.dist-info/licenses/LICENSE.txt +28 -0
  27. package/backend/adal-backend/_internal/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz +0 -0
  28. package/backend/adal-backend/_internal/frozenlist/_frozenlist.cpython-312-x86_64-linux-gnu.so +0 -0
  29. package/backend/adal-backend/_internal/greenlet/_greenlet.cpython-312-x86_64-linux-gnu.so +0 -0
  30. package/backend/adal-backend/_internal/importlib_metadata-8.7.0.dist-info/INSTALLER +1 -0
  31. package/backend/adal-backend/_internal/importlib_metadata-8.7.0.dist-info/METADATA +134 -0
  32. package/backend/adal-backend/_internal/importlib_metadata-8.7.0.dist-info/RECORD +20 -0
  33. package/backend/adal-backend/_internal/importlib_metadata-8.7.0.dist-info/WHEEL +5 -0
  34. package/backend/adal-backend/_internal/importlib_metadata-8.7.0.dist-info/licenses/LICENSE +202 -0
  35. package/backend/adal-backend/_internal/importlib_metadata-8.7.0.dist-info/top_level.txt +1 -0
  36. package/backend/adal-backend/_internal/jiter/jiter.cpython-312-x86_64-linux-gnu.so +0 -0
  37. package/backend/adal-backend/_internal/jsonschema/benchmarks/issue232/issue.json +2653 -0
  38. package/backend/adal-backend/_internal/jsonschema-4.25.1.dist-info/INSTALLER +1 -0
  39. package/backend/adal-backend/_internal/jsonschema-4.25.1.dist-info/METADATA +170 -0
  40. package/backend/adal-backend/_internal/jsonschema-4.25.1.dist-info/RECORD +44 -0
  41. package/backend/adal-backend/_internal/jsonschema-4.25.1.dist-info/WHEEL +4 -0
  42. package/backend/adal-backend/_internal/jsonschema-4.25.1.dist-info/entry_points.txt +2 -0
  43. package/backend/adal-backend/_internal/jsonschema-4.25.1.dist-info/licenses/COPYING +19 -0
  44. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft201909/metaschema.json +42 -0
  45. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft201909/vocabularies/applicator +56 -0
  46. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft201909/vocabularies/content +17 -0
  47. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft201909/vocabularies/core +57 -0
  48. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft201909/vocabularies/meta-data +37 -0
  49. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft201909/vocabularies/validation +98 -0
  50. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft202012/metaschema.json +58 -0
  51. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft202012/vocabularies/applicator +48 -0
  52. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft202012/vocabularies/content +17 -0
  53. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft202012/vocabularies/core +51 -0
  54. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft202012/vocabularies/format +14 -0
  55. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft202012/vocabularies/format-annotation +14 -0
  56. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft202012/vocabularies/format-assertion +14 -0
  57. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft202012/vocabularies/meta-data +37 -0
  58. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft202012/vocabularies/unevaluated +15 -0
  59. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft202012/vocabularies/validation +98 -0
  60. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft3/metaschema.json +172 -0
  61. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft4/metaschema.json +149 -0
  62. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft6/metaschema.json +153 -0
  63. package/backend/adal-backend/_internal/jsonschema_specifications/schemas/draft7/metaschema.json +166 -0
  64. package/backend/adal-backend/_internal/lib-dynload/_asyncio.cpython-312-x86_64-linux-gnu.so +0 -0
  65. package/backend/adal-backend/_internal/lib-dynload/_bisect.cpython-312-x86_64-linux-gnu.so +0 -0
  66. package/backend/adal-backend/_internal/lib-dynload/_blake2.cpython-312-x86_64-linux-gnu.so +0 -0
  67. package/backend/adal-backend/_internal/lib-dynload/_bz2.cpython-312-x86_64-linux-gnu.so +0 -0
  68. package/backend/adal-backend/_internal/lib-dynload/_codecs_cn.cpython-312-x86_64-linux-gnu.so +0 -0
  69. package/backend/adal-backend/_internal/lib-dynload/_codecs_hk.cpython-312-x86_64-linux-gnu.so +0 -0
  70. package/backend/adal-backend/_internal/lib-dynload/_codecs_iso2022.cpython-312-x86_64-linux-gnu.so +0 -0
  71. package/backend/adal-backend/_internal/lib-dynload/_codecs_jp.cpython-312-x86_64-linux-gnu.so +0 -0
  72. package/backend/adal-backend/_internal/lib-dynload/_codecs_kr.cpython-312-x86_64-linux-gnu.so +0 -0
  73. package/backend/adal-backend/_internal/lib-dynload/_codecs_tw.cpython-312-x86_64-linux-gnu.so +0 -0
  74. package/backend/adal-backend/_internal/lib-dynload/_contextvars.cpython-312-x86_64-linux-gnu.so +0 -0
  75. package/backend/adal-backend/_internal/lib-dynload/_csv.cpython-312-x86_64-linux-gnu.so +0 -0
  76. package/backend/adal-backend/_internal/lib-dynload/_ctypes.cpython-312-x86_64-linux-gnu.so +0 -0
  77. package/backend/adal-backend/_internal/lib-dynload/_datetime.cpython-312-x86_64-linux-gnu.so +0 -0
  78. package/backend/adal-backend/_internal/lib-dynload/_decimal.cpython-312-x86_64-linux-gnu.so +0 -0
  79. package/backend/adal-backend/_internal/lib-dynload/_elementtree.cpython-312-x86_64-linux-gnu.so +0 -0
  80. package/backend/adal-backend/_internal/lib-dynload/_hashlib.cpython-312-x86_64-linux-gnu.so +0 -0
  81. package/backend/adal-backend/_internal/lib-dynload/_heapq.cpython-312-x86_64-linux-gnu.so +0 -0
  82. package/backend/adal-backend/_internal/lib-dynload/_json.cpython-312-x86_64-linux-gnu.so +0 -0
  83. package/backend/adal-backend/_internal/lib-dynload/_lzma.cpython-312-x86_64-linux-gnu.so +0 -0
  84. package/backend/adal-backend/_internal/lib-dynload/_md5.cpython-312-x86_64-linux-gnu.so +0 -0
  85. package/backend/adal-backend/_internal/lib-dynload/_multibytecodec.cpython-312-x86_64-linux-gnu.so +0 -0
  86. package/backend/adal-backend/_internal/lib-dynload/_multiprocessing.cpython-312-x86_64-linux-gnu.so +0 -0
  87. package/backend/adal-backend/_internal/lib-dynload/_opcode.cpython-312-x86_64-linux-gnu.so +0 -0
  88. package/backend/adal-backend/_internal/lib-dynload/_pickle.cpython-312-x86_64-linux-gnu.so +0 -0
  89. package/backend/adal-backend/_internal/lib-dynload/_posixshmem.cpython-312-x86_64-linux-gnu.so +0 -0
  90. package/backend/adal-backend/_internal/lib-dynload/_posixsubprocess.cpython-312-x86_64-linux-gnu.so +0 -0
  91. package/backend/adal-backend/_internal/lib-dynload/_queue.cpython-312-x86_64-linux-gnu.so +0 -0
  92. package/backend/adal-backend/_internal/lib-dynload/_random.cpython-312-x86_64-linux-gnu.so +0 -0
  93. package/backend/adal-backend/_internal/lib-dynload/_sha1.cpython-312-x86_64-linux-gnu.so +0 -0
  94. package/backend/adal-backend/_internal/lib-dynload/_sha2.cpython-312-x86_64-linux-gnu.so +0 -0
  95. package/backend/adal-backend/_internal/lib-dynload/_sha3.cpython-312-x86_64-linux-gnu.so +0 -0
  96. package/backend/adal-backend/_internal/lib-dynload/_socket.cpython-312-x86_64-linux-gnu.so +0 -0
  97. package/backend/adal-backend/_internal/lib-dynload/_sqlite3.cpython-312-x86_64-linux-gnu.so +0 -0
  98. package/backend/adal-backend/_internal/lib-dynload/_ssl.cpython-312-x86_64-linux-gnu.so +0 -0
  99. package/backend/adal-backend/_internal/lib-dynload/_statistics.cpython-312-x86_64-linux-gnu.so +0 -0
  100. package/backend/adal-backend/_internal/lib-dynload/_struct.cpython-312-x86_64-linux-gnu.so +0 -0
  101. package/backend/adal-backend/_internal/lib-dynload/_uuid.cpython-312-x86_64-linux-gnu.so +0 -0
  102. package/backend/adal-backend/_internal/lib-dynload/_zoneinfo.cpython-312-x86_64-linux-gnu.so +0 -0
  103. package/backend/adal-backend/_internal/lib-dynload/array.cpython-312-x86_64-linux-gnu.so +0 -0
  104. package/backend/adal-backend/_internal/lib-dynload/binascii.cpython-312-x86_64-linux-gnu.so +0 -0
  105. package/backend/adal-backend/_internal/lib-dynload/fcntl.cpython-312-x86_64-linux-gnu.so +0 -0
  106. package/backend/adal-backend/_internal/lib-dynload/grp.cpython-312-x86_64-linux-gnu.so +0 -0
  107. package/backend/adal-backend/_internal/lib-dynload/math.cpython-312-x86_64-linux-gnu.so +0 -0
  108. package/backend/adal-backend/_internal/lib-dynload/mmap.cpython-312-x86_64-linux-gnu.so +0 -0
  109. package/backend/adal-backend/_internal/lib-dynload/pyexpat.cpython-312-x86_64-linux-gnu.so +0 -0
  110. package/backend/adal-backend/_internal/lib-dynload/readline.cpython-312-x86_64-linux-gnu.so +0 -0
  111. package/backend/adal-backend/_internal/lib-dynload/resource.cpython-312-x86_64-linux-gnu.so +0 -0
  112. package/backend/adal-backend/_internal/lib-dynload/select.cpython-312-x86_64-linux-gnu.so +0 -0
  113. package/backend/adal-backend/_internal/lib-dynload/syslog.cpython-312-x86_64-linux-gnu.so +0 -0
  114. package/backend/adal-backend/_internal/lib-dynload/termios.cpython-312-x86_64-linux-gnu.so +0 -0
  115. package/backend/adal-backend/_internal/lib-dynload/unicodedata.cpython-312-x86_64-linux-gnu.so +0 -0
  116. package/backend/adal-backend/_internal/lib-dynload/zlib.cpython-312-x86_64-linux-gnu.so +0 -0
  117. package/backend/adal-backend/_internal/libbz2.so.1.0 +0 -0
  118. package/backend/adal-backend/_internal/libcrypto.so.3 +0 -0
  119. package/backend/adal-backend/_internal/libffi.so.8 +0 -0
  120. package/backend/adal-backend/_internal/libgcc_s.so.1 +0 -0
  121. package/backend/adal-backend/_internal/liblzma.so.5 +0 -0
  122. package/backend/{adal-backend → adal-backend/_internal/libpython3.12.so.1.0} +0 -0
  123. package/backend/adal-backend/_internal/libreadline.so.8 +0 -0
  124. package/backend/adal-backend/_internal/libsqlite3.so.0 +0 -0
  125. package/backend/adal-backend/_internal/libssl.so.3 +0 -0
  126. package/backend/adal-backend/_internal/libstdc++.so.6 +0 -0
  127. package/backend/adal-backend/_internal/libtinfo.so.6 +0 -0
  128. package/backend/adal-backend/_internal/libuuid.so.1 +0 -0
  129. package/backend/adal-backend/_internal/libz.so.1 +0 -0
  130. package/backend/adal-backend/_internal/markupsafe/_speedups.cpython-312-x86_64-linux-gnu.so +0 -0
  131. package/backend/adal-backend/_internal/mcp-1.13.1.dist-info/INSTALLER +1 -0
  132. package/backend/adal-backend/_internal/mcp-1.13.1.dist-info/METADATA +2191 -0
  133. package/backend/adal-backend/_internal/mcp-1.13.1.dist-info/RECORD +88 -0
  134. package/backend/adal-backend/_internal/mcp-1.13.1.dist-info/WHEEL +4 -0
  135. package/backend/adal-backend/_internal/mcp-1.13.1.dist-info/entry_points.txt +2 -0
  136. package/backend/adal-backend/_internal/mcp-1.13.1.dist-info/licenses/LICENSE +21 -0
  137. package/backend/adal-backend/_internal/multidict/_multidict.cpython-312-x86_64-linux-gnu.so +0 -0
  138. package/backend/adal-backend/_internal/numpy/_core/_multiarray_tests.cpython-312-x86_64-linux-gnu.so +0 -0
  139. package/backend/adal-backend/_internal/numpy/_core/_multiarray_umath.cpython-312-x86_64-linux-gnu.so +0 -0
  140. package/backend/adal-backend/_internal/numpy/fft/_pocketfft_umath.cpython-312-x86_64-linux-gnu.so +0 -0
  141. package/backend/adal-backend/_internal/numpy/linalg/_umath_linalg.cpython-312-x86_64-linux-gnu.so +0 -0
  142. package/backend/adal-backend/_internal/numpy/random/_bounded_integers.cpython-312-x86_64-linux-gnu.so +0 -0
  143. package/backend/adal-backend/_internal/numpy/random/_common.cpython-312-x86_64-linux-gnu.so +0 -0
  144. package/backend/adal-backend/_internal/numpy/random/_generator.cpython-312-x86_64-linux-gnu.so +0 -0
  145. package/backend/adal-backend/_internal/numpy/random/_mt19937.cpython-312-x86_64-linux-gnu.so +0 -0
  146. package/backend/adal-backend/_internal/numpy/random/_pcg64.cpython-312-x86_64-linux-gnu.so +0 -0
  147. package/backend/adal-backend/_internal/numpy/random/_philox.cpython-312-x86_64-linux-gnu.so +0 -0
  148. package/backend/adal-backend/_internal/numpy/random/_sfc64.cpython-312-x86_64-linux-gnu.so +0 -0
  149. package/backend/adal-backend/_internal/numpy/random/bit_generator.cpython-312-x86_64-linux-gnu.so +0 -0
  150. package/backend/adal-backend/_internal/numpy/random/mtrand.cpython-312-x86_64-linux-gnu.so +0 -0
  151. package/backend/adal-backend/_internal/numpy-2.3.2.dist-info/INSTALLER +1 -0
  152. package/backend/adal-backend/_internal/numpy-2.3.2.dist-info/LICENSE.txt +971 -0
  153. package/backend/adal-backend/_internal/numpy-2.3.2.dist-info/METADATA +1093 -0
  154. package/backend/adal-backend/_internal/numpy-2.3.2.dist-info/RECORD +904 -0
  155. package/backend/adal-backend/_internal/numpy-2.3.2.dist-info/WHEEL +6 -0
  156. package/backend/adal-backend/_internal/numpy-2.3.2.dist-info/entry_points.txt +13 -0
  157. package/backend/adal-backend/_internal/numpy.libs/libgfortran-040039e1-0352e75f.so.5.0.0 +0 -0
  158. package/backend/adal-backend/_internal/numpy.libs/libquadmath-96973f99-934c22de.so.0.0.0 +0 -0
  159. package/backend/adal-backend/_internal/numpy.libs/libscipy_openblas64_-8fb3d286.so +0 -0
  160. package/backend/adal-backend/_internal/ollama-0.5.4.dist-info/INSTALLER +1 -0
  161. package/backend/adal-backend/_internal/ollama-0.5.4.dist-info/METADATA +206 -0
  162. package/backend/adal-backend/_internal/ollama-0.5.4.dist-info/RECORD +10 -0
  163. package/backend/adal-backend/_internal/ollama-0.5.4.dist-info/WHEEL +4 -0
  164. package/backend/adal-backend/_internal/ollama-0.5.4.dist-info/licenses/LICENSE +21 -0
  165. package/backend/adal-backend/_internal/propcache/_helpers_c.cpython-312-x86_64-linux-gnu.so +0 -0
  166. package/backend/adal-backend/_internal/pydantic_core/_pydantic_core.cpython-312-x86_64-linux-gnu.so +0 -0
  167. package/backend/adal-backend/_internal/regex/_regex.cpython-312-x86_64-linux-gnu.so +0 -0
  168. package/backend/adal-backend/_internal/rg +0 -0
  169. package/backend/adal-backend/_internal/rpds/rpds.cpython-312-x86_64-linux-gnu.so +0 -0
  170. package/backend/adal-backend/_internal/setuptools/_vendor/jaraco/text/Lorem ipsum.txt +2 -0
  171. package/backend/adal-backend/_internal/tiktoken/_tiktoken.cpython-312-x86_64-linux-gnu.so +0 -0
  172. package/backend/adal-backend/_internal/werkzeug-3.1.3.dist-info/INSTALLER +1 -0
  173. package/backend/adal-backend/_internal/werkzeug-3.1.3.dist-info/LICENSE.txt +28 -0
  174. package/backend/adal-backend/_internal/werkzeug-3.1.3.dist-info/METADATA +99 -0
  175. package/backend/adal-backend/_internal/werkzeug-3.1.3.dist-info/RECORD +64 -0
  176. package/backend/adal-backend/_internal/werkzeug-3.1.3.dist-info/WHEEL +4 -0
  177. package/backend/adal-backend/_internal/yaml/_yaml.cpython-312-x86_64-linux-gnu.so +0 -0
  178. package/backend/adal-backend/_internal/yarl/_quoting_c.cpython-312-x86_64-linux-gnu.so +0 -0
  179. package/backend/adal-backend/adal-backend +0 -0
  180. package/package.json +1 -1
@@ -0,0 +1,2191 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcp
3
+ Version: 1.13.1
4
+ Summary: Model Context Protocol SDK
5
+ Project-URL: Homepage, https://modelcontextprotocol.io
6
+ Project-URL: Repository, https://github.com/modelcontextprotocol/python-sdk
7
+ Project-URL: Issues, https://github.com/modelcontextprotocol/python-sdk/issues
8
+ Author: Anthropic, PBC.
9
+ Maintainer-email: David Soria Parra <davidsp@anthropic.com>, Justin Spahr-Summers <justin@anthropic.com>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: automation,git,llm,mcp
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: anyio>=4.5
23
+ Requires-Dist: httpx-sse>=0.4
24
+ Requires-Dist: httpx>=0.27.1
25
+ Requires-Dist: jsonschema>=4.20.0
26
+ Requires-Dist: pydantic-settings>=2.5.2
27
+ Requires-Dist: pydantic<3.0.0,>=2.11.0
28
+ Requires-Dist: python-multipart>=0.0.9
29
+ Requires-Dist: pywin32>=310; sys_platform == 'win32'
30
+ Requires-Dist: sse-starlette>=1.6.1
31
+ Requires-Dist: starlette>=0.27
32
+ Requires-Dist: uvicorn>=0.31.1; sys_platform != 'emscripten'
33
+ Provides-Extra: cli
34
+ Requires-Dist: python-dotenv>=1.0.0; extra == 'cli'
35
+ Requires-Dist: typer>=0.16.0; extra == 'cli'
36
+ Provides-Extra: rich
37
+ Requires-Dist: rich>=13.9.4; extra == 'rich'
38
+ Provides-Extra: ws
39
+ Requires-Dist: websockets>=15.0.1; extra == 'ws'
40
+ Description-Content-Type: text/markdown
41
+
42
+ # MCP Python SDK
43
+
44
+ <div align="center">
45
+
46
+ <strong>Python implementation of the Model Context Protocol (MCP)</strong>
47
+
48
+ [![PyPI][pypi-badge]][pypi-url]
49
+ [![MIT licensed][mit-badge]][mit-url]
50
+ [![Python Version][python-badge]][python-url]
51
+ [![Documentation][docs-badge]][docs-url]
52
+ [![Specification][spec-badge]][spec-url]
53
+ [![GitHub Discussions][discussions-badge]][discussions-url]
54
+
55
+ </div>
56
+
57
+ <!-- omit in toc -->
58
+ ## Table of Contents
59
+
60
+ - [MCP Python SDK](#mcp-python-sdk)
61
+ - [Overview](#overview)
62
+ - [Installation](#installation)
63
+ - [Adding MCP to your python project](#adding-mcp-to-your-python-project)
64
+ - [Running the standalone MCP development tools](#running-the-standalone-mcp-development-tools)
65
+ - [Quickstart](#quickstart)
66
+ - [What is MCP?](#what-is-mcp)
67
+ - [Core Concepts](#core-concepts)
68
+ - [Server](#server)
69
+ - [Resources](#resources)
70
+ - [Tools](#tools)
71
+ - [Structured Output](#structured-output)
72
+ - [Prompts](#prompts)
73
+ - [Images](#images)
74
+ - [Context](#context)
75
+ - [Completions](#completions)
76
+ - [Elicitation](#elicitation)
77
+ - [Sampling](#sampling)
78
+ - [Logging and Notifications](#logging-and-notifications)
79
+ - [Authentication](#authentication)
80
+ - [FastMCP Properties](#fastmcp-properties)
81
+ - [Session Properties](#session-properties-and-methods)
82
+ - [Request Context Properties](#request-context-properties)
83
+ - [Running Your Server](#running-your-server)
84
+ - [Development Mode](#development-mode)
85
+ - [Claude Desktop Integration](#claude-desktop-integration)
86
+ - [Direct Execution](#direct-execution)
87
+ - [Streamable HTTP Transport](#streamable-http-transport)
88
+ - [Mounting to an Existing ASGI Server](#mounting-to-an-existing-asgi-server)
89
+ - [Advanced Usage](#advanced-usage)
90
+ - [Low-Level Server](#low-level-server)
91
+ - [Writing MCP Clients](#writing-mcp-clients)
92
+ - [Client Display Utilities](#client-display-utilities)
93
+ - [OAuth Authentication for Clients](#oauth-authentication-for-clients)
94
+ - [Parsing Tool Results](#parsing-tool-results)
95
+ - [MCP Primitives](#mcp-primitives)
96
+ - [Server Capabilities](#server-capabilities)
97
+ - [Documentation](#documentation)
98
+ - [Contributing](#contributing)
99
+ - [License](#license)
100
+
101
+ [pypi-badge]: https://img.shields.io/pypi/v/mcp.svg
102
+ [pypi-url]: https://pypi.org/project/mcp/
103
+ [mit-badge]: https://img.shields.io/pypi/l/mcp.svg
104
+ [mit-url]: https://github.com/modelcontextprotocol/python-sdk/blob/main/LICENSE
105
+ [python-badge]: https://img.shields.io/pypi/pyversions/mcp.svg
106
+ [python-url]: https://www.python.org/downloads/
107
+ [docs-badge]: https://img.shields.io/badge/docs-modelcontextprotocol.io-blue.svg
108
+ [docs-url]: https://modelcontextprotocol.io
109
+ [spec-badge]: https://img.shields.io/badge/spec-spec.modelcontextprotocol.io-blue.svg
110
+ [spec-url]: https://spec.modelcontextprotocol.io
111
+ [discussions-badge]: https://img.shields.io/github/discussions/modelcontextprotocol/python-sdk
112
+ [discussions-url]: https://github.com/modelcontextprotocol/python-sdk/discussions
113
+
114
+ ## Overview
115
+
116
+ 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 Python SDK implements the full MCP specification, making it easy to:
117
+
118
+ - Build MCP clients that can connect to any MCP server
119
+ - Create MCP servers that expose resources, prompts and tools
120
+ - Use standard transports like stdio, SSE, and Streamable HTTP
121
+ - Handle all MCP protocol messages and lifecycle events
122
+
123
+ ## Installation
124
+
125
+ ### Adding MCP to your python project
126
+
127
+ We recommend using [uv](https://docs.astral.sh/uv/) to manage your Python projects.
128
+
129
+ If you haven't created a uv-managed project yet, create one:
130
+
131
+ ```bash
132
+ uv init mcp-server-demo
133
+ cd mcp-server-demo
134
+ ```
135
+
136
+ Then add MCP to your project dependencies:
137
+
138
+ ```bash
139
+ uv add "mcp[cli]"
140
+ ```
141
+
142
+ Alternatively, for projects using pip for dependencies:
143
+
144
+ ```bash
145
+ pip install "mcp[cli]"
146
+ ```
147
+
148
+ ### Running the standalone MCP development tools
149
+
150
+ To run the mcp command with uv:
151
+
152
+ ```bash
153
+ uv run mcp
154
+ ```
155
+
156
+ ## Quickstart
157
+
158
+ Let's create a simple MCP server that exposes a calculator tool and some data:
159
+
160
+ <!-- snippet-source examples/snippets/servers/fastmcp_quickstart.py -->
161
+ ```python
162
+ """
163
+ FastMCP quickstart example.
164
+
165
+ cd to the `examples/snippets/clients` directory and run:
166
+ uv run server fastmcp_quickstart stdio
167
+ """
168
+
169
+ from mcp.server.fastmcp import FastMCP
170
+
171
+ # Create an MCP server
172
+ mcp = FastMCP("Demo")
173
+
174
+
175
+ # Add an addition tool
176
+ @mcp.tool()
177
+ def add(a: int, b: int) -> int:
178
+ """Add two numbers"""
179
+ return a + b
180
+
181
+
182
+ # Add a dynamic greeting resource
183
+ @mcp.resource("greeting://{name}")
184
+ def get_greeting(name: str) -> str:
185
+ """Get a personalized greeting"""
186
+ return f"Hello, {name}!"
187
+
188
+
189
+ # Add a prompt
190
+ @mcp.prompt()
191
+ def greet_user(name: str, style: str = "friendly") -> str:
192
+ """Generate a greeting prompt"""
193
+ styles = {
194
+ "friendly": "Please write a warm, friendly greeting",
195
+ "formal": "Please write a formal, professional greeting",
196
+ "casual": "Please write a casual, relaxed greeting",
197
+ }
198
+
199
+ return f"{styles.get(style, styles['friendly'])} for someone named {name}."
200
+ ```
201
+
202
+ _Full example: [examples/snippets/servers/fastmcp_quickstart.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/fastmcp_quickstart.py)_
203
+ <!-- /snippet-source -->
204
+
205
+ You can install this server in [Claude Desktop](https://claude.ai/download) and interact with it right away by running:
206
+
207
+ ```bash
208
+ uv run mcp install server.py
209
+ ```
210
+
211
+ Alternatively, you can test it with the MCP Inspector:
212
+
213
+ ```bash
214
+ uv run mcp dev server.py
215
+ ```
216
+
217
+ ## What is MCP?
218
+
219
+ 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:
220
+
221
+ - Expose data through **Resources** (think of these sort of like GET endpoints; they are used to load information into the LLM's context)
222
+ - Provide functionality through **Tools** (sort of like POST endpoints; they are used to execute code or otherwise produce a side effect)
223
+ - Define interaction patterns through **Prompts** (reusable templates for LLM interactions)
224
+ - And more!
225
+
226
+ ## Core Concepts
227
+
228
+ ### Server
229
+
230
+ The FastMCP server is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing:
231
+
232
+ <!-- snippet-source examples/snippets/servers/lifespan_example.py -->
233
+ ```python
234
+ """Example showing lifespan support for startup/shutdown with strong typing."""
235
+
236
+ from collections.abc import AsyncIterator
237
+ from contextlib import asynccontextmanager
238
+ from dataclasses import dataclass
239
+
240
+ from mcp.server.fastmcp import Context, FastMCP
241
+ from mcp.server.session import ServerSession
242
+
243
+
244
+ # Mock database class for example
245
+ class Database:
246
+ """Mock database class for example."""
247
+
248
+ @classmethod
249
+ async def connect(cls) -> "Database":
250
+ """Connect to database."""
251
+ return cls()
252
+
253
+ async def disconnect(self) -> None:
254
+ """Disconnect from database."""
255
+ pass
256
+
257
+ def query(self) -> str:
258
+ """Execute a query."""
259
+ return "Query result"
260
+
261
+
262
+ @dataclass
263
+ class AppContext:
264
+ """Application context with typed dependencies."""
265
+
266
+ db: Database
267
+
268
+
269
+ @asynccontextmanager
270
+ async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
271
+ """Manage application lifecycle with type-safe context."""
272
+ # Initialize on startup
273
+ db = await Database.connect()
274
+ try:
275
+ yield AppContext(db=db)
276
+ finally:
277
+ # Cleanup on shutdown
278
+ await db.disconnect()
279
+
280
+
281
+ # Pass lifespan to server
282
+ mcp = FastMCP("My App", lifespan=app_lifespan)
283
+
284
+
285
+ # Access type-safe lifespan context in tools
286
+ @mcp.tool()
287
+ def query_db(ctx: Context[ServerSession, AppContext]) -> str:
288
+ """Tool that uses initialized resources."""
289
+ db = ctx.request_context.lifespan_context.db
290
+ return db.query()
291
+ ```
292
+
293
+ _Full example: [examples/snippets/servers/lifespan_example.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/lifespan_example.py)_
294
+ <!-- /snippet-source -->
295
+
296
+ ### Resources
297
+
298
+ 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:
299
+
300
+ <!-- snippet-source examples/snippets/servers/basic_resource.py -->
301
+ ```python
302
+ from mcp.server.fastmcp import FastMCP
303
+
304
+ mcp = FastMCP(name="Resource Example")
305
+
306
+
307
+ @mcp.resource("file://documents/{name}")
308
+ def read_document(name: str) -> str:
309
+ """Read a document by name."""
310
+ # This would normally read from disk
311
+ return f"Content of {name}"
312
+
313
+
314
+ @mcp.resource("config://settings")
315
+ def get_settings() -> str:
316
+ """Get application settings."""
317
+ return """{
318
+ "theme": "dark",
319
+ "language": "en",
320
+ "debug": false
321
+ }"""
322
+ ```
323
+
324
+ _Full example: [examples/snippets/servers/basic_resource.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/basic_resource.py)_
325
+ <!-- /snippet-source -->
326
+
327
+ ### Tools
328
+
329
+ Tools let LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects:
330
+
331
+ <!-- snippet-source examples/snippets/servers/basic_tool.py -->
332
+ ```python
333
+ from mcp.server.fastmcp import FastMCP
334
+
335
+ mcp = FastMCP(name="Tool Example")
336
+
337
+
338
+ @mcp.tool()
339
+ def sum(a: int, b: int) -> int:
340
+ """Add two numbers together."""
341
+ return a + b
342
+
343
+
344
+ @mcp.tool()
345
+ def get_weather(city: str, unit: str = "celsius") -> str:
346
+ """Get weather for a city."""
347
+ # This would normally call a weather API
348
+ return f"Weather in {city}: 22degrees{unit[0].upper()}"
349
+ ```
350
+
351
+ _Full example: [examples/snippets/servers/basic_tool.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/basic_tool.py)_
352
+ <!-- /snippet-source -->
353
+
354
+ Tools can optionally receive a Context object by including a parameter with the `Context` type annotation. This context is automatically injected by the FastMCP framework and provides access to MCP capabilities:
355
+
356
+ <!-- snippet-source examples/snippets/servers/tool_progress.py -->
357
+ ```python
358
+ from mcp.server.fastmcp import Context, FastMCP
359
+ from mcp.server.session import ServerSession
360
+
361
+ mcp = FastMCP(name="Progress Example")
362
+
363
+
364
+ @mcp.tool()
365
+ async def long_running_task(task_name: str, ctx: Context[ServerSession, None], steps: int = 5) -> str:
366
+ """Execute a task with progress updates."""
367
+ await ctx.info(f"Starting: {task_name}")
368
+
369
+ for i in range(steps):
370
+ progress = (i + 1) / steps
371
+ await ctx.report_progress(
372
+ progress=progress,
373
+ total=1.0,
374
+ message=f"Step {i + 1}/{steps}",
375
+ )
376
+ await ctx.debug(f"Completed step {i + 1}")
377
+
378
+ return f"Task '{task_name}' completed"
379
+ ```
380
+
381
+ _Full example: [examples/snippets/servers/tool_progress.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/tool_progress.py)_
382
+ <!-- /snippet-source -->
383
+
384
+ #### Structured Output
385
+
386
+ Tools will return structured results by default, if their return type
387
+ annotation is compatible. Otherwise, they will return unstructured results.
388
+
389
+ Structured output supports these return types:
390
+
391
+ - Pydantic models (BaseModel subclasses)
392
+ - TypedDicts
393
+ - Dataclasses and other classes with type hints
394
+ - `dict[str, T]` (where T is any JSON-serializable type)
395
+ - Primitive types (str, int, float, bool, bytes, None) - wrapped in `{"result": value}`
396
+ - Generic types (list, tuple, Union, Optional, etc.) - wrapped in `{"result": value}`
397
+
398
+ Classes without type hints cannot be serialized for structured output. Only
399
+ classes with properly annotated attributes will be converted to Pydantic models
400
+ for schema generation and validation.
401
+
402
+ Structured results are automatically validated against the output schema
403
+ generated from the annotation. This ensures the tool returns well-typed,
404
+ validated data that clients can easily process.
405
+
406
+ **Note:** For backward compatibility, unstructured results are also
407
+ returned. Unstructured results are provided for backward compatibility
408
+ with previous versions of the MCP specification, and are quirks-compatible
409
+ with previous versions of FastMCP in the current version of the SDK.
410
+
411
+ **Note:** In cases where a tool function's return type annotation
412
+ causes the tool to be classified as structured _and this is undesirable_,
413
+ the classification can be suppressed by passing `structured_output=False`
414
+ to the `@tool` decorator.
415
+
416
+ <!-- snippet-source examples/snippets/servers/structured_output.py -->
417
+ ```python
418
+ """Example showing structured output with tools."""
419
+
420
+ from typing import TypedDict
421
+
422
+ from pydantic import BaseModel, Field
423
+
424
+ from mcp.server.fastmcp import FastMCP
425
+
426
+ mcp = FastMCP("Structured Output Example")
427
+
428
+
429
+ # Using Pydantic models for rich structured data
430
+ class WeatherData(BaseModel):
431
+ """Weather information structure."""
432
+
433
+ temperature: float = Field(description="Temperature in Celsius")
434
+ humidity: float = Field(description="Humidity percentage")
435
+ condition: str
436
+ wind_speed: float
437
+
438
+
439
+ @mcp.tool()
440
+ def get_weather(city: str) -> WeatherData:
441
+ """Get weather for a city - returns structured data."""
442
+ # Simulated weather data
443
+ return WeatherData(
444
+ temperature=72.5,
445
+ humidity=45.0,
446
+ condition="sunny",
447
+ wind_speed=5.2,
448
+ )
449
+
450
+
451
+ # Using TypedDict for simpler structures
452
+ class LocationInfo(TypedDict):
453
+ latitude: float
454
+ longitude: float
455
+ name: str
456
+
457
+
458
+ @mcp.tool()
459
+ def get_location(address: str) -> LocationInfo:
460
+ """Get location coordinates"""
461
+ return LocationInfo(latitude=51.5074, longitude=-0.1278, name="London, UK")
462
+
463
+
464
+ # Using dict[str, Any] for flexible schemas
465
+ @mcp.tool()
466
+ def get_statistics(data_type: str) -> dict[str, float]:
467
+ """Get various statistics"""
468
+ return {"mean": 42.5, "median": 40.0, "std_dev": 5.2}
469
+
470
+
471
+ # Ordinary classes with type hints work for structured output
472
+ class UserProfile:
473
+ name: str
474
+ age: int
475
+ email: str | None = None
476
+
477
+ def __init__(self, name: str, age: int, email: str | None = None):
478
+ self.name = name
479
+ self.age = age
480
+ self.email = email
481
+
482
+
483
+ @mcp.tool()
484
+ def get_user(user_id: str) -> UserProfile:
485
+ """Get user profile - returns structured data"""
486
+ return UserProfile(name="Alice", age=30, email="alice@example.com")
487
+
488
+
489
+ # Classes WITHOUT type hints cannot be used for structured output
490
+ class UntypedConfig:
491
+ def __init__(self, setting1, setting2): # type: ignore[reportMissingParameterType]
492
+ self.setting1 = setting1
493
+ self.setting2 = setting2
494
+
495
+
496
+ @mcp.tool()
497
+ def get_config() -> UntypedConfig:
498
+ """This returns unstructured output - no schema generated"""
499
+ return UntypedConfig("value1", "value2")
500
+
501
+
502
+ # Lists and other types are wrapped automatically
503
+ @mcp.tool()
504
+ def list_cities() -> list[str]:
505
+ """Get a list of cities"""
506
+ return ["London", "Paris", "Tokyo"]
507
+ # Returns: {"result": ["London", "Paris", "Tokyo"]}
508
+
509
+
510
+ @mcp.tool()
511
+ def get_temperature(city: str) -> float:
512
+ """Get temperature as a simple float"""
513
+ return 22.5
514
+ # Returns: {"result": 22.5}
515
+ ```
516
+
517
+ _Full example: [examples/snippets/servers/structured_output.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/structured_output.py)_
518
+ <!-- /snippet-source -->
519
+
520
+ ### Prompts
521
+
522
+ Prompts are reusable templates that help LLMs interact with your server effectively:
523
+
524
+ <!-- snippet-source examples/snippets/servers/basic_prompt.py -->
525
+ ```python
526
+ from mcp.server.fastmcp import FastMCP
527
+ from mcp.server.fastmcp.prompts import base
528
+
529
+ mcp = FastMCP(name="Prompt Example")
530
+
531
+
532
+ @mcp.prompt(title="Code Review")
533
+ def review_code(code: str) -> str:
534
+ return f"Please review this code:\n\n{code}"
535
+
536
+
537
+ @mcp.prompt(title="Debug Assistant")
538
+ def debug_error(error: str) -> list[base.Message]:
539
+ return [
540
+ base.UserMessage("I'm seeing this error:"),
541
+ base.UserMessage(error),
542
+ base.AssistantMessage("I'll help debug that. What have you tried so far?"),
543
+ ]
544
+ ```
545
+
546
+ _Full example: [examples/snippets/servers/basic_prompt.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/basic_prompt.py)_
547
+ <!-- /snippet-source -->
548
+
549
+ ### Images
550
+
551
+ FastMCP provides an `Image` class that automatically handles image data:
552
+
553
+ <!-- snippet-source examples/snippets/servers/images.py -->
554
+ ```python
555
+ """Example showing image handling with FastMCP."""
556
+
557
+ from PIL import Image as PILImage
558
+
559
+ from mcp.server.fastmcp import FastMCP, Image
560
+
561
+ mcp = FastMCP("Image Example")
562
+
563
+
564
+ @mcp.tool()
565
+ def create_thumbnail(image_path: str) -> Image:
566
+ """Create a thumbnail from an image"""
567
+ img = PILImage.open(image_path)
568
+ img.thumbnail((100, 100))
569
+ return Image(data=img.tobytes(), format="png")
570
+ ```
571
+
572
+ _Full example: [examples/snippets/servers/images.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/images.py)_
573
+ <!-- /snippet-source -->
574
+
575
+ ### Context
576
+
577
+ The Context object is automatically injected into tool and resource functions that request it via type hints. It provides access to MCP capabilities like logging, progress reporting, resource reading, user interaction, and request metadata.
578
+
579
+ #### Getting Context in Functions
580
+
581
+ To use context in a tool or resource function, add a parameter with the `Context` type annotation:
582
+
583
+ ```python
584
+ from mcp.server.fastmcp import Context, FastMCP
585
+
586
+ mcp = FastMCP(name="Context Example")
587
+
588
+
589
+ @mcp.tool()
590
+ async def my_tool(x: int, ctx: Context) -> str:
591
+ """Tool that uses context capabilities."""
592
+ # The context parameter can have any name as long as it's type-annotated
593
+ return await process_with_context(x, ctx)
594
+ ```
595
+
596
+ #### Context Properties and Methods
597
+
598
+ The Context object provides the following capabilities:
599
+
600
+ - `ctx.request_id` - Unique ID for the current request
601
+ - `ctx.client_id` - Client ID if available
602
+ - `ctx.fastmcp` - Access to the FastMCP server instance (see [FastMCP Properties](#fastmcp-properties))
603
+ - `ctx.session` - Access to the underlying session for advanced communication (see [Session Properties and Methods](#session-properties-and-methods))
604
+ - `ctx.request_context` - Access to request-specific data and lifespan resources (see [Request Context Properties](#request-context-properties))
605
+ - `await ctx.debug(message)` - Send debug log message
606
+ - `await ctx.info(message)` - Send info log message
607
+ - `await ctx.warning(message)` - Send warning log message
608
+ - `await ctx.error(message)` - Send error log message
609
+ - `await ctx.log(level, message, logger_name=None)` - Send log with custom level
610
+ - `await ctx.report_progress(progress, total=None, message=None)` - Report operation progress
611
+ - `await ctx.read_resource(uri)` - Read a resource by URI
612
+ - `await ctx.elicit(message, schema)` - Request additional information from user with validation
613
+
614
+ <!-- snippet-source examples/snippets/servers/tool_progress.py -->
615
+ ```python
616
+ from mcp.server.fastmcp import Context, FastMCP
617
+ from mcp.server.session import ServerSession
618
+
619
+ mcp = FastMCP(name="Progress Example")
620
+
621
+
622
+ @mcp.tool()
623
+ async def long_running_task(task_name: str, ctx: Context[ServerSession, None], steps: int = 5) -> str:
624
+ """Execute a task with progress updates."""
625
+ await ctx.info(f"Starting: {task_name}")
626
+
627
+ for i in range(steps):
628
+ progress = (i + 1) / steps
629
+ await ctx.report_progress(
630
+ progress=progress,
631
+ total=1.0,
632
+ message=f"Step {i + 1}/{steps}",
633
+ )
634
+ await ctx.debug(f"Completed step {i + 1}")
635
+
636
+ return f"Task '{task_name}' completed"
637
+ ```
638
+
639
+ _Full example: [examples/snippets/servers/tool_progress.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/tool_progress.py)_
640
+ <!-- /snippet-source -->
641
+
642
+ ### Completions
643
+
644
+ MCP supports providing completion suggestions for prompt arguments and resource template parameters. With the context parameter, servers can provide completions based on previously resolved values:
645
+
646
+ Client usage:
647
+
648
+ <!-- snippet-source examples/snippets/clients/completion_client.py -->
649
+ ```python
650
+ """
651
+ cd to the `examples/snippets` directory and run:
652
+ uv run completion-client
653
+ """
654
+
655
+ import asyncio
656
+ import os
657
+
658
+ from mcp import ClientSession, StdioServerParameters
659
+ from mcp.client.stdio import stdio_client
660
+ from mcp.types import PromptReference, ResourceTemplateReference
661
+
662
+ # Create server parameters for stdio connection
663
+ server_params = StdioServerParameters(
664
+ command="uv", # Using uv to run the server
665
+ args=["run", "server", "completion", "stdio"], # Server with completion support
666
+ env={"UV_INDEX": os.environ.get("UV_INDEX", "")},
667
+ )
668
+
669
+
670
+ async def run():
671
+ """Run the completion client example."""
672
+ async with stdio_client(server_params) as (read, write):
673
+ async with ClientSession(read, write) as session:
674
+ # Initialize the connection
675
+ await session.initialize()
676
+
677
+ # List available resource templates
678
+ templates = await session.list_resource_templates()
679
+ print("Available resource templates:")
680
+ for template in templates.resourceTemplates:
681
+ print(f" - {template.uriTemplate}")
682
+
683
+ # List available prompts
684
+ prompts = await session.list_prompts()
685
+ print("\nAvailable prompts:")
686
+ for prompt in prompts.prompts:
687
+ print(f" - {prompt.name}")
688
+
689
+ # Complete resource template arguments
690
+ if templates.resourceTemplates:
691
+ template = templates.resourceTemplates[0]
692
+ print(f"\nCompleting arguments for resource template: {template.uriTemplate}")
693
+
694
+ # Complete without context
695
+ result = await session.complete(
696
+ ref=ResourceTemplateReference(type="ref/resource", uri=template.uriTemplate),
697
+ argument={"name": "owner", "value": "model"},
698
+ )
699
+ print(f"Completions for 'owner' starting with 'model': {result.completion.values}")
700
+
701
+ # Complete with context - repo suggestions based on owner
702
+ result = await session.complete(
703
+ ref=ResourceTemplateReference(type="ref/resource", uri=template.uriTemplate),
704
+ argument={"name": "repo", "value": ""},
705
+ context_arguments={"owner": "modelcontextprotocol"},
706
+ )
707
+ print(f"Completions for 'repo' with owner='modelcontextprotocol': {result.completion.values}")
708
+
709
+ # Complete prompt arguments
710
+ if prompts.prompts:
711
+ prompt_name = prompts.prompts[0].name
712
+ print(f"\nCompleting arguments for prompt: {prompt_name}")
713
+
714
+ result = await session.complete(
715
+ ref=PromptReference(type="ref/prompt", name=prompt_name),
716
+ argument={"name": "style", "value": ""},
717
+ )
718
+ print(f"Completions for 'style' argument: {result.completion.values}")
719
+
720
+
721
+ def main():
722
+ """Entry point for the completion client."""
723
+ asyncio.run(run())
724
+
725
+
726
+ if __name__ == "__main__":
727
+ main()
728
+ ```
729
+
730
+ _Full example: [examples/snippets/clients/completion_client.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/completion_client.py)_
731
+ <!-- /snippet-source -->
732
+ ### Elicitation
733
+
734
+ Request additional information from users. This example shows an Elicitation during a Tool Call:
735
+
736
+ <!-- snippet-source examples/snippets/servers/elicitation.py -->
737
+ ```python
738
+ from pydantic import BaseModel, Field
739
+
740
+ from mcp.server.fastmcp import Context, FastMCP
741
+ from mcp.server.session import ServerSession
742
+
743
+ mcp = FastMCP(name="Elicitation Example")
744
+
745
+
746
+ class BookingPreferences(BaseModel):
747
+ """Schema for collecting user preferences."""
748
+
749
+ checkAlternative: bool = Field(description="Would you like to check another date?")
750
+ alternativeDate: str = Field(
751
+ default="2024-12-26",
752
+ description="Alternative date (YYYY-MM-DD)",
753
+ )
754
+
755
+
756
+ @mcp.tool()
757
+ async def book_table(date: str, time: str, party_size: int, ctx: Context[ServerSession, None]) -> str:
758
+ """Book a table with date availability check."""
759
+ # Check if date is available
760
+ if date == "2024-12-25":
761
+ # Date unavailable - ask user for alternative
762
+ result = await ctx.elicit(
763
+ message=(f"No tables available for {party_size} on {date}. Would you like to try another date?"),
764
+ schema=BookingPreferences,
765
+ )
766
+
767
+ if result.action == "accept" and result.data:
768
+ if result.data.checkAlternative:
769
+ return f"[SUCCESS] Booked for {result.data.alternativeDate}"
770
+ return "[CANCELLED] No booking made"
771
+ return "[CANCELLED] Booking cancelled"
772
+
773
+ # Date available
774
+ return f"[SUCCESS] Booked for {date} at {time}"
775
+ ```
776
+
777
+ _Full example: [examples/snippets/servers/elicitation.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/elicitation.py)_
778
+ <!-- /snippet-source -->
779
+
780
+ The `elicit()` method returns an `ElicitationResult` with:
781
+
782
+ - `action`: "accept", "decline", or "cancel"
783
+ - `data`: The validated response (only when accepted)
784
+ - `validation_error`: Any validation error message
785
+
786
+ ### Sampling
787
+
788
+ Tools can interact with LLMs through sampling (generating text):
789
+
790
+ <!-- snippet-source examples/snippets/servers/sampling.py -->
791
+ ```python
792
+ from mcp.server.fastmcp import Context, FastMCP
793
+ from mcp.server.session import ServerSession
794
+ from mcp.types import SamplingMessage, TextContent
795
+
796
+ mcp = FastMCP(name="Sampling Example")
797
+
798
+
799
+ @mcp.tool()
800
+ async def generate_poem(topic: str, ctx: Context[ServerSession, None]) -> str:
801
+ """Generate a poem using LLM sampling."""
802
+ prompt = f"Write a short poem about {topic}"
803
+
804
+ result = await ctx.session.create_message(
805
+ messages=[
806
+ SamplingMessage(
807
+ role="user",
808
+ content=TextContent(type="text", text=prompt),
809
+ )
810
+ ],
811
+ max_tokens=100,
812
+ )
813
+
814
+ if result.content.type == "text":
815
+ return result.content.text
816
+ return str(result.content)
817
+ ```
818
+
819
+ _Full example: [examples/snippets/servers/sampling.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/sampling.py)_
820
+ <!-- /snippet-source -->
821
+
822
+ ### Logging and Notifications
823
+
824
+ Tools can send logs and notifications through the context:
825
+
826
+ <!-- snippet-source examples/snippets/servers/notifications.py -->
827
+ ```python
828
+ from mcp.server.fastmcp import Context, FastMCP
829
+ from mcp.server.session import ServerSession
830
+
831
+ mcp = FastMCP(name="Notifications Example")
832
+
833
+
834
+ @mcp.tool()
835
+ async def process_data(data: str, ctx: Context[ServerSession, None]) -> str:
836
+ """Process data with logging."""
837
+ # Different log levels
838
+ await ctx.debug(f"Debug: Processing '{data}'")
839
+ await ctx.info("Info: Starting processing")
840
+ await ctx.warning("Warning: This is experimental")
841
+ await ctx.error("Error: (This is just a demo)")
842
+
843
+ # Notify about resource changes
844
+ await ctx.session.send_resource_list_changed()
845
+
846
+ return f"Processed: {data}"
847
+ ```
848
+
849
+ _Full example: [examples/snippets/servers/notifications.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/notifications.py)_
850
+ <!-- /snippet-source -->
851
+
852
+ ### Authentication
853
+
854
+ Authentication can be used by servers that want to expose tools accessing protected resources.
855
+
856
+ `mcp.server.auth` implements OAuth 2.1 resource server functionality, where MCP servers act as Resource Servers (RS) that validate tokens issued by separate Authorization Servers (AS). This follows the [MCP authorization specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization) and implements RFC 9728 (Protected Resource Metadata) for AS discovery.
857
+
858
+ MCP servers can use authentication by providing an implementation of the `TokenVerifier` protocol:
859
+
860
+ <!-- snippet-source examples/snippets/servers/oauth_server.py -->
861
+ ```python
862
+ """
863
+ Run from the repository root:
864
+ uv run examples/snippets/servers/oauth_server.py
865
+ """
866
+
867
+ from pydantic import AnyHttpUrl
868
+
869
+ from mcp.server.auth.provider import AccessToken, TokenVerifier
870
+ from mcp.server.auth.settings import AuthSettings
871
+ from mcp.server.fastmcp import FastMCP
872
+
873
+
874
+ class SimpleTokenVerifier(TokenVerifier):
875
+ """Simple token verifier for demonstration."""
876
+
877
+ async def verify_token(self, token: str) -> AccessToken | None:
878
+ pass # This is where you would implement actual token validation
879
+
880
+
881
+ # Create FastMCP instance as a Resource Server
882
+ mcp = FastMCP(
883
+ "Weather Service",
884
+ # Token verifier for authentication
885
+ token_verifier=SimpleTokenVerifier(),
886
+ # Auth settings for RFC 9728 Protected Resource Metadata
887
+ auth=AuthSettings(
888
+ issuer_url=AnyHttpUrl("https://auth.example.com"), # Authorization Server URL
889
+ resource_server_url=AnyHttpUrl("http://localhost:3001"), # This server's URL
890
+ required_scopes=["user"],
891
+ ),
892
+ )
893
+
894
+
895
+ @mcp.tool()
896
+ async def get_weather(city: str = "London") -> dict[str, str]:
897
+ """Get weather data for a city"""
898
+ return {
899
+ "city": city,
900
+ "temperature": "22",
901
+ "condition": "Partly cloudy",
902
+ "humidity": "65%",
903
+ }
904
+
905
+
906
+ if __name__ == "__main__":
907
+ mcp.run(transport="streamable-http")
908
+ ```
909
+
910
+ _Full example: [examples/snippets/servers/oauth_server.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/oauth_server.py)_
911
+ <!-- /snippet-source -->
912
+
913
+ For a complete example with separate Authorization Server and Resource Server implementations, see [`examples/servers/simple-auth/`](examples/servers/simple-auth/).
914
+
915
+ **Architecture:**
916
+
917
+ - **Authorization Server (AS)**: Handles OAuth flows, user authentication, and token issuance
918
+ - **Resource Server (RS)**: Your MCP server that validates tokens and serves protected resources
919
+ - **Client**: Discovers AS through RFC 9728, obtains tokens, and uses them with the MCP server
920
+
921
+ See [TokenVerifier](src/mcp/server/auth/provider.py) for more details on implementing token validation.
922
+
923
+ ### FastMCP Properties
924
+
925
+ The FastMCP server instance accessible via `ctx.fastmcp` provides access to server configuration and metadata:
926
+
927
+ - `ctx.fastmcp.name` - The server's name as defined during initialization
928
+ - `ctx.fastmcp.instructions` - Server instructions/description provided to clients
929
+ - `ctx.fastmcp.settings` - Complete server configuration object containing:
930
+ - `debug` - Debug mode flag
931
+ - `log_level` - Current logging level
932
+ - `host` and `port` - Server network configuration
933
+ - `mount_path`, `sse_path`, `streamable_http_path` - Transport paths
934
+ - `stateless_http` - Whether the server operates in stateless mode
935
+ - And other configuration options
936
+
937
+ ```python
938
+ @mcp.tool()
939
+ def server_info(ctx: Context) -> dict:
940
+ """Get information about the current server."""
941
+ return {
942
+ "name": ctx.fastmcp.name,
943
+ "instructions": ctx.fastmcp.instructions,
944
+ "debug_mode": ctx.fastmcp.settings.debug,
945
+ "log_level": ctx.fastmcp.settings.log_level,
946
+ "host": ctx.fastmcp.settings.host,
947
+ "port": ctx.fastmcp.settings.port,
948
+ }
949
+ ```
950
+
951
+ ### Session Properties and Methods
952
+
953
+ The session object accessible via `ctx.session` provides advanced control over client communication:
954
+
955
+ - `ctx.session.client_params` - Client initialization parameters and declared capabilities
956
+ - `await ctx.session.send_log_message(level, data, logger)` - Send log messages with full control
957
+ - `await ctx.session.create_message(messages, max_tokens)` - Request LLM sampling/completion
958
+ - `await ctx.session.send_progress_notification(token, progress, total, message)` - Direct progress updates
959
+ - `await ctx.session.send_resource_updated(uri)` - Notify clients that a specific resource changed
960
+ - `await ctx.session.send_resource_list_changed()` - Notify clients that the resource list changed
961
+ - `await ctx.session.send_tool_list_changed()` - Notify clients that the tool list changed
962
+ - `await ctx.session.send_prompt_list_changed()` - Notify clients that the prompt list changed
963
+
964
+ ```python
965
+ @mcp.tool()
966
+ async def notify_data_update(resource_uri: str, ctx: Context) -> str:
967
+ """Update data and notify clients of the change."""
968
+ # Perform data update logic here
969
+
970
+ # Notify clients that this specific resource changed
971
+ await ctx.session.send_resource_updated(AnyUrl(resource_uri))
972
+
973
+ # If this affects the overall resource list, notify about that too
974
+ await ctx.session.send_resource_list_changed()
975
+
976
+ return f"Updated {resource_uri} and notified clients"
977
+ ```
978
+
979
+ ### Request Context Properties
980
+
981
+ The request context accessible via `ctx.request_context` contains request-specific information and resources:
982
+
983
+ - `ctx.request_context.lifespan_context` - Access to resources initialized during server startup
984
+ - Database connections, configuration objects, shared services
985
+ - Type-safe access to resources defined in your server's lifespan function
986
+ - `ctx.request_context.meta` - Request metadata from the client including:
987
+ - `progressToken` - Token for progress notifications
988
+ - Other client-provided metadata
989
+ - `ctx.request_context.request` - The original MCP request object for advanced processing
990
+ - `ctx.request_context.request_id` - Unique identifier for this request
991
+
992
+ ```python
993
+ # Example with typed lifespan context
994
+ @dataclass
995
+ class AppContext:
996
+ db: Database
997
+ config: AppConfig
998
+
999
+ @mcp.tool()
1000
+ def query_with_config(query: str, ctx: Context) -> str:
1001
+ """Execute a query using shared database and configuration."""
1002
+ # Access typed lifespan context
1003
+ app_ctx: AppContext = ctx.request_context.lifespan_context
1004
+
1005
+ # Use shared resources
1006
+ connection = app_ctx.db
1007
+ settings = app_ctx.config
1008
+
1009
+ # Execute query with configuration
1010
+ result = connection.execute(query, timeout=settings.query_timeout)
1011
+ return str(result)
1012
+ ```
1013
+
1014
+ _Full lifespan example: [examples/snippets/servers/lifespan_example.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/lifespan_example.py)_
1015
+
1016
+ ## Running Your Server
1017
+
1018
+ ### Development Mode
1019
+
1020
+ The fastest way to test and debug your server is with the MCP Inspector:
1021
+
1022
+ ```bash
1023
+ uv run mcp dev server.py
1024
+
1025
+ # Add dependencies
1026
+ uv run mcp dev server.py --with pandas --with numpy
1027
+
1028
+ # Mount local code
1029
+ uv run mcp dev server.py --with-editable .
1030
+ ```
1031
+
1032
+ ### Claude Desktop Integration
1033
+
1034
+ Once your server is ready, install it in Claude Desktop:
1035
+
1036
+ ```bash
1037
+ uv run mcp install server.py
1038
+
1039
+ # Custom name
1040
+ uv run mcp install server.py --name "My Analytics Server"
1041
+
1042
+ # Environment variables
1043
+ uv run mcp install server.py -v API_KEY=abc123 -v DB_URL=postgres://...
1044
+ uv run mcp install server.py -f .env
1045
+ ```
1046
+
1047
+ ### Direct Execution
1048
+
1049
+ For advanced scenarios like custom deployments:
1050
+
1051
+ <!-- snippet-source examples/snippets/servers/direct_execution.py -->
1052
+ ```python
1053
+ """Example showing direct execution of an MCP server.
1054
+
1055
+ This is the simplest way to run an MCP server directly.
1056
+ cd to the `examples/snippets` directory and run:
1057
+ uv run direct-execution-server
1058
+ or
1059
+ python servers/direct_execution.py
1060
+ """
1061
+
1062
+ from mcp.server.fastmcp import FastMCP
1063
+
1064
+ mcp = FastMCP("My App")
1065
+
1066
+
1067
+ @mcp.tool()
1068
+ def hello(name: str = "World") -> str:
1069
+ """Say hello to someone."""
1070
+ return f"Hello, {name}!"
1071
+
1072
+
1073
+ def main():
1074
+ """Entry point for the direct execution server."""
1075
+ mcp.run()
1076
+
1077
+
1078
+ if __name__ == "__main__":
1079
+ main()
1080
+ ```
1081
+
1082
+ _Full example: [examples/snippets/servers/direct_execution.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/direct_execution.py)_
1083
+ <!-- /snippet-source -->
1084
+
1085
+ Run it with:
1086
+
1087
+ ```bash
1088
+ python servers/direct_execution.py
1089
+ # or
1090
+ uv run mcp run servers/direct_execution.py
1091
+ ```
1092
+
1093
+ Note that `uv run mcp run` or `uv run mcp dev` only supports server using FastMCP and not the low-level server variant.
1094
+
1095
+ ### Streamable HTTP Transport
1096
+
1097
+ > **Note**: Streamable HTTP transport is superseding SSE transport for production deployments.
1098
+
1099
+ <!-- snippet-source examples/snippets/servers/streamable_config.py -->
1100
+ ```python
1101
+ """
1102
+ Run from the repository root:
1103
+ uv run examples/snippets/servers/streamable_config.py
1104
+ """
1105
+
1106
+ from mcp.server.fastmcp import FastMCP
1107
+
1108
+ # Stateful server (maintains session state)
1109
+ mcp = FastMCP("StatefulServer")
1110
+
1111
+ # Other configuration options:
1112
+ # Stateless server (no session persistence)
1113
+ # mcp = FastMCP("StatelessServer", stateless_http=True)
1114
+
1115
+ # Stateless server (no session persistence, no sse stream with supported client)
1116
+ # mcp = FastMCP("StatelessServer", stateless_http=True, json_response=True)
1117
+
1118
+
1119
+ # Add a simple tool to demonstrate the server
1120
+ @mcp.tool()
1121
+ def greet(name: str = "World") -> str:
1122
+ """Greet someone by name."""
1123
+ return f"Hello, {name}!"
1124
+
1125
+
1126
+ # Run server with streamable_http transport
1127
+ if __name__ == "__main__":
1128
+ mcp.run(transport="streamable-http")
1129
+ ```
1130
+
1131
+ _Full example: [examples/snippets/servers/streamable_config.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_config.py)_
1132
+ <!-- /snippet-source -->
1133
+
1134
+ You can mount multiple FastMCP servers in a Starlette application:
1135
+
1136
+ <!-- snippet-source examples/snippets/servers/streamable_starlette_mount.py -->
1137
+ ```python
1138
+ """
1139
+ Run from the repository root:
1140
+ uvicorn examples.snippets.servers.streamable_starlette_mount:app --reload
1141
+ """
1142
+
1143
+ import contextlib
1144
+
1145
+ from starlette.applications import Starlette
1146
+ from starlette.routing import Mount
1147
+
1148
+ from mcp.server.fastmcp import FastMCP
1149
+
1150
+ # Create the Echo server
1151
+ echo_mcp = FastMCP(name="EchoServer", stateless_http=True)
1152
+
1153
+
1154
+ @echo_mcp.tool()
1155
+ def echo(message: str) -> str:
1156
+ """A simple echo tool"""
1157
+ return f"Echo: {message}"
1158
+
1159
+
1160
+ # Create the Math server
1161
+ math_mcp = FastMCP(name="MathServer", stateless_http=True)
1162
+
1163
+
1164
+ @math_mcp.tool()
1165
+ def add_two(n: int) -> int:
1166
+ """Tool to add two to the input"""
1167
+ return n + 2
1168
+
1169
+
1170
+ # Create a combined lifespan to manage both session managers
1171
+ @contextlib.asynccontextmanager
1172
+ async def lifespan(app: Starlette):
1173
+ async with contextlib.AsyncExitStack() as stack:
1174
+ await stack.enter_async_context(echo_mcp.session_manager.run())
1175
+ await stack.enter_async_context(math_mcp.session_manager.run())
1176
+ yield
1177
+
1178
+
1179
+ # Create the Starlette app and mount the MCP servers
1180
+ app = Starlette(
1181
+ routes=[
1182
+ Mount("/echo", echo_mcp.streamable_http_app()),
1183
+ Mount("/math", math_mcp.streamable_http_app()),
1184
+ ],
1185
+ lifespan=lifespan,
1186
+ )
1187
+
1188
+ # Note: Clients connect to http://localhost:8000/echo/mcp and http://localhost:8000/math/mcp
1189
+ # To mount at the root of each path (e.g., /echo instead of /echo/mcp):
1190
+ # echo_mcp.settings.streamable_http_path = "/"
1191
+ # math_mcp.settings.streamable_http_path = "/"
1192
+ ```
1193
+
1194
+ _Full example: [examples/snippets/servers/streamable_starlette_mount.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_starlette_mount.py)_
1195
+ <!-- /snippet-source -->
1196
+
1197
+ For low level server with Streamable HTTP implementations, see:
1198
+
1199
+ - Stateful server: [`examples/servers/simple-streamablehttp/`](examples/servers/simple-streamablehttp/)
1200
+ - Stateless server: [`examples/servers/simple-streamablehttp-stateless/`](examples/servers/simple-streamablehttp-stateless/)
1201
+
1202
+ The streamable HTTP transport supports:
1203
+
1204
+ - Stateful and stateless operation modes
1205
+ - Resumability with event stores
1206
+ - JSON or SSE response formats
1207
+ - Better scalability for multi-node deployments
1208
+
1209
+ #### CORS Configuration for Browser-Based Clients
1210
+
1211
+ 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:
1212
+
1213
+ ```python
1214
+ from starlette.applications import Starlette
1215
+ from starlette.middleware.cors import CORSMiddleware
1216
+
1217
+ # Create your Starlette app first
1218
+ starlette_app = Starlette(routes=[...])
1219
+
1220
+ # Then wrap it with CORS middleware
1221
+ starlette_app = CORSMiddleware(
1222
+ starlette_app,
1223
+ allow_origins=["*"], # Configure appropriately for production
1224
+ allow_methods=["GET", "POST", "DELETE"], # MCP streamable HTTP methods
1225
+ expose_headers=["Mcp-Session-Id"],
1226
+ )
1227
+ ```
1228
+
1229
+ This configuration is necessary because:
1230
+
1231
+ - The MCP streamable HTTP transport uses the `Mcp-Session-Id` header for session management
1232
+ - Browsers restrict access to response headers unless explicitly exposed via CORS
1233
+ - Without this configuration, browser-based clients won't be able to read the session ID from initialization responses
1234
+
1235
+ ### Mounting to an Existing ASGI Server
1236
+
1237
+ By default, SSE servers are mounted at `/sse` and Streamable HTTP servers are mounted at `/mcp`. You can customize these paths using the methods described below.
1238
+
1239
+ For more information on mounting applications in Starlette, see the [Starlette documentation](https://www.starlette.io/routing/#submounting-routes).
1240
+
1241
+ #### StreamableHTTP servers
1242
+
1243
+ You can mount the StreamableHTTP server to an existing ASGI server using the `streamable_http_app` method. This allows you to integrate the StreamableHTTP server with other ASGI applications.
1244
+
1245
+ ##### Basic mounting
1246
+
1247
+ <!-- snippet-source examples/snippets/servers/streamable_http_basic_mounting.py -->
1248
+ ```python
1249
+ """
1250
+ Basic example showing how to mount StreamableHTTP server in Starlette.
1251
+
1252
+ Run from the repository root:
1253
+ uvicorn examples.snippets.servers.streamable_http_basic_mounting:app --reload
1254
+ """
1255
+
1256
+ from starlette.applications import Starlette
1257
+ from starlette.routing import Mount
1258
+
1259
+ from mcp.server.fastmcp import FastMCP
1260
+
1261
+ # Create MCP server
1262
+ mcp = FastMCP("My App")
1263
+
1264
+
1265
+ @mcp.tool()
1266
+ def hello() -> str:
1267
+ """A simple hello tool"""
1268
+ return "Hello from MCP!"
1269
+
1270
+
1271
+ # Mount the StreamableHTTP server to the existing ASGI server
1272
+ app = Starlette(
1273
+ routes=[
1274
+ Mount("/", app=mcp.streamable_http_app()),
1275
+ ]
1276
+ )
1277
+ ```
1278
+
1279
+ _Full example: [examples/snippets/servers/streamable_http_basic_mounting.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_http_basic_mounting.py)_
1280
+ <!-- /snippet-source -->
1281
+
1282
+ ##### Host-based routing
1283
+
1284
+ <!-- snippet-source examples/snippets/servers/streamable_http_host_mounting.py -->
1285
+ ```python
1286
+ """
1287
+ Example showing how to mount StreamableHTTP server using Host-based routing.
1288
+
1289
+ Run from the repository root:
1290
+ uvicorn examples.snippets.servers.streamable_http_host_mounting:app --reload
1291
+ """
1292
+
1293
+ from starlette.applications import Starlette
1294
+ from starlette.routing import Host
1295
+
1296
+ from mcp.server.fastmcp import FastMCP
1297
+
1298
+ # Create MCP server
1299
+ mcp = FastMCP("MCP Host App")
1300
+
1301
+
1302
+ @mcp.tool()
1303
+ def domain_info() -> str:
1304
+ """Get domain-specific information"""
1305
+ return "This is served from mcp.acme.corp"
1306
+
1307
+
1308
+ # Mount using Host-based routing
1309
+ app = Starlette(
1310
+ routes=[
1311
+ Host("mcp.acme.corp", app=mcp.streamable_http_app()),
1312
+ ]
1313
+ )
1314
+ ```
1315
+
1316
+ _Full example: [examples/snippets/servers/streamable_http_host_mounting.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_http_host_mounting.py)_
1317
+ <!-- /snippet-source -->
1318
+
1319
+ ##### Multiple servers with path configuration
1320
+
1321
+ <!-- snippet-source examples/snippets/servers/streamable_http_multiple_servers.py -->
1322
+ ```python
1323
+ """
1324
+ Example showing how to mount multiple StreamableHTTP servers with path configuration.
1325
+
1326
+ Run from the repository root:
1327
+ uvicorn examples.snippets.servers.streamable_http_multiple_servers:app --reload
1328
+ """
1329
+
1330
+ from starlette.applications import Starlette
1331
+ from starlette.routing import Mount
1332
+
1333
+ from mcp.server.fastmcp import FastMCP
1334
+
1335
+ # Create multiple MCP servers
1336
+ api_mcp = FastMCP("API Server")
1337
+ chat_mcp = FastMCP("Chat Server")
1338
+
1339
+
1340
+ @api_mcp.tool()
1341
+ def api_status() -> str:
1342
+ """Get API status"""
1343
+ return "API is running"
1344
+
1345
+
1346
+ @chat_mcp.tool()
1347
+ def send_message(message: str) -> str:
1348
+ """Send a chat message"""
1349
+ return f"Message sent: {message}"
1350
+
1351
+
1352
+ # Configure servers to mount at the root of each path
1353
+ # This means endpoints will be at /api and /chat instead of /api/mcp and /chat/mcp
1354
+ api_mcp.settings.streamable_http_path = "/"
1355
+ chat_mcp.settings.streamable_http_path = "/"
1356
+
1357
+ # Mount the servers
1358
+ app = Starlette(
1359
+ routes=[
1360
+ Mount("/api", app=api_mcp.streamable_http_app()),
1361
+ Mount("/chat", app=chat_mcp.streamable_http_app()),
1362
+ ]
1363
+ )
1364
+ ```
1365
+
1366
+ _Full example: [examples/snippets/servers/streamable_http_multiple_servers.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_http_multiple_servers.py)_
1367
+ <!-- /snippet-source -->
1368
+
1369
+ ##### Path configuration at initialization
1370
+
1371
+ <!-- snippet-source examples/snippets/servers/streamable_http_path_config.py -->
1372
+ ```python
1373
+ """
1374
+ Example showing path configuration during FastMCP initialization.
1375
+
1376
+ Run from the repository root:
1377
+ uvicorn examples.snippets.servers.streamable_http_path_config:app --reload
1378
+ """
1379
+
1380
+ from starlette.applications import Starlette
1381
+ from starlette.routing import Mount
1382
+
1383
+ from mcp.server.fastmcp import FastMCP
1384
+
1385
+ # Configure streamable_http_path during initialization
1386
+ # This server will mount at the root of wherever it's mounted
1387
+ mcp_at_root = FastMCP("My Server", streamable_http_path="/")
1388
+
1389
+
1390
+ @mcp_at_root.tool()
1391
+ def process_data(data: str) -> str:
1392
+ """Process some data"""
1393
+ return f"Processed: {data}"
1394
+
1395
+
1396
+ # Mount at /process - endpoints will be at /process instead of /process/mcp
1397
+ app = Starlette(
1398
+ routes=[
1399
+ Mount("/process", app=mcp_at_root.streamable_http_app()),
1400
+ ]
1401
+ )
1402
+ ```
1403
+
1404
+ _Full example: [examples/snippets/servers/streamable_http_path_config.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/streamable_http_path_config.py)_
1405
+ <!-- /snippet-source -->
1406
+
1407
+ #### SSE servers
1408
+
1409
+ > **Note**: SSE transport is being superseded by [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http).
1410
+
1411
+ You can mount the SSE server to an existing ASGI server using the `sse_app` method. This allows you to integrate the SSE server with other ASGI applications.
1412
+
1413
+ ```python
1414
+ from starlette.applications import Starlette
1415
+ from starlette.routing import Mount, Host
1416
+ from mcp.server.fastmcp import FastMCP
1417
+
1418
+
1419
+ mcp = FastMCP("My App")
1420
+
1421
+ # Mount the SSE server to the existing ASGI server
1422
+ app = Starlette(
1423
+ routes=[
1424
+ Mount('/', app=mcp.sse_app()),
1425
+ ]
1426
+ )
1427
+
1428
+ # or dynamically mount as host
1429
+ app.router.routes.append(Host('mcp.acme.corp', app=mcp.sse_app()))
1430
+ ```
1431
+
1432
+ When mounting multiple MCP servers under different paths, you can configure the mount path in several ways:
1433
+
1434
+ ```python
1435
+ from starlette.applications import Starlette
1436
+ from starlette.routing import Mount
1437
+ from mcp.server.fastmcp import FastMCP
1438
+
1439
+ # Create multiple MCP servers
1440
+ github_mcp = FastMCP("GitHub API")
1441
+ browser_mcp = FastMCP("Browser")
1442
+ curl_mcp = FastMCP("Curl")
1443
+ search_mcp = FastMCP("Search")
1444
+
1445
+ # Method 1: Configure mount paths via settings (recommended for persistent configuration)
1446
+ github_mcp.settings.mount_path = "/github"
1447
+ browser_mcp.settings.mount_path = "/browser"
1448
+
1449
+ # Method 2: Pass mount path directly to sse_app (preferred for ad-hoc mounting)
1450
+ # This approach doesn't modify the server's settings permanently
1451
+
1452
+ # Create Starlette app with multiple mounted servers
1453
+ app = Starlette(
1454
+ routes=[
1455
+ # Using settings-based configuration
1456
+ Mount("/github", app=github_mcp.sse_app()),
1457
+ Mount("/browser", app=browser_mcp.sse_app()),
1458
+ # Using direct mount path parameter
1459
+ Mount("/curl", app=curl_mcp.sse_app("/curl")),
1460
+ Mount("/search", app=search_mcp.sse_app("/search")),
1461
+ ]
1462
+ )
1463
+
1464
+ # Method 3: For direct execution, you can also pass the mount path to run()
1465
+ if __name__ == "__main__":
1466
+ search_mcp.run(transport="sse", mount_path="/search")
1467
+ ```
1468
+
1469
+ For more information on mounting applications in Starlette, see the [Starlette documentation](https://www.starlette.io/routing/#submounting-routes).
1470
+
1471
+ ## Advanced Usage
1472
+
1473
+ ### Low-Level Server
1474
+
1475
+ For more control, you can use the low-level server implementation directly. This gives you full access to the protocol and allows you to customize every aspect of your server, including lifecycle management through the lifespan API:
1476
+
1477
+ <!-- snippet-source examples/snippets/servers/lowlevel/lifespan.py -->
1478
+ ```python
1479
+ """
1480
+ Run from the repository root:
1481
+ uv run examples/snippets/servers/lowlevel/lifespan.py
1482
+ """
1483
+
1484
+ from collections.abc import AsyncIterator
1485
+ from contextlib import asynccontextmanager
1486
+ from typing import Any
1487
+
1488
+ import mcp.server.stdio
1489
+ import mcp.types as types
1490
+ from mcp.server.lowlevel import NotificationOptions, Server
1491
+ from mcp.server.models import InitializationOptions
1492
+
1493
+
1494
+ # Mock database class for example
1495
+ class Database:
1496
+ """Mock database class for example."""
1497
+
1498
+ @classmethod
1499
+ async def connect(cls) -> "Database":
1500
+ """Connect to database."""
1501
+ print("Database connected")
1502
+ return cls()
1503
+
1504
+ async def disconnect(self) -> None:
1505
+ """Disconnect from database."""
1506
+ print("Database disconnected")
1507
+
1508
+ async def query(self, query_str: str) -> list[dict[str, str]]:
1509
+ """Execute a query."""
1510
+ # Simulate database query
1511
+ return [{"id": "1", "name": "Example", "query": query_str}]
1512
+
1513
+
1514
+ @asynccontextmanager
1515
+ async def server_lifespan(_server: Server) -> AsyncIterator[dict[str, Any]]:
1516
+ """Manage server startup and shutdown lifecycle."""
1517
+ # Initialize resources on startup
1518
+ db = await Database.connect()
1519
+ try:
1520
+ yield {"db": db}
1521
+ finally:
1522
+ # Clean up on shutdown
1523
+ await db.disconnect()
1524
+
1525
+
1526
+ # Pass lifespan to server
1527
+ server = Server("example-server", lifespan=server_lifespan)
1528
+
1529
+
1530
+ @server.list_tools()
1531
+ async def handle_list_tools() -> list[types.Tool]:
1532
+ """List available tools."""
1533
+ return [
1534
+ types.Tool(
1535
+ name="query_db",
1536
+ description="Query the database",
1537
+ inputSchema={
1538
+ "type": "object",
1539
+ "properties": {"query": {"type": "string", "description": "SQL query to execute"}},
1540
+ "required": ["query"],
1541
+ },
1542
+ )
1543
+ ]
1544
+
1545
+
1546
+ @server.call_tool()
1547
+ async def query_db(name: str, arguments: dict[str, Any]) -> list[types.TextContent]:
1548
+ """Handle database query tool call."""
1549
+ if name != "query_db":
1550
+ raise ValueError(f"Unknown tool: {name}")
1551
+
1552
+ # Access lifespan context
1553
+ ctx = server.request_context
1554
+ db = ctx.lifespan_context["db"]
1555
+
1556
+ # Execute query
1557
+ results = await db.query(arguments["query"])
1558
+
1559
+ return [types.TextContent(type="text", text=f"Query results: {results}")]
1560
+
1561
+
1562
+ async def run():
1563
+ """Run the server with lifespan management."""
1564
+ async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
1565
+ await server.run(
1566
+ read_stream,
1567
+ write_stream,
1568
+ InitializationOptions(
1569
+ server_name="example-server",
1570
+ server_version="0.1.0",
1571
+ capabilities=server.get_capabilities(
1572
+ notification_options=NotificationOptions(),
1573
+ experimental_capabilities={},
1574
+ ),
1575
+ ),
1576
+ )
1577
+
1578
+
1579
+ if __name__ == "__main__":
1580
+ import asyncio
1581
+
1582
+ asyncio.run(run())
1583
+ ```
1584
+
1585
+ _Full example: [examples/snippets/servers/lowlevel/lifespan.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/lowlevel/lifespan.py)_
1586
+ <!-- /snippet-source -->
1587
+
1588
+ The lifespan API provides:
1589
+
1590
+ - A way to initialize resources when the server starts and clean them up when it stops
1591
+ - Access to initialized resources through the request context in handlers
1592
+ - Type-safe context passing between lifespan and request handlers
1593
+
1594
+ <!-- snippet-source examples/snippets/servers/lowlevel/basic.py -->
1595
+ ```python
1596
+ """
1597
+ Run from the repository root:
1598
+ uv run examples/snippets/servers/lowlevel/basic.py
1599
+ """
1600
+
1601
+ import asyncio
1602
+
1603
+ import mcp.server.stdio
1604
+ import mcp.types as types
1605
+ from mcp.server.lowlevel import NotificationOptions, Server
1606
+ from mcp.server.models import InitializationOptions
1607
+
1608
+ # Create a server instance
1609
+ server = Server("example-server")
1610
+
1611
+
1612
+ @server.list_prompts()
1613
+ async def handle_list_prompts() -> list[types.Prompt]:
1614
+ """List available prompts."""
1615
+ return [
1616
+ types.Prompt(
1617
+ name="example-prompt",
1618
+ description="An example prompt template",
1619
+ arguments=[types.PromptArgument(name="arg1", description="Example argument", required=True)],
1620
+ )
1621
+ ]
1622
+
1623
+
1624
+ @server.get_prompt()
1625
+ async def handle_get_prompt(name: str, arguments: dict[str, str] | None) -> types.GetPromptResult:
1626
+ """Get a specific prompt by name."""
1627
+ if name != "example-prompt":
1628
+ raise ValueError(f"Unknown prompt: {name}")
1629
+
1630
+ arg1_value = (arguments or {}).get("arg1", "default")
1631
+
1632
+ return types.GetPromptResult(
1633
+ description="Example prompt",
1634
+ messages=[
1635
+ types.PromptMessage(
1636
+ role="user",
1637
+ content=types.TextContent(type="text", text=f"Example prompt text with argument: {arg1_value}"),
1638
+ )
1639
+ ],
1640
+ )
1641
+
1642
+
1643
+ async def run():
1644
+ """Run the basic low-level server."""
1645
+ async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
1646
+ await server.run(
1647
+ read_stream,
1648
+ write_stream,
1649
+ InitializationOptions(
1650
+ server_name="example",
1651
+ server_version="0.1.0",
1652
+ capabilities=server.get_capabilities(
1653
+ notification_options=NotificationOptions(),
1654
+ experimental_capabilities={},
1655
+ ),
1656
+ ),
1657
+ )
1658
+
1659
+
1660
+ if __name__ == "__main__":
1661
+ asyncio.run(run())
1662
+ ```
1663
+
1664
+ _Full example: [examples/snippets/servers/lowlevel/basic.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/lowlevel/basic.py)_
1665
+ <!-- /snippet-source -->
1666
+
1667
+ Caution: The `uv run mcp run` and `uv run mcp dev` tool doesn't support low-level server.
1668
+
1669
+ #### Structured Output Support
1670
+
1671
+ The low-level server supports structured output for tools, allowing you to return both human-readable content and machine-readable structured data. Tools can define an `outputSchema` to validate their structured output:
1672
+
1673
+ <!-- snippet-source examples/snippets/servers/lowlevel/structured_output.py -->
1674
+ ```python
1675
+ """
1676
+ Run from the repository root:
1677
+ uv run examples/snippets/servers/lowlevel/structured_output.py
1678
+ """
1679
+
1680
+ import asyncio
1681
+ from typing import Any
1682
+
1683
+ import mcp.server.stdio
1684
+ import mcp.types as types
1685
+ from mcp.server.lowlevel import NotificationOptions, Server
1686
+ from mcp.server.models import InitializationOptions
1687
+
1688
+ server = Server("example-server")
1689
+
1690
+
1691
+ @server.list_tools()
1692
+ async def list_tools() -> list[types.Tool]:
1693
+ """List available tools with structured output schemas."""
1694
+ return [
1695
+ types.Tool(
1696
+ name="get_weather",
1697
+ description="Get current weather for a city",
1698
+ inputSchema={
1699
+ "type": "object",
1700
+ "properties": {"city": {"type": "string", "description": "City name"}},
1701
+ "required": ["city"],
1702
+ },
1703
+ outputSchema={
1704
+ "type": "object",
1705
+ "properties": {
1706
+ "temperature": {"type": "number", "description": "Temperature in Celsius"},
1707
+ "condition": {"type": "string", "description": "Weather condition"},
1708
+ "humidity": {"type": "number", "description": "Humidity percentage"},
1709
+ "city": {"type": "string", "description": "City name"},
1710
+ },
1711
+ "required": ["temperature", "condition", "humidity", "city"],
1712
+ },
1713
+ )
1714
+ ]
1715
+
1716
+
1717
+ @server.call_tool()
1718
+ async def call_tool(name: str, arguments: dict[str, Any]) -> dict[str, Any]:
1719
+ """Handle tool calls with structured output."""
1720
+ if name == "get_weather":
1721
+ city = arguments["city"]
1722
+
1723
+ # Simulated weather data - in production, call a weather API
1724
+ weather_data = {
1725
+ "temperature": 22.5,
1726
+ "condition": "partly cloudy",
1727
+ "humidity": 65,
1728
+ "city": city, # Include the requested city
1729
+ }
1730
+
1731
+ # low-level server will validate structured output against the tool's
1732
+ # output schema, and additionally serialize it into a TextContent block
1733
+ # for backwards compatibility with pre-2025-06-18 clients.
1734
+ return weather_data
1735
+ else:
1736
+ raise ValueError(f"Unknown tool: {name}")
1737
+
1738
+
1739
+ async def run():
1740
+ """Run the structured output server."""
1741
+ async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
1742
+ await server.run(
1743
+ read_stream,
1744
+ write_stream,
1745
+ InitializationOptions(
1746
+ server_name="structured-output-example",
1747
+ server_version="0.1.0",
1748
+ capabilities=server.get_capabilities(
1749
+ notification_options=NotificationOptions(),
1750
+ experimental_capabilities={},
1751
+ ),
1752
+ ),
1753
+ )
1754
+
1755
+
1756
+ if __name__ == "__main__":
1757
+ asyncio.run(run())
1758
+ ```
1759
+
1760
+ _Full example: [examples/snippets/servers/lowlevel/structured_output.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/lowlevel/structured_output.py)_
1761
+ <!-- /snippet-source -->
1762
+
1763
+ Tools can return data in three ways:
1764
+
1765
+ 1. **Content only**: Return a list of content blocks (default behavior before spec revision 2025-06-18)
1766
+ 2. **Structured data only**: Return a dictionary that will be serialized to JSON (Introduced in spec revision 2025-06-18)
1767
+ 3. **Both**: Return a tuple of (content, structured_data) preferred option to use for backwards compatibility
1768
+
1769
+ When an `outputSchema` is defined, the server automatically validates the structured output against the schema. This ensures type safety and helps catch errors early.
1770
+
1771
+ ### Writing MCP Clients
1772
+
1773
+ The SDK provides a high-level client interface for connecting to MCP servers using various [transports](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports):
1774
+
1775
+ <!-- snippet-source examples/snippets/clients/stdio_client.py -->
1776
+ ```python
1777
+ """
1778
+ cd to the `examples/snippets/clients` directory and run:
1779
+ uv run client
1780
+ """
1781
+
1782
+ import asyncio
1783
+ import os
1784
+
1785
+ from pydantic import AnyUrl
1786
+
1787
+ from mcp import ClientSession, StdioServerParameters, types
1788
+ from mcp.client.stdio import stdio_client
1789
+ from mcp.shared.context import RequestContext
1790
+
1791
+ # Create server parameters for stdio connection
1792
+ server_params = StdioServerParameters(
1793
+ command="uv", # Using uv to run the server
1794
+ args=["run", "server", "fastmcp_quickstart", "stdio"], # We're already in snippets dir
1795
+ env={"UV_INDEX": os.environ.get("UV_INDEX", "")},
1796
+ )
1797
+
1798
+
1799
+ # Optional: create a sampling callback
1800
+ async def handle_sampling_message(
1801
+ context: RequestContext[ClientSession, None], params: types.CreateMessageRequestParams
1802
+ ) -> types.CreateMessageResult:
1803
+ print(f"Sampling request: {params.messages}")
1804
+ return types.CreateMessageResult(
1805
+ role="assistant",
1806
+ content=types.TextContent(
1807
+ type="text",
1808
+ text="Hello, world! from model",
1809
+ ),
1810
+ model="gpt-3.5-turbo",
1811
+ stopReason="endTurn",
1812
+ )
1813
+
1814
+
1815
+ async def run():
1816
+ async with stdio_client(server_params) as (read, write):
1817
+ async with ClientSession(read, write, sampling_callback=handle_sampling_message) as session:
1818
+ # Initialize the connection
1819
+ await session.initialize()
1820
+
1821
+ # List available prompts
1822
+ prompts = await session.list_prompts()
1823
+ print(f"Available prompts: {[p.name for p in prompts.prompts]}")
1824
+
1825
+ # Get a prompt (greet_user prompt from fastmcp_quickstart)
1826
+ if prompts.prompts:
1827
+ prompt = await session.get_prompt("greet_user", arguments={"name": "Alice", "style": "friendly"})
1828
+ print(f"Prompt result: {prompt.messages[0].content}")
1829
+
1830
+ # List available resources
1831
+ resources = await session.list_resources()
1832
+ print(f"Available resources: {[r.uri for r in resources.resources]}")
1833
+
1834
+ # List available tools
1835
+ tools = await session.list_tools()
1836
+ print(f"Available tools: {[t.name for t in tools.tools]}")
1837
+
1838
+ # Read a resource (greeting resource from fastmcp_quickstart)
1839
+ resource_content = await session.read_resource(AnyUrl("greeting://World"))
1840
+ content_block = resource_content.contents[0]
1841
+ if isinstance(content_block, types.TextContent):
1842
+ print(f"Resource content: {content_block.text}")
1843
+
1844
+ # Call a tool (add tool from fastmcp_quickstart)
1845
+ result = await session.call_tool("add", arguments={"a": 5, "b": 3})
1846
+ result_unstructured = result.content[0]
1847
+ if isinstance(result_unstructured, types.TextContent):
1848
+ print(f"Tool result: {result_unstructured.text}")
1849
+ result_structured = result.structuredContent
1850
+ print(f"Structured tool result: {result_structured}")
1851
+
1852
+
1853
+ def main():
1854
+ """Entry point for the client script."""
1855
+ asyncio.run(run())
1856
+
1857
+
1858
+ if __name__ == "__main__":
1859
+ main()
1860
+ ```
1861
+
1862
+ _Full example: [examples/snippets/clients/stdio_client.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/stdio_client.py)_
1863
+ <!-- /snippet-source -->
1864
+
1865
+ Clients can also connect using [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http):
1866
+
1867
+ <!-- snippet-source examples/snippets/clients/streamable_basic.py -->
1868
+ ```python
1869
+ """
1870
+ Run from the repository root:
1871
+ uv run examples/snippets/clients/streamable_basic.py
1872
+ """
1873
+
1874
+ import asyncio
1875
+
1876
+ from mcp import ClientSession
1877
+ from mcp.client.streamable_http import streamablehttp_client
1878
+
1879
+
1880
+ async def main():
1881
+ # Connect to a streamable HTTP server
1882
+ async with streamablehttp_client("http://localhost:8000/mcp") as (
1883
+ read_stream,
1884
+ write_stream,
1885
+ _,
1886
+ ):
1887
+ # Create a session using the client streams
1888
+ async with ClientSession(read_stream, write_stream) as session:
1889
+ # Initialize the connection
1890
+ await session.initialize()
1891
+ # List available tools
1892
+ tools = await session.list_tools()
1893
+ print(f"Available tools: {[tool.name for tool in tools.tools]}")
1894
+
1895
+
1896
+ if __name__ == "__main__":
1897
+ asyncio.run(main())
1898
+ ```
1899
+
1900
+ _Full example: [examples/snippets/clients/streamable_basic.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/streamable_basic.py)_
1901
+ <!-- /snippet-source -->
1902
+
1903
+ ### Client Display Utilities
1904
+
1905
+ When building MCP clients, the SDK provides utilities to help display human-readable names for tools, resources, and prompts:
1906
+
1907
+ <!-- snippet-source examples/snippets/clients/display_utilities.py -->
1908
+ ```python
1909
+ """
1910
+ cd to the `examples/snippets` directory and run:
1911
+ uv run display-utilities-client
1912
+ """
1913
+
1914
+ import asyncio
1915
+ import os
1916
+
1917
+ from mcp import ClientSession, StdioServerParameters
1918
+ from mcp.client.stdio import stdio_client
1919
+ from mcp.shared.metadata_utils import get_display_name
1920
+
1921
+ # Create server parameters for stdio connection
1922
+ server_params = StdioServerParameters(
1923
+ command="uv", # Using uv to run the server
1924
+ args=["run", "server", "fastmcp_quickstart", "stdio"],
1925
+ env={"UV_INDEX": os.environ.get("UV_INDEX", "")},
1926
+ )
1927
+
1928
+
1929
+ async def display_tools(session: ClientSession):
1930
+ """Display available tools with human-readable names"""
1931
+ tools_response = await session.list_tools()
1932
+
1933
+ for tool in tools_response.tools:
1934
+ # get_display_name() returns the title if available, otherwise the name
1935
+ display_name = get_display_name(tool)
1936
+ print(f"Tool: {display_name}")
1937
+ if tool.description:
1938
+ print(f" {tool.description}")
1939
+
1940
+
1941
+ async def display_resources(session: ClientSession):
1942
+ """Display available resources with human-readable names"""
1943
+ resources_response = await session.list_resources()
1944
+
1945
+ for resource in resources_response.resources:
1946
+ display_name = get_display_name(resource)
1947
+ print(f"Resource: {display_name} ({resource.uri})")
1948
+
1949
+ templates_response = await session.list_resource_templates()
1950
+ for template in templates_response.resourceTemplates:
1951
+ display_name = get_display_name(template)
1952
+ print(f"Resource Template: {display_name}")
1953
+
1954
+
1955
+ async def run():
1956
+ """Run the display utilities example."""
1957
+ async with stdio_client(server_params) as (read, write):
1958
+ async with ClientSession(read, write) as session:
1959
+ # Initialize the connection
1960
+ await session.initialize()
1961
+
1962
+ print("=== Available Tools ===")
1963
+ await display_tools(session)
1964
+
1965
+ print("\n=== Available Resources ===")
1966
+ await display_resources(session)
1967
+
1968
+
1969
+ def main():
1970
+ """Entry point for the display utilities client."""
1971
+ asyncio.run(run())
1972
+
1973
+
1974
+ if __name__ == "__main__":
1975
+ main()
1976
+ ```
1977
+
1978
+ _Full example: [examples/snippets/clients/display_utilities.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/display_utilities.py)_
1979
+ <!-- /snippet-source -->
1980
+
1981
+ The `get_display_name()` function implements the proper precedence rules for displaying names:
1982
+
1983
+ - For tools: `title` > `annotations.title` > `name`
1984
+ - For other objects: `title` > `name`
1985
+
1986
+ This ensures your client UI shows the most user-friendly names that servers provide.
1987
+
1988
+ ### OAuth Authentication for Clients
1989
+
1990
+ The SDK includes [authorization support](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization) for connecting to protected MCP servers:
1991
+
1992
+ <!-- snippet-source examples/snippets/clients/oauth_client.py -->
1993
+ ```python
1994
+ """
1995
+ Before running, specify running MCP RS server URL.
1996
+ To spin up RS server locally, see
1997
+ examples/servers/simple-auth/README.md
1998
+
1999
+ cd to the `examples/snippets` directory and run:
2000
+ uv run oauth-client
2001
+ """
2002
+
2003
+ import asyncio
2004
+ from urllib.parse import parse_qs, urlparse
2005
+
2006
+ from pydantic import AnyUrl
2007
+
2008
+ from mcp import ClientSession
2009
+ from mcp.client.auth import OAuthClientProvider, TokenStorage
2010
+ from mcp.client.streamable_http import streamablehttp_client
2011
+ from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthToken
2012
+
2013
+
2014
+ class InMemoryTokenStorage(TokenStorage):
2015
+ """Demo In-memory token storage implementation."""
2016
+
2017
+ def __init__(self):
2018
+ self.tokens: OAuthToken | None = None
2019
+ self.client_info: OAuthClientInformationFull | None = None
2020
+
2021
+ async def get_tokens(self) -> OAuthToken | None:
2022
+ """Get stored tokens."""
2023
+ return self.tokens
2024
+
2025
+ async def set_tokens(self, tokens: OAuthToken) -> None:
2026
+ """Store tokens."""
2027
+ self.tokens = tokens
2028
+
2029
+ async def get_client_info(self) -> OAuthClientInformationFull | None:
2030
+ """Get stored client information."""
2031
+ return self.client_info
2032
+
2033
+ async def set_client_info(self, client_info: OAuthClientInformationFull) -> None:
2034
+ """Store client information."""
2035
+ self.client_info = client_info
2036
+
2037
+
2038
+ async def handle_redirect(auth_url: str) -> None:
2039
+ print(f"Visit: {auth_url}")
2040
+
2041
+
2042
+ async def handle_callback() -> tuple[str, str | None]:
2043
+ callback_url = input("Paste callback URL: ")
2044
+ params = parse_qs(urlparse(callback_url).query)
2045
+ return params["code"][0], params.get("state", [None])[0]
2046
+
2047
+
2048
+ async def main():
2049
+ """Run the OAuth client example."""
2050
+ oauth_auth = OAuthClientProvider(
2051
+ server_url="http://localhost:8001",
2052
+ client_metadata=OAuthClientMetadata(
2053
+ client_name="Example MCP Client",
2054
+ redirect_uris=[AnyUrl("http://localhost:3000/callback")],
2055
+ grant_types=["authorization_code", "refresh_token"],
2056
+ response_types=["code"],
2057
+ scope="user",
2058
+ ),
2059
+ storage=InMemoryTokenStorage(),
2060
+ redirect_handler=handle_redirect,
2061
+ callback_handler=handle_callback,
2062
+ )
2063
+
2064
+ async with streamablehttp_client("http://localhost:8001/mcp", auth=oauth_auth) as (read, write, _):
2065
+ async with ClientSession(read, write) as session:
2066
+ await session.initialize()
2067
+
2068
+ tools = await session.list_tools()
2069
+ print(f"Available tools: {[tool.name for tool in tools.tools]}")
2070
+
2071
+ resources = await session.list_resources()
2072
+ print(f"Available resources: {[r.uri for r in resources.resources]}")
2073
+
2074
+
2075
+ def run():
2076
+ asyncio.run(main())
2077
+
2078
+
2079
+ if __name__ == "__main__":
2080
+ run()
2081
+ ```
2082
+
2083
+ _Full example: [examples/snippets/clients/oauth_client.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/oauth_client.py)_
2084
+ <!-- /snippet-source -->
2085
+
2086
+ For a complete working example, see [`examples/clients/simple-auth-client/`](examples/clients/simple-auth-client/).
2087
+
2088
+ ### Parsing Tool Results
2089
+
2090
+ When calling tools through MCP, the `CallToolResult` object contains the tool's response in a structured format. Understanding how to parse this result is essential for properly handling tool outputs.
2091
+
2092
+ ```python
2093
+ """examples/snippets/clients/parsing_tool_results.py"""
2094
+
2095
+ import asyncio
2096
+
2097
+ from mcp import ClientSession, StdioServerParameters, types
2098
+ from mcp.client.stdio import stdio_client
2099
+
2100
+
2101
+ async def parse_tool_results():
2102
+ """Demonstrates how to parse different types of content in CallToolResult."""
2103
+ server_params = StdioServerParameters(
2104
+ command="python", args=["path/to/mcp_server.py"]
2105
+ )
2106
+
2107
+ async with stdio_client(server_params) as (read, write):
2108
+ async with ClientSession(read, write) as session:
2109
+ await session.initialize()
2110
+
2111
+ # Example 1: Parsing text content
2112
+ result = await session.call_tool("get_data", {"format": "text"})
2113
+ for content in result.content:
2114
+ if isinstance(content, types.TextContent):
2115
+ print(f"Text: {content.text}")
2116
+
2117
+ # Example 2: Parsing structured content from JSON tools
2118
+ result = await session.call_tool("get_user", {"id": "123"})
2119
+ if hasattr(result, "structuredContent") and result.structuredContent:
2120
+ # Access structured data directly
2121
+ user_data = result.structuredContent
2122
+ print(f"User: {user_data.get('name')}, Age: {user_data.get('age')}")
2123
+
2124
+ # Example 3: Parsing embedded resources
2125
+ result = await session.call_tool("read_config", {})
2126
+ for content in result.content:
2127
+ if isinstance(content, types.EmbeddedResource):
2128
+ resource = content.resource
2129
+ if isinstance(resource, types.TextResourceContents):
2130
+ print(f"Config from {resource.uri}: {resource.text}")
2131
+ elif isinstance(resource, types.BlobResourceContents):
2132
+ print(f"Binary data from {resource.uri}")
2133
+
2134
+ # Example 4: Parsing image content
2135
+ result = await session.call_tool("generate_chart", {"data": [1, 2, 3]})
2136
+ for content in result.content:
2137
+ if isinstance(content, types.ImageContent):
2138
+ print(f"Image ({content.mimeType}): {len(content.data)} bytes")
2139
+
2140
+ # Example 5: Handling errors
2141
+ result = await session.call_tool("failing_tool", {})
2142
+ if result.isError:
2143
+ print("Tool execution failed!")
2144
+ for content in result.content:
2145
+ if isinstance(content, types.TextContent):
2146
+ print(f"Error: {content.text}")
2147
+
2148
+
2149
+ async def main():
2150
+ await parse_tool_results()
2151
+
2152
+
2153
+ if __name__ == "__main__":
2154
+ asyncio.run(main())
2155
+ ```
2156
+
2157
+ ### MCP Primitives
2158
+
2159
+ The MCP protocol defines three core primitives that servers can implement:
2160
+
2161
+ | Primitive | Control | Description | Example Use |
2162
+ |-----------|-----------------------|-----------------------------------------------------|------------------------------|
2163
+ | Prompts | User-controlled | Interactive templates invoked by user choice | Slash commands, menu options |
2164
+ | Resources | Application-controlled| Contextual data managed by the client application | File contents, API responses |
2165
+ | Tools | Model-controlled | Functions exposed to the LLM to take actions | API calls, data updates |
2166
+
2167
+ ### Server Capabilities
2168
+
2169
+ MCP servers declare capabilities during initialization:
2170
+
2171
+ | Capability | Feature Flag | Description |
2172
+ |--------------|------------------------------|------------------------------------|
2173
+ | `prompts` | `listChanged` | Prompt template management |
2174
+ | `resources` | `subscribe`<br/>`listChanged`| Resource exposure and updates |
2175
+ | `tools` | `listChanged` | Tool discovery and execution |
2176
+ | `logging` | - | Server logging configuration |
2177
+ | `completions`| - | Argument completion suggestions |
2178
+
2179
+ ## Documentation
2180
+
2181
+ - [Model Context Protocol documentation](https://modelcontextprotocol.io)
2182
+ - [Model Context Protocol specification](https://spec.modelcontextprotocol.io)
2183
+ - [Officially supported servers](https://github.com/modelcontextprotocol/servers)
2184
+
2185
+ ## Contributing
2186
+
2187
+ We are passionate about supporting contributors of all levels of experience and would love to see you get involved in the project. See the [contributing guide](CONTRIBUTING.md) to get started.
2188
+
2189
+ ## License
2190
+
2191
+ This project is licensed under the MIT License - see the LICENSE file for details.