ruby-mcp-client 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/OAUTH.md +555 -0
  3. data/README.md +914 -49
  4. data/lib/mcp_client/audio_content.rb +1 -1
  5. data/lib/mcp_client/auth/browser_oauth.rb +131 -21
  6. data/lib/mcp_client/auth/oauth_provider/challenge_handling.rb +532 -0
  7. data/lib/mcp_client/auth/oauth_provider/client_authentication.rb +121 -0
  8. data/lib/mcp_client/auth/oauth_provider/pending_requests.rb +51 -0
  9. data/lib/mcp_client/auth/oauth_provider/registration_store.rb +486 -0
  10. data/lib/mcp_client/auth/oauth_provider/response_validation.rb +441 -0
  11. data/lib/mcp_client/auth/oauth_provider/scope_selection.rb +134 -0
  12. data/lib/mcp_client/auth/oauth_provider/token_store.rb +419 -0
  13. data/lib/mcp_client/auth/oauth_provider.rb +1367 -311
  14. data/lib/mcp_client/auth/peer_text.rb +174 -0
  15. data/lib/mcp_client/auth.rb +298 -36
  16. data/lib/mcp_client/cached_result.rb +145 -0
  17. data/lib/mcp_client/called_tool_definition.rb +138 -0
  18. data/lib/mcp_client/client/cache_slices.rb +195 -0
  19. data/lib/mcp_client/client/list_aggregation.rb +243 -0
  20. data/lib/mcp_client/client/notification_routing.rb +155 -0
  21. data/lib/mcp_client/client/sampling_validation.rb +200 -0
  22. data/lib/mcp_client/client/task_api.rb +531 -0
  23. data/lib/mcp_client/client/task_lifetimes.rb +269 -0
  24. data/lib/mcp_client/client/task_registry.rb +254 -0
  25. data/lib/mcp_client/client/task_shape.rb +102 -0
  26. data/lib/mcp_client/client/task_support.rb +1166 -0
  27. data/lib/mcp_client/client/task_updates.rb +457 -0
  28. data/lib/mcp_client/client/task_wait_boundaries.rb +198 -0
  29. data/lib/mcp_client/client/task_workers.rb +63 -0
  30. data/lib/mcp_client/client.rb +851 -478
  31. data/lib/mcp_client/deep_copy.rb +49 -0
  32. data/lib/mcp_client/deprecation_notices.rb +94 -0
  33. data/lib/mcp_client/deprecations.rb +419 -0
  34. data/lib/mcp_client/elicitation_validator.rb +56 -13
  35. data/lib/mcp_client/errors.rb +481 -6
  36. data/lib/mcp_client/header_params.rb +320 -0
  37. data/lib/mcp_client/http_transport_base/bounded_inflate.rb +41 -0
  38. data/lib/mcp_client/http_transport_base/cache_support.rb +694 -0
  39. data/lib/mcp_client/http_transport_base/era_detection.rb +134 -0
  40. data/lib/mcp_client/http_transport_base/listen_stream.rb +763 -0
  41. data/lib/mcp_client/http_transport_base/param_headers.rb +35 -0
  42. data/lib/mcp_client/http_transport_base/request_recovery.rb +156 -0
  43. data/lib/mcp_client/http_transport_base/session_recovery.rb +113 -0
  44. data/lib/mcp_client/http_transport_base/sse_event_scanner.rb +145 -0
  45. data/lib/mcp_client/http_transport_base/stream_capture.rb +160 -0
  46. data/lib/mcp_client/http_transport_base/stream_recovery.rb +318 -0
  47. data/lib/mcp_client/http_transport_base/tool_listing.rb +277 -0
  48. data/lib/mcp_client/http_transport_base.rb +669 -100
  49. data/lib/mcp_client/input_round_trips.rb +128 -0
  50. data/lib/mcp_client/json_rpc_common/envelopes.rb +32 -0
  51. data/lib/mcp_client/json_rpc_common/error_bodies.rb +105 -0
  52. data/lib/mcp_client/json_rpc_common/input_waits.rb +167 -0
  53. data/lib/mcp_client/json_rpc_common.rb +967 -11
  54. data/lib/mcp_client/oauth_client.rb +14 -5
  55. data/lib/mcp_client/prompt.rb +4 -0
  56. data/lib/mcp_client/request_authorization.rb +128 -0
  57. data/lib/mcp_client/request_meta_scope.rb +77 -0
  58. data/lib/mcp_client/request_metadata.rb +287 -0
  59. data/lib/mcp_client/resource.rb +4 -2
  60. data/lib/mcp_client/resource_content.rb +20 -0
  61. data/lib/mcp_client/resource_link.rb +0 -2
  62. data/lib/mcp_client/resource_template.rb +4 -2
  63. data/lib/mcp_client/result_caching.rb +999 -0
  64. data/lib/mcp_client/result_completeness.rb +34 -0
  65. data/lib/mcp_client/root.rb +6 -0
  66. data/lib/mcp_client/round_trip_marker.rb +28 -0
  67. data/lib/mcp_client/schema_validator/annotations.rb +82 -0
  68. data/lib/mcp_client/schema_validator/composition.rb +86 -0
  69. data/lib/mcp_client/schema_validator/dialects.rb +66 -0
  70. data/lib/mcp_client/schema_validator/ecma_patterns.rb +567 -0
  71. data/lib/mcp_client/schema_validator/evaluation.rb +517 -0
  72. data/lib/mcp_client/schema_validator/input_requirements.rb +84 -0
  73. data/lib/mcp_client/schema_validator/instances.rb +449 -0
  74. data/lib/mcp_client/schema_validator/keyword_scan.rb +121 -0
  75. data/lib/mcp_client/schema_validator/normalization.rb +104 -0
  76. data/lib/mcp_client/schema_validator/references.rb +610 -0
  77. data/lib/mcp_client/schema_validator/scalars.rb +126 -0
  78. data/lib/mcp_client/schema_validator/shapes.rb +319 -0
  79. data/lib/mcp_client/schema_validator/uri_references.rb +153 -0
  80. data/lib/mcp_client/schema_validator.rb +893 -175
  81. data/lib/mcp_client/server_base.rb +233 -5
  82. data/lib/mcp_client/server_factory.rb +12 -3
  83. data/lib/mcp_client/server_http/json_rpc_transport.rb +220 -5
  84. data/lib/mcp_client/server_http.rb +307 -90
  85. data/lib/mcp_client/server_sse/json_rpc_transport.rb +146 -30
  86. data/lib/mcp_client/server_sse/origin_policy.rb +57 -0
  87. data/lib/mcp_client/server_sse/reconnect_monitor.rb +7 -1
  88. data/lib/mcp_client/server_sse/sse_parser.rb +77 -13
  89. data/lib/mcp_client/server_sse.rb +310 -76
  90. data/lib/mcp_client/server_stdio/child_session.rb +98 -0
  91. data/lib/mcp_client/server_stdio/json_rpc_transport.rb +1004 -29
  92. data/lib/mcp_client/server_stdio.rb +778 -186
  93. data/lib/mcp_client/server_streamable_http/json_rpc_transport.rb +262 -28
  94. data/lib/mcp_client/server_streamable_http.rb +547 -157
  95. data/lib/mcp_client/session_pin.rb +119 -0
  96. data/lib/mcp_client/subscription/notification_dispatcher.rb +354 -0
  97. data/lib/mcp_client/subscription.rb +852 -0
  98. data/lib/mcp_client/subscription_support.rb +715 -0
  99. data/lib/mcp_client/task.rb +286 -14
  100. data/lib/mcp_client/tool.rb +31 -5
  101. data/lib/mcp_client/version.rb +21 -6
  102. data/lib/mcp_client.rb +115 -19
  103. metadata +71 -7
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MCPClient
4
+ class ServerStdio
5
+ # The record of one server child process: when the open subscriptions were
6
+ # handed to it, and when its handles were torn down. One record per
7
+ # process, written only by that process's own lifecycle, so nothing
8
+ # another thread does to another process can change what this one says.
9
+ #
10
+ # It exists for the crash-loop bound. That bound used to live in flags and
11
+ # a timestamp on the transport, which two restarts (or a restart and a host
12
+ # request that re-established the process first) raced over: whichever
13
+ # finished last decided what the *other* process's uptime had been, and a
14
+ # server that exited on sight could be respawned for ever. The question a
15
+ # restart actually has to answer is about one particular process — "did the
16
+ # last process we gave these subscriptions to die straight after we gave
17
+ # them to it?" — and both facts it needs are recorded here, on that
18
+ # process, at the two moments they happen.
19
+ class ChildSession
20
+ # @return [Float, nil] monotonic time the open subscriptions were re-sent
21
+ # to this process, nil if it was never asked to carry any
22
+ attr_reader :carried_at
23
+ # @return [Float, nil] monotonic time this process's handles were torn
24
+ # down, nil while it is still the live session
25
+ attr_reader :ended_at
26
+
27
+ def initialize
28
+ @carried_at = nil
29
+ @ended_at = nil
30
+ @exited_unexpectedly = false
31
+ end
32
+
33
+ # @return [Boolean] whether this process went on its own rather than
34
+ # being torn down by the client
35
+ def exited_unexpectedly?
36
+ @exited_unexpectedly
37
+ end
38
+
39
+ # @return [Float] monotonic seconds
40
+ def self.now
41
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
42
+ end
43
+
44
+ # This process has been handed the subscriptions a previous process left
45
+ # open. Stamped before they go out, not after: the process can exit while
46
+ # they are still being written, and an exit under the re-send is the
47
+ # clearest crash loop there is.
48
+ # @return [void]
49
+ def carrying_subscriptions
50
+ @carried_at = ChildSession.now
51
+ end
52
+
53
+ # This process is gone (its stdio handles have been torn down). First
54
+ # stamp wins: a host `cleanup` racing the reader's own teardown must not
55
+ # move the moment the process ended.
56
+ # @return [void]
57
+ def ended
58
+ return if @ended_at
59
+
60
+ @ended_at = ChildSession.now
61
+ end
62
+
63
+ # This process exited on its own — the reader saw EOF on a stdin the
64
+ # client had not closed (see
65
+ # {MCPClient::ServerStdio#handle_server_exit}). Recorded separately from
66
+ # {#ended}, which every teardown stamps, because only an exit is a crash.
67
+ # @return [void]
68
+ def exited_unexpectedly
69
+ @exited_unexpectedly = true
70
+ end
71
+
72
+ # Whether this process died too soon after being given the subscriptions
73
+ # to be given them again — the crash loop the restart bound exists to
74
+ # stop. A process that was never given any is not part of that loop, and
75
+ # one that is still alive has not ended anything.
76
+ #
77
+ # Neither is a process the client itself shut down. A host that closes
78
+ # the transport and reconnects — as a `cleanup`/request cycle does, and
79
+ # as re-authenticating or re-configuring a server does — tears the
80
+ # process down whenever it likes, and reading that as the server crashing
81
+ # closed the very subscriptions the reconnect exists to carry across.
82
+ # Only an exit the reader actually watched happen counts.
83
+ #
84
+ # The interval is measured from the moment it received them, so a server
85
+ # that is slow to start is credited with none of its own handshake, and a
86
+ # process that was already gone when they were sent to it (its exit
87
+ # stamped before the re-send) counts as having survived no time at all.
88
+ # @param min_uptime [Numeric] seconds it had to last (see
89
+ # {MCPClient::ServerStdio::SUBSCRIPTION_RESTART_MIN_INTERVAL})
90
+ # @return [Boolean]
91
+ def died_carrying_subscriptions?(min_uptime)
92
+ return false unless @exited_unexpectedly && @carried_at && @ended_at
93
+
94
+ (@ended_at - @carried_at) < min_uptime
95
+ end
96
+ end
97
+ end
98
+ end