effect 4.0.7 → 4.1.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.
- package/README.md +1 -1
- package/dist/BigDecimal.d.ts.map +1 -1
- package/dist/BigDecimal.js +3 -0
- package/dist/BigDecimal.js.map +1 -1
- package/dist/Channel.d.ts +12 -6
- package/dist/Channel.d.ts.map +1 -1
- package/dist/Channel.js +9 -3
- package/dist/Channel.js.map +1 -1
- package/dist/Config.d.ts +7 -1
- package/dist/Config.d.ts.map +1 -1
- package/dist/Config.js.map +1 -1
- package/dist/Context.d.ts +18 -1
- package/dist/Context.d.ts.map +1 -1
- package/dist/Context.js +27 -8
- package/dist/Context.js.map +1 -1
- package/dist/Cron.d.ts +81 -11
- package/dist/Cron.d.ts.map +1 -1
- package/dist/Cron.js +101 -26
- package/dist/Cron.js.map +1 -1
- package/dist/Duration.d.ts +78 -0
- package/dist/Duration.d.ts.map +1 -1
- package/dist/Duration.js +123 -0
- package/dist/Duration.js.map +1 -1
- package/dist/Effect.d.ts +164 -7
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +11 -1
- package/dist/Effect.js.map +1 -1
- package/dist/FileSystem.d.ts +59 -4
- package/dist/FileSystem.d.ts.map +1 -1
- package/dist/FileSystem.js +9 -2
- package/dist/FileSystem.js.map +1 -1
- package/dist/Glob.d.ts +165 -0
- package/dist/Glob.d.ts.map +1 -0
- package/dist/Glob.js +127 -0
- package/dist/Glob.js.map +1 -0
- package/dist/Match.d.ts +86 -0
- package/dist/Match.d.ts.map +1 -1
- package/dist/Match.js +72 -0
- package/dist/Match.js.map +1 -1
- package/dist/Pool.d.ts +51 -0
- package/dist/Pool.d.ts.map +1 -1
- package/dist/Pool.js +59 -9
- package/dist/Pool.js.map +1 -1
- package/dist/Random.d.ts.map +1 -1
- package/dist/Random.js +3 -3
- package/dist/Random.js.map +1 -1
- package/dist/RcMap.d.ts.map +1 -1
- package/dist/RcMap.js +8 -2
- package/dist/RcMap.js.map +1 -1
- package/dist/Schedule.d.ts +18 -1
- package/dist/Schedule.d.ts.map +1 -1
- package/dist/Schedule.js +7 -2
- package/dist/Schedule.js.map +1 -1
- package/dist/Schema.d.ts +666 -41
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +650 -0
- package/dist/Schema.js.map +1 -1
- package/dist/SchemaGetter.d.ts +6 -1
- package/dist/SchemaGetter.d.ts.map +1 -1
- package/dist/SchemaGetter.js +22 -11
- package/dist/SchemaGetter.js.map +1 -1
- package/dist/SchemaRepresentation.d.ts +14 -0
- package/dist/SchemaRepresentation.d.ts.map +1 -1
- package/dist/SchemaRepresentation.js +16 -0
- package/dist/SchemaRepresentation.js.map +1 -1
- package/dist/SchemaTransformation.d.ts +8 -3
- package/dist/SchemaTransformation.d.ts.map +1 -1
- package/dist/SchemaTransformation.js +6 -3
- package/dist/SchemaTransformation.js.map +1 -1
- package/dist/ScopedCache.d.ts.map +1 -1
- package/dist/ScopedCache.js +5 -7
- package/dist/ScopedCache.js.map +1 -1
- package/dist/Semaphore.d.ts +130 -0
- package/dist/Semaphore.d.ts.map +1 -1
- package/dist/Semaphore.js +40 -0
- package/dist/Semaphore.js.map +1 -1
- package/dist/Stream.d.ts +13 -4
- package/dist/Stream.d.ts.map +1 -1
- package/dist/Stream.js +9 -0
- package/dist/Stream.js.map +1 -1
- package/dist/Terminal.d.ts +18 -1
- package/dist/Terminal.d.ts.map +1 -1
- package/dist/Terminal.js +1 -1
- package/dist/Terminal.js.map +1 -1
- package/dist/ai/AiError.d.ts +43 -116
- package/dist/ai/AiError.d.ts.map +1 -1
- package/dist/ai/AiError.js +53 -72
- package/dist/ai/AiError.js.map +1 -1
- package/dist/ai/Decision.d.ts +196 -15
- package/dist/ai/Decision.d.ts.map +1 -1
- package/dist/ai/Decision.js +130 -0
- package/dist/ai/Decision.js.map +1 -1
- package/dist/ai/DecisionModel.d.ts +54 -13
- package/dist/ai/DecisionModel.d.ts.map +1 -1
- package/dist/ai/DecisionModel.js +123 -12
- package/dist/ai/DecisionModel.js.map +1 -1
- package/dist/ai/McpServer.d.ts +630 -29
- package/dist/ai/McpServer.d.ts.map +1 -1
- package/dist/ai/McpServer.js +562 -116
- package/dist/ai/McpServer.js.map +1 -1
- package/dist/ai/McpTest.d.ts +130 -0
- package/dist/ai/McpTest.d.ts.map +1 -0
- package/dist/ai/McpTest.js +237 -0
- package/dist/ai/McpTest.js.map +1 -0
- package/dist/ai/Response.d.ts +1 -25
- package/dist/ai/Response.d.ts.map +1 -1
- package/dist/ai/Response.js +1 -10
- package/dist/ai/Response.js.map +1 -1
- package/dist/ai/Tool.d.ts +19 -1
- package/dist/ai/Tool.d.ts.map +1 -1
- package/dist/ai/Tool.js.map +1 -1
- package/dist/ai/index.d.ts +5 -0
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +5 -0
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/internal/mcpCore.js +17 -12
- package/dist/ai/internal/mcpCore.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2024_11_05.js +4 -5
- package/dist/ai/internal/mcpProtocol/v2024_11_05.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_03_26.js +4 -5
- package/dist/ai/internal/mcpProtocol/v2025_03_26.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_06_18.js +4 -5
- package/dist/ai/internal/mcpProtocol/v2025_06_18.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_11_25.js +4 -5
- package/dist/ai/internal/mcpProtocol/v2025_11_25.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2026_07_28.js +18 -10
- package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
- package/dist/ai/internal/mcpProtocol.js +13 -0
- package/dist/ai/internal/mcpProtocol.js.map +1 -1
- package/dist/ai/internal/mcpRuntime.js +5 -3
- package/dist/ai/internal/mcpRuntime.js.map +1 -1
- package/dist/ai/internal/mcpStatefulRuntime.js +6 -1
- package/dist/ai/internal/mcpStatefulRuntime.js.map +1 -1
- package/dist/cli/Argument.d.ts +6 -0
- package/dist/cli/Argument.d.ts.map +1 -1
- package/dist/cli/Argument.js +2 -0
- package/dist/cli/Argument.js.map +1 -1
- package/dist/cli/CliOutput.d.ts +65 -1
- package/dist/cli/CliOutput.d.ts.map +1 -1
- package/dist/cli/CliOutput.js +94 -37
- package/dist/cli/CliOutput.js.map +1 -1
- package/dist/cli/Command.d.ts +23 -0
- package/dist/cli/Command.d.ts.map +1 -1
- package/dist/cli/Command.js +8 -0
- package/dist/cli/Command.js.map +1 -1
- package/dist/cli/Flag.d.ts +107 -0
- package/dist/cli/Flag.d.ts.map +1 -1
- package/dist/cli/Flag.js +37 -0
- package/dist/cli/Flag.js.map +1 -1
- package/dist/cli/HelpDoc.d.ts +15 -1
- package/dist/cli/HelpDoc.d.ts.map +1 -1
- package/dist/cli/Param.d.ts +6 -0
- package/dist/cli/Param.d.ts.map +1 -1
- package/dist/cli/Param.js +2 -0
- package/dist/cli/Param.js.map +1 -1
- package/dist/cli/Primitive.d.ts +14 -0
- package/dist/cli/Primitive.d.ts.map +1 -1
- package/dist/cli/Primitive.js +15 -1
- package/dist/cli/Primitive.js.map +1 -1
- package/dist/cli/Prompt.d.ts +1 -0
- package/dist/cli/Prompt.d.ts.map +1 -1
- package/dist/cli/Prompt.js +1 -0
- package/dist/cli/Prompt.js.map +1 -1
- package/dist/cli/internal/help.d.ts.map +1 -1
- package/dist/cli/internal/help.js +17 -10
- package/dist/cli/internal/help.js.map +1 -1
- package/dist/cluster/Sharding.d.ts.map +1 -1
- package/dist/cluster/Sharding.js +90 -28
- package/dist/cluster/Sharding.js.map +1 -1
- package/dist/cluster/ShardingConfig.d.ts +72 -6
- package/dist/cluster/ShardingConfig.d.ts.map +1 -1
- package/dist/cluster/ShardingConfig.js +4 -0
- package/dist/cluster/ShardingConfig.js.map +1 -1
- package/dist/cluster/SingleRunner.d.ts +3 -3
- package/dist/cluster/SingleRunner.d.ts.map +1 -1
- package/dist/cluster/SingleRunner.js +1 -1
- package/dist/cluster/SqlMessageStorage.d.ts +37 -6
- package/dist/cluster/SqlMessageStorage.d.ts.map +1 -1
- package/dist/cluster/SqlMessageStorage.js +34 -7
- package/dist/cluster/SqlMessageStorage.js.map +1 -1
- package/dist/cluster/SqlRunnerStorage.d.ts.map +1 -1
- package/dist/cluster/SqlRunnerStorage.js +23 -8
- package/dist/cluster/SqlRunnerStorage.js.map +1 -1
- package/dist/crypto/Aead.d.ts +322 -0
- package/dist/crypto/Aead.d.ts.map +1 -0
- package/dist/crypto/Aead.js +331 -0
- package/dist/crypto/Aead.js.map +1 -0
- package/dist/crypto/Cipher.d.ts +235 -0
- package/dist/crypto/Cipher.d.ts.map +1 -0
- package/dist/crypto/Cipher.js +233 -0
- package/dist/crypto/Cipher.js.map +1 -0
- package/dist/crypto/CryptoError.d.ts +212 -0
- package/dist/crypto/CryptoError.d.ts.map +1 -0
- package/dist/crypto/CryptoError.js +202 -0
- package/dist/crypto/CryptoError.js.map +1 -0
- package/dist/crypto/Digest.d.ts +143 -0
- package/dist/crypto/Digest.d.ts.map +1 -0
- package/dist/crypto/Digest.js +146 -0
- package/dist/crypto/Digest.js.map +1 -0
- package/dist/crypto/Kdf.d.ts +220 -0
- package/dist/crypto/Kdf.d.ts.map +1 -0
- package/dist/crypto/Kdf.js +212 -0
- package/dist/crypto/Kdf.js.map +1 -0
- package/dist/crypto/KeyAgreement.d.ts +323 -0
- package/dist/crypto/KeyAgreement.d.ts.map +1 -0
- package/dist/crypto/KeyAgreement.js +303 -0
- package/dist/crypto/KeyAgreement.js.map +1 -0
- package/dist/crypto/KeyFormat.d.ts +309 -0
- package/dist/crypto/KeyFormat.d.ts.map +1 -0
- package/dist/crypto/KeyFormat.js +556 -0
- package/dist/crypto/KeyFormat.js.map +1 -0
- package/dist/crypto/KeyWrap.d.ts +389 -0
- package/dist/crypto/KeyWrap.d.ts.map +1 -0
- package/dist/crypto/KeyWrap.js +381 -0
- package/dist/crypto/KeyWrap.js.map +1 -0
- package/dist/crypto/Mac.d.ts +277 -0
- package/dist/crypto/Mac.d.ts.map +1 -0
- package/dist/crypto/Mac.js +288 -0
- package/dist/crypto/Mac.js.map +1 -0
- package/dist/crypto/SecureRandom.d.ts +340 -0
- package/dist/crypto/SecureRandom.d.ts.map +1 -0
- package/dist/crypto/SecureRandom.js +257 -0
- package/dist/crypto/SecureRandom.js.map +1 -0
- package/dist/crypto/Signature.d.ts +448 -0
- package/dist/crypto/Signature.d.ts.map +1 -0
- package/dist/crypto/Signature.js +484 -0
- package/dist/crypto/Signature.js.map +1 -0
- package/dist/crypto/WebCrypto.d.ts +61 -0
- package/dist/crypto/WebCrypto.d.ts.map +1 -0
- package/dist/crypto/WebCrypto.js +63 -0
- package/dist/crypto/WebCrypto.js.map +1 -0
- package/dist/crypto/index.d.ts +65 -0
- package/dist/crypto/index.d.ts.map +1 -0
- package/dist/crypto/index.js +66 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/crypto/internal/asymmetric.d.ts +2 -0
- package/dist/crypto/internal/asymmetric.d.ts.map +1 -0
- package/dist/crypto/internal/asymmetric.js +210 -0
- package/dist/crypto/internal/asymmetric.js.map +1 -0
- package/dist/crypto/internal/chacha.d.ts +46 -0
- package/dist/crypto/internal/chacha.d.ts.map +1 -0
- package/dist/crypto/internal/chacha.js +885 -0
- package/dist/crypto/internal/chacha.js.map +1 -0
- package/dist/crypto/internal/der.d.ts +6 -0
- package/dist/crypto/internal/der.d.ts.map +1 -0
- package/dist/crypto/internal/der.js +86 -0
- package/dist/crypto/internal/der.js.map +1 -0
- package/dist/crypto/internal/digest.d.ts +2 -0
- package/dist/crypto/internal/digest.d.ts.map +1 -0
- package/dist/crypto/internal/digest.js +15 -0
- package/dist/crypto/internal/digest.js.map +1 -0
- package/dist/crypto/internal/key.d.ts +2 -0
- package/dist/crypto/internal/key.d.ts.map +1 -0
- package/dist/crypto/internal/key.js +170 -0
- package/dist/crypto/internal/key.js.map +1 -0
- package/dist/crypto/internal/md5.d.ts +39 -0
- package/dist/crypto/internal/md5.d.ts.map +1 -0
- package/dist/crypto/internal/md5.js +179 -0
- package/dist/crypto/internal/md5.js.map +1 -0
- package/dist/crypto/internal/webCrypto.d.ts +2 -0
- package/dist/crypto/internal/webCrypto.d.ts.map +1 -0
- package/dist/crypto/internal/webCrypto.js +111 -0
- package/dist/crypto/internal/webCrypto.js.map +1 -0
- package/dist/encoding/Base64Url.d.ts.map +1 -1
- package/dist/encoding/Base64Url.js +3 -2
- package/dist/encoding/Base64Url.js.map +1 -1
- package/dist/encoding/Csv.d.ts +608 -0
- package/dist/encoding/Csv.d.ts.map +1 -0
- package/dist/encoding/Csv.js +718 -0
- package/dist/encoding/Csv.js.map +1 -0
- package/dist/encoding/Hex.d.ts +3 -2
- package/dist/encoding/Hex.d.ts.map +1 -1
- package/dist/encoding/Hex.js +3 -2
- package/dist/encoding/Hex.js.map +1 -1
- package/dist/encoding/SchemaBinary.d.ts.map +1 -1
- package/dist/encoding/SchemaBinary.js +42 -4
- package/dist/encoding/SchemaBinary.js.map +1 -1
- package/dist/encoding/Toml.d.ts +17 -3
- package/dist/encoding/Toml.d.ts.map +1 -1
- package/dist/encoding/Toml.js +104 -16
- package/dist/encoding/Toml.js.map +1 -1
- package/dist/encoding/Yaml.d.ts +8 -1
- package/dist/encoding/Yaml.d.ts.map +1 -1
- package/dist/encoding/Yaml.js +54 -18
- package/dist/encoding/Yaml.js.map +1 -1
- package/dist/encoding/index.d.ts +5 -0
- package/dist/encoding/index.d.ts.map +1 -1
- package/dist/encoding/index.js +5 -0
- package/dist/encoding/index.js.map +1 -1
- package/dist/eventlog/EventJournal.d.ts.map +1 -1
- package/dist/eventlog/EventJournal.js +6 -3
- package/dist/eventlog/EventJournal.js.map +1 -1
- package/dist/eventlog/EventLog.d.ts +119 -4
- package/dist/eventlog/EventLog.d.ts.map +1 -1
- package/dist/eventlog/EventLog.js +200 -12
- package/dist/eventlog/EventLog.js.map +1 -1
- package/dist/eventlog/EventLogEncryption.d.ts +41 -4
- package/dist/eventlog/EventLogEncryption.d.ts.map +1 -1
- package/dist/eventlog/EventLogEncryption.js +83 -64
- package/dist/eventlog/EventLogEncryption.js.map +1 -1
- package/dist/eventlog/EventLogMessage.d.ts +279 -0
- package/dist/eventlog/EventLogMessage.d.ts.map +1 -1
- package/dist/eventlog/EventLogMessage.js +217 -1
- package/dist/eventlog/EventLogMessage.js.map +1 -1
- package/dist/eventlog/EventLogRemote.d.ts +200 -28
- package/dist/eventlog/EventLogRemote.d.ts.map +1 -1
- package/dist/eventlog/EventLogRemote.js +186 -16
- package/dist/eventlog/EventLogRemote.js.map +1 -1
- package/dist/eventlog/EventLogServer.d.ts +3 -0
- package/dist/eventlog/EventLogServer.d.ts.map +1 -1
- package/dist/eventlog/EventLogServer.js +18 -10
- package/dist/eventlog/EventLogServer.js.map +1 -1
- package/dist/eventlog/EventLogServerEncrypted.d.ts +326 -5
- package/dist/eventlog/EventLogServerEncrypted.d.ts.map +1 -1
- package/dist/eventlog/EventLogServerEncrypted.js +769 -14
- package/dist/eventlog/EventLogServerEncrypted.js.map +1 -1
- package/dist/eventlog/EventLogSessionAuth.d.ts +5 -3
- package/dist/eventlog/EventLogSessionAuth.d.ts.map +1 -1
- package/dist/eventlog/EventLogSessionAuth.js +41 -64
- package/dist/eventlog/EventLogSessionAuth.js.map +1 -1
- package/dist/eventlog/EventRow.d.ts +4 -0
- package/dist/eventlog/EventRow.d.ts.map +1 -1
- package/dist/eventlog/EventRow.js +12 -2
- package/dist/eventlog/EventRow.js.map +1 -1
- package/dist/eventlog/SqlEventJournal.js +2 -2
- package/dist/eventlog/SqlEventJournal.js.map +1 -1
- package/dist/eventlog/SqlEventLogServerEncrypted.d.ts +21 -5
- package/dist/eventlog/SqlEventLogServerEncrypted.d.ts.map +1 -1
- package/dist/eventlog/SqlEventLogServerEncrypted.js +1048 -113
- package/dist/eventlog/SqlEventLogServerEncrypted.js.map +1 -1
- package/dist/eventlog/SqlEventLogServerUnencrypted.js +2 -2
- package/dist/eventlog/SqlEventLogServerUnencrypted.js.map +1 -1
- package/dist/eventlog/internal/batches.d.ts +12 -0
- package/dist/eventlog/internal/batches.d.ts.map +1 -0
- package/dist/eventlog/internal/batches.js +26 -0
- package/dist/eventlog/internal/batches.js.map +1 -0
- package/dist/eventlog/internal/compaction.d.ts +38 -0
- package/dist/eventlog/internal/compaction.d.ts.map +1 -0
- package/dist/eventlog/internal/compaction.js +33 -0
- package/dist/eventlog/internal/compaction.js.map +1 -0
- package/dist/eventlog/internal/identityRootSecretDerivation.js +66 -67
- package/dist/eventlog/internal/identityRootSecretDerivation.js.map +1 -1
- package/dist/eventlog/internal/remoteCompaction.d.ts +31 -0
- package/dist/eventlog/internal/remoteCompaction.d.ts.map +1 -0
- package/dist/eventlog/internal/remoteCompaction.js +76 -0
- package/dist/eventlog/internal/remoteCompaction.js.map +1 -0
- package/dist/http/Cookies.d.ts +172 -0
- package/dist/http/Cookies.d.ts.map +1 -1
- package/dist/http/Cookies.js +143 -1
- package/dist/http/Cookies.js.map +1 -1
- package/dist/http/FetchHttpClient.d.ts +26 -2
- package/dist/http/FetchHttpClient.d.ts.map +1 -1
- package/dist/http/FetchHttpClient.js +78 -15
- package/dist/http/FetchHttpClient.js.map +1 -1
- package/dist/http/HttpClient.d.ts +79 -8
- package/dist/http/HttpClient.d.ts.map +1 -1
- package/dist/http/HttpClient.js +65 -5
- package/dist/http/HttpClient.js.map +1 -1
- package/dist/http/HttpClientRequest.d.ts +9 -5
- package/dist/http/HttpClientRequest.d.ts.map +1 -1
- package/dist/http/HttpClientRequest.js +2 -3
- package/dist/http/HttpClientRequest.js.map +1 -1
- package/dist/http/HttpClientResponse.d.ts.map +1 -1
- package/dist/http/HttpClientResponse.js +3 -2
- package/dist/http/HttpClientResponse.js.map +1 -1
- package/dist/http/HttpMethod.d.ts +7 -1
- package/dist/http/HttpMethod.d.ts.map +1 -1
- package/dist/http/HttpMethod.js +3 -8
- package/dist/http/HttpMethod.js.map +1 -1
- package/dist/http/HttpServerRequest.d.ts +5 -2
- package/dist/http/HttpServerRequest.d.ts.map +1 -1
- package/dist/http/HttpServerRequest.js +6 -17
- package/dist/http/HttpServerRequest.js.map +1 -1
- package/dist/http/HttpServerResponse.d.ts.map +1 -1
- package/dist/http/HttpServerResponse.js +3 -2
- package/dist/http/HttpServerResponse.js.map +1 -1
- package/dist/http/HttpTraceContext.d.ts +2 -1
- package/dist/http/HttpTraceContext.d.ts.map +1 -1
- package/dist/http/HttpTraceContext.js +7 -5
- package/dist/http/HttpTraceContext.js.map +1 -1
- package/dist/http/ProblemDetails.d.ts +96 -0
- package/dist/http/ProblemDetails.d.ts.map +1 -0
- package/dist/http/ProblemDetails.js +98 -0
- package/dist/http/ProblemDetails.js.map +1 -0
- package/dist/http/index.d.ts +5 -0
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +5 -0
- package/dist/http/index.js.map +1 -1
- package/dist/http/internal/decompression.js +3 -2
- package/dist/http/internal/decompression.js.map +1 -1
- package/dist/http/internal/urlParams.js +8 -0
- package/dist/http/internal/urlParams.js.map +1 -1
- package/dist/http-api/HttpApiBuilder.js +19 -5
- package/dist/http-api/HttpApiBuilder.js.map +1 -1
- package/dist/http-api/HttpApiClient.d.ts +63 -0
- package/dist/http-api/HttpApiClient.d.ts.map +1 -1
- package/dist/http-api/HttpApiClient.js +51 -0
- package/dist/http-api/HttpApiClient.js.map +1 -1
- package/dist/http-api/HttpApiEndpoint.d.ts.map +1 -1
- package/dist/http-api/HttpApiEndpoint.js +14 -5
- package/dist/http-api/HttpApiEndpoint.js.map +1 -1
- package/dist/http-api/HttpApiError.d.ts +185 -13
- package/dist/http-api/HttpApiError.d.ts.map +1 -1
- package/dist/http-api/HttpApiError.js +128 -0
- package/dist/http-api/HttpApiError.js.map +1 -1
- package/dist/http-api/HttpApiMiddleware.d.ts +64 -17
- package/dist/http-api/HttpApiMiddleware.d.ts.map +1 -1
- package/dist/http-api/HttpApiMiddleware.js +85 -1
- package/dist/http-api/HttpApiMiddleware.js.map +1 -1
- package/dist/http-api/HttpApiSchema.d.ts +76 -14
- package/dist/http-api/HttpApiSchema.d.ts.map +1 -1
- package/dist/http-api/HttpApiSchema.js +53 -0
- package/dist/http-api/HttpApiSchema.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/internal/effect.js +25 -14
- package/dist/internal/effect.js.map +1 -1
- package/dist/internal/glob.d.ts +2 -0
- package/dist/internal/glob.d.ts.map +1 -0
- package/dist/internal/glob.js +630 -0
- package/dist/internal/glob.js.map +1 -0
- package/dist/internal/matcher.js +10 -0
- package/dist/internal/matcher.js.map +1 -1
- package/dist/internal/persistence.js +9 -0
- package/dist/internal/persistence.js.map +1 -1
- package/dist/internal/rcRef.js +4 -1
- package/dist/internal/rcRef.js.map +1 -1
- package/dist/internal/schema/toCodeDocument.js +6 -3
- package/dist/internal/schema/toCodeDocument.js.map +1 -1
- package/dist/internal/trie.js +42 -13
- package/dist/internal/trie.js.map +1 -1
- package/dist/internal/ulid.js +41 -0
- package/dist/internal/ulid.js.map +1 -1
- package/dist/internal/uuid.js +52 -8
- package/dist/internal/uuid.js.map +1 -1
- package/dist/internal/version.js +1 -1
- package/dist/observability/Otlp.d.ts +15 -4
- package/dist/observability/Otlp.d.ts.map +1 -1
- package/dist/observability/Otlp.js +10 -0
- package/dist/observability/Otlp.js.map +1 -1
- package/dist/observability/OtlpResource.d.ts +7 -0
- package/dist/observability/OtlpResource.d.ts.map +1 -1
- package/dist/observability/OtlpResource.js +11 -1
- package/dist/observability/OtlpResource.js.map +1 -1
- package/dist/persistence/KeyValueStore.d.ts +47 -2
- package/dist/persistence/KeyValueStore.d.ts.map +1 -1
- package/dist/persistence/KeyValueStore.js +129 -18
- package/dist/persistence/KeyValueStore.js.map +1 -1
- package/dist/persistence/PersistedQueue.d.ts +9 -0
- package/dist/persistence/PersistedQueue.d.ts.map +1 -1
- package/dist/persistence/PersistedQueue.js +94 -52
- package/dist/persistence/PersistedQueue.js.map +1 -1
- package/dist/persistence/Persistence.d.ts +10 -4
- package/dist/persistence/Persistence.d.ts.map +1 -1
- package/dist/persistence/Persistence.js +18 -11
- package/dist/persistence/Persistence.js.map +1 -1
- package/dist/process/ChildProcess.d.ts +207 -67
- package/dist/process/ChildProcess.d.ts.map +1 -1
- package/dist/process/ChildProcess.js +56 -158
- package/dist/process/ChildProcess.js.map +1 -1
- package/dist/process/ChildProcessSpawner.d.ts +5 -0
- package/dist/process/ChildProcessSpawner.d.ts.map +1 -1
- package/dist/process/ChildProcessSpawner.js.map +1 -1
- package/dist/process/CommandLine.d.ts +294 -0
- package/dist/process/CommandLine.d.ts.map +1 -0
- package/dist/process/CommandLine.js +481 -0
- package/dist/process/CommandLine.js.map +1 -0
- package/dist/process/index.d.ts +5 -0
- package/dist/process/index.d.ts.map +1 -1
- package/dist/process/index.js +5 -0
- package/dist/process/index.js.map +1 -1
- package/dist/reactivity/Atom.d.ts +2 -1
- package/dist/reactivity/Atom.d.ts.map +1 -1
- package/dist/reactivity/Atom.js +3 -4
- package/dist/reactivity/Atom.js.map +1 -1
- package/dist/reactivity/AtomRegistry.js.map +1 -1
- package/dist/rpc/RpcClient.d.ts +16 -1
- package/dist/rpc/RpcClient.d.ts.map +1 -1
- package/dist/rpc/RpcClient.js +50 -16
- package/dist/rpc/RpcClient.js.map +1 -1
- package/dist/rpc/RpcMessage.d.ts +10 -1
- package/dist/rpc/RpcMessage.d.ts.map +1 -1
- package/dist/rpc/RpcMessage.js +1 -0
- package/dist/rpc/RpcMessage.js.map +1 -1
- package/dist/rpc/RpcSerialization.d.ts.map +1 -1
- package/dist/rpc/RpcSerialization.js +6 -0
- package/dist/rpc/RpcSerialization.js.map +1 -1
- package/dist/rpc/RpcServer.d.ts +31 -0
- package/dist/rpc/RpcServer.d.ts.map +1 -1
- package/dist/rpc/RpcServer.js +139 -66
- package/dist/rpc/RpcServer.js.map +1 -1
- package/dist/schema/Model.d.ts +14 -0
- package/dist/schema/Model.d.ts.map +1 -1
- package/dist/schema/Model.js +18 -3
- package/dist/schema/Model.js.map +1 -1
- package/dist/sql/Migrator.d.ts +0 -19
- package/dist/sql/Migrator.d.ts.map +1 -1
- package/dist/sql/Migrator.js +3 -48
- package/dist/sql/Migrator.js.map +1 -1
- package/dist/sql/MigratorFileSystem.d.ts +21 -0
- package/dist/sql/MigratorFileSystem.d.ts.map +1 -0
- package/dist/sql/MigratorFileSystem.js +57 -0
- package/dist/sql/MigratorFileSystem.js.map +1 -0
- package/dist/sql/SqlClient.d.ts.map +1 -1
- package/dist/sql/SqlClient.js +5 -9
- package/dist/sql/SqlClient.js.map +1 -1
- package/dist/sql/Statement.d.ts +20 -6
- package/dist/sql/Statement.d.ts.map +1 -1
- package/dist/sql/Statement.js +0 -13
- package/dist/sql/Statement.js.map +1 -1
- package/dist/sql/index.js +1 -1
- package/dist/testing/index.d.ts +0 -5
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +0 -5
- package/dist/testing/index.js.map +1 -1
- package/dist/workflow/internal/crypto.js +9 -8
- package/dist/workflow/internal/crypto.js.map +1 -1
- package/package.json +3 -1
- package/src/BigDecimal.ts +3 -0
- package/src/Channel.ts +17 -11
- package/src/Config.ts +7 -1
- package/src/Context.ts +34 -10
- package/src/Cron.ts +178 -29
- package/src/Duration.ts +140 -0
- package/src/Effect.ts +914 -85
- package/src/FileSystem.ts +65 -4
- package/src/Glob.ts +205 -0
- package/src/Match.ts +127 -0
- package/src/Pool.ts +73 -15
- package/src/Random.ts +3 -3
- package/src/RcMap.ts +5 -1
- package/src/Schedule.ts +26 -3
- package/src/Schema.ts +1189 -62
- package/src/SchemaGetter.ts +27 -11
- package/src/SchemaRepresentation.ts +19 -0
- package/src/SchemaTransformation.ts +8 -3
- package/src/ScopedCache.ts +5 -9
- package/src/Semaphore.ts +141 -0
- package/src/Stream.ts +15 -6
- package/src/Terminal.ts +19 -1
- package/src/ai/AiError.ts +56 -80
- package/src/ai/Decision.ts +257 -6
- package/src/ai/DecisionModel.ts +171 -28
- package/src/ai/McpServer.ts +1269 -286
- package/src/ai/McpTest.ts +380 -0
- package/src/ai/Response.ts +2 -22
- package/src/ai/Tool.ts +27 -8
- package/src/ai/index.ts +6 -0
- package/src/ai/internal/mcpCore.ts +78 -44
- package/src/ai/internal/mcpProtocol/v2024_11_05.ts +4 -7
- package/src/ai/internal/mcpProtocol/v2025_03_26.ts +4 -7
- package/src/ai/internal/mcpProtocol/v2025_06_18.ts +4 -7
- package/src/ai/internal/mcpProtocol/v2025_11_25.ts +4 -7
- package/src/ai/internal/mcpProtocol/v2026_07_28.ts +15 -10
- package/src/ai/internal/mcpProtocol.ts +29 -0
- package/src/ai/internal/mcpRuntime.ts +9 -5
- package/src/ai/internal/mcpStatefulRuntime.ts +14 -2
- package/src/cli/Argument.ts +6 -0
- package/src/cli/CliOutput.ts +117 -49
- package/src/cli/Command.ts +23 -0
- package/src/cli/Flag.ts +107 -0
- package/src/cli/HelpDoc.ts +18 -1
- package/src/cli/Param.ts +6 -0
- package/src/cli/Primitive.ts +16 -1
- package/src/cli/Prompt.ts +1 -0
- package/src/cli/internal/help.ts +21 -9
- package/src/cluster/Sharding.ts +93 -26
- package/src/cluster/ShardingConfig.ts +41 -3
- package/src/cluster/SingleRunner.ts +3 -3
- package/src/cluster/SqlMessageStorage.ts +49 -17
- package/src/cluster/SqlRunnerStorage.ts +37 -27
- package/src/crypto/Aead.ts +517 -0
- package/src/crypto/Cipher.ts +326 -0
- package/src/crypto/CryptoError.ts +242 -0
- package/src/crypto/Digest.ts +167 -0
- package/src/crypto/Kdf.ts +315 -0
- package/src/crypto/KeyAgreement.ts +489 -0
- package/src/crypto/KeyFormat.ts +712 -0
- package/src/crypto/KeyWrap.ts +639 -0
- package/src/crypto/Mac.ts +386 -0
- package/src/crypto/SecureRandom.ts +459 -0
- package/src/crypto/Signature.ts +673 -0
- package/src/crypto/WebCrypto.ts +64 -0
- package/src/crypto/index.ts +78 -0
- package/src/crypto/internal/asymmetric.ts +329 -0
- package/src/crypto/internal/chacha.ts +1088 -0
- package/src/crypto/internal/der.ts +110 -0
- package/src/crypto/internal/digest.ts +16 -0
- package/src/crypto/internal/key.ts +235 -0
- package/src/crypto/internal/md5.ts +170 -0
- package/src/crypto/internal/webCrypto.ts +150 -0
- package/src/encoding/Base64Url.ts +5 -2
- package/src/encoding/Csv.ts +1298 -0
- package/src/encoding/Hex.ts +3 -2
- package/src/encoding/SchemaBinary.ts +40 -4
- package/src/encoding/Toml.ts +85 -20
- package/src/encoding/Yaml.ts +61 -18
- package/src/encoding/index.ts +6 -0
- package/src/eventlog/EventJournal.ts +4 -3
- package/src/eventlog/EventLog.ts +340 -15
- package/src/eventlog/EventLogEncryption.ts +118 -110
- package/src/eventlog/EventLogMessage.ts +273 -1
- package/src/eventlog/EventLogRemote.ts +367 -23
- package/src/eventlog/EventLogServer.ts +23 -8
- package/src/eventlog/EventLogServerEncrypted.ts +1130 -67
- package/src/eventlog/EventLogSessionAuth.ts +56 -70
- package/src/eventlog/EventRow.ts +12 -3
- package/src/eventlog/SqlEventJournal.ts +2 -2
- package/src/eventlog/SqlEventLogServerEncrypted.ts +1244 -136
- package/src/eventlog/SqlEventLogServerUnencrypted.ts +2 -2
- package/src/eventlog/internal/batches.ts +30 -0
- package/src/eventlog/internal/compaction.ts +64 -0
- package/src/eventlog/internal/identityRootSecretDerivation.ts +93 -123
- package/src/eventlog/internal/remoteCompaction.ts +92 -0
- package/src/http/Cookies.ts +281 -1
- package/src/http/FetchHttpClient.ts +88 -16
- package/src/http/HttpClient.ts +89 -22
- package/src/http/HttpClientRequest.ts +12 -9
- package/src/http/HttpClientResponse.ts +3 -2
- package/src/http/HttpMethod.ts +10 -2
- package/src/http/HttpServerRequest.ts +15 -30
- package/src/http/HttpServerResponse.ts +3 -2
- package/src/http/HttpTraceContext.ts +7 -4
- package/src/http/ProblemDetails.ts +149 -0
- package/src/http/index.ts +6 -0
- package/src/http/internal/decompression.ts +4 -4
- package/src/http/internal/urlParams.ts +8 -0
- package/src/http-api/HttpApiBuilder.ts +24 -12
- package/src/http-api/HttpApiClient.ts +67 -1
- package/src/http-api/HttpApiEndpoint.ts +14 -5
- package/src/http-api/HttpApiError.ts +250 -0
- package/src/http-api/HttpApiMiddleware.ts +95 -1
- package/src/http-api/HttpApiSchema.ts +105 -14
- package/src/index.ts +6 -6
- package/src/internal/effect.ts +34 -18
- package/src/internal/glob.ts +736 -0
- package/src/internal/matcher.ts +17 -0
- package/src/internal/persistence.ts +11 -0
- package/src/internal/rcRef.ts +4 -1
- package/src/internal/schema/toCodeDocument.ts +7 -3
- package/src/internal/trie.ts +43 -14
- package/src/internal/ulid.ts +44 -0
- package/src/internal/uuid.ts +57 -16
- package/src/internal/version.ts +1 -1
- package/src/observability/Otlp.ts +21 -4
- package/src/observability/OtlpResource.ts +9 -1
- package/src/persistence/KeyValueStore.ts +219 -22
- package/src/persistence/PersistedQueue.ts +128 -59
- package/src/persistence/Persistence.ts +20 -13
- package/src/process/ChildProcess.ts +255 -260
- package/src/process/ChildProcessSpawner.ts +5 -0
- package/src/process/CommandLine.ts +630 -0
- package/src/process/index.ts +6 -0
- package/src/reactivity/Atom.ts +5 -5
- package/src/reactivity/AtomRegistry.ts +1 -1
- package/src/rpc/RpcClient.ts +62 -26
- package/src/rpc/RpcMessage.ts +10 -1
- package/src/rpc/RpcSerialization.ts +3 -0
- package/src/rpc/RpcServer.ts +176 -78
- package/src/schema/Model.ts +26 -3
- package/src/sql/Migrator.ts +4 -63
- package/src/sql/MigratorFileSystem.ts +71 -0
- package/src/sql/SqlClient.ts +2 -16
- package/src/sql/Statement.ts +8 -7
- package/src/sql/index.ts +1 -1
- package/src/testing/index.ts +0 -6
- package/src/workflow/internal/crypto.ts +15 -11
- package/dist/Crypto.d.ts +0 -244
- package/dist/Crypto.d.ts.map +0 -1
- package/dist/Crypto.js +0 -139
- package/dist/Crypto.js.map +0 -1
- package/dist/testing/TestCrypto.d.ts +0 -73
- package/dist/testing/TestCrypto.d.ts.map +0 -1
- package/dist/testing/TestCrypto.js +0 -87
- package/dist/testing/TestCrypto.js.map +0 -1
- package/src/Crypto.ts +0 -347
- package/src/testing/TestCrypto.ts +0 -94
package/src/ai/McpServer.ts
CHANGED
|
@@ -16,6 +16,7 @@ import * as Cause from "../Cause.ts"
|
|
|
16
16
|
import * as Context from "../Context.ts"
|
|
17
17
|
import * as Data from "../Data.ts"
|
|
18
18
|
import * as Deferred from "../Deferred.ts"
|
|
19
|
+
import * as Duration from "../Duration.ts"
|
|
19
20
|
import * as Effect from "../Effect.ts"
|
|
20
21
|
import * as ErrorReporter from "../ErrorReporter.ts"
|
|
21
22
|
import * as Exit from "../Exit.ts"
|
|
@@ -46,6 +47,8 @@ import * as SchemaAST from "../SchemaAST.ts"
|
|
|
46
47
|
import * as Scope from "../Scope.ts"
|
|
47
48
|
import type { Stdio } from "../Stdio.ts"
|
|
48
49
|
import * as Stream from "../Stream.ts"
|
|
50
|
+
import * as Tracer from "../Tracer.ts"
|
|
51
|
+
import type * as Types from "../Types.ts"
|
|
49
52
|
import * as AiError from "./AiError.ts"
|
|
50
53
|
import * as McpCore from "./internal/mcpCore.ts"
|
|
51
54
|
import * as McpProtocolInternal from "./internal/mcpProtocol.ts"
|
|
@@ -105,6 +108,7 @@ const internalState = new WeakMap<object, {
|
|
|
105
108
|
readonly core: McpCore.McpCore
|
|
106
109
|
readonly notifications: Queue.Dequeue<QueuedServerNotification>
|
|
107
110
|
readonly notificationDelivery: { consumers: number; listChangeRevision: number; requestEndpoints: number }
|
|
111
|
+
readonly globalMiddleware: Array<middleware.Fn<any, any, any>>
|
|
108
112
|
}>()
|
|
109
113
|
|
|
110
114
|
// Request-scoped HTTP servers deliver a request's own notifications through
|
|
@@ -166,13 +170,29 @@ const toInternalServerNotification = (
|
|
|
166
170
|
}
|
|
167
171
|
}
|
|
168
172
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
const middlewareKeyPrefix = "effect/ai/McpServer/Middleware-"
|
|
174
|
+
|
|
175
|
+
// Do not capture request services or middleware stacks at registration.
|
|
176
|
+
const requestServiceKeys = new Set<string>([
|
|
177
|
+
McpRequestContext.key,
|
|
178
|
+
McpServerClient.key,
|
|
179
|
+
HttpServerRequest.HttpServerRequest.key,
|
|
180
|
+
HttpServerRequest.ParsedSearchParams.key,
|
|
181
|
+
HttpRouter.RouteContext.key,
|
|
182
|
+
CurrentLogLevel.key,
|
|
183
|
+
Scope.Scope.key,
|
|
184
|
+
Tracer.ParentSpan.key
|
|
185
|
+
])
|
|
186
|
+
|
|
187
|
+
const omitRequestServices = <Services>(context: Context.Context<Services>): Context.Context<Services> => {
|
|
188
|
+
const services = new Map<string, any>()
|
|
189
|
+
for (const [key, value] of context.mapUnsafe) {
|
|
190
|
+
if (!requestServiceKeys.has(key) && !key.startsWith(middlewareKeyPrefix)) {
|
|
191
|
+
services.set(key, value)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return Context.makeUnsafe(services)
|
|
195
|
+
}
|
|
176
196
|
|
|
177
197
|
const provideInvocationContext = <A, E, R>(
|
|
178
198
|
effect: Effect.Effect<A, E, R>,
|
|
@@ -199,6 +219,703 @@ const provideInvocationContext = <A, E, R>(
|
|
|
199
219
|
>
|
|
200
220
|
}
|
|
201
221
|
|
|
222
|
+
// -----------------------------------------------------------------------------
|
|
223
|
+
// Request-level dependencies
|
|
224
|
+
// -----------------------------------------------------------------------------
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Marks a request-level dependency of an MCP registration or middleware.
|
|
228
|
+
*
|
|
229
|
+
* **Details**
|
|
230
|
+
*
|
|
231
|
+
* Registrations carry handler dependencies as `McpRequest<"Requires", S>`.
|
|
232
|
+
* MCP middleware and transports discharge these markers; providing `S` with
|
|
233
|
+
* `Layer.provide` or `Effect.provide` does not.
|
|
234
|
+
*
|
|
235
|
+
* `layerHttp` and `layerHttpRequestScoped` convert the markers they don't
|
|
236
|
+
* discharge into `HttpRouter.Request` markers, so `HttpRouter.middleware` can
|
|
237
|
+
* satisfy them. On stdio and custom transports an undischarged marker is a
|
|
238
|
+
* compile error.
|
|
239
|
+
*
|
|
240
|
+
* @see {@link middleware} for discharging request-level dependencies
|
|
241
|
+
*
|
|
242
|
+
* @stability unstable
|
|
243
|
+
* @category utility types
|
|
244
|
+
* @since 4.0.0
|
|
245
|
+
*/
|
|
246
|
+
export interface McpRequest<Kind extends string, T> {
|
|
247
|
+
readonly _: unique symbol
|
|
248
|
+
readonly kind: Kind
|
|
249
|
+
readonly type: T
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Helper types for `McpRequest` markers.
|
|
254
|
+
*
|
|
255
|
+
* @stability unstable
|
|
256
|
+
* @category utility types
|
|
257
|
+
* @since 4.0.0
|
|
258
|
+
*/
|
|
259
|
+
export declare namespace McpRequest {
|
|
260
|
+
/**
|
|
261
|
+
* Wraps each member of a union in an `McpRequest` marker of the supplied kind.
|
|
262
|
+
*
|
|
263
|
+
* @stability unstable
|
|
264
|
+
* @category utility types
|
|
265
|
+
* @since 4.0.0
|
|
266
|
+
*/
|
|
267
|
+
export type From<Kind extends string, R> = R extends infer T ? McpRequest<Kind, T> : never
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Extracts the services from `McpRequest` markers of the supplied kind.
|
|
271
|
+
*
|
|
272
|
+
* @stability unstable
|
|
273
|
+
* @category utility types
|
|
274
|
+
* @since 4.0.0
|
|
275
|
+
*/
|
|
276
|
+
export type Only<Kind extends string, A> = A extends McpRequest<Kind, infer T> ? T : never
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Removes `McpRequest` markers from a union.
|
|
280
|
+
*
|
|
281
|
+
* @stability unstable
|
|
282
|
+
* @category utility types
|
|
283
|
+
* @since 4.0.0
|
|
284
|
+
*/
|
|
285
|
+
export type Without<A> = A extends McpRequest<infer _Kind, infer _> ? never : A
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Opaque handler result. Middleware can wrap execution and provide services,
|
|
290
|
+
* but cannot inspect or replace the result.
|
|
291
|
+
*
|
|
292
|
+
* @stability unstable
|
|
293
|
+
* @category middleware
|
|
294
|
+
* @since 4.0.0
|
|
295
|
+
*/
|
|
296
|
+
export interface MiddlewareSuccess {
|
|
297
|
+
readonly _: unique symbol
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* The request methods that run MCP middleware. More methods may be added, so
|
|
302
|
+
* don't `switch` on it exhaustively without a default case.
|
|
303
|
+
*
|
|
304
|
+
* @stability unstable
|
|
305
|
+
* @category middleware
|
|
306
|
+
* @since 4.0.0
|
|
307
|
+
*/
|
|
308
|
+
export type MiddlewareMethod =
|
|
309
|
+
| "tools/call"
|
|
310
|
+
| "resources/read"
|
|
311
|
+
| "prompts/get"
|
|
312
|
+
| "completion/complete"
|
|
313
|
+
| "tools/list"
|
|
314
|
+
| "resources/list"
|
|
315
|
+
| "resources/templates/list"
|
|
316
|
+
| "prompts/list"
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Request metadata passed to MCP middleware.
|
|
320
|
+
*
|
|
321
|
+
* **Details**
|
|
322
|
+
*
|
|
323
|
+
* `target` is the tool name, the requested resource URI, or the prompt name.
|
|
324
|
+
* For `completion/complete` it is the prompt name, or the URI template of the
|
|
325
|
+
* resource template being completed. `payload` holds the request parameters.
|
|
326
|
+
*
|
|
327
|
+
* On list requests, middleware runs only when it wraps a {@link visibility},
|
|
328
|
+
* once for each registration it wraps. `target` is then the tool name, the
|
|
329
|
+
* resource URI, the URI template or the prompt name of that registration, and
|
|
330
|
+
* `payload` is `undefined`.
|
|
331
|
+
*
|
|
332
|
+
* `headers` are the HTTP request headers. They are empty on stdio, on custom
|
|
333
|
+
* transports and for in-process calls. `sessionId` is the `Mcp-Session-Id`
|
|
334
|
+
* header, so it is `undefined` outside sessionful HTTP.
|
|
335
|
+
*
|
|
336
|
+
* @stability unstable
|
|
337
|
+
* @category middleware
|
|
338
|
+
* @since 4.0.0
|
|
339
|
+
*/
|
|
340
|
+
export interface MiddlewareOptions {
|
|
341
|
+
readonly method: MiddlewareMethod
|
|
342
|
+
readonly target: string
|
|
343
|
+
readonly payload: unknown
|
|
344
|
+
readonly requestContext: McpRequestContext["Service"]
|
|
345
|
+
readonly headers: Headers.Headers
|
|
346
|
+
readonly sessionId: string | undefined
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const MiddlewareTypeId = "~effect/ai/McpServer/Middleware"
|
|
350
|
+
|
|
351
|
+
type MiddlewareConfig = {
|
|
352
|
+
provides: any
|
|
353
|
+
error: McpErrorBase
|
|
354
|
+
requires: any
|
|
355
|
+
layerError: any
|
|
356
|
+
layerRequires: any
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
type MiddlewareLayer<Config extends MiddlewareConfig, R> = Layer.Layer<
|
|
360
|
+
McpRequest.From<"Requires", Config["provides"]>,
|
|
361
|
+
Config["layerError"],
|
|
362
|
+
R | Config["layerRequires"] | McpRequest.From<"Requires", Config["requires"]>
|
|
363
|
+
>
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Composable MCP middleware.
|
|
367
|
+
*
|
|
368
|
+
* **Details**
|
|
369
|
+
*
|
|
370
|
+
* Provide `layer` to registration layers, or to the Effect form of a
|
|
371
|
+
* registration, to run the middleware for those registrations. Use
|
|
372
|
+
* `layerGlobal` to run it for every request on the server.
|
|
373
|
+
*
|
|
374
|
+
* Services the middleware reads per request are `McpRequest<"Requires", S>`
|
|
375
|
+
* requirements of `layer` and `layerGlobal`, like the request-level
|
|
376
|
+
* dependencies of a registration. A middleware that provides `S` meets them
|
|
377
|
+
* when it runs outside this one: `a.combine(b)` runs `b` outside `a`, and so
|
|
378
|
+
* does providing `b.layer` or `b.layerGlobal` outside `a.layer`, though
|
|
379
|
+
* `layerGlobal` installs `b` for the whole server. The transport discharges
|
|
380
|
+
* the services it supplies, and `layerHttp` turns the rest into
|
|
381
|
+
* `HttpRouter.Request` requirements that an `HttpRouter.middleware` can meet.
|
|
382
|
+
*
|
|
383
|
+
* @see {@link middleware} for creating MCP middleware
|
|
384
|
+
*
|
|
385
|
+
* @stability unstable
|
|
386
|
+
* @category middleware
|
|
387
|
+
* @since 4.0.0
|
|
388
|
+
*/
|
|
389
|
+
export interface Middleware<Config extends MiddlewareConfig> {
|
|
390
|
+
readonly [MiddlewareTypeId]: Config
|
|
391
|
+
readonly layer: MiddlewareLayer<Config, never>
|
|
392
|
+
readonly layerGlobal: MiddlewareLayer<Config, McpServer>
|
|
393
|
+
readonly combine: <Config2 extends MiddlewareConfig>(other: Middleware<Config2>) => Middleware<{
|
|
394
|
+
provides: Config2["provides"] | Config["provides"]
|
|
395
|
+
error: Config2["error"] | Config["error"]
|
|
396
|
+
requires: Exclude<Config["requires"], Config2["provides"]> | Config2["requires"]
|
|
397
|
+
layerError: Config["layerError"] | Config2["layerError"]
|
|
398
|
+
layerRequires: Config["layerRequires"] | Config2["layerRequires"]
|
|
399
|
+
}>
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Creates MCP middleware that runs around `tools/call`, `resources/read`,
|
|
404
|
+
* `prompts/get` and `completion/complete` requests.
|
|
405
|
+
*
|
|
406
|
+
* **Details**
|
|
407
|
+
*
|
|
408
|
+
* Middleware is how request-level dependencies are satisfied: declare the
|
|
409
|
+
* services it provides with `middleware<{ provides: S }>()` and provide them to
|
|
410
|
+
* the handler effect. It receives the request headers, session id and request
|
|
411
|
+
* context, so it can resolve the current user per request.
|
|
412
|
+
*
|
|
413
|
+
* Pass a function, or an Effect that builds the function. Services the Effect
|
|
414
|
+
* reads are requirements of the middleware layer. Services the function reads
|
|
415
|
+
* per request, other than `McpRequestContext`, are per-request requirements
|
|
416
|
+
* of the result; see {@link Middleware} for how they are met.
|
|
417
|
+
*
|
|
418
|
+
* Failures must be `McpErrorBase` values. A middleware failure is sent as a
|
|
419
|
+
* JSON-RPC error with its `code`, `message` and `data`, never as a tool
|
|
420
|
+
* result. Pick custom codes from the implementation-defined range
|
|
421
|
+
* (-32000 to -32019): any numeric `code` is sent as is. Defects are logged,
|
|
422
|
+
* reported to the `ErrorReporter` and sent as `InternalError("Internal error")`.
|
|
423
|
+
* Interruption propagates.
|
|
424
|
+
*
|
|
425
|
+
* Local middleware runs inside global middleware, and middleware provided both
|
|
426
|
+
* locally and globally runs once. Middleware never runs for `initialize`. It
|
|
427
|
+
* runs for list requests only when it wraps a {@link visibility}, so that the
|
|
428
|
+
* visibility check can read the services it provides.
|
|
429
|
+
*
|
|
430
|
+
* **Example** (Resolving the current user per request)
|
|
431
|
+
*
|
|
432
|
+
* ```ts
|
|
433
|
+
* import { Context, Effect, Layer, Schema } from "effect"
|
|
434
|
+
* import { McpSchema, McpServer, Tool, Toolkit } from "effect/ai"
|
|
435
|
+
*
|
|
436
|
+
* class CurrentUser extends Context.Service<CurrentUser, { readonly id: string }>()("CurrentUser") {}
|
|
437
|
+
*
|
|
438
|
+
* class Unauthorized extends Schema.Error<Unauthorized>("Unauthorized")({
|
|
439
|
+
* ...McpSchema.McpErrorBase.fields,
|
|
440
|
+
* _tag: Schema.tag("Unauthorized"),
|
|
441
|
+
* code: Schema.tag(-32001)
|
|
442
|
+
* }) {}
|
|
443
|
+
*
|
|
444
|
+
* const WhoAmI = Tool.make("WhoAmI", { success: Schema.String, dependencies: [CurrentUser] })
|
|
445
|
+
* const Tools = Toolkit.make(WhoAmI)
|
|
446
|
+
*
|
|
447
|
+
* const Auth = McpServer.middleware<{ provides: CurrentUser }>()((effect, { headers }) => {
|
|
448
|
+
* const token = headers["authorization"]
|
|
449
|
+
* return token === undefined
|
|
450
|
+
* ? Effect.fail(new Unauthorized({ message: "Missing credentials" }))
|
|
451
|
+
* : Effect.provideService(effect, CurrentUser, { id: token })
|
|
452
|
+
* })
|
|
453
|
+
*
|
|
454
|
+
* const Registrations = McpServer.toolkit(Tools).pipe(
|
|
455
|
+
* Layer.provide(Auth.layer),
|
|
456
|
+
* Layer.provide(Tools.toLayer({
|
|
457
|
+
* WhoAmI: () => Effect.map(CurrentUser, (user) => user.id)
|
|
458
|
+
* }))
|
|
459
|
+
* )
|
|
460
|
+
* ```
|
|
461
|
+
*
|
|
462
|
+
* @stability unstable
|
|
463
|
+
* @category middleware
|
|
464
|
+
* @since 4.0.0
|
|
465
|
+
*/
|
|
466
|
+
export const middleware:
|
|
467
|
+
& middleware.Make<never>
|
|
468
|
+
& (<Config extends { provides?: any } = {}>() => middleware.Make<
|
|
469
|
+
Config extends { provides: infer P } ? P : never
|
|
470
|
+
>) = function() {
|
|
471
|
+
if (arguments.length === 0) {
|
|
472
|
+
return makeMiddleware as any
|
|
473
|
+
}
|
|
474
|
+
return makeMiddleware(arguments[0], arguments[1]) as any
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Types used by `middleware`.
|
|
479
|
+
*
|
|
480
|
+
* @stability unstable
|
|
481
|
+
* @category middleware
|
|
482
|
+
* @since 4.0.0
|
|
483
|
+
*/
|
|
484
|
+
export declare namespace middleware {
|
|
485
|
+
/**
|
|
486
|
+
* The middleware function.
|
|
487
|
+
*
|
|
488
|
+
* @stability unstable
|
|
489
|
+
* @category middleware
|
|
490
|
+
* @since 4.0.0
|
|
491
|
+
*/
|
|
492
|
+
export type Fn<Provides, E, R> = (
|
|
493
|
+
effect: Effect.Effect<MiddlewareSuccess, McpErrorBase, Types.NoInfer<Provides>>,
|
|
494
|
+
options: MiddlewareOptions
|
|
495
|
+
) => Effect.Effect<MiddlewareSuccess, E, R>
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* The overloads of a middleware constructor.
|
|
499
|
+
*
|
|
500
|
+
* @stability unstable
|
|
501
|
+
* @category middleware
|
|
502
|
+
* @since 4.0.0
|
|
503
|
+
*/
|
|
504
|
+
export type Make<Provides> = {
|
|
505
|
+
<E extends McpErrorBase, R, EX, RX, const Global extends boolean = false>(
|
|
506
|
+
middleware: Effect.Effect<Fn<Provides, E, R>, EX, RX>,
|
|
507
|
+
options?: { readonly global?: Global | undefined }
|
|
508
|
+
): Made<Global, {
|
|
509
|
+
provides: Provides
|
|
510
|
+
error: E
|
|
511
|
+
requires: Exclude<R, McpRequestContext>
|
|
512
|
+
layerError: EX
|
|
513
|
+
layerRequires: Exclude<RX, Scope.Scope>
|
|
514
|
+
}>
|
|
515
|
+
<E extends McpErrorBase, R, const Global extends boolean = false>(
|
|
516
|
+
middleware: Fn<Provides, E, R>,
|
|
517
|
+
options?: { readonly global?: Global | undefined }
|
|
518
|
+
): Made<Global, {
|
|
519
|
+
provides: Provides
|
|
520
|
+
error: E
|
|
521
|
+
requires: Exclude<R, McpRequestContext>
|
|
522
|
+
layerError: never
|
|
523
|
+
layerRequires: never
|
|
524
|
+
}>
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Creates middleware that hides registrations per request.
|
|
530
|
+
*
|
|
531
|
+
* **Details**
|
|
532
|
+
*
|
|
533
|
+
* The check runs for every registration the middleware is provided to, on list
|
|
534
|
+
* requests and on calls, reads, gets and completions. When it returns `false`
|
|
535
|
+
* the registration is left out of list results, and requests for it fail as if
|
|
536
|
+
* it was not registered.
|
|
537
|
+
*
|
|
538
|
+
* The result is a `Middleware` and composes like one. The check receives the
|
|
539
|
+
* same {@link MiddlewareOptions} as middleware, and services it reads per
|
|
540
|
+
* request, other than `McpRequestContext`, are met the same way. Scope a
|
|
541
|
+
* visibility to some tools by providing its layer to their toolkit only, or
|
|
542
|
+
* branch on `target`.
|
|
543
|
+
*
|
|
544
|
+
* On a list request, the check and the middleware outside the innermost
|
|
545
|
+
* visibility run once for each registration. Middleware inside it does not
|
|
546
|
+
* run. A failure of that middleware or of a check fails the whole list
|
|
547
|
+
* request. It never hides a registration.
|
|
548
|
+
*
|
|
549
|
+
* Unlike `EnabledWhen`, a visibility runs the middleware around it on list
|
|
550
|
+
* requests, and middleware sees requests for hidden registrations.
|
|
551
|
+
*
|
|
552
|
+
* **Example** (Hiding admin tools from other users)
|
|
553
|
+
*
|
|
554
|
+
* ```ts
|
|
555
|
+
* import { Context, Effect, Layer, Schema } from "effect"
|
|
556
|
+
* import { McpServer, Tool, Toolkit } from "effect/ai"
|
|
557
|
+
*
|
|
558
|
+
* class CurrentUser extends Context.Service<CurrentUser, { readonly isAdmin: boolean }>()("CurrentUser") {}
|
|
559
|
+
*
|
|
560
|
+
* const AdminTools = Toolkit.make(Tool.make("DeleteUser", { success: Schema.String }))
|
|
561
|
+
*
|
|
562
|
+
* const Auth = McpServer.middleware<{ provides: CurrentUser }>()((effect, { headers }) =>
|
|
563
|
+
* Effect.provideService(effect, CurrentUser, { isAdmin: headers["x-role"] === "admin" })
|
|
564
|
+
* )
|
|
565
|
+
* const AdminOnly = McpServer.visibility(() => Effect.map(CurrentUser, (user) => user.isAdmin))
|
|
566
|
+
*
|
|
567
|
+
* const Registrations = McpServer.toolkit(AdminTools).pipe(
|
|
568
|
+
* Layer.provide(AdminOnly.combine(Auth).layer),
|
|
569
|
+
* Layer.provide(AdminTools.toLayer({ DeleteUser: () => Effect.succeed("deleted") }))
|
|
570
|
+
* )
|
|
571
|
+
* ```
|
|
572
|
+
*
|
|
573
|
+
* @see {@link middleware} for providing the services a check reads
|
|
574
|
+
*
|
|
575
|
+
* @stability unstable
|
|
576
|
+
* @category middleware
|
|
577
|
+
* @since 4.0.0
|
|
578
|
+
*/
|
|
579
|
+
export const visibility: visibility.Make = ((
|
|
580
|
+
check: AnyVisibilityCheck | Effect.Effect<AnyVisibilityCheck, any, any>,
|
|
581
|
+
options?: { readonly global?: boolean | undefined }
|
|
582
|
+
) => makeMiddleware(Effect.isEffect(check) ? Effect.map(check, makeGate) : makeGate(check), options)) as any
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Types used by `visibility`.
|
|
586
|
+
*
|
|
587
|
+
* @stability unstable
|
|
588
|
+
* @category middleware
|
|
589
|
+
* @since 4.0.0
|
|
590
|
+
*/
|
|
591
|
+
export declare namespace visibility {
|
|
592
|
+
/**
|
|
593
|
+
* The visibility check.
|
|
594
|
+
*
|
|
595
|
+
* @stability unstable
|
|
596
|
+
* @category middleware
|
|
597
|
+
* @since 4.0.0
|
|
598
|
+
*/
|
|
599
|
+
export type Check<E, R> = (options: MiddlewareOptions) => Effect.Effect<boolean, E, R>
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* The overloads of `visibility`.
|
|
603
|
+
*
|
|
604
|
+
* @stability unstable
|
|
605
|
+
* @category middleware
|
|
606
|
+
* @since 4.0.0
|
|
607
|
+
*/
|
|
608
|
+
export type Make = {
|
|
609
|
+
<E extends McpErrorBase, R, EX, RX, const Global extends boolean = false>(
|
|
610
|
+
check: Effect.Effect<Check<E, R>, EX, RX>,
|
|
611
|
+
options?: { readonly global?: Global | undefined }
|
|
612
|
+
): Made<Global, {
|
|
613
|
+
provides: never
|
|
614
|
+
error: E
|
|
615
|
+
requires: Exclude<R, McpRequestContext>
|
|
616
|
+
layerError: EX
|
|
617
|
+
layerRequires: Exclude<RX, Scope.Scope>
|
|
618
|
+
}>
|
|
619
|
+
<E extends McpErrorBase, R, const Global extends boolean = false>(
|
|
620
|
+
check: Check<E, R>,
|
|
621
|
+
options?: { readonly global?: Global | undefined }
|
|
622
|
+
): Made<Global, {
|
|
623
|
+
provides: never
|
|
624
|
+
error: E
|
|
625
|
+
requires: Exclude<R, McpRequestContext>
|
|
626
|
+
layerError: never
|
|
627
|
+
layerRequires: never
|
|
628
|
+
}>
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
type AnyVisibilityCheck = visibility.Check<any, any>
|
|
633
|
+
|
|
634
|
+
// Raised by a visibility gate. It is left out of the types because `makeInvoke`
|
|
635
|
+
// replaces it with the not-found error of the request. It still has the shape
|
|
636
|
+
// of an MCP error, since middleware outside the gate sees it as one.
|
|
637
|
+
class Hidden extends Data.TaggedError("effect/ai/McpServer/Hidden")<{
|
|
638
|
+
readonly code: number
|
|
639
|
+
readonly message: string
|
|
640
|
+
}> {}
|
|
641
|
+
|
|
642
|
+
const failHidden = Effect.fail(
|
|
643
|
+
new Hidden({ code: McpSchema.INVALID_PARAMS_ERROR_CODE, message: "Not found" })
|
|
644
|
+
) as Effect.Effect<never>
|
|
645
|
+
|
|
646
|
+
// Marks the middleware functions created by `visibility`.
|
|
647
|
+
const gates = new WeakSet<AnyMiddlewareFn>()
|
|
648
|
+
|
|
649
|
+
const makeGate = (check: AnyVisibilityCheck): AnyMiddlewareFn => {
|
|
650
|
+
const gate: AnyMiddlewareFn = (effect, options) =>
|
|
651
|
+
Effect.flatMap(check(options), (visible) => visible ? effect : failHidden)
|
|
652
|
+
gates.add(gate)
|
|
653
|
+
return gate
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
type Made<Global extends boolean, Config extends MiddlewareConfig> = Global extends true
|
|
657
|
+
? MiddlewareLayer<Config, McpServer>
|
|
658
|
+
: Middleware<Config>
|
|
659
|
+
|
|
660
|
+
type AnyMiddlewareFn = middleware.Fn<any, any, any>
|
|
661
|
+
|
|
662
|
+
class MiddlewareFn extends Context.Service<MiddlewareFn, AnyMiddlewareFn>()("effect/ai/McpServer/MiddlewareFn") {}
|
|
663
|
+
|
|
664
|
+
const makeMiddleware = (
|
|
665
|
+
fn: AnyMiddlewareFn | Effect.Effect<AnyMiddlewareFn, any, any>,
|
|
666
|
+
options?: { readonly global?: boolean | undefined }
|
|
667
|
+
) => {
|
|
668
|
+
const middleware = new MiddlewareImpl(
|
|
669
|
+
Effect.isEffect(fn) ? Layer.effect(MiddlewareFn)(fn) : Layer.succeed(MiddlewareFn, fn)
|
|
670
|
+
)
|
|
671
|
+
return options?.global ? middleware.layerGlobal : middleware
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
let middlewareId = 0
|
|
675
|
+
|
|
676
|
+
class MiddlewareImpl {
|
|
677
|
+
readonly [MiddlewareTypeId] = {} as any
|
|
678
|
+
readonly layer: Layer.Layer<never, any, any>
|
|
679
|
+
readonly layerGlobal: Layer.Layer<never, any, any>
|
|
680
|
+
readonly layerFn: Layer.Layer<MiddlewareFn, any, any>
|
|
681
|
+
readonly dependencies: Layer.Layer<any, any, any> | undefined
|
|
682
|
+
|
|
683
|
+
constructor(
|
|
684
|
+
layerFn: Layer.Layer<MiddlewareFn, any, any>,
|
|
685
|
+
dependencies?: Layer.Layer<any, any, any> | undefined
|
|
686
|
+
) {
|
|
687
|
+
this.layerFn = layerFn
|
|
688
|
+
this.dependencies = dependencies
|
|
689
|
+
const contextKey = `${middlewareKeyPrefix}${++middlewareId}`
|
|
690
|
+
this.layer = Layer.effectContext(Effect.gen(function*() {
|
|
691
|
+
const stack: Array<AnyMiddlewareFn> = [yield* MiddlewareFn]
|
|
692
|
+
if (dependencies) {
|
|
693
|
+
const memoMap = yield* Layer.CurrentMemoMap
|
|
694
|
+
const scope = yield* Effect.scope
|
|
695
|
+
stack.push(...getMiddleware(yield* Layer.buildWithMemoMap(dependencies, memoMap, scope)))
|
|
696
|
+
}
|
|
697
|
+
return Context.makeUnsafe<never>(new Map([[contextKey, stack]]))
|
|
698
|
+
})).pipe(Layer.provide(layerFn))
|
|
699
|
+
this.layerGlobal = Layer.effectDiscard(Effect.gen(function*() {
|
|
700
|
+
const stack: ReadonlyArray<AnyMiddlewareFn> = Context.getOrUndefinedUnsafe(
|
|
701
|
+
yield* Effect.context<never>(),
|
|
702
|
+
contextKey as any
|
|
703
|
+
)!
|
|
704
|
+
const { globalMiddleware } = internalState.get(yield* McpServer)!
|
|
705
|
+
yield* Effect.acquireRelease(
|
|
706
|
+
Effect.sync(() => {
|
|
707
|
+
globalMiddleware.push(...stack)
|
|
708
|
+
}),
|
|
709
|
+
() =>
|
|
710
|
+
Effect.sync(() => {
|
|
711
|
+
for (const fn of stack) {
|
|
712
|
+
const index = globalMiddleware.indexOf(fn)
|
|
713
|
+
if (index !== -1) globalMiddleware.splice(index, 1)
|
|
714
|
+
}
|
|
715
|
+
})
|
|
716
|
+
)
|
|
717
|
+
})).pipe(Layer.provide(this.layer))
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
combine(other: Middleware<any>): Middleware<any> {
|
|
721
|
+
return new MiddlewareImpl(
|
|
722
|
+
this.layerFn,
|
|
723
|
+
this.dependencies ? Layer.provideMerge(this.dependencies, other.layer as any) : other.layer as any
|
|
724
|
+
) as any
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// The innermost middleware comes first.
|
|
729
|
+
const getMiddleware = (context: Context.Context<never>): ReadonlyArray<AnyMiddlewareFn> => {
|
|
730
|
+
const stacks: Array<ReadonlyArray<AnyMiddlewareFn>> = []
|
|
731
|
+
let maxLength = 0
|
|
732
|
+
for (const [key, value] of context.mapUnsafe) {
|
|
733
|
+
if (key.startsWith(middlewareKeyPrefix)) {
|
|
734
|
+
stacks.push(value)
|
|
735
|
+
maxLength = Math.max(maxLength, value.length)
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
const unique = new Set<AnyMiddlewareFn>()
|
|
739
|
+
for (let i = maxLength - 1; i >= 0; i--) {
|
|
740
|
+
for (const stack of stacks) {
|
|
741
|
+
if (i < stack.length) unique.add(stack[i])
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return Arr.fromIterable(unique).reverse()
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const isMcpError = (error: unknown): error is McpErrorBase =>
|
|
748
|
+
Predicate.hasProperty(error, "code") && typeof error.code === "number" &&
|
|
749
|
+
Predicate.hasProperty(error, "message") && typeof error.message === "string"
|
|
750
|
+
|
|
751
|
+
// Distinguishes handler failures from failures raised or rewritten by middleware.
|
|
752
|
+
const HandlerFailure = Context.Reference<boolean>("effect/ai/McpServer/HandlerFailure", {
|
|
753
|
+
defaultValue: () => false
|
|
754
|
+
})
|
|
755
|
+
const handlerFailure = Context.make(HandlerFailure, true)
|
|
756
|
+
|
|
757
|
+
const internalError = new InternalError({ message: "Internal error" })
|
|
758
|
+
|
|
759
|
+
const rejectWith = (error: McpErrorBase) =>
|
|
760
|
+
Effect.fail(new McpCore.RequestRejected({ code: error.code, message: error.message, data: error.data, error }))
|
|
761
|
+
|
|
762
|
+
type CallMethod = "tools/call" | "resources/read" | "prompts/get" | "completion/complete"
|
|
763
|
+
|
|
764
|
+
type ListMethod = Exclude<MiddlewareMethod, CallMethod>
|
|
765
|
+
|
|
766
|
+
interface NotFoundErrors {
|
|
767
|
+
readonly "tools/call": McpCore.ToolNotFound
|
|
768
|
+
readonly "resources/read": McpCore.ResourceNotFound
|
|
769
|
+
readonly "prompts/get": McpCore.PromptNotFound
|
|
770
|
+
readonly "completion/complete": InvalidParams
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
const notFound = (method: CallMethod, target: string): NotFoundErrors[CallMethod] => {
|
|
774
|
+
switch (method) {
|
|
775
|
+
case "tools/call":
|
|
776
|
+
return new McpCore.ToolNotFound({ name: target })
|
|
777
|
+
case "resources/read":
|
|
778
|
+
return new McpCore.ResourceNotFound({ uri: target })
|
|
779
|
+
case "prompts/get":
|
|
780
|
+
return new McpCore.PromptNotFound({ name: target })
|
|
781
|
+
case "completion/complete":
|
|
782
|
+
return new InvalidParams({ message: "Unknown completion reference or argument" })
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
const isHidden = (error: unknown): error is Hidden => error instanceof Hidden
|
|
787
|
+
|
|
788
|
+
// Recovers a request rejected by a visibility gate. Any other cause, including
|
|
789
|
+
// one that also holds an interrupt, is propagated unchanged.
|
|
790
|
+
const catchHidden =
|
|
791
|
+
<B, E2>(onHidden: () => Effect.Effect<B, E2>) =>
|
|
792
|
+
<A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A | B, Exclude<E, Hidden> | E2, R> =>
|
|
793
|
+
Effect.catchCause(self, (cause): Effect.Effect<B, Exclude<E, Hidden> | E2> => {
|
|
794
|
+
const failure = Cause.findFail(cause)
|
|
795
|
+
return !Cause.hasInterrupts(cause) && Result.isSuccess(failure) && isHidden(failure.success.error)
|
|
796
|
+
? onHidden()
|
|
797
|
+
: Effect.failCause(cause as Cause.Cause<Exclude<E, Hidden>>)
|
|
798
|
+
})
|
|
799
|
+
|
|
800
|
+
// The innermost middleware comes first. Middleware provided both locally and
|
|
801
|
+
// globally runs once, at its outermost position.
|
|
802
|
+
const middlewareStack = (
|
|
803
|
+
localMiddleware: ReadonlyArray<AnyMiddlewareFn>,
|
|
804
|
+
globalMiddleware: ReadonlyArray<AnyMiddlewareFn>
|
|
805
|
+
): ReadonlyArray<AnyMiddlewareFn> => {
|
|
806
|
+
if (globalMiddleware.length === 0) {
|
|
807
|
+
return localMiddleware
|
|
808
|
+
}
|
|
809
|
+
if (localMiddleware.length === 0) {
|
|
810
|
+
return globalMiddleware
|
|
811
|
+
}
|
|
812
|
+
const stack = [...localMiddleware, ...globalMiddleware]
|
|
813
|
+
return stack.filter((fn, index) => stack.lastIndexOf(fn) === index)
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// Runs a handler inside middleware. The handler's own failures pass through, a
|
|
817
|
+
// visibility gate fails with `Hidden`, and other failures become
|
|
818
|
+
// `RequestRejected`: middleware failures as JSON-RPC errors, anything else as
|
|
819
|
+
// a reported internal error.
|
|
820
|
+
const runMiddleware = <A, E>(
|
|
821
|
+
handler: Effect.Effect<A, E, any>,
|
|
822
|
+
middleware: ReadonlyArray<AnyMiddlewareFn>,
|
|
823
|
+
invocation: McpCore.McpInvocation,
|
|
824
|
+
request: Pick<MiddlewareOptions, "method" | "target" | "payload">
|
|
825
|
+
): Effect.Effect<A, E | Hidden | McpCore.RequestRejected> => {
|
|
826
|
+
const effect = middleware.length === 0 ? handler : Effect.withFiber((fiber) => {
|
|
827
|
+
const headers = Context.getOrUndefined(fiber.context, HttpServerRequest.HttpServerRequest)?.headers ??
|
|
828
|
+
Headers.empty
|
|
829
|
+
const options: MiddlewareOptions = {
|
|
830
|
+
...request,
|
|
831
|
+
requestContext: invocation.requestContext,
|
|
832
|
+
headers,
|
|
833
|
+
sessionId: headers[MCP_SESSION_ID_HEADER]
|
|
834
|
+
}
|
|
835
|
+
const handled = Effect.catchCause(handler, (cause) => Effect.failCause(Cause.annotate(cause, handlerFailure)))
|
|
836
|
+
return middleware.reduce((effect: Effect.Effect<any, any, any>, fn) => fn(effect, options), handled)
|
|
837
|
+
})
|
|
838
|
+
return provideInvocationContext(effect, invocation).pipe(
|
|
839
|
+
Effect.catchCause((cause): Effect.Effect<never, E | Hidden | McpCore.RequestRejected> => {
|
|
840
|
+
if (Cause.hasInterrupts(cause)) {
|
|
841
|
+
return Effect.failCause(cause)
|
|
842
|
+
}
|
|
843
|
+
const failure = Cause.findFail(cause)
|
|
844
|
+
if (Result.isSuccess(failure) && !Cause.hasDies(cause)) {
|
|
845
|
+
const error = failure.success.error
|
|
846
|
+
if (isHidden(error)) {
|
|
847
|
+
return Effect.fail(error)
|
|
848
|
+
}
|
|
849
|
+
if (middleware.length === 0 || Context.get(Cause.reasonAnnotations(failure.success), HandlerFailure)) {
|
|
850
|
+
return Effect.failCause(cause)
|
|
851
|
+
}
|
|
852
|
+
if (isMcpError(error)) {
|
|
853
|
+
return rejectWith(error)
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
return Effect.logError(cause).pipe(
|
|
857
|
+
Effect.andThen(ErrorReporter.report(cause)),
|
|
858
|
+
Effect.andThen(rejectWith(internalError))
|
|
859
|
+
)
|
|
860
|
+
})
|
|
861
|
+
) as Effect.Effect<A, E | Hidden | McpCore.RequestRejected>
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// Request services override captured services; global middleware wraps local middleware.
|
|
865
|
+
const makeInvoke = (
|
|
866
|
+
context: Context.Context<never> | undefined,
|
|
867
|
+
globalMiddleware: ReadonlyArray<AnyMiddlewareFn>
|
|
868
|
+
) => {
|
|
869
|
+
const services = context === undefined ? Context.empty() : omitRequestServices(context)
|
|
870
|
+
const localMiddleware = context === undefined ? [] : getMiddleware(context)
|
|
871
|
+
const invoke = <A, E, M extends CallMethod>(
|
|
872
|
+
handler: Effect.Effect<A, E, any>,
|
|
873
|
+
invocation: McpCore.McpInvocation,
|
|
874
|
+
request: {
|
|
875
|
+
readonly method: M
|
|
876
|
+
readonly target: string
|
|
877
|
+
readonly payload: unknown
|
|
878
|
+
}
|
|
879
|
+
): Effect.Effect<A, E | NotFoundErrors[M] | McpCore.RequestRejected> =>
|
|
880
|
+
runMiddleware(
|
|
881
|
+
services.mapUnsafe.size === 0
|
|
882
|
+
? handler
|
|
883
|
+
: Effect.updateContext(handler, (context: Context.Context<any>) => Context.merge(services, context)),
|
|
884
|
+
middlewareStack(localMiddleware, globalMiddleware),
|
|
885
|
+
invocation,
|
|
886
|
+
request
|
|
887
|
+
).pipe(
|
|
888
|
+
catchHidden(() => Effect.fail(notFound(request.method, request.target) as NotFoundErrors[M]))
|
|
889
|
+
) as Effect.Effect<A, E | NotFoundErrors[M] | McpCore.RequestRejected>
|
|
890
|
+
// On list requests only the middleware outside the innermost visibility runs.
|
|
891
|
+
const visible = (method: ListMethod, target: string): McpCore.Visible => (invocation) => {
|
|
892
|
+
const middleware = middlewareStack(localMiddleware, globalMiddleware)
|
|
893
|
+
const innermost = middleware.findIndex((fn) => gates.has(fn))
|
|
894
|
+
if (innermost === -1) {
|
|
895
|
+
return Effect.succeed(true)
|
|
896
|
+
}
|
|
897
|
+
return runMiddleware(Effect.void, middleware.slice(innermost), invocation, { method, target, payload: undefined })
|
|
898
|
+
.pipe(
|
|
899
|
+
Effect.as(true),
|
|
900
|
+
catchHidden(() => Effect.succeed(false))
|
|
901
|
+
)
|
|
902
|
+
}
|
|
903
|
+
return { invoke, visible }
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// Applies the `EnabledWhen` annotation of a registration.
|
|
907
|
+
const isEnabled = (annotations: Context.Context<never>) => {
|
|
908
|
+
const enabledWhen = Context.getOrUndefined(annotations, EnabledWhen)
|
|
909
|
+
return (profile: McpCore.NegotiatedProtocolProfile<string>): boolean =>
|
|
910
|
+
enabledWhen === undefined || enabledWhen({
|
|
911
|
+
protocolVersion: profile.protocolVersion,
|
|
912
|
+
capabilities: profile.clientCapabilities,
|
|
913
|
+
clientInfo: profile.clientInfo
|
|
914
|
+
})
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
const rejectedError = (rejected: McpCore.RequestRejected) => Effect.fail(rejected.error)
|
|
918
|
+
|
|
202
919
|
/**
|
|
203
920
|
* Service that stores and serves an MCP server's registered tools, resources,
|
|
204
921
|
* prompts, completions, and outgoing notifications.
|
|
@@ -225,6 +942,7 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
225
942
|
readonly addTool: (options: {
|
|
226
943
|
readonly tool: McpTool
|
|
227
944
|
readonly annotations: Context.Context<never>
|
|
945
|
+
readonly services?: Context.Context<never> | undefined
|
|
228
946
|
readonly handle: (
|
|
229
947
|
payload: any
|
|
230
948
|
) => Effect.Effect<
|
|
@@ -235,7 +953,7 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
235
953
|
}) => Effect.Effect<void>
|
|
236
954
|
readonly callTool: (
|
|
237
955
|
requests: typeof CallTool.payloadSchema.Type
|
|
238
|
-
) => Effect.Effect<CallToolResult, InternalError | InvalidParams, McpServerClient>
|
|
956
|
+
) => Effect.Effect<CallToolResult, McpErrorBase | InternalError | InvalidParams, McpServerClient>
|
|
239
957
|
|
|
240
958
|
readonly resources: ReadonlyArray<{
|
|
241
959
|
readonly resource: Resource
|
|
@@ -244,6 +962,7 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
244
962
|
readonly addResource: (options: {
|
|
245
963
|
readonly resource: Resource
|
|
246
964
|
readonly annotations: Context.Context<never>
|
|
965
|
+
readonly services?: Context.Context<never> | undefined
|
|
247
966
|
readonly handle: Effect.Effect<
|
|
248
967
|
ReadResourceResult,
|
|
249
968
|
InternalError,
|
|
@@ -259,6 +978,7 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
259
978
|
options: {
|
|
260
979
|
readonly template: ResourceTemplate
|
|
261
980
|
readonly annotations: Context.Context<never>
|
|
981
|
+
readonly services?: Context.Context<never> | undefined
|
|
262
982
|
readonly routerPath: string
|
|
263
983
|
readonly completions: Record<
|
|
264
984
|
string,
|
|
@@ -289,6 +1009,7 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
289
1009
|
readonly addPrompt: (options: {
|
|
290
1010
|
readonly prompt: Prompt
|
|
291
1011
|
readonly annotations: Context.Context<never>
|
|
1012
|
+
readonly services?: Context.Context<never> | undefined
|
|
292
1013
|
readonly completions: Record<
|
|
293
1014
|
string,
|
|
294
1015
|
(
|
|
@@ -302,11 +1023,11 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
302
1023
|
}) => Effect.Effect<void>
|
|
303
1024
|
readonly getPromptResult: (
|
|
304
1025
|
request: typeof GetPrompt.payloadSchema.Type
|
|
305
|
-
) => Effect.Effect<GetPromptResult, InternalError | InvalidParams, McpServerClient>
|
|
1026
|
+
) => Effect.Effect<GetPromptResult, McpErrorBase | InternalError | InvalidParams, McpServerClient>
|
|
306
1027
|
|
|
307
1028
|
readonly completion: (
|
|
308
1029
|
complete: typeof Complete.payloadSchema.Type
|
|
309
|
-
) => Effect.Effect<CompleteResult, InvalidParams | InternalError, McpServerClient>
|
|
1030
|
+
) => Effect.Effect<CompleteResult, McpErrorBase | InvalidParams | InternalError, McpServerClient>
|
|
310
1031
|
}>()("effect/ai/McpServer") {
|
|
311
1032
|
/**
|
|
312
1033
|
* Builds an MCP server service from registered tools, prompts, resources, and completions.
|
|
@@ -333,6 +1054,7 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
333
1054
|
readonly annotations: Context.Context<never>
|
|
334
1055
|
}> = []
|
|
335
1056
|
const notificationsQueue = yield* Queue.make<QueuedServerNotification>()
|
|
1057
|
+
const globalMiddleware: Array<AnyMiddlewareFn> = []
|
|
336
1058
|
const notificationDelivery = { consumers: 0, listChangeRevision: 0, requestEndpoints: 0 }
|
|
337
1059
|
// With only request-scoped endpoints, no long-lived server ever takes from the shared queue.
|
|
338
1060
|
const withoutSharedConsumer = () =>
|
|
@@ -431,22 +1153,20 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
431
1153
|
} else {
|
|
432
1154
|
tools[existingIndex] = options
|
|
433
1155
|
}
|
|
434
|
-
const
|
|
1156
|
+
const { invoke, visible } = makeInvoke(options.services, globalMiddleware)
|
|
435
1157
|
yield* internalCore.tools.register({
|
|
436
1158
|
descriptor: new McpTool({
|
|
437
1159
|
...options.tool,
|
|
438
1160
|
title: options.tool.title ?? options.tool.annotations?.title
|
|
439
1161
|
}),
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
{
|
|
443
|
-
protocolVersion: profile.protocolVersion,
|
|
444
|
-
capabilities: profile.clientCapabilities,
|
|
445
|
-
clientInfo: profile.clientInfo
|
|
446
|
-
}
|
|
447
|
-
),
|
|
1162
|
+
visible: visible("tools/list", options.tool.name),
|
|
1163
|
+
isVisible: isEnabled(options.annotations),
|
|
448
1164
|
handle: (call, invocation) =>
|
|
449
|
-
|
|
1165
|
+
invoke(options.handle(call.arguments), invocation, {
|
|
1166
|
+
method: "tools/call",
|
|
1167
|
+
target: call.name,
|
|
1168
|
+
payload: call
|
|
1169
|
+
}).pipe(
|
|
450
1170
|
Effect.catchTags({
|
|
451
1171
|
InternalError: (error) =>
|
|
452
1172
|
Effect.fail(
|
|
@@ -481,12 +1201,14 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
481
1201
|
request,
|
|
482
1202
|
McpProtocolInternal.invocationFromClient(client)
|
|
483
1203
|
).pipe(
|
|
484
|
-
Effect.
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
1204
|
+
Effect.catch((error) =>
|
|
1205
|
+
error._tag === "RequestRejected" ? rejectedError(error) : Effect.fail(
|
|
1206
|
+
new InvalidParams({
|
|
1207
|
+
message: error._tag === "ToolNotFound"
|
|
1208
|
+
? `Tool '${error.name}' not found`
|
|
1209
|
+
: error.message
|
|
1210
|
+
})
|
|
1211
|
+
)
|
|
490
1212
|
)
|
|
491
1213
|
)
|
|
492
1214
|
if (result._tag === "InputRequired") {
|
|
@@ -507,80 +1229,87 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
507
1229
|
} else {
|
|
508
1230
|
resources[existingIndex] = options
|
|
509
1231
|
}
|
|
1232
|
+
const { invoke, visible } = makeInvoke(options.services, globalMiddleware)
|
|
510
1233
|
yield* internalCore.resources.register({
|
|
511
1234
|
descriptor: options.resource,
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
1235
|
+
visible: visible("resources/list", options.resource.uri),
|
|
1236
|
+
isVisible: isEnabled(options.annotations),
|
|
1237
|
+
read: (invocation) =>
|
|
1238
|
+
invoke(options.handle, invocation, {
|
|
1239
|
+
method: "resources/read",
|
|
1240
|
+
target: options.resource.uri,
|
|
1241
|
+
payload: { uri: options.resource.uri }
|
|
518
1242
|
})
|
|
519
|
-
},
|
|
520
|
-
read: (invocation) => provideInvocationContext(options.handle, invocation)
|
|
521
1243
|
})
|
|
522
1244
|
yield* notifications.client["notifications/resources/list_changed"]({})
|
|
523
1245
|
}),
|
|
524
|
-
addResourceTemplate: Effect.fnUntraced(
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
if (existingIndex === -1) {
|
|
529
|
-
resourceTemplates.push({ template, annotations })
|
|
530
|
-
} else {
|
|
531
|
-
resourceTemplates[existingIndex] = { template, annotations }
|
|
532
|
-
}
|
|
533
|
-
const templateMatcher = makeUriMatcher<true>()
|
|
534
|
-
templateMatcher.add(routerPath, true)
|
|
535
|
-
yield* internalCore.resources.registerTemplate({
|
|
536
|
-
descriptor: template,
|
|
537
|
-
isVisible: (profile) => {
|
|
538
|
-
const enabledWhen = Context.getOrUndefined(annotations, EnabledWhen)
|
|
539
|
-
return enabledWhen === undefined || enabledWhen({
|
|
540
|
-
protocolVersion: profile.protocolVersion,
|
|
541
|
-
capabilities: profile.clientCapabilities,
|
|
542
|
-
clientInfo: profile.clientInfo
|
|
543
|
-
})
|
|
544
|
-
},
|
|
545
|
-
match: (uri) => {
|
|
546
|
-
const match = templateMatcher.find(uri)
|
|
547
|
-
if (match === undefined) {
|
|
548
|
-
return undefined
|
|
549
|
-
}
|
|
550
|
-
const params: Array<string> = []
|
|
551
|
-
for (const key of Object.keys(match.params)) {
|
|
552
|
-
params[Number(key)] = match.params[key]!
|
|
553
|
-
}
|
|
554
|
-
return params
|
|
555
|
-
},
|
|
556
|
-
read: (uri, params, invocation) => provideInvocationContext(handle(uri, Array.from(params)), invocation)
|
|
557
|
-
})
|
|
558
|
-
for (const [param, handle] of Object.entries(completions)) {
|
|
559
|
-
yield* internalCore.completions.register(
|
|
560
|
-
`resource/${template.uriTemplate}/${param}`,
|
|
561
|
-
(request, invocation) =>
|
|
562
|
-
provideInvocationContext(handle(request.argument.value, request.context), invocation).pipe(
|
|
563
|
-
Effect.map((result) => ({
|
|
564
|
-
values: result.completion.values,
|
|
565
|
-
total: result.completion.total,
|
|
566
|
-
hasMore: result.completion.hasMore,
|
|
567
|
-
metadata: result._meta
|
|
568
|
-
}))
|
|
569
|
-
)
|
|
1246
|
+
addResourceTemplate: Effect.fnUntraced(
|
|
1247
|
+
function*({ annotations, completions, handle, routerPath, services, template }) {
|
|
1248
|
+
const existingIndex = resourceTemplates.findIndex(({ template: current }) =>
|
|
1249
|
+
current.uriTemplate === template.uriTemplate
|
|
570
1250
|
)
|
|
1251
|
+
if (existingIndex === -1) {
|
|
1252
|
+
resourceTemplates.push({ template, annotations })
|
|
1253
|
+
} else {
|
|
1254
|
+
resourceTemplates[existingIndex] = { template, annotations }
|
|
1255
|
+
}
|
|
1256
|
+
const { invoke, visible } = makeInvoke(services, globalMiddleware)
|
|
1257
|
+
const templateMatcher = makeUriMatcher<true>()
|
|
1258
|
+
templateMatcher.add(routerPath, true)
|
|
1259
|
+
yield* internalCore.resources.registerTemplate({
|
|
1260
|
+
descriptor: template,
|
|
1261
|
+
visible: visible("resources/templates/list", template.uriTemplate),
|
|
1262
|
+
isVisible: isEnabled(annotations),
|
|
1263
|
+
match: (uri) => {
|
|
1264
|
+
const match = templateMatcher.find(uri)
|
|
1265
|
+
if (match === undefined) {
|
|
1266
|
+
return undefined
|
|
1267
|
+
}
|
|
1268
|
+
const params: Array<string> = []
|
|
1269
|
+
for (const key of Object.keys(match.params)) {
|
|
1270
|
+
params[Number(key)] = match.params[key]!
|
|
1271
|
+
}
|
|
1272
|
+
return params
|
|
1273
|
+
},
|
|
1274
|
+
read: (uri, params, invocation) =>
|
|
1275
|
+
invoke(handle(uri, Array.from(params)), invocation, {
|
|
1276
|
+
method: "resources/read",
|
|
1277
|
+
target: uri,
|
|
1278
|
+
payload: { uri }
|
|
1279
|
+
})
|
|
1280
|
+
})
|
|
1281
|
+
for (const [param, handle] of Object.entries(completions)) {
|
|
1282
|
+
yield* internalCore.completions.register(
|
|
1283
|
+
`resource/${template.uriTemplate}/${param}`,
|
|
1284
|
+
(request, invocation) =>
|
|
1285
|
+
invoke(handle(request.argument.value, request.context), invocation, {
|
|
1286
|
+
method: "completion/complete",
|
|
1287
|
+
target: template.uriTemplate,
|
|
1288
|
+
payload: request
|
|
1289
|
+
}).pipe(
|
|
1290
|
+
Effect.map((result) => ({
|
|
1291
|
+
values: result.completion.values,
|
|
1292
|
+
total: result.completion.total,
|
|
1293
|
+
hasMore: result.completion.hasMore,
|
|
1294
|
+
metadata: result._meta
|
|
1295
|
+
}))
|
|
1296
|
+
)
|
|
1297
|
+
)
|
|
1298
|
+
}
|
|
1299
|
+
yield* notifications.client["notifications/resources/list_changed"]({})
|
|
571
1300
|
}
|
|
572
|
-
|
|
573
|
-
}),
|
|
1301
|
+
),
|
|
574
1302
|
findResource: Effect.fnUntraced(function*(uri) {
|
|
575
1303
|
const client = yield* McpServerClient
|
|
576
1304
|
return yield* internalCore.resources.read(
|
|
577
1305
|
uri,
|
|
578
1306
|
McpProtocolInternal.invocationFromClient(client)
|
|
579
1307
|
).pipe(
|
|
580
|
-
Effect.
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
1308
|
+
Effect.catchTags({
|
|
1309
|
+
ResourceNotFound: (error) =>
|
|
1310
|
+
Effect.fail(new InvalidParams({ message: `Resource '${error.uri}' not found` })),
|
|
1311
|
+
RequestRejected: rejectedError
|
|
1312
|
+
})
|
|
584
1313
|
)
|
|
585
1314
|
}),
|
|
586
1315
|
get prompts() {
|
|
@@ -593,18 +1322,17 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
593
1322
|
} else {
|
|
594
1323
|
prompts[existingIndex] = options
|
|
595
1324
|
}
|
|
1325
|
+
const { invoke, visible } = makeInvoke(options.services, globalMiddleware)
|
|
596
1326
|
yield* internalCore.prompts.register({
|
|
597
1327
|
descriptor: options.prompt,
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
return enabledWhen === undefined || enabledWhen({
|
|
601
|
-
protocolVersion: profile.protocolVersion,
|
|
602
|
-
capabilities: profile.clientCapabilities,
|
|
603
|
-
clientInfo: profile.clientInfo
|
|
604
|
-
})
|
|
605
|
-
},
|
|
1328
|
+
visible: visible("prompts/list", options.prompt.name),
|
|
1329
|
+
isVisible: isEnabled(options.annotations),
|
|
606
1330
|
get: (params, invocation) =>
|
|
607
|
-
|
|
1331
|
+
invoke(options.handle(params), invocation, {
|
|
1332
|
+
method: "prompts/get",
|
|
1333
|
+
target: options.prompt.name,
|
|
1334
|
+
payload: { name: options.prompt.name, arguments: params }
|
|
1335
|
+
}).pipe(
|
|
608
1336
|
Effect.map((result) =>
|
|
609
1337
|
Predicate.isTagged(result, "InputRequired")
|
|
610
1338
|
? McpCore.OperationOutcome.InputRequired(result)
|
|
@@ -616,7 +1344,11 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
616
1344
|
yield* internalCore.completions.register(
|
|
617
1345
|
`prompt/${options.prompt.name}/${param}`,
|
|
618
1346
|
(request, invocation) =>
|
|
619
|
-
|
|
1347
|
+
invoke(handle(request.argument.value, request.context), invocation, {
|
|
1348
|
+
method: "completion/complete",
|
|
1349
|
+
target: options.prompt.name,
|
|
1350
|
+
payload: request
|
|
1351
|
+
}).pipe(
|
|
620
1352
|
Effect.map((result) => ({
|
|
621
1353
|
values: result.completion.values,
|
|
622
1354
|
total: result.completion.total,
|
|
@@ -635,7 +1367,10 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
635
1367
|
params ?? {},
|
|
636
1368
|
McpProtocolInternal.invocationFromClient(client)
|
|
637
1369
|
).pipe(
|
|
638
|
-
Effect.
|
|
1370
|
+
Effect.catchTags({
|
|
1371
|
+
PromptNotFound: () => new InvalidParams({ message: `Prompt '${name}' not found` }),
|
|
1372
|
+
RequestRejected: rejectedError
|
|
1373
|
+
})
|
|
639
1374
|
)
|
|
640
1375
|
return outcome._tag === "Complete"
|
|
641
1376
|
? outcome.value
|
|
@@ -650,7 +1385,9 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
650
1385
|
: { type: "prompt", name: ref.name },
|
|
651
1386
|
argument: complete.argument,
|
|
652
1387
|
context: complete.context
|
|
653
|
-
}, McpProtocolInternal.invocationFromClient(client))
|
|
1388
|
+
}, McpProtocolInternal.invocationFromClient(client)).pipe(
|
|
1389
|
+
Effect.catchTag("RequestRejected", rejectedError)
|
|
1390
|
+
)
|
|
654
1391
|
return {
|
|
655
1392
|
_meta: result.metadata,
|
|
656
1393
|
completion: {
|
|
@@ -661,17 +1398,28 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
661
1398
|
}
|
|
662
1399
|
})
|
|
663
1400
|
})
|
|
664
|
-
internalState.set(service, {
|
|
1401
|
+
internalState.set(service, {
|
|
1402
|
+
core: internalCore,
|
|
1403
|
+
notifications: notificationsQueue,
|
|
1404
|
+
notificationDelivery,
|
|
1405
|
+
globalMiddleware
|
|
1406
|
+
})
|
|
665
1407
|
return service
|
|
666
1408
|
})
|
|
667
1409
|
|
|
668
1410
|
/**
|
|
669
|
-
* Layer that provides the MCP server
|
|
1411
|
+
* Layer that provides the MCP server service.
|
|
1412
|
+
*
|
|
1413
|
+
* **Details**
|
|
1414
|
+
*
|
|
1415
|
+
* It doesn't run a transport or discharge request-level dependencies. Use it
|
|
1416
|
+
* to call registrations in process, providing their request-level
|
|
1417
|
+
* dependencies with MCP middleware.
|
|
670
1418
|
*
|
|
671
1419
|
* @stability unstable
|
|
672
1420
|
* @since 4.0.0
|
|
673
1421
|
*/
|
|
674
|
-
static readonly layer: Layer.Layer<McpServer
|
|
1422
|
+
static readonly layer: Layer.Layer<McpServer> = Layer.effect(McpServer)(McpServer.make)
|
|
675
1423
|
}
|
|
676
1424
|
|
|
677
1425
|
const MCP_SESSION_ID_HEADER = "mcp-session-id"
|
|
@@ -693,6 +1441,82 @@ class McpClientKey extends Data.Class<{
|
|
|
693
1441
|
readonly profile: McpCore.NegotiatedProtocolProfile<string>
|
|
694
1442
|
}> {}
|
|
695
1443
|
|
|
1444
|
+
/**
|
|
1445
|
+
* A client cache hint for one kind of `v2026_07_28` result.
|
|
1446
|
+
*
|
|
1447
|
+
* **Details**
|
|
1448
|
+
*
|
|
1449
|
+
* `ttl` is how long a client may treat the result as fresh. It is sent as a
|
|
1450
|
+
* whole number of milliseconds, rounded down, so it must be finite,
|
|
1451
|
+
* nonnegative and at most `Number.MAX_SAFE_INTEGER` milliseconds. Only
|
|
1452
|
+
* `"private"` scope is supported, so shared caches must not store the result.
|
|
1453
|
+
*
|
|
1454
|
+
* @stability unstable
|
|
1455
|
+
* @category models
|
|
1456
|
+
* @since 4.1.0
|
|
1457
|
+
*/
|
|
1458
|
+
export interface CacheHint {
|
|
1459
|
+
readonly ttl: Duration.Input
|
|
1460
|
+
readonly cacheScope: "private"
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
/**
|
|
1464
|
+
* Client cache hints for `v2026_07_28` discovery and list results.
|
|
1465
|
+
*
|
|
1466
|
+
* **Details**
|
|
1467
|
+
*
|
|
1468
|
+
* Each entry sets the freshness and `cacheScope` of one result kind:
|
|
1469
|
+
* `server/discover`, `tools/list`, `resources/list`,
|
|
1470
|
+
* `resources/templates/list` and `prompts/list`. Omitted entries, and
|
|
1471
|
+
* `resources/read` results, keep `{ ttlMs: 0, cacheScope: "private" }`.
|
|
1472
|
+
* Earlier protocol revisions have no cache hints and ignore this option.
|
|
1473
|
+
*
|
|
1474
|
+
* These are hints for clients. The server does not cache results, and
|
|
1475
|
+
* visibility and invocation checks still run on every request.
|
|
1476
|
+
*
|
|
1477
|
+
* **Gotchas**
|
|
1478
|
+
*
|
|
1479
|
+
* A positive `ttl` lets a client keep showing a list after registrations or
|
|
1480
|
+
* per-request visibility change, until the hint expires. Private scope does
|
|
1481
|
+
* not key a client's cache by user, so a client shared between users can
|
|
1482
|
+
* reuse one user's list for another.
|
|
1483
|
+
*
|
|
1484
|
+
* @stability unstable
|
|
1485
|
+
* @category models
|
|
1486
|
+
* @since 4.1.0
|
|
1487
|
+
*/
|
|
1488
|
+
export interface CacheHints {
|
|
1489
|
+
readonly discover?: CacheHint | undefined
|
|
1490
|
+
readonly tools?: CacheHint | undefined
|
|
1491
|
+
readonly resources?: CacheHint | undefined
|
|
1492
|
+
readonly resourceTemplates?: CacheHint | undefined
|
|
1493
|
+
readonly prompts?: CacheHint | undefined
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
// Converts each hint to the wire's nonnegative integer `ttlMs` once, when the server is built.
|
|
1497
|
+
const normalizeCacheHints = (
|
|
1498
|
+
cacheHints: CacheHints | undefined
|
|
1499
|
+
): Effect.Effect<McpProtocolInternal.CacheHints | undefined, Cause.IllegalArgumentError> => {
|
|
1500
|
+
if (cacheHints === undefined) return Effect.succeed(undefined)
|
|
1501
|
+
const normalized: Record<string, { readonly ttlMs: number; readonly cacheScope: "private" }> = {}
|
|
1502
|
+
for (const [name, hint] of Object.entries(cacheHints)) {
|
|
1503
|
+
if (hint === undefined) continue
|
|
1504
|
+
const ttlMs = Option.match(Duration.fromInput(hint.ttl), {
|
|
1505
|
+
onNone: () => NaN,
|
|
1506
|
+
onSome: (ttl) => Math.floor(Duration.toMillis(ttl))
|
|
1507
|
+
})
|
|
1508
|
+
if (!Number.isSafeInteger(ttlMs) || ttlMs < 0 || hint.cacheScope !== "private") {
|
|
1509
|
+
return Effect.fail(
|
|
1510
|
+
new Cause.IllegalArgumentError(
|
|
1511
|
+
`McpServer cacheHints.${name} must have a finite, nonnegative ttl and a "private" cacheScope`
|
|
1512
|
+
)
|
|
1513
|
+
)
|
|
1514
|
+
}
|
|
1515
|
+
normalized[name] = { ttlMs, cacheScope: "private" }
|
|
1516
|
+
}
|
|
1517
|
+
return Effect.succeed(normalized)
|
|
1518
|
+
}
|
|
1519
|
+
|
|
696
1520
|
/**
|
|
697
1521
|
* Runs an MCP server over the current `RpcServer.Protocol`.
|
|
698
1522
|
*
|
|
@@ -715,6 +1539,7 @@ export const run: (options: {
|
|
|
715
1539
|
readonly icons?: ReadonlyArray<McpSchema.Icon> | undefined
|
|
716
1540
|
readonly protocols: Arr.NonEmptyReadonlyArray<McpProtocol.ProtocolAdapter>
|
|
717
1541
|
readonly extensions?: ServerExtensions | undefined
|
|
1542
|
+
readonly cacheHints?: CacheHints | undefined
|
|
718
1543
|
}) => Effect.Effect<
|
|
719
1544
|
never,
|
|
720
1545
|
Cause.IllegalArgumentError,
|
|
@@ -728,12 +1553,14 @@ export const run: (options: {
|
|
|
728
1553
|
readonly icons?: ReadonlyArray<McpSchema.Icon> | undefined
|
|
729
1554
|
readonly protocols: Arr.NonEmptyReadonlyArray<McpProtocol.ProtocolAdapter>
|
|
730
1555
|
readonly extensions?: ServerExtensions | undefined
|
|
1556
|
+
readonly cacheHints?: CacheHints | undefined
|
|
731
1557
|
}) {
|
|
1558
|
+
const cacheHints = yield* normalizeCacheHints(options.cacheHints)
|
|
732
1559
|
const runtimeOption = yield* Effect.serviceOption(McpRuntime.ServerRuntime)
|
|
733
1560
|
const runtime = Option.isSome(runtimeOption)
|
|
734
1561
|
? runtimeOption.value
|
|
735
1562
|
: yield* McpRuntime.make(options.protocols)
|
|
736
|
-
return yield* runWithRuntime(options, runtime, "custom")
|
|
1563
|
+
return yield* runWithRuntime({ ...options, cacheHints }, runtime, "custom")
|
|
737
1564
|
})
|
|
738
1565
|
|
|
739
1566
|
const runWithRuntime = Effect.fnUntraced(function*(
|
|
@@ -745,6 +1572,7 @@ const runWithRuntime = Effect.fnUntraced(function*(
|
|
|
745
1572
|
readonly websiteUrl?: string | undefined
|
|
746
1573
|
readonly icons?: ReadonlyArray<McpSchema.Icon> | undefined
|
|
747
1574
|
readonly extensions?: ServerExtensions | undefined
|
|
1575
|
+
readonly cacheHints?: McpProtocolInternal.CacheHints | undefined
|
|
748
1576
|
readonly allowSubscriptions?: boolean | undefined
|
|
749
1577
|
},
|
|
750
1578
|
runtime: McpRuntime.ServerRuntimeShape,
|
|
@@ -1365,7 +2193,8 @@ const runWithRuntime = Effect.fnUntraced(function*(
|
|
|
1365
2193
|
(requestContext?.clientId === clientId ? requestHeaders : undefined) ??
|
|
1366
2194
|
clientStates.get(clientId)?.sessionHeaders ?? Headers.empty,
|
|
1367
2195
|
notification,
|
|
1368
|
-
defaultLogLevel
|
|
2196
|
+
defaultLogLevel,
|
|
2197
|
+
listChangeRevision
|
|
1369
2198
|
)
|
|
1370
2199
|
) {
|
|
1371
2200
|
return
|
|
@@ -1412,6 +2241,37 @@ const runWithRuntime = Effect.fnUntraced(function*(
|
|
|
1412
2241
|
)
|
|
1413
2242
|
}, Effect.scoped)
|
|
1414
2243
|
|
|
2244
|
+
type StatefulProtocolAdapter = McpProtocol.ProtocolAdapter<
|
|
2245
|
+
McpProtocol.ProtocolVersion,
|
|
2246
|
+
McpProtocol.StatefulRuntimeDescriptor
|
|
2247
|
+
>
|
|
2248
|
+
|
|
2249
|
+
/**
|
|
2250
|
+
* Request-level services supplied by a transport.
|
|
2251
|
+
*
|
|
2252
|
+
* **Details**
|
|
2253
|
+
*
|
|
2254
|
+
* Every transport supplies `Scope`. `McpServerClient` is supplied only when
|
|
2255
|
+
* every configured protocol is known to be sessionful: a `v2026_07_28` request
|
|
2256
|
+
* has no client session, and a protocol array widened to `ProtocolAdapter`
|
|
2257
|
+
* might contain one. HTTP transports also supply `HttpServerRequest`.
|
|
2258
|
+
*
|
|
2259
|
+
* @stability unstable
|
|
2260
|
+
* @category utility types
|
|
2261
|
+
* @since 4.0.0
|
|
2262
|
+
*/
|
|
2263
|
+
export type ProvidedServices<
|
|
2264
|
+
Protocols extends ReadonlyArray<McpProtocol.ProtocolAdapter>,
|
|
2265
|
+
Http extends boolean = false
|
|
2266
|
+
> =
|
|
2267
|
+
| Scope.Scope
|
|
2268
|
+
| (Protocols[number] extends StatefulProtocolAdapter ? McpServerClient : never)
|
|
2269
|
+
| (Http extends true ? HttpServerRequest.HttpServerRequest : never)
|
|
2270
|
+
|
|
2271
|
+
type RemainingRequestServices<RR, Provided> = Exclude<McpRequest.Only<"Requires", RR>, Provided>
|
|
2272
|
+
|
|
2273
|
+
type RegistrationServices<RR> = Exclude<McpRequest.Without<RR>, McpServer>
|
|
2274
|
+
|
|
1415
2275
|
/**
|
|
1416
2276
|
* Creates a layer that starts an MCP server over an existing
|
|
1417
2277
|
* `RpcServer.Protocol` and provides `McpServer`. Request handlers receive
|
|
@@ -1430,6 +2290,11 @@ const runWithRuntime = Effect.fnUntraced(function*(
|
|
|
1430
2290
|
* `McpServer.layer`, so registration layers can use the `McpServer` service
|
|
1431
2291
|
* while the server is running.
|
|
1432
2292
|
*
|
|
2293
|
+
* Pass registration layers as `registrations` to build them before the server
|
|
2294
|
+
* reads its first request. The transport discharges the request-level
|
|
2295
|
+
* dependencies it supplies (see `ProvidedServices`); any other `McpRequest`
|
|
2296
|
+
* marker stays in the layer's requirements and must be met by MCP middleware.
|
|
2297
|
+
*
|
|
1433
2298
|
* **Gotchas**
|
|
1434
2299
|
*
|
|
1435
2300
|
* Unlike `layerStdio` and `layerHttp`, this layer does not install a concrete
|
|
@@ -1443,19 +2308,32 @@ const runWithRuntime = Effect.fnUntraced(function*(
|
|
|
1443
2308
|
* @category layers
|
|
1444
2309
|
* @since 4.0.0
|
|
1445
2310
|
*/
|
|
1446
|
-
export const layer =
|
|
2311
|
+
export const layer = <
|
|
2312
|
+
const Protocols extends Arr.NonEmptyReadonlyArray<McpProtocol.ProtocolAdapter>,
|
|
2313
|
+
RA = never,
|
|
2314
|
+
RE = never,
|
|
2315
|
+
RR = never
|
|
2316
|
+
>(options: {
|
|
1447
2317
|
readonly name: string
|
|
1448
2318
|
readonly version: string
|
|
1449
2319
|
readonly instructions?: string | undefined
|
|
1450
2320
|
readonly description?: string | undefined
|
|
1451
2321
|
readonly websiteUrl?: string | undefined
|
|
1452
2322
|
readonly icons?: ReadonlyArray<McpSchema.Icon> | undefined
|
|
1453
|
-
readonly protocols:
|
|
2323
|
+
readonly protocols: Protocols
|
|
1454
2324
|
readonly extensions?: ServerExtensions | undefined
|
|
1455
|
-
|
|
2325
|
+
readonly cacheHints?: CacheHints | undefined
|
|
2326
|
+
readonly registrations?: Layer.Layer<RA, RE, RR> | undefined
|
|
2327
|
+
}): Layer.Layer<
|
|
2328
|
+
McpServer | McpRequest.From<"Requires", ProvidedServices<Protocols>>,
|
|
2329
|
+
Cause.IllegalArgumentError | RE,
|
|
2330
|
+
| RpcServer.Protocol
|
|
2331
|
+
| RegistrationServices<RR>
|
|
2332
|
+
| McpRequest.From<"Requires", RemainingRequestServices<RR, ProvidedServices<Protocols>>>
|
|
2333
|
+
> =>
|
|
1456
2334
|
layerWithRuntime(options, "custom").pipe(
|
|
1457
2335
|
Layer.provide(McpRuntime.layer(options.protocols))
|
|
1458
|
-
)
|
|
2336
|
+
) as any
|
|
1459
2337
|
|
|
1460
2338
|
const layerWithRuntime = (options: {
|
|
1461
2339
|
readonly name: string
|
|
@@ -1465,18 +2343,18 @@ const layerWithRuntime = (options: {
|
|
|
1465
2343
|
readonly websiteUrl?: string | undefined
|
|
1466
2344
|
readonly icons?: ReadonlyArray<McpSchema.Icon> | undefined
|
|
1467
2345
|
readonly extensions?: ServerExtensions | undefined
|
|
2346
|
+
readonly cacheHints?: CacheHints | undefined
|
|
1468
2347
|
readonly allowSubscriptions?: boolean | undefined
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
never,
|
|
1472
|
-
RpcServer.Protocol | McpRuntime.ServerRuntime
|
|
1473
|
-
> =>
|
|
2348
|
+
readonly registrations?: Layer.Layer<any, any, any> | undefined
|
|
2349
|
+
}, transport: "custom" | "http" | "stdio") =>
|
|
1474
2350
|
Layer.effectDiscard(
|
|
1475
2351
|
Effect.gen(function*() {
|
|
2352
|
+
const cacheHints = yield* normalizeCacheHints(options.cacheHints)
|
|
1476
2353
|
const runtime = yield* McpRuntime.ServerRuntime
|
|
1477
|
-
yield* Effect.forkScoped(runWithRuntime(options, runtime, transport))
|
|
2354
|
+
yield* Effect.forkScoped(runWithRuntime({ ...options, cacheHints }, runtime, transport))
|
|
1478
2355
|
})
|
|
1479
2356
|
).pipe(
|
|
2357
|
+
Layer.provide(options.registrations ?? Layer.empty),
|
|
1480
2358
|
Layer.provideMerge(McpServer.layer)
|
|
1481
2359
|
)
|
|
1482
2360
|
|
|
@@ -1495,6 +2373,11 @@ const layerWithRuntime = (options: {
|
|
|
1495
2373
|
* `McpRequestContext` and legacy `McpServerClient` services to handlers, and
|
|
1496
2374
|
* requires `Stdio`.
|
|
1497
2375
|
*
|
|
2376
|
+
* Pass registration layers as `registrations` to build them before the server
|
|
2377
|
+
* reads its first request. The transport discharges the request-level
|
|
2378
|
+
* dependencies it supplies (see `ProvidedServices`); any other `McpRequest`
|
|
2379
|
+
* marker stays in the layer's requirements and must be met by MCP middleware.
|
|
2380
|
+
*
|
|
1498
2381
|
* @see {@link layer} for running over an existing `RpcServer.Protocol`
|
|
1499
2382
|
* @see {@link layerHttp} for the single-endpoint HTTP transport
|
|
1500
2383
|
*
|
|
@@ -1502,16 +2385,29 @@ const layerWithRuntime = (options: {
|
|
|
1502
2385
|
* @category layers
|
|
1503
2386
|
* @since 4.0.0
|
|
1504
2387
|
*/
|
|
1505
|
-
export const layerStdio =
|
|
2388
|
+
export const layerStdio = <
|
|
2389
|
+
const Protocols extends Arr.NonEmptyReadonlyArray<McpProtocol.ProtocolAdapter>,
|
|
2390
|
+
RA = never,
|
|
2391
|
+
RE = never,
|
|
2392
|
+
RR = never
|
|
2393
|
+
>(options: {
|
|
1506
2394
|
readonly name: string
|
|
1507
2395
|
readonly version: string
|
|
1508
2396
|
readonly instructions?: string | undefined
|
|
1509
2397
|
readonly description?: string | undefined
|
|
1510
2398
|
readonly websiteUrl?: string | undefined
|
|
1511
2399
|
readonly icons?: ReadonlyArray<McpSchema.Icon> | undefined
|
|
1512
|
-
readonly protocols:
|
|
2400
|
+
readonly protocols: Protocols
|
|
1513
2401
|
readonly extensions?: ServerExtensions | undefined
|
|
1514
|
-
|
|
2402
|
+
readonly cacheHints?: CacheHints | undefined
|
|
2403
|
+
readonly registrations?: Layer.Layer<RA, RE, RR> | undefined
|
|
2404
|
+
}): Layer.Layer<
|
|
2405
|
+
McpServer | McpRequest.From<"Requires", ProvidedServices<Protocols>>,
|
|
2406
|
+
Cause.IllegalArgumentError | RE,
|
|
2407
|
+
| Stdio
|
|
2408
|
+
| RegistrationServices<RR>
|
|
2409
|
+
| McpRequest.From<"Requires", RemainingRequestServices<RR, ProvidedServices<Protocols>>>
|
|
2410
|
+
> =>
|
|
1515
2411
|
layerWithRuntime(options, "stdio").pipe(
|
|
1516
2412
|
Layer.provide(McpRuntime.layer(options.protocols)),
|
|
1517
2413
|
Layer.provide(RpcServer.layerProtocolStdio),
|
|
@@ -1519,7 +2415,7 @@ export const layerStdio = (options: {
|
|
|
1519
2415
|
RpcSerialization.RpcSerialization,
|
|
1520
2416
|
mcpStdioSerialization(options.protocols)
|
|
1521
2417
|
))
|
|
1522
|
-
)
|
|
2418
|
+
) as any
|
|
1523
2419
|
|
|
1524
2420
|
const mcpStdioSerialization = (
|
|
1525
2421
|
protocols: Arr.NonEmptyReadonlyArray<McpProtocol.ProtocolAdapter>
|
|
@@ -1621,6 +2517,13 @@ const mcpStdioSerialization = (
|
|
|
1621
2517
|
* serverless hosts where execution must belong to each request, use
|
|
1622
2518
|
* `layerHttpRequestScoped` instead.
|
|
1623
2519
|
*
|
|
2520
|
+
* Pass registration layers as `registrations` to build them before the
|
|
2521
|
+
* endpoint serves its first request. The transport discharges the
|
|
2522
|
+
* request-level dependencies it supplies (see `ProvidedServices`) and converts
|
|
2523
|
+
* the remaining `McpRequest` markers into `HttpRouter.Request` markers, so
|
|
2524
|
+
* `HttpRouter.middleware` can provide them, for example to authenticate the
|
|
2525
|
+
* request and resolve the current user.
|
|
2526
|
+
*
|
|
1624
2527
|
* `layerHttp` always implements the single-endpoint Streamable HTTP topology.
|
|
1625
2528
|
* Using `v2024_11_05` here is a custom compatibility transport for that
|
|
1626
2529
|
* revision's schema. It does not implement the historical two-endpoint
|
|
@@ -1638,7 +2541,12 @@ const mcpStdioSerialization = (
|
|
|
1638
2541
|
* @category layers
|
|
1639
2542
|
* @since 4.0.0
|
|
1640
2543
|
*/
|
|
1641
|
-
export const layerHttp =
|
|
2544
|
+
export const layerHttp = <
|
|
2545
|
+
const Protocols extends Arr.NonEmptyReadonlyArray<McpProtocol.ProtocolAdapter>,
|
|
2546
|
+
RA = never,
|
|
2547
|
+
RE = never,
|
|
2548
|
+
RR = never
|
|
2549
|
+
>(options: {
|
|
1642
2550
|
readonly name: string
|
|
1643
2551
|
readonly version: string
|
|
1644
2552
|
readonly instructions?: string | undefined
|
|
@@ -1646,18 +2554,26 @@ export const layerHttp = (options: {
|
|
|
1646
2554
|
readonly websiteUrl?: string | undefined
|
|
1647
2555
|
readonly icons?: ReadonlyArray<McpSchema.Icon> | undefined
|
|
1648
2556
|
readonly path: HttpRouter.PathInput
|
|
1649
|
-
readonly protocols:
|
|
2557
|
+
readonly protocols: Protocols
|
|
1650
2558
|
readonly extensions?: ServerExtensions | undefined
|
|
2559
|
+
readonly cacheHints?: CacheHints | undefined
|
|
1651
2560
|
readonly allowedOrigins?: ReadonlyArray<string> | undefined
|
|
1652
2561
|
readonly allowSessionTermination?: boolean | undefined
|
|
1653
2562
|
readonly allowSubscriptions?: boolean | undefined
|
|
1654
|
-
|
|
2563
|
+
readonly registrations?: Layer.Layer<RA, RE, RR> | undefined
|
|
2564
|
+
}): Layer.Layer<
|
|
2565
|
+
McpServer | McpRequest.From<"Requires", ProvidedServices<Protocols, true>>,
|
|
2566
|
+
Cause.IllegalArgumentError | RE,
|
|
2567
|
+
| HttpRouter.HttpRouter
|
|
2568
|
+
| RegistrationServices<RR>
|
|
2569
|
+
| HttpRouter.Request.From<"Requires", RemainingRequestServices<RR, ProvidedServices<Protocols, true>>>
|
|
2570
|
+
> => {
|
|
1655
2571
|
const runtime = McpRuntime.layer(options.protocols)
|
|
1656
2572
|
return layerWithRuntime(options, "http").pipe(
|
|
1657
2573
|
Layer.provide(layerMcpProtocolHttp(options)),
|
|
1658
2574
|
Layer.provide(runtime),
|
|
1659
2575
|
Layer.provide(RpcSerialization.layerJsonRpc())
|
|
1660
|
-
)
|
|
2576
|
+
) as any
|
|
1661
2577
|
}
|
|
1662
2578
|
|
|
1663
2579
|
/**
|
|
@@ -1688,13 +2604,24 @@ export const layerHttp = (options: {
|
|
|
1688
2604
|
* Configuring a sessionful protocol through an untyped caller fails layer
|
|
1689
2605
|
* construction with `IllegalArgumentError`.
|
|
1690
2606
|
*
|
|
2607
|
+
* Registrations passed as `registrations` are built once, before the routes
|
|
2608
|
+
* are added, and shared by every POST. Remaining `McpRequest` markers become
|
|
2609
|
+
* `HttpRouter.Request` markers, as with `layerHttp`.
|
|
2610
|
+
*
|
|
1691
2611
|
* @see {@link layerHttp} for the long-lived HTTP transport and HTTP admission policy
|
|
1692
2612
|
*
|
|
1693
2613
|
* @stability unstable
|
|
1694
2614
|
* @category layers
|
|
1695
2615
|
* @since 4.0.0
|
|
1696
2616
|
*/
|
|
1697
|
-
export const layerHttpRequestScoped =
|
|
2617
|
+
export const layerHttpRequestScoped = <
|
|
2618
|
+
const Protocols extends Arr.NonEmptyReadonlyArray<
|
|
2619
|
+
McpProtocol.ProtocolAdapter<McpProtocol.ProtocolVersion, McpProtocol.StatelessRuntimeDescriptor>
|
|
2620
|
+
>,
|
|
2621
|
+
RA = never,
|
|
2622
|
+
RE = never,
|
|
2623
|
+
RR = never
|
|
2624
|
+
>(options: {
|
|
1698
2625
|
readonly name: string
|
|
1699
2626
|
readonly version: string
|
|
1700
2627
|
readonly instructions?: string | undefined
|
|
@@ -1702,12 +2629,18 @@ export const layerHttpRequestScoped = (options: {
|
|
|
1702
2629
|
readonly websiteUrl?: string | undefined
|
|
1703
2630
|
readonly icons?: ReadonlyArray<McpSchema.Icon> | undefined
|
|
1704
2631
|
readonly path: HttpRouter.PathInput
|
|
1705
|
-
readonly protocols:
|
|
1706
|
-
McpProtocol.ProtocolAdapter<McpProtocol.ProtocolVersion, McpProtocol.StatelessRuntimeDescriptor>
|
|
1707
|
-
>
|
|
2632
|
+
readonly protocols: Protocols
|
|
1708
2633
|
readonly extensions?: ServerExtensions | undefined
|
|
2634
|
+
readonly cacheHints?: CacheHints | undefined
|
|
1709
2635
|
readonly allowedOrigins?: ReadonlyArray<string> | undefined
|
|
1710
|
-
|
|
2636
|
+
readonly registrations?: Layer.Layer<RA, RE, RR> | undefined
|
|
2637
|
+
}): Layer.Layer<
|
|
2638
|
+
McpServer | McpRequest.From<"Requires", ProvidedServices<Protocols, true>>,
|
|
2639
|
+
Cause.IllegalArgumentError | RE,
|
|
2640
|
+
| HttpRouter.HttpRouter
|
|
2641
|
+
| RegistrationServices<RR>
|
|
2642
|
+
| HttpRouter.Request.From<"Requires", RemainingRequestServices<RR, ProvidedServices<Protocols, true>>>
|
|
2643
|
+
> =>
|
|
1711
2644
|
Layer.effectDiscard(Effect.gen(function*() {
|
|
1712
2645
|
const runtime = yield* McpRuntime.ServerRuntime
|
|
1713
2646
|
const stateful = runtime.protocols.find((protocol) => protocol.runtime._tag === "Stateful")
|
|
@@ -1716,6 +2649,7 @@ export const layerHttpRequestScoped = (options: {
|
|
|
1716
2649
|
`McpServer.layerHttpRequestScoped does not support the sessionful MCP protocol ${stateful.protocolVersion}`
|
|
1717
2650
|
)
|
|
1718
2651
|
}
|
|
2652
|
+
const serverOptions = { ...options, cacheHints: yield* normalizeCacheHints(options.cacheHints) }
|
|
1719
2653
|
const server = yield* McpServer
|
|
1720
2654
|
const { core, notificationDelivery } = internalState.get(server)!
|
|
1721
2655
|
// Subscriptions are disabled, so handlers hold no per-request state and
|
|
@@ -1726,7 +2660,7 @@ export const layerHttpRequestScoped = (options: {
|
|
|
1726
2660
|
subscribeServerNotifications: Effect.die("Subscriptions are disabled for request-scoped MCP servers"),
|
|
1727
2661
|
getListChangeRevision: () => notificationDelivery.listChangeRevision,
|
|
1728
2662
|
defaultLogLevel: yield* CurrentLogLevel,
|
|
1729
|
-
serverInfo:
|
|
2663
|
+
serverInfo: serverOptions
|
|
1730
2664
|
}) as Effect.Effect<Context.Context<never>>)
|
|
1731
2665
|
yield* Effect.acquireRelease(
|
|
1732
2666
|
Effect.sync(() => {
|
|
@@ -1746,7 +2680,7 @@ export const layerHttpRequestScoped = (options: {
|
|
|
1746
2680
|
Effect.provideService(RpcSerialization.RpcSerialization, mcpHttpSerialization)
|
|
1747
2681
|
)
|
|
1748
2682
|
const notifications = yield* Queue.make<QueuedServerNotification>()
|
|
1749
|
-
yield* runWithRuntime(
|
|
2683
|
+
yield* runWithRuntime(serverOptions, runtime, "http", { notifications, handlers }).pipe(
|
|
1750
2684
|
Effect.provideService(RpcServer.Protocol, protocol),
|
|
1751
2685
|
Effect.provideService(McpServer, server),
|
|
1752
2686
|
Effect.forkScoped({ startImmediately: true })
|
|
@@ -1755,9 +2689,10 @@ export const layerHttpRequestScoped = (options: {
|
|
|
1755
2689
|
})
|
|
1756
2690
|
)
|
|
1757
2691
|
})).pipe(
|
|
2692
|
+
Layer.provide(options.registrations ?? Layer.empty),
|
|
1758
2693
|
Layer.provideMerge(McpServer.layer),
|
|
1759
2694
|
Layer.provide(McpRuntime.layer(options.protocols))
|
|
1760
|
-
)
|
|
2695
|
+
) as any
|
|
1761
2696
|
|
|
1762
2697
|
const layerMcpProtocolHttp = (options: {
|
|
1763
2698
|
readonly path: HttpRouter.PathInput
|
|
@@ -2026,20 +2961,50 @@ const toolErrorResult = (message: string): CallToolResult =>
|
|
|
2026
2961
|
const toolResultContent = (encoded: unknown): CallToolResult["content"] =>
|
|
2027
2962
|
encoded === undefined ? [] : [{ type: "text", text: JSON.stringify(encoded) }]
|
|
2028
2963
|
|
|
2964
|
+
/**
|
|
2965
|
+
* The requirements of a toolkit registration.
|
|
2966
|
+
*
|
|
2967
|
+
* **Details**
|
|
2968
|
+
*
|
|
2969
|
+
* Services a tool declares with `dependencies` or `Tool.addDependency` are
|
|
2970
|
+
* request-level: they are carried as `McpRequest<"Requires", S>` markers and
|
|
2971
|
+
* must be provided by MCP middleware or the transport. Parameter decoding and
|
|
2972
|
+
* result encoding services are required when the toolkit is registered,
|
|
2973
|
+
* unless a tool also declares them.
|
|
2974
|
+
*
|
|
2975
|
+
* @stability unstable
|
|
2976
|
+
* @category utility types
|
|
2977
|
+
* @since 4.0.0
|
|
2978
|
+
*/
|
|
2979
|
+
export type ToolkitRequirements<Tools extends Record<string, Tool.Any>> = 0 extends
|
|
2980
|
+
(1 & Tool.Requirements<Tools[keyof Tools]>) ? any :
|
|
2981
|
+
| Tool.HandlersFor<Tools>
|
|
2982
|
+
| RegistrationRequirements<Tool.SchemaServices<Tools[keyof Tools]>, Tool.Requirements<Tools[keyof Tools]>>
|
|
2983
|
+
|
|
2984
|
+
type RegistrationRequirements<SchemaServices, RequestServices> =
|
|
2985
|
+
| McpServer
|
|
2986
|
+
| Exclude<SchemaServices, RequestServices | McpRequestContext>
|
|
2987
|
+
| McpRequest.From<"Requires", Exclude<RequestServices, McpRequestContext>>
|
|
2988
|
+
|
|
2029
2989
|
/**
|
|
2030
2990
|
* Registers a `Toolkit` with the `McpServer`.
|
|
2031
2991
|
*
|
|
2992
|
+
* **Details**
|
|
2993
|
+
*
|
|
2994
|
+
* The MCP middleware in the current context, provided with
|
|
2995
|
+
* `Effect.provide(middleware.layer)`, runs around every call to the toolkit.
|
|
2996
|
+
*
|
|
2997
|
+
* @see {@link ToolkitRequirements} for which services are request-level
|
|
2998
|
+
*
|
|
2032
2999
|
* @stability unstable
|
|
2033
3000
|
* @category handlers
|
|
2034
3001
|
* @since 4.0.0
|
|
2035
3002
|
*/
|
|
2036
3003
|
export const registerToolkit: <Tools extends Record<string, Tool.Any>>(
|
|
2037
3004
|
toolkit: Toolkit.Toolkit<Tools>
|
|
2038
|
-
) => Effect.Effect<
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
McpServer | Tool.HandlersFor<Tools> | Exclude<Tool.HandlerServices<Tools[keyof Tools]>, McpRequestContext>
|
|
2042
|
-
> = Effect.fnUntraced(function*<Tools extends Record<string, Tool.Any>>(
|
|
3005
|
+
) => Effect.Effect<void, never, ToolkitRequirements<Tools>> = Effect.fnUntraced(function*<
|
|
3006
|
+
Tools extends Record<string, Tool.Any>
|
|
3007
|
+
>(
|
|
2043
3008
|
toolkit: Toolkit.Toolkit<Tools>
|
|
2044
3009
|
) {
|
|
2045
3010
|
const registry = yield* McpServer
|
|
@@ -2058,19 +3023,14 @@ export const registerToolkit: <Tools extends Record<string, Tool.Any>>(
|
|
|
2058
3023
|
}
|
|
2059
3024
|
return Context.makeUnsafe(services)
|
|
2060
3025
|
}))
|
|
2061
|
-
const services =
|
|
2062
|
-
yield* Effect.context<
|
|
2063
|
-
Exclude<Tool.HandlerServices<Tools[keyof Tools]>, McpRequestContext>
|
|
2064
|
-
>()
|
|
2065
|
-
)
|
|
2066
|
-
const reportCause = (cause: Cause.Cause<unknown>) => Effect.provideContext(ErrorReporter.report(cause), services)
|
|
3026
|
+
const services = yield* Effect.context<never>()
|
|
2067
3027
|
// Interruption propagates; anything else is logged, reported and scrubbed.
|
|
2068
3028
|
const internalToolError = (cause: Cause.Cause<unknown>) => {
|
|
2069
3029
|
const failure = Cause.findFail(cause)
|
|
2070
3030
|
return Result.isFailure(failure) && !Cause.hasDies(cause)
|
|
2071
3031
|
? Effect.failCause(failure.failure)
|
|
2072
3032
|
: Effect.logError(cause).pipe(
|
|
2073
|
-
Effect.andThen(
|
|
3033
|
+
Effect.andThen(ErrorReporter.report(cause)),
|
|
2074
3034
|
Effect.as(toolErrorResult(INTERNAL_TOOL_ERROR_MESSAGE))
|
|
2075
3035
|
)
|
|
2076
3036
|
}
|
|
@@ -2168,10 +3128,10 @@ export const registerToolkit: <Tools extends Record<string, Tool.Any>>(
|
|
|
2168
3128
|
})
|
|
2169
3129
|
)
|
|
2170
3130
|
),
|
|
2171
|
-
Effect.catchCause(handleCause)
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
3131
|
+
Effect.catchCause(handleCause)
|
|
3132
|
+
) as Effect.Effect<CallToolResult, InvalidParams, never>
|
|
3133
|
+
},
|
|
3134
|
+
services
|
|
2175
3135
|
})
|
|
2176
3136
|
}
|
|
2177
3137
|
for (const registration of registrations) {
|
|
@@ -2201,7 +3161,14 @@ const toolJsonSchema = (schema: Schema.Constraint, strict: boolean): JsonSchema.
|
|
|
2201
3161
|
}
|
|
2202
3162
|
|
|
2203
3163
|
/**
|
|
2204
|
-
*
|
|
3164
|
+
* Creates a layer that registers a `Toolkit` with the `McpServer`.
|
|
3165
|
+
*
|
|
3166
|
+
* **Details**
|
|
3167
|
+
*
|
|
3168
|
+
* The layer requires `McpServer`. Pass it to a transport through the
|
|
3169
|
+
* `registrations` option, or provide the transport layer to it.
|
|
3170
|
+
*
|
|
3171
|
+
* @see {@link ToolkitRequirements} for which services are request-level
|
|
2205
3172
|
*
|
|
2206
3173
|
* @stability unstable
|
|
2207
3174
|
* @category layers
|
|
@@ -2209,14 +3176,7 @@ const toolJsonSchema = (schema: Schema.Constraint, strict: boolean): JsonSchema.
|
|
|
2209
3176
|
*/
|
|
2210
3177
|
export const toolkit = <Tools extends Record<string, Tool.Any>>(
|
|
2211
3178
|
toolkit: Toolkit.Toolkit<Tools>
|
|
2212
|
-
): Layer.Layer<
|
|
2213
|
-
never,
|
|
2214
|
-
never,
|
|
2215
|
-
Tool.HandlersFor<Tools> | Exclude<Tool.HandlerServices<Tools[keyof Tools]>, McpRequestContext>
|
|
2216
|
-
> =>
|
|
2217
|
-
Layer.effectDiscard(registerToolkit(toolkit)).pipe(
|
|
2218
|
-
Layer.provide(McpServer.layer)
|
|
2219
|
-
)
|
|
3179
|
+
): Layer.Layer<never, never, ToolkitRequirements<Tools>> => Layer.effectDiscard(registerToolkit(toolkit))
|
|
2220
3180
|
|
|
2221
3181
|
/**
|
|
2222
3182
|
* Utility type that validates a completion-handler record against the allowed
|
|
@@ -2272,6 +3232,90 @@ export type ResourceCompletions<Schemas extends ReadonlyArray<Schema.Constraint>
|
|
|
2272
3232
|
) => Effect.Effect<Array<Schemas[K]["Type"]>, any, any>
|
|
2273
3233
|
}
|
|
2274
3234
|
|
|
3235
|
+
// Leave defects and interruption to makeInvoke; convert only expected failures.
|
|
3236
|
+
const handlerFailureToInternalError = (cause: Cause.Cause<unknown>): Effect.Effect<never, InternalError> =>
|
|
3237
|
+
Cause.hasDies(cause) || Cause.hasInterrupts(cause)
|
|
3238
|
+
? Effect.failCause(cause as Cause.Cause<never>)
|
|
3239
|
+
: Effect.fail(new InternalError({ message: Cause.prettyErrors(cause)[0].message }))
|
|
3240
|
+
|
|
3241
|
+
const completionHandler = (
|
|
3242
|
+
handle: (input: string, context: CompletionContext) => Effect.Effect<any, any, any>,
|
|
3243
|
+
encodeValues: (values: unknown) => Effect.Effect<ReadonlyArray<unknown>, any, any>
|
|
3244
|
+
) =>
|
|
3245
|
+
(input: string, context: CompletionContext): Effect.Effect<CompleteResult, InternalError> =>
|
|
3246
|
+
handle(input, context).pipe(
|
|
3247
|
+
Effect.flatMap(encodeValues),
|
|
3248
|
+
Effect.map((values) => ({
|
|
3249
|
+
completion: {
|
|
3250
|
+
values: values as Array<string>,
|
|
3251
|
+
total: values.length,
|
|
3252
|
+
hasMore: false
|
|
3253
|
+
}
|
|
3254
|
+
})),
|
|
3255
|
+
Effect.catchCause(handlerFailureToInternalError)
|
|
3256
|
+
) as any
|
|
3257
|
+
|
|
3258
|
+
type TemplateParamKey<Schemas extends ReadonlyArray<Schema.Constraint>, K extends keyof Schemas> = Schemas[K] extends
|
|
3259
|
+
Param<infer Id, infer _S> ? Id : `param${K & string}`
|
|
3260
|
+
|
|
3261
|
+
type TemplateSchemaServices<Schemas extends ReadonlyArray<Schema.Constraint>, Completions> =
|
|
3262
|
+
| Schemas[number]["DecodingServices"]
|
|
3263
|
+
| {
|
|
3264
|
+
[K in Extract<keyof Schemas, `${number}`>]: TemplateParamKey<Schemas, K> extends keyof Completions
|
|
3265
|
+
? Schemas[K]["EncodingServices"]
|
|
3266
|
+
: never
|
|
3267
|
+
}[Extract<keyof Schemas, `${number}`>]
|
|
3268
|
+
|
|
3269
|
+
type PromptSchemaServices<Params extends Schema.Struct.Fields, Completions> =
|
|
3270
|
+
| Schema.Struct.DecodingServices<Params>
|
|
3271
|
+
| Params[Extract<keyof Completions, keyof Params>]["EncodingServices"]
|
|
3272
|
+
|
|
3273
|
+
/**
|
|
3274
|
+
* The requirements of a resource registration: the content handler's services
|
|
3275
|
+
* are request-level.
|
|
3276
|
+
*
|
|
3277
|
+
* @stability unstable
|
|
3278
|
+
* @category utility types
|
|
3279
|
+
* @since 4.0.0
|
|
3280
|
+
*/
|
|
3281
|
+
export type ResourceRequirements<R> = RegistrationRequirements<never, R>
|
|
3282
|
+
|
|
3283
|
+
/**
|
|
3284
|
+
* The requirements of a resource template registration.
|
|
3285
|
+
*
|
|
3286
|
+
* **Details**
|
|
3287
|
+
*
|
|
3288
|
+
* Services read by the content and completion handlers are request-level.
|
|
3289
|
+
* Parameter decoding services, and the encoding services of parameters with a
|
|
3290
|
+
* completion handler, are required when the template is registered, unless a
|
|
3291
|
+
* handler also reads them.
|
|
3292
|
+
*
|
|
3293
|
+
* @stability unstable
|
|
3294
|
+
* @category utility types
|
|
3295
|
+
* @since 4.0.0
|
|
3296
|
+
*/
|
|
3297
|
+
export type ResourceTemplateRequirements<Schemas extends ReadonlyArray<Schema.Constraint>, R, Completions> =
|
|
3298
|
+
RegistrationRequirements<TemplateSchemaServices<Schemas, Completions>, R | CompletionServices<Completions>>
|
|
3299
|
+
|
|
3300
|
+
/**
|
|
3301
|
+
* The requirements of a prompt registration.
|
|
3302
|
+
*
|
|
3303
|
+
* **Details**
|
|
3304
|
+
*
|
|
3305
|
+
* Services read by the content and completion handlers are request-level.
|
|
3306
|
+
* Parameter decoding services, and the encoding services of parameters with a
|
|
3307
|
+
* completion handler, are required when the prompt is registered, unless a
|
|
3308
|
+
* handler also reads them.
|
|
3309
|
+
*
|
|
3310
|
+
* @stability unstable
|
|
3311
|
+
* @category utility types
|
|
3312
|
+
* @since 4.0.0
|
|
3313
|
+
*/
|
|
3314
|
+
export type PromptRequirements<Params extends Schema.Struct.Fields, R, Completions> = RegistrationRequirements<
|
|
3315
|
+
PromptSchemaServices<Params, Completions>,
|
|
3316
|
+
R | CompletionServices<Completions>
|
|
3317
|
+
>
|
|
3318
|
+
|
|
2275
3319
|
/**
|
|
2276
3320
|
* Registers an MCP resource or resource template from an Effect program.
|
|
2277
3321
|
*
|
|
@@ -2306,7 +3350,7 @@ export const registerResource: {
|
|
|
2306
3350
|
>
|
|
2307
3351
|
readonly annotations?: Context.Context<never> | undefined
|
|
2308
3352
|
}
|
|
2309
|
-
): Effect.Effect<void, never,
|
|
3353
|
+
): Effect.Effect<void, never, ResourceRequirements<R>>
|
|
2310
3354
|
/**
|
|
2311
3355
|
* Registers an MCP resource or resource template from an Effect program.
|
|
2312
3356
|
*
|
|
@@ -2333,18 +3377,7 @@ export const registerResource: {
|
|
|
2333
3377
|
R
|
|
2334
3378
|
>
|
|
2335
3379
|
readonly annotations?: Context.Context<never> | undefined
|
|
2336
|
-
}) => Effect.Effect<
|
|
2337
|
-
void,
|
|
2338
|
-
never,
|
|
2339
|
-
| Exclude<
|
|
2340
|
-
| Schemas[number]["DecodingServices"]
|
|
2341
|
-
| Schemas[number]["EncodingServices"]
|
|
2342
|
-
| R
|
|
2343
|
-
| CompletionServices<Completions>,
|
|
2344
|
-
McpRequestContext
|
|
2345
|
-
>
|
|
2346
|
-
| McpServer
|
|
2347
|
-
>
|
|
3380
|
+
}) => Effect.Effect<void, never, ResourceTemplateRequirements<Schemas, R, Completions>>
|
|
2348
3381
|
} = function() {
|
|
2349
3382
|
if (arguments.length === 1) {
|
|
2350
3383
|
const options = arguments[0] as {
|
|
@@ -2358,20 +3391,17 @@ export const registerResource: {
|
|
|
2358
3391
|
readonly annotations?: Context.Context<never> | undefined
|
|
2359
3392
|
}
|
|
2360
3393
|
return Effect.gen(function*() {
|
|
2361
|
-
const services =
|
|
3394
|
+
const services = yield* Effect.context<never>()
|
|
2362
3395
|
const registry = yield* McpServer
|
|
2363
3396
|
yield* registry.addResource({
|
|
2364
3397
|
resource: new Resource({
|
|
2365
3398
|
...options,
|
|
2366
3399
|
annotations: options
|
|
2367
3400
|
}),
|
|
3401
|
+
services,
|
|
2368
3402
|
handle: options.content.pipe(
|
|
2369
|
-
Effect.provideContext(services),
|
|
2370
3403
|
Effect.map((content) => resolveResourceContent(options.uri, content)),
|
|
2371
|
-
Effect.catchCause(
|
|
2372
|
-
const prettyError = Cause.prettyErrors(cause)[0]
|
|
2373
|
-
return Effect.fail(new InternalError({ message: prettyError.message }))
|
|
2374
|
-
})
|
|
3404
|
+
Effect.catchCause(handlerFailureToInternalError)
|
|
2375
3405
|
),
|
|
2376
3406
|
annotations: options.annotations ?? Context.empty()
|
|
2377
3407
|
})
|
|
@@ -2405,7 +3435,7 @@ export const registerResource: {
|
|
|
2405
3435
|
>
|
|
2406
3436
|
readonly annotations?: Context.Context<never> | undefined
|
|
2407
3437
|
}) {
|
|
2408
|
-
const services =
|
|
3438
|
+
const services = yield* Effect.context<never>()
|
|
2409
3439
|
const registry = yield* McpServer
|
|
2410
3440
|
const decode = Schema.decodeUnknownEffect(schema)
|
|
2411
3441
|
const template = new ResourceTemplate({
|
|
@@ -2421,32 +3451,13 @@ export const registerResource: {
|
|
|
2421
3451
|
) => Effect.Effect<CompleteResult, InternalError>
|
|
2422
3452
|
> = Object.create(null)
|
|
2423
3453
|
for (const [param, handle] of Object.entries(options.completion ?? {})) {
|
|
2424
|
-
|
|
2425
|
-
const handler = (
|
|
2426
|
-
input: string,
|
|
2427
|
-
context: CompletionContext
|
|
2428
|
-
) =>
|
|
2429
|
-
handle(input, context).pipe(
|
|
2430
|
-
Effect.flatMap(encodeArray),
|
|
2431
|
-
Effect.map((values) => ({
|
|
2432
|
-
completion: {
|
|
2433
|
-
values: values as Array<string>,
|
|
2434
|
-
total: values.length,
|
|
2435
|
-
hasMore: false
|
|
2436
|
-
}
|
|
2437
|
-
})),
|
|
2438
|
-
Effect.catchCause((cause) => {
|
|
2439
|
-
const prettyError = Cause.prettyErrors(cause)[0]
|
|
2440
|
-
return Effect.fail(new InternalError({ message: prettyError.message }))
|
|
2441
|
-
}),
|
|
2442
|
-
Effect.provideContext(services)
|
|
2443
|
-
)
|
|
2444
|
-
completions[param] = handler
|
|
3454
|
+
completions[param] = completionHandler(handle, Schema.encodeUnknownEffect(Schema.Array(params[param])))
|
|
2445
3455
|
}
|
|
2446
3456
|
yield* registry.addResourceTemplate({
|
|
2447
3457
|
template,
|
|
2448
3458
|
routerPath,
|
|
2449
3459
|
completions,
|
|
3460
|
+
services,
|
|
2450
3461
|
annotations: options.annotations ?? Context.empty(),
|
|
2451
3462
|
handle: (uri, params) =>
|
|
2452
3463
|
decode(params).pipe(
|
|
@@ -2454,14 +3465,10 @@ export const registerResource: {
|
|
|
2454
3465
|
Effect.flatMap((params: any) =>
|
|
2455
3466
|
options.content(uri, ...params).pipe(
|
|
2456
3467
|
Effect.map((content) => resolveResourceContent(uri, content)),
|
|
2457
|
-
Effect.catchCause(
|
|
2458
|
-
const prettyError = Cause.prettyErrors(cause)[0]
|
|
2459
|
-
return Effect.fail(new InternalError({ message: prettyError.message }))
|
|
2460
|
-
})
|
|
3468
|
+
Effect.catchCause(handlerFailureToInternalError)
|
|
2461
3469
|
)
|
|
2462
|
-
)
|
|
2463
|
-
|
|
2464
|
-
)
|
|
3470
|
+
)
|
|
3471
|
+
) as Effect.Effect<ReadResourceResult, InvalidParams | InternalError>
|
|
2465
3472
|
})
|
|
2466
3473
|
})
|
|
2467
3474
|
} as any
|
|
@@ -2469,7 +3476,15 @@ export const registerResource: {
|
|
|
2469
3476
|
/**
|
|
2470
3477
|
* Creates a layer that registers an MCP resource or resource template.
|
|
2471
3478
|
*
|
|
3479
|
+
* **Details**
|
|
3480
|
+
*
|
|
3481
|
+
* Services read by the content and completion handlers are request-level, so
|
|
3482
|
+
* providing them with `Layer.provide` doesn't satisfy them. Read
|
|
3483
|
+
* construction-time services in an enclosing `Layer.unwrap` and close over
|
|
3484
|
+
* them, and provide per-request services with MCP middleware.
|
|
3485
|
+
*
|
|
2472
3486
|
* @see {@link registerResource} for the Effect-level resource registration API
|
|
3487
|
+
* @see {@link middleware} for providing request-level dependencies
|
|
2473
3488
|
*
|
|
2474
3489
|
* @stability unstable
|
|
2475
3490
|
* @category layers
|
|
@@ -2479,7 +3494,15 @@ export const resource: {
|
|
|
2479
3494
|
/**
|
|
2480
3495
|
* Creates a layer that registers an MCP resource or resource template.
|
|
2481
3496
|
*
|
|
3497
|
+
* **Details**
|
|
3498
|
+
*
|
|
3499
|
+
* Services read by the content and completion handlers are request-level, so
|
|
3500
|
+
* providing them with `Layer.provide` doesn't satisfy them. Read
|
|
3501
|
+
* construction-time services in an enclosing `Layer.unwrap` and close over
|
|
3502
|
+
* them, and provide per-request services with MCP middleware.
|
|
3503
|
+
*
|
|
2482
3504
|
* @see {@link registerResource} for the Effect-level resource registration API
|
|
3505
|
+
* @see {@link middleware} for providing request-level dependencies
|
|
2483
3506
|
*
|
|
2484
3507
|
* @stability unstable
|
|
2485
3508
|
* @category layers
|
|
@@ -2499,11 +3522,19 @@ export const resource: {
|
|
|
2499
3522
|
R
|
|
2500
3523
|
>
|
|
2501
3524
|
}
|
|
2502
|
-
): Layer.Layer<never, never,
|
|
3525
|
+
): Layer.Layer<never, never, ResourceRequirements<R>>
|
|
2503
3526
|
/**
|
|
2504
3527
|
* Creates a layer that registers an MCP resource or resource template.
|
|
2505
3528
|
*
|
|
3529
|
+
* **Details**
|
|
3530
|
+
*
|
|
3531
|
+
* Services read by the content and completion handlers are request-level, so
|
|
3532
|
+
* providing them with `Layer.provide` doesn't satisfy them. Read
|
|
3533
|
+
* construction-time services in an enclosing `Layer.unwrap` and close over
|
|
3534
|
+
* them, and provide per-request services with MCP middleware.
|
|
3535
|
+
*
|
|
2506
3536
|
* @see {@link registerResource} for the Effect-level resource registration API
|
|
3537
|
+
* @see {@link middleware} for providing request-level dependencies
|
|
2507
3538
|
*
|
|
2508
3539
|
* @stability unstable
|
|
2509
3540
|
* @category layers
|
|
@@ -2525,28 +3556,13 @@ export const resource: {
|
|
|
2525
3556
|
E,
|
|
2526
3557
|
R
|
|
2527
3558
|
>
|
|
2528
|
-
}) => Layer.Layer<
|
|
2529
|
-
never,
|
|
2530
|
-
never,
|
|
2531
|
-
Exclude<
|
|
2532
|
-
| Schemas[number]["DecodingServices"]
|
|
2533
|
-
| Schemas[number]["EncodingServices"]
|
|
2534
|
-
| R
|
|
2535
|
-
| CompletionServices<Completions>,
|
|
2536
|
-
McpRequestContext
|
|
2537
|
-
>
|
|
2538
|
-
>
|
|
3559
|
+
}) => Layer.Layer<never, never, ResourceTemplateRequirements<Schemas, R, Completions>>
|
|
2539
3560
|
} = function() {
|
|
2540
3561
|
if (arguments.length === 1) {
|
|
2541
|
-
return Layer.effectDiscard(registerResource(arguments[0]))
|
|
2542
|
-
Layer.provide(McpServer.layer)
|
|
2543
|
-
)
|
|
3562
|
+
return Layer.effectDiscard(registerResource(arguments[0]))
|
|
2544
3563
|
}
|
|
2545
3564
|
const register = registerResource(...(arguments as any as [any, any]))
|
|
2546
|
-
return (options: any) =>
|
|
2547
|
-
Layer.effectDiscard(register(options)).pipe(
|
|
2548
|
-
Layer.provide(McpServer.layer)
|
|
2549
|
-
)
|
|
3565
|
+
return (options: any) => Layer.effectDiscard(register(options))
|
|
2550
3566
|
} as any
|
|
2551
3567
|
|
|
2552
3568
|
/**
|
|
@@ -2585,18 +3601,7 @@ export const registerPrompt = <
|
|
|
2585
3601
|
) => Effect.Effect<Array<PromptMessage> | string, E, R>
|
|
2586
3602
|
readonly annotations?: Context.Context<never> | undefined
|
|
2587
3603
|
}
|
|
2588
|
-
): Effect.Effect<
|
|
2589
|
-
void,
|
|
2590
|
-
never,
|
|
2591
|
-
| Exclude<
|
|
2592
|
-
| Schema.Struct.DecodingServices<Params>
|
|
2593
|
-
| Params[Extract<keyof Completions, keyof Params>]["EncodingServices"]
|
|
2594
|
-
| R
|
|
2595
|
-
| CompletionServices<Completions>,
|
|
2596
|
-
McpRequestContext
|
|
2597
|
-
>
|
|
2598
|
-
| McpServer
|
|
2599
|
-
> => {
|
|
3604
|
+
): Effect.Effect<void, never, PromptRequirements<Params, R, Completions>> => {
|
|
2600
3605
|
const args = Arr.empty<PromptArgument>()
|
|
2601
3606
|
const props: Record<string, Schema.Constraint> = options.parameters ?? {}
|
|
2602
3607
|
for (const [name, prop] of Object.entries(props)) {
|
|
@@ -2624,17 +3629,7 @@ export const registerPrompt = <
|
|
|
2624
3629
|
> = options.completion ?? {}
|
|
2625
3630
|
return Effect.gen(function*() {
|
|
2626
3631
|
const registry = yield* McpServer
|
|
2627
|
-
const services =
|
|
2628
|
-
yield* Effect.context<
|
|
2629
|
-
Exclude<
|
|
2630
|
-
| R
|
|
2631
|
-
| Schema.Struct.DecodingServices<Params>
|
|
2632
|
-
| Params[Extract<keyof Completions, keyof Params>]["EncodingServices"]
|
|
2633
|
-
| CompletionServices<Completions>,
|
|
2634
|
-
McpRequestContext
|
|
2635
|
-
>
|
|
2636
|
-
>()
|
|
2637
|
-
)
|
|
3632
|
+
const services = yield* Effect.context<never>()
|
|
2638
3633
|
const completions: Record<
|
|
2639
3634
|
string,
|
|
2640
3635
|
(
|
|
@@ -2643,41 +3638,19 @@ export const registerPrompt = <
|
|
|
2643
3638
|
) => Effect.Effect<CompleteResult, InternalError, McpRequestContext>
|
|
2644
3639
|
> = Object.create(null)
|
|
2645
3640
|
for (const [param, handle] of Object.entries(completion)) {
|
|
2646
|
-
|
|
2647
|
-
const handler = (
|
|
2648
|
-
input: string,
|
|
2649
|
-
context: CompletionContext
|
|
2650
|
-
) =>
|
|
2651
|
-
handle(input, context).pipe(
|
|
2652
|
-
Effect.flatMap(encodeArray),
|
|
2653
|
-
Effect.map((values) => ({
|
|
2654
|
-
completion: {
|
|
2655
|
-
values: values as Array<string>,
|
|
2656
|
-
total: values.length,
|
|
2657
|
-
hasMore: false
|
|
2658
|
-
}
|
|
2659
|
-
})),
|
|
2660
|
-
Effect.catchCause((cause) => {
|
|
2661
|
-
const prettyError = Cause.prettyErrors(cause)[0]
|
|
2662
|
-
return Effect.fail(new InternalError({ message: prettyError.message }))
|
|
2663
|
-
}),
|
|
2664
|
-
Effect.provide(services)
|
|
2665
|
-
)
|
|
2666
|
-
completions[param] = handler as any
|
|
3641
|
+
completions[param] = completionHandler(handle, Schema.encodeUnknownEffect(Schema.Array(props[param])))
|
|
2667
3642
|
}
|
|
2668
3643
|
yield* registry.addPrompt({
|
|
2669
3644
|
prompt,
|
|
2670
3645
|
completions,
|
|
3646
|
+
services,
|
|
2671
3647
|
annotations: options.annotations ?? Context.empty(),
|
|
2672
3648
|
handle: (params) =>
|
|
2673
3649
|
decode(params).pipe(
|
|
2674
3650
|
Effect.mapError((error) => new InvalidParams({ message: error.message })),
|
|
2675
3651
|
Effect.flatMap((params) =>
|
|
2676
3652
|
options.content(params as any).pipe(
|
|
2677
|
-
Effect.catchCause(
|
|
2678
|
-
const prettyError = Cause.prettyErrors(cause)[0]
|
|
2679
|
-
return Effect.fail(new InternalError({ message: prettyError.message }))
|
|
2680
|
-
})
|
|
3653
|
+
Effect.catchCause(handlerFailureToInternalError)
|
|
2681
3654
|
)
|
|
2682
3655
|
),
|
|
2683
3656
|
Effect.map((messages) => {
|
|
@@ -2688,9 +3661,8 @@ export const registerPrompt = <
|
|
|
2688
3661
|
}] :
|
|
2689
3662
|
messages
|
|
2690
3663
|
return new GetPromptResult({ messages, description: prompt.description })
|
|
2691
|
-
})
|
|
2692
|
-
|
|
2693
|
-
)
|
|
3664
|
+
})
|
|
3665
|
+
) as Effect.Effect<GetPromptResult, InvalidParams | InternalError>
|
|
2694
3666
|
})
|
|
2695
3667
|
})
|
|
2696
3668
|
}
|
|
@@ -2698,7 +3670,15 @@ export const registerPrompt = <
|
|
|
2698
3670
|
/**
|
|
2699
3671
|
* Creates a layer that registers an MCP prompt.
|
|
2700
3672
|
*
|
|
3673
|
+
* **Details**
|
|
3674
|
+
*
|
|
3675
|
+
* Services read by the content and completion handlers are request-level, so
|
|
3676
|
+
* providing them with `Layer.provide` doesn't satisfy them. Read
|
|
3677
|
+
* construction-time services in an enclosing `Layer.unwrap` and close over
|
|
3678
|
+
* them, and provide per-request services with MCP middleware.
|
|
3679
|
+
*
|
|
2701
3680
|
* @see {@link registerPrompt} for the Effect-level prompt registration API
|
|
3681
|
+
* @see {@link middleware} for providing request-level dependencies
|
|
2702
3682
|
*
|
|
2703
3683
|
* @stability unstable
|
|
2704
3684
|
* @category layers
|
|
@@ -2726,20 +3706,7 @@ export const prompt = <
|
|
|
2726
3706
|
) => Effect.Effect<Array<PromptMessage> | string, E, R>
|
|
2727
3707
|
readonly annotations?: Context.Context<never> | undefined
|
|
2728
3708
|
}
|
|
2729
|
-
): Layer.Layer<
|
|
2730
|
-
never,
|
|
2731
|
-
never,
|
|
2732
|
-
Exclude<
|
|
2733
|
-
| Schema.Struct.DecodingServices<Params>
|
|
2734
|
-
| Params[Extract<keyof Completions, keyof Params>]["EncodingServices"]
|
|
2735
|
-
| R
|
|
2736
|
-
| CompletionServices<Completions>,
|
|
2737
|
-
McpRequestContext
|
|
2738
|
-
>
|
|
2739
|
-
> =>
|
|
2740
|
-
Layer.effectDiscard(registerPrompt(options)).pipe(
|
|
2741
|
-
Layer.provide(McpServer.layer)
|
|
2742
|
-
)
|
|
3709
|
+
): Layer.Layer<never, never, PromptRequirements<Params, R, Completions>> => Layer.effectDiscard(registerPrompt(options))
|
|
2743
3710
|
|
|
2744
3711
|
/**
|
|
2745
3712
|
* Collects structured input from the current MCP client and decodes the
|
|
@@ -2799,6 +3766,22 @@ export const clientCapabilities: Effect.Effect<
|
|
|
2799
3766
|
McpRequestContext
|
|
2800
3767
|
> = McpRequestContext.useSync((_) => _.clientCapabilities)
|
|
2801
3768
|
|
|
3769
|
+
/**
|
|
3770
|
+
* Decodes an `Mcp-Name` header, including base64-wrapped values.
|
|
3771
|
+
* Returns `undefined` for malformed values.
|
|
3772
|
+
*
|
|
3773
|
+
* **Details**
|
|
3774
|
+
*
|
|
3775
|
+
* Use in `HttpRouter.middleware` to identify the tool, prompt or resource
|
|
3776
|
+
* targeted by a `2026-07-28` request. Earlier protocols carry the name only
|
|
3777
|
+
* in the JSON-RPC body.
|
|
3778
|
+
*
|
|
3779
|
+
* @stability unstable
|
|
3780
|
+
* @category decoding
|
|
3781
|
+
* @since 4.0.0
|
|
3782
|
+
*/
|
|
3783
|
+
export const decodeNameHeader = (value: string): string | undefined => McpProtocolInternal.decodeRoutingHeader(value)
|
|
3784
|
+
|
|
2802
3785
|
// -----------------------------------------------------------------------------
|
|
2803
3786
|
// Internal
|
|
2804
3787
|
// -----------------------------------------------------------------------------
|