effect 4.1.0 → 4.1.1
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 +30 -0
- package/THIRD_PARTY_NOTICES.md +62 -0
- package/dist/BigDecimal.d.ts +139 -7
- package/dist/BigDecimal.d.ts.map +1 -1
- package/dist/BigDecimal.js +83 -14
- package/dist/BigDecimal.js.map +1 -1
- package/dist/Equal.d.ts +2 -2
- package/dist/Equal.d.ts.map +1 -1
- package/dist/Equal.js +3 -3
- package/dist/Equal.js.map +1 -1
- package/dist/Hash.d.ts +3 -2
- package/dist/Hash.d.ts.map +1 -1
- package/dist/Hash.js +5 -4
- package/dist/Hash.js.map +1 -1
- package/dist/Iterable.d.ts.map +1 -1
- package/dist/Iterable.js +11 -8
- package/dist/Iterable.js.map +1 -1
- package/dist/Path.d.ts.map +1 -1
- package/dist/Path.js +5 -1
- package/dist/Path.js.map +1 -1
- package/dist/Pool.d.ts.map +1 -1
- package/dist/Pool.js +2 -1
- package/dist/Pool.js.map +1 -1
- package/dist/Schema.d.ts +2 -1
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +2 -1
- package/dist/Schema.js.map +1 -1
- package/dist/SchemaRepresentation.d.ts +4 -2
- package/dist/SchemaRepresentation.d.ts.map +1 -1
- package/dist/SchemaRepresentation.js +4 -2
- package/dist/SchemaRepresentation.js.map +1 -1
- package/dist/SchemaTransformation.d.ts +2 -2
- package/dist/SchemaTransformation.js +2 -2
- package/dist/ai/McpClient.d.ts +171 -19
- package/dist/ai/McpClient.d.ts.map +1 -1
- package/dist/ai/McpClient.js +277 -66
- package/dist/ai/McpClient.js.map +1 -1
- package/dist/ai/McpSchema.d.ts +108 -0
- package/dist/ai/McpSchema.d.ts.map +1 -1
- package/dist/ai/McpSchema.js +99 -0
- package/dist/ai/McpSchema.js.map +1 -1
- package/dist/ai/McpServer.d.ts +120 -6
- package/dist/ai/McpServer.d.ts.map +1 -1
- package/dist/ai/McpServer.js +215 -34
- package/dist/ai/McpServer.js.map +1 -1
- package/dist/ai/Prompt.d.ts.map +1 -1
- package/dist/ai/Prompt.js +20 -11
- package/dist/ai/Prompt.js.map +1 -1
- package/dist/ai/Tool.d.ts.map +1 -1
- package/dist/ai/Tool.js +5 -7
- package/dist/ai/Tool.js.map +1 -1
- package/dist/ai/internal/mcpCore.js +72 -3
- package/dist/ai/internal/mcpCore.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2024_11_05.js +3 -1
- package/dist/ai/internal/mcpProtocol/v2024_11_05.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_03_26.js +3 -1
- package/dist/ai/internal/mcpProtocol/v2025_03_26.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_06_18.js +3 -1
- package/dist/ai/internal/mcpProtocol/v2025_06_18.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_11_25.js +3 -1
- package/dist/ai/internal/mcpProtocol/v2025_11_25.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2026_07_28.js +42 -7
- package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
- package/dist/ai/internal/mcpProtocol.js +12 -0
- package/dist/ai/internal/mcpProtocol.js.map +1 -1
- package/dist/ai/internal/mcpRuntime.js +3 -3
- package/dist/ai/internal/mcpRuntime.js.map +1 -1
- package/dist/ai/internal/mcpSkills.d.ts +2 -0
- package/dist/ai/internal/mcpSkills.d.ts.map +1 -0
- package/dist/ai/internal/mcpSkills.js +428 -0
- package/dist/ai/internal/mcpSkills.js.map +1 -0
- package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.js +15 -5
- package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
- package/dist/cluster/Reply.d.ts +1 -1
- package/dist/cluster/Reply.d.ts.map +1 -1
- package/dist/cluster/Reply.js.map +1 -1
- package/dist/cluster/Sharding.d.ts.map +1 -1
- package/dist/cluster/Sharding.js +4 -1
- package/dist/cluster/Sharding.js.map +1 -1
- package/dist/cluster/internal/entityManager.js +15 -7
- package/dist/cluster/internal/entityManager.js.map +1 -1
- package/dist/crypto/Aead.d.ts +2 -1
- package/dist/crypto/Aead.d.ts.map +1 -1
- package/dist/crypto/Aead.js +2 -2
- package/dist/crypto/Aead.js.map +1 -1
- package/dist/crypto/Cipher.d.ts +2 -1
- package/dist/crypto/Cipher.d.ts.map +1 -1
- package/dist/crypto/Cipher.js +2 -2
- package/dist/crypto/Cipher.js.map +1 -1
- package/dist/crypto/KeyAgreement.d.ts +3 -2
- package/dist/crypto/KeyAgreement.d.ts.map +1 -1
- package/dist/crypto/KeyAgreement.js +2 -2
- package/dist/crypto/KeyAgreement.js.map +1 -1
- package/dist/crypto/KeyFormat.d.ts +33 -1
- package/dist/crypto/KeyFormat.d.ts.map +1 -1
- package/dist/crypto/KeyFormat.js.map +1 -1
- package/dist/crypto/KeyWrap.d.ts +4 -3
- package/dist/crypto/KeyWrap.d.ts.map +1 -1
- package/dist/crypto/KeyWrap.js +2 -2
- package/dist/crypto/KeyWrap.js.map +1 -1
- package/dist/crypto/Mac.d.ts +2 -1
- package/dist/crypto/Mac.d.ts.map +1 -1
- package/dist/crypto/Mac.js +2 -2
- package/dist/crypto/Mac.js.map +1 -1
- package/dist/crypto/Signature.d.ts +3 -2
- package/dist/crypto/Signature.d.ts.map +1 -1
- package/dist/crypto/Signature.js +2 -2
- package/dist/crypto/Signature.js.map +1 -1
- package/dist/crypto/WebCrypto.d.ts +19 -0
- package/dist/crypto/WebCrypto.d.ts.map +1 -1
- package/dist/crypto/WebCrypto.js.map +1 -1
- package/dist/crypto/internal/asymmetric.js.map +1 -1
- package/dist/crypto/internal/key.js +3 -2
- package/dist/crypto/internal/key.js.map +1 -1
- package/dist/encoding/Base64Url.d.ts.map +1 -1
- package/dist/encoding/Base64Url.js +26 -3
- package/dist/encoding/Base64Url.js.map +1 -1
- package/dist/encoding/Ndjson.d.ts +5 -4
- package/dist/encoding/Ndjson.d.ts.map +1 -1
- package/dist/encoding/Ndjson.js +6 -5
- package/dist/encoding/Ndjson.js.map +1 -1
- package/dist/encoding/Sse.d.ts +3 -1
- package/dist/encoding/Sse.d.ts.map +1 -1
- package/dist/encoding/Sse.js +5 -3
- package/dist/encoding/Sse.js.map +1 -1
- package/dist/eventlog/EventLog.d.ts +7 -0
- package/dist/eventlog/EventLog.d.ts.map +1 -1
- package/dist/eventlog/EventLog.js +7 -0
- package/dist/eventlog/EventLog.js.map +1 -1
- package/dist/eventlog/EventLogServer.d.ts +7 -1
- package/dist/eventlog/EventLogServer.d.ts.map +1 -1
- package/dist/eventlog/EventLogServer.js +27 -12
- package/dist/eventlog/EventLogServer.js.map +1 -1
- package/dist/eventlog/EventLogServerEncrypted.d.ts +10 -0
- package/dist/eventlog/EventLogServerEncrypted.d.ts.map +1 -1
- package/dist/eventlog/EventLogServerEncrypted.js.map +1 -1
- package/dist/eventlog/EventLogServerUnencrypted.d.ts +17 -0
- package/dist/eventlog/EventLogServerUnencrypted.d.ts.map +1 -1
- package/dist/eventlog/EventLogServerUnencrypted.js +14 -2
- package/dist/eventlog/EventLogServerUnencrypted.js.map +1 -1
- package/dist/eventlog/EventLogSessionAuth.d.ts +21 -0
- package/dist/eventlog/EventLogSessionAuth.d.ts.map +1 -1
- package/dist/eventlog/EventLogSessionAuth.js +22 -0
- package/dist/eventlog/EventLogSessionAuth.js.map +1 -1
- package/dist/graphql/GraphQLClient.d.ts +3 -1
- package/dist/graphql/GraphQLClient.d.ts.map +1 -1
- package/dist/graphql/GraphQLClient.js.map +1 -1
- package/dist/graphql/GraphQLClientError.d.ts +14 -9
- package/dist/graphql/GraphQLClientError.d.ts.map +1 -1
- package/dist/graphql/GraphQLClientError.js +14 -21
- package/dist/graphql/GraphQLClientError.js.map +1 -1
- package/dist/graphql/GraphQLProtocol.d.ts +12 -3
- package/dist/graphql/GraphQLProtocol.d.ts.map +1 -1
- package/dist/graphql/GraphQLProtocol.js +4 -1
- package/dist/graphql/GraphQLProtocol.js.map +1 -1
- package/dist/http/HttpClient.d.ts +189 -7
- package/dist/http/HttpClient.d.ts.map +1 -1
- package/dist/http/HttpClient.js +75 -2
- package/dist/http/HttpClient.js.map +1 -1
- package/dist/http/HttpServerRequest.d.ts.map +1 -1
- package/dist/http/HttpServerRequest.js +38 -22
- package/dist/http/HttpServerRequest.js.map +1 -1
- package/dist/http/internal/mimeTypes.d.ts +4 -1
- package/dist/http/internal/mimeTypes.d.ts.map +1 -1
- package/dist/http/internal/mimeTypes.js +4 -1
- package/dist/http/internal/mimeTypes.js.map +1 -1
- package/dist/http-api/HttpApiSchema.js +1 -1
- package/dist/http-api/HttpApiSchema.js.map +1 -1
- package/dist/internal/dateTime.js +49 -24
- package/dist/internal/dateTime.js.map +1 -1
- package/dist/internal/effect.js +10 -0
- package/dist/internal/effect.js.map +1 -1
- package/dist/internal/glob.js +234 -56
- package/dist/internal/glob.js.map +1 -1
- package/dist/internal/graphql.js +21 -7
- package/dist/internal/graphql.js.map +1 -1
- package/dist/internal/graphqlHeaders.d.ts +7 -0
- package/dist/internal/graphqlHeaders.d.ts.map +1 -1
- package/dist/internal/graphqlHeaders.js +11 -0
- package/dist/internal/graphqlHeaders.js.map +1 -1
- package/dist/internal/request.js +29 -42
- package/dist/internal/request.js.map +1 -1
- package/dist/internal/schema/fromJsonSchemaDocument.js +7 -8
- package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
- package/dist/internal/version.js +1 -1
- package/dist/jose/JoseError.d.ts +285 -0
- package/dist/jose/JoseError.d.ts.map +1 -0
- package/dist/jose/JoseError.js +269 -0
- package/dist/jose/JoseError.js.map +1 -0
- package/dist/jose/Jwe.d.ts +128 -0
- package/dist/jose/Jwe.d.ts.map +1 -0
- package/dist/jose/Jwe.js +134 -0
- package/dist/jose/Jwe.js.map +1 -0
- package/dist/jose/Jwk.d.ts +597 -0
- package/dist/jose/Jwk.d.ts.map +1 -0
- package/dist/jose/Jwk.js +503 -0
- package/dist/jose/Jwk.js.map +1 -0
- package/dist/jose/Jwks.d.ts +236 -0
- package/dist/jose/Jwks.d.ts.map +1 -0
- package/dist/jose/Jwks.js +306 -0
- package/dist/jose/Jwks.js.map +1 -0
- package/dist/jose/Jws.d.ts +177 -0
- package/dist/jose/Jws.d.ts.map +1 -0
- package/dist/jose/Jws.js +183 -0
- package/dist/jose/Jws.js.map +1 -0
- package/dist/jose/Jwt.d.ts +270 -0
- package/dist/jose/Jwt.d.ts.map +1 -0
- package/dist/jose/Jwt.js +293 -0
- package/dist/jose/Jwt.js.map +1 -0
- package/dist/jose/index.d.ts +51 -0
- package/dist/jose/index.d.ts.map +1 -0
- package/dist/jose/index.js +52 -0
- package/dist/jose/index.js.map +1 -0
- package/dist/jose/internal/compact.d.ts +2 -0
- package/dist/jose/internal/compact.d.ts.map +1 -0
- package/dist/jose/internal/compact.js +179 -0
- package/dist/jose/internal/compact.js.map +1 -0
- package/dist/net/Host.d.ts.map +1 -1
- package/dist/net/Host.js +5 -10
- package/dist/net/Host.js.map +1 -1
- package/dist/observability/OtlpSerialization.d.ts +1 -1
- package/dist/observability/OtlpSerialization.d.ts.map +1 -1
- package/dist/observability/OtlpSerialization.js +10 -4
- package/dist/observability/OtlpSerialization.js.map +1 -1
- package/dist/observability/internal/otlpProtobuf.js +3 -3
- package/dist/observability/internal/otlpProtobuf.js.map +1 -1
- package/dist/observability/internal/protobuf.js +6 -0
- package/dist/observability/internal/protobuf.js.map +1 -1
- package/dist/persistence/CircuitBreaker.d.ts +98 -38
- package/dist/persistence/CircuitBreaker.d.ts.map +1 -1
- package/dist/persistence/CircuitBreaker.js +67 -29
- package/dist/persistence/CircuitBreaker.js.map +1 -1
- package/dist/persistence/RateLimiter.d.ts +8 -0
- package/dist/persistence/RateLimiter.d.ts.map +1 -1
- package/dist/persistence/RateLimiter.js +63 -12
- package/dist/persistence/RateLimiter.js.map +1 -1
- package/dist/persistence/index.d.ts +1 -1
- package/dist/persistence/index.js +1 -1
- package/dist/reactivity/AtomRegistry.js +17 -7
- package/dist/reactivity/AtomRegistry.js.map +1 -1
- package/dist/rpc/Rpc.d.ts +1 -1
- package/dist/rpc/Rpc.d.ts.map +1 -1
- package/dist/rpc/Rpc.js.map +1 -1
- package/dist/rpc/RpcClient.d.ts +14 -2
- package/dist/rpc/RpcClient.d.ts.map +1 -1
- package/dist/rpc/RpcClient.js +38 -21
- package/dist/rpc/RpcClient.js.map +1 -1
- package/dist/rpc/RpcMessage.d.ts +3 -2
- package/dist/rpc/RpcMessage.d.ts.map +1 -1
- package/dist/rpc/RpcMessage.js.map +1 -1
- package/dist/sql/Migrator.d.ts +9 -1
- package/dist/sql/Migrator.d.ts.map +1 -1
- package/dist/sql/Migrator.js +18 -6
- package/dist/sql/Migrator.js.map +1 -1
- package/dist/sql/SqlClient.d.ts +38 -3
- package/dist/sql/SqlClient.d.ts.map +1 -1
- package/dist/sql/SqlClient.js +4 -3
- package/dist/sql/SqlClient.js.map +1 -1
- package/dist/sql/Statement.d.ts +41 -3
- package/dist/sql/Statement.d.ts.map +1 -1
- package/dist/sql/Statement.js +24 -20
- package/dist/sql/Statement.js.map +1 -1
- package/dist/workflow/Workflow.d.ts +4 -0
- package/dist/workflow/Workflow.d.ts.map +1 -1
- package/dist/workflow/Workflow.js.map +1 -1
- package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
- package/dist/workflow/WorkflowEngine.js +5 -0
- package/dist/workflow/WorkflowEngine.js.map +1 -1
- package/package.json +5 -1
- package/src/BigDecimal.ts +170 -16
- package/src/Equal.ts +3 -3
- package/src/Hash.ts +5 -4
- package/src/Iterable.ts +8 -5
- package/src/Path.ts +6 -1
- package/src/Pool.ts +2 -1
- package/src/Schema.ts +3 -2
- package/src/SchemaRepresentation.ts +4 -2
- package/src/SchemaTransformation.ts +2 -2
- package/src/ai/McpClient.ts +488 -91
- package/src/ai/McpSchema.ts +119 -0
- package/src/ai/McpServer.ts +261 -38
- package/src/ai/Prompt.ts +16 -11
- package/src/ai/Tool.ts +5 -10
- package/src/ai/internal/mcpCore.ts +165 -12
- package/src/ai/internal/mcpProtocol/v2024_11_05.ts +3 -1
- package/src/ai/internal/mcpProtocol/v2025_03_26.ts +3 -1
- package/src/ai/internal/mcpProtocol/v2025_06_18.ts +3 -1
- package/src/ai/internal/mcpProtocol/v2025_11_25.ts +3 -1
- package/src/ai/internal/mcpProtocol/v2026_07_28.ts +41 -7
- package/src/ai/internal/mcpProtocol.ts +15 -0
- package/src/ai/internal/mcpRuntime.ts +6 -3
- package/src/ai/internal/mcpSkills.ts +480 -0
- package/src/cluster/ClusterWorkflowEngine.ts +22 -8
- package/src/cluster/Reply.ts +1 -1
- package/src/cluster/Sharding.ts +4 -1
- package/src/cluster/internal/entityManager.ts +24 -10
- package/src/crypto/Aead.ts +3 -3
- package/src/crypto/Cipher.ts +3 -3
- package/src/crypto/KeyAgreement.ts +4 -4
- package/src/crypto/KeyFormat.ts +30 -1
- package/src/crypto/KeyWrap.ts +6 -6
- package/src/crypto/Mac.ts +3 -3
- package/src/crypto/Signature.ts +4 -4
- package/src/crypto/WebCrypto.ts +20 -0
- package/src/crypto/internal/asymmetric.ts +2 -1
- package/src/crypto/internal/key.ts +12 -5
- package/src/encoding/Base64Url.ts +27 -3
- package/src/encoding/Ndjson.ts +6 -5
- package/src/encoding/Sse.ts +5 -3
- package/src/eventlog/EventLog.ts +7 -0
- package/src/eventlog/EventLogServer.ts +31 -12
- package/src/eventlog/EventLogServerEncrypted.ts +5 -0
- package/src/eventlog/EventLogServerUnencrypted.ts +19 -2
- package/src/eventlog/EventLogSessionAuth.ts +26 -0
- package/src/graphql/GraphQLClient.ts +3 -1
- package/src/graphql/GraphQLClientError.ts +15 -22
- package/src/graphql/GraphQLProtocol.ts +12 -3
- package/src/http/HttpClient.ts +210 -11
- package/src/http/HttpServerRequest.ts +61 -23
- package/src/http/internal/mimeTypes.ts +4 -1
- package/src/http-api/HttpApiSchema.ts +1 -1
- package/src/internal/dateTime.ts +54 -24
- package/src/internal/effect.ts +6 -0
- package/src/internal/glob.ts +279 -63
- package/src/internal/graphql.ts +27 -9
- package/src/internal/graphqlHeaders.ts +13 -0
- package/src/internal/request.ts +51 -58
- package/src/internal/schema/fromJsonSchemaDocument.ts +11 -10
- package/src/internal/version.ts +1 -1
- package/src/jose/JoseError.ts +329 -0
- package/src/jose/Jwe.ts +196 -0
- package/src/jose/Jwk.ts +777 -0
- package/src/jose/Jwks.ts +418 -0
- package/src/jose/Jws.ts +312 -0
- package/src/jose/Jwt.ts +493 -0
- package/src/jose/index.ts +58 -0
- package/src/jose/internal/compact.ts +195 -0
- package/src/net/Host.ts +5 -10
- package/src/observability/OtlpSerialization.ts +13 -5
- package/src/observability/internal/otlpProtobuf.ts +6 -6
- package/src/observability/internal/protobuf.ts +10 -0
- package/src/persistence/CircuitBreaker.ts +113 -48
- package/src/persistence/RateLimiter.ts +84 -15
- package/src/persistence/index.ts +1 -1
- package/src/reactivity/AtomRegistry.ts +19 -8
- package/src/rpc/Rpc.ts +3 -1
- package/src/rpc/RpcClient.ts +44 -24
- package/src/rpc/RpcMessage.ts +3 -2
- package/src/sql/Migrator.ts +35 -18
- package/src/sql/SqlClient.ts +43 -8
- package/src/sql/Statement.ts +52 -22
- package/src/workflow/Workflow.ts +4 -0
- package/src/workflow/WorkflowEngine.ts +5 -0
package/src/ai/McpSchema.ts
CHANGED
|
@@ -1382,6 +1382,125 @@ export class ResourceUpdatedNotification extends Rpc.make("notifications/resourc
|
|
|
1382
1382
|
}
|
|
1383
1383
|
}) {}
|
|
1384
1384
|
|
|
1385
|
+
// =============================================================================
|
|
1386
|
+
// Skills
|
|
1387
|
+
// =============================================================================
|
|
1388
|
+
|
|
1389
|
+
/**
|
|
1390
|
+
* Schema for a file of an Agent Skill served under the MCP Skills extension
|
|
1391
|
+
* (SEP-2640), with the digest and size of its content.
|
|
1392
|
+
*
|
|
1393
|
+
* @stability unstable
|
|
1394
|
+
* @category schemas
|
|
1395
|
+
* @since 4.1.1
|
|
1396
|
+
*/
|
|
1397
|
+
export class SkillResource extends Schema.Class<SkillResource>(
|
|
1398
|
+
"@effect/ai/McpSchema/SkillResource"
|
|
1399
|
+
)({
|
|
1400
|
+
/**
|
|
1401
|
+
* The resource URI of the file.
|
|
1402
|
+
*/
|
|
1403
|
+
uri: Schema.String,
|
|
1404
|
+
/**
|
|
1405
|
+
* The SHA-256 digest of the file's bytes, as `sha256:` followed by 64
|
|
1406
|
+
* lowercase hexadecimal digits.
|
|
1407
|
+
*/
|
|
1408
|
+
digest: Schema.String.check(Schema.isPattern(/^sha256:[0-9a-f]{64}$/)),
|
|
1409
|
+
/**
|
|
1410
|
+
* The length of the file's bytes.
|
|
1411
|
+
*/
|
|
1412
|
+
size: Schema.Natural
|
|
1413
|
+
}) {}
|
|
1414
|
+
|
|
1415
|
+
/**
|
|
1416
|
+
* Schema for the YAML frontmatter of an Agent Skill's `SKILL.md`, as the
|
|
1417
|
+
* Agent Skills specification defines it (https://agentskills.io/specification).
|
|
1418
|
+
*
|
|
1419
|
+
* **Details**
|
|
1420
|
+
*
|
|
1421
|
+
* Fields the specification does not define are kept unchanged. `McpServer`
|
|
1422
|
+
* checks a skill's frontmatter against this schema before serving it. The
|
|
1423
|
+
* frontmatter of a {@link Skill} received from a server is only checked for
|
|
1424
|
+
* the `name` and `description` that the Skills extension requires; decode it
|
|
1425
|
+
* with this schema for the other fields.
|
|
1426
|
+
*
|
|
1427
|
+
* @stability unstable
|
|
1428
|
+
* @category schemas
|
|
1429
|
+
* @since 4.1.1
|
|
1430
|
+
*/
|
|
1431
|
+
export const SkillFrontmatter = Schema.StructWithRest(
|
|
1432
|
+
Schema.Struct({
|
|
1433
|
+
/**
|
|
1434
|
+
* 1 to 64 lowercase letters, digits and hyphens, with no leading, trailing
|
|
1435
|
+
* or consecutive hyphen. It names the directory holding `SKILL.md`.
|
|
1436
|
+
*/
|
|
1437
|
+
name: Schema.String.check(Schema.isMaxLength(64), Schema.isPattern(/^[a-z0-9]+(?:-[a-z0-9]+)*$/)),
|
|
1438
|
+
/**
|
|
1439
|
+
* What the skill does and when to use it, in 1 to 1024 characters.
|
|
1440
|
+
*/
|
|
1441
|
+
description: Schema.String.check(Schema.isNonEmpty(), Schema.isMaxLength(1024)),
|
|
1442
|
+
/**
|
|
1443
|
+
* The skill's license, or the name of a bundled license file.
|
|
1444
|
+
*/
|
|
1445
|
+
license: Schema.optionalKey(Schema.String),
|
|
1446
|
+
/**
|
|
1447
|
+
* Environment requirements, in 1 to 500 characters.
|
|
1448
|
+
*/
|
|
1449
|
+
compatibility: Schema.optionalKey(Schema.String.check(Schema.isNonEmpty(), Schema.isMaxLength(500))),
|
|
1450
|
+
/**
|
|
1451
|
+
* Additional properties not defined by the specification.
|
|
1452
|
+
*/
|
|
1453
|
+
metadata: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),
|
|
1454
|
+
/**
|
|
1455
|
+
* Space-separated tools the skill asks to run without approval. Hosts must
|
|
1456
|
+
* ignore it for a skill served over MCP unless the user approved the grant.
|
|
1457
|
+
*/
|
|
1458
|
+
"allowed-tools": Schema.optionalKey(Schema.String)
|
|
1459
|
+
}),
|
|
1460
|
+
[UnknownObject]
|
|
1461
|
+
)
|
|
1462
|
+
|
|
1463
|
+
/**
|
|
1464
|
+
* The YAML frontmatter of an Agent Skill's `SKILL.md`.
|
|
1465
|
+
*
|
|
1466
|
+
* @stability unstable
|
|
1467
|
+
* @category models
|
|
1468
|
+
* @since 4.1.1
|
|
1469
|
+
*/
|
|
1470
|
+
export type SkillFrontmatter = typeof SkillFrontmatter.Type
|
|
1471
|
+
|
|
1472
|
+
/**
|
|
1473
|
+
* Schema for an Agent Skill served under the MCP Skills extension (SEP-2640),
|
|
1474
|
+
* as `skills/list` and `skills/get` describe it.
|
|
1475
|
+
*
|
|
1476
|
+
* @stability unstable
|
|
1477
|
+
* @category schemas
|
|
1478
|
+
* @since 4.1.1
|
|
1479
|
+
*/
|
|
1480
|
+
export class Skill extends Schema.Class<Skill>(
|
|
1481
|
+
"@effect/ai/McpSchema/Skill"
|
|
1482
|
+
)({
|
|
1483
|
+
/**
|
|
1484
|
+
* The resource URI of the skill's `SKILL.md`.
|
|
1485
|
+
*/
|
|
1486
|
+
uri: Schema.String,
|
|
1487
|
+
/**
|
|
1488
|
+
* The YAML frontmatter of the skill's `SKILL.md`, as a JSON object, with
|
|
1489
|
+
* every field the author wrote. See {@link SkillFrontmatter} for the fields
|
|
1490
|
+
* the Agent Skills specification defines.
|
|
1491
|
+
*/
|
|
1492
|
+
frontmatter: Schema.StructWithRest(
|
|
1493
|
+
Schema.Struct({ name: Schema.String, description: Schema.String }),
|
|
1494
|
+
[UnknownObject]
|
|
1495
|
+
),
|
|
1496
|
+
/**
|
|
1497
|
+
* Every file of the skill, `SKILL.md` included, or `"dynamic"` when the
|
|
1498
|
+
* server generates the skill's content and publishes no digests.
|
|
1499
|
+
*/
|
|
1500
|
+
resources: Schema.Union([Schema.Array(SkillResource), Schema.Literal("dynamic")]),
|
|
1501
|
+
_meta: optional(UnknownObject)
|
|
1502
|
+
}) {}
|
|
1503
|
+
|
|
1385
1504
|
// =============================================================================
|
|
1386
1505
|
// Prompts
|
|
1387
1506
|
// =============================================================================
|
package/src/ai/McpServer.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import * as Arr from "../Array.ts"
|
|
15
15
|
import * as Cause from "../Cause.ts"
|
|
16
16
|
import * as Context from "../Context.ts"
|
|
17
|
+
import * as Digest from "../crypto/Digest.ts"
|
|
17
18
|
import * as Data from "../Data.ts"
|
|
18
19
|
import * as Deferred from "../Deferred.ts"
|
|
19
20
|
import * as Duration from "../Duration.ts"
|
|
@@ -53,6 +54,7 @@ import * as AiError from "./AiError.ts"
|
|
|
53
54
|
import * as McpCore from "./internal/mcpCore.ts"
|
|
54
55
|
import * as McpProtocolInternal from "./internal/mcpProtocol.ts"
|
|
55
56
|
import * as McpRuntime from "./internal/mcpRuntime.ts"
|
|
57
|
+
import * as McpSkills from "./internal/mcpSkills.ts"
|
|
56
58
|
import * as InternalStructuredOutput from "./internal/structured-output.ts"
|
|
57
59
|
import type * as McpProtocol from "./McpProtocol.ts"
|
|
58
60
|
import * as McpSchema from "./McpSchema.ts"
|
|
@@ -106,6 +108,8 @@ interface QueuedServerNotification {
|
|
|
106
108
|
|
|
107
109
|
const internalState = new WeakMap<object, {
|
|
108
110
|
readonly core: McpCore.McpCore
|
|
111
|
+
// Registers a skill's own file, skipping the check that reserves it for the skill.
|
|
112
|
+
readonly addSkillFile: McpServer["Service"]["addResource"]
|
|
109
113
|
readonly notifications: Queue.Dequeue<QueuedServerNotification>
|
|
110
114
|
readonly notificationDelivery: { consumers: number; listChangeRevision: number; requestEndpoints: number }
|
|
111
115
|
readonly globalMiddleware: Array<middleware.Fn<any, any, any>>
|
|
@@ -314,6 +318,9 @@ export type MiddlewareMethod =
|
|
|
314
318
|
| "resources/list"
|
|
315
319
|
| "resources/templates/list"
|
|
316
320
|
| "prompts/list"
|
|
321
|
+
| "skills/list"
|
|
322
|
+
| "skills/get"
|
|
323
|
+
| "resources/directory/read"
|
|
317
324
|
|
|
318
325
|
/**
|
|
319
326
|
* Request metadata passed to MCP middleware.
|
|
@@ -322,12 +329,17 @@ export type MiddlewareMethod =
|
|
|
322
329
|
*
|
|
323
330
|
* `target` is the tool name, the requested resource URI, or the prompt name.
|
|
324
331
|
* For `completion/complete` it is the prompt name, or the URI template of the
|
|
325
|
-
* resource template being completed. `
|
|
332
|
+
* resource template being completed. For `skills/get` it is the URI of the
|
|
333
|
+
* skill's `SKILL.md`. `payload` holds the request parameters.
|
|
326
334
|
*
|
|
327
335
|
* On list requests, middleware runs only when it wraps a {@link visibility},
|
|
328
336
|
* once for each registration it wraps. `target` is then the tool name, the
|
|
329
337
|
* resource URI, the URI template or the prompt name of that registration, and
|
|
330
|
-
* `payload` is `undefined`.
|
|
338
|
+
* `payload` is `undefined`. `skills/list` and `resources/directory/read` are
|
|
339
|
+
* list requests whose `target` is the `SKILL.md` URI of each skill; a
|
|
340
|
+
* directory read checks only the skills that have the directory. A skill's
|
|
341
|
+
* files are resources, checked as `resources/list` and `resources/read` with
|
|
342
|
+
* their own URIs.
|
|
331
343
|
*
|
|
332
344
|
* `headers` are the HTTP request headers. They are empty on stdio, on custom
|
|
333
345
|
* transports and for in-process calls. `sessionId` is the `Mcp-Session-Id`
|
|
@@ -759,7 +771,7 @@ const internalError = new InternalError({ message: "Internal error" })
|
|
|
759
771
|
const rejectWith = (error: McpErrorBase) =>
|
|
760
772
|
Effect.fail(new McpCore.RequestRejected({ code: error.code, message: error.message, data: error.data, error }))
|
|
761
773
|
|
|
762
|
-
type CallMethod = "tools/call" | "resources/read" | "prompts/get" | "completion/complete"
|
|
774
|
+
type CallMethod = "tools/call" | "resources/read" | "prompts/get" | "completion/complete" | "skills/get"
|
|
763
775
|
|
|
764
776
|
type ListMethod = Exclude<MiddlewareMethod, CallMethod>
|
|
765
777
|
|
|
@@ -768,6 +780,7 @@ interface NotFoundErrors {
|
|
|
768
780
|
readonly "resources/read": McpCore.ResourceNotFound
|
|
769
781
|
readonly "prompts/get": McpCore.PromptNotFound
|
|
770
782
|
readonly "completion/complete": InvalidParams
|
|
783
|
+
readonly "skills/get": McpCore.SkillNotFound
|
|
771
784
|
}
|
|
772
785
|
|
|
773
786
|
const notFound = (method: CallMethod, target: string): NotFoundErrors[CallMethod] => {
|
|
@@ -780,6 +793,8 @@ const notFound = (method: CallMethod, target: string): NotFoundErrors[CallMethod
|
|
|
780
793
|
return new McpCore.PromptNotFound({ name: target })
|
|
781
794
|
case "completion/complete":
|
|
782
795
|
return new InvalidParams({ message: "Unknown completion reference or argument" })
|
|
796
|
+
case "skills/get":
|
|
797
|
+
return new McpCore.SkillNotFound({ uri: target })
|
|
783
798
|
}
|
|
784
799
|
}
|
|
785
800
|
|
|
@@ -1127,6 +1142,38 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
1127
1142
|
))
|
|
1128
1143
|
})
|
|
1129
1144
|
|
|
1145
|
+
// Checking skill ownership and both writes happen in one synchronous step,
|
|
1146
|
+
// so a skill cannot claim the URI in between. Skill files skip the check.
|
|
1147
|
+
const addResource = (
|
|
1148
|
+
options: Parameters<McpServer["Service"]["addResource"]>[0],
|
|
1149
|
+
checkOwnership: boolean
|
|
1150
|
+
): Effect.Effect<void> =>
|
|
1151
|
+
Effect.suspend(() => {
|
|
1152
|
+
if (checkOwnership && internalCore.skills.ownsFile(options.resource.uri)) {
|
|
1153
|
+
return Effect.die(
|
|
1154
|
+
new Cause.IllegalArgumentError(`Resource ${options.resource.uri} is a file of a registered skill`)
|
|
1155
|
+
)
|
|
1156
|
+
}
|
|
1157
|
+
const existingIndex = resources.findIndex(({ resource }) => resource.uri === options.resource.uri)
|
|
1158
|
+
if (existingIndex === -1) {
|
|
1159
|
+
resources.push(options)
|
|
1160
|
+
} else {
|
|
1161
|
+
resources[existingIndex] = options
|
|
1162
|
+
}
|
|
1163
|
+
const { invoke, visible } = makeInvoke(options.services, globalMiddleware)
|
|
1164
|
+
internalCore.resources.register({
|
|
1165
|
+
descriptor: options.resource,
|
|
1166
|
+
visible: visible("resources/list", options.resource.uri),
|
|
1167
|
+
isVisible: isEnabled(options.annotations),
|
|
1168
|
+
read: (invocation) =>
|
|
1169
|
+
invoke(options.handle, invocation, {
|
|
1170
|
+
method: "resources/read",
|
|
1171
|
+
target: options.resource.uri,
|
|
1172
|
+
payload: { uri: options.resource.uri }
|
|
1173
|
+
})
|
|
1174
|
+
})
|
|
1175
|
+
return notifications.client["notifications/resources/list_changed"]({})
|
|
1176
|
+
})
|
|
1130
1177
|
const service = McpServer.of({
|
|
1131
1178
|
notifications: notifications.client,
|
|
1132
1179
|
notifyElicitationComplete: ({ clientId, elicitationId }) =>
|
|
@@ -1222,27 +1269,7 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
1222
1269
|
get resourceTemplates() {
|
|
1223
1270
|
return resourceTemplates
|
|
1224
1271
|
},
|
|
1225
|
-
addResource:
|
|
1226
|
-
const existingIndex = resources.findIndex(({ resource }) => resource.uri === options.resource.uri)
|
|
1227
|
-
if (existingIndex === -1) {
|
|
1228
|
-
resources.push(options)
|
|
1229
|
-
} else {
|
|
1230
|
-
resources[existingIndex] = options
|
|
1231
|
-
}
|
|
1232
|
-
const { invoke, visible } = makeInvoke(options.services, globalMiddleware)
|
|
1233
|
-
yield* internalCore.resources.register({
|
|
1234
|
-
descriptor: options.resource,
|
|
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 }
|
|
1242
|
-
})
|
|
1243
|
-
})
|
|
1244
|
-
yield* notifications.client["notifications/resources/list_changed"]({})
|
|
1245
|
-
}),
|
|
1272
|
+
addResource: (options) => addResource(options, true),
|
|
1246
1273
|
addResourceTemplate: Effect.fnUntraced(
|
|
1247
1274
|
function*({ annotations, completions, handle, routerPath, services, template }) {
|
|
1248
1275
|
const existingIndex = resourceTemplates.findIndex(({ template: current }) =>
|
|
@@ -1400,6 +1427,7 @@ export class McpServer extends Context.Service<McpServer, {
|
|
|
1400
1427
|
})
|
|
1401
1428
|
internalState.set(service, {
|
|
1402
1429
|
core: internalCore,
|
|
1430
|
+
addSkillFile: (options) => addResource(options, false),
|
|
1403
1431
|
notifications: notificationsQueue,
|
|
1404
1432
|
notificationDelivery,
|
|
1405
1433
|
globalMiddleware
|
|
@@ -1467,9 +1495,11 @@ export interface CacheHint {
|
|
|
1467
1495
|
*
|
|
1468
1496
|
* Each entry sets the freshness and `cacheScope` of one result kind:
|
|
1469
1497
|
* `server/discover`, `tools/list`, `resources/list`,
|
|
1470
|
-
* `resources/templates/list` and `prompts/list`.
|
|
1471
|
-
* `
|
|
1472
|
-
*
|
|
1498
|
+
* `resources/templates/list` and `prompts/list`. `skills` covers the Skills
|
|
1499
|
+
* extension's `skills/list`, `skills/get` and `resources/directory/read`.
|
|
1500
|
+
* Omitted entries, and `resources/read` results, keep
|
|
1501
|
+
* `{ ttlMs: 0, cacheScope: "private" }`. Earlier protocol revisions have no
|
|
1502
|
+
* cache hints and ignore this option.
|
|
1473
1503
|
*
|
|
1474
1504
|
* These are hints for clients. The server does not cache results, and
|
|
1475
1505
|
* visibility and invocation checks still run on every request.
|
|
@@ -1491,6 +1521,7 @@ export interface CacheHints {
|
|
|
1491
1521
|
readonly resources?: CacheHint | undefined
|
|
1492
1522
|
readonly resourceTemplates?: CacheHint | undefined
|
|
1493
1523
|
readonly prompts?: CacheHint | undefined
|
|
1524
|
+
readonly skills?: CacheHint | undefined
|
|
1494
1525
|
}
|
|
1495
1526
|
|
|
1496
1527
|
// Converts each hint to the wire's nonnegative integer `ttlMs` once, when the server is built.
|
|
@@ -3058,6 +3089,32 @@ export const registerToolkit: <Tools extends Record<string, Tool.Any>>(
|
|
|
3058
3089
|
? Effect.succeed(toolErrorResult(error.message))
|
|
3059
3090
|
: Effect.map(encodeFailure(error), (encoded) =>
|
|
3060
3091
|
new CallToolResult({ isError: true, content: toolResultContent(encoded) }))
|
|
3092
|
+
// `structuredContent` must be JSON and match the advertised `outputSchema`, which is derived from the JSON codec.
|
|
3093
|
+
// The Toolkit has already encoded the result, so only the JSON conversion of its encoded form runs here.
|
|
3094
|
+
const encodeStructured = Schema.encodeUnknownEffect(Schema.toCodecJson(Schema.toEncoded(tool.successSchema))) as (
|
|
3095
|
+
result: unknown
|
|
3096
|
+
) => Effect.Effect<unknown, Schema.SchemaError, Tool.HandlerServices<Tools[keyof Tools]>>
|
|
3097
|
+
const successResult = (result: unknown) =>
|
|
3098
|
+
encodeStructured(result).pipe(
|
|
3099
|
+
Effect.mapError((cause) =>
|
|
3100
|
+
AiError.make({
|
|
3101
|
+
module: "McpServer",
|
|
3102
|
+
method: "registerToolkit",
|
|
3103
|
+
reason: new AiError.ToolResultEncodingError({
|
|
3104
|
+
toolName: tool.name,
|
|
3105
|
+
toolResult: result,
|
|
3106
|
+
description: cause.message
|
|
3107
|
+
})
|
|
3108
|
+
})
|
|
3109
|
+
),
|
|
3110
|
+
Effect.map((structuredContent) =>
|
|
3111
|
+
new CallToolResult({
|
|
3112
|
+
isError: false,
|
|
3113
|
+
structuredContent,
|
|
3114
|
+
content: toolResultContent(structuredContent)
|
|
3115
|
+
})
|
|
3116
|
+
)
|
|
3117
|
+
)
|
|
3061
3118
|
const handleCause = (cause: Cause.Cause<unknown>) => {
|
|
3062
3119
|
const failure = Cause.findFail(cause)
|
|
3063
3120
|
if (Result.isSuccess(failure)) {
|
|
@@ -3113,21 +3170,27 @@ export const registerToolkit: <Tools extends Record<string, Tool.Any>>(
|
|
|
3113
3170
|
Stream.unwrap,
|
|
3114
3171
|
Stream.runLast,
|
|
3115
3172
|
Effect.flatMap(Effect.fromOption),
|
|
3116
|
-
Effect.flatMap((result)
|
|
3173
|
+
Effect.flatMap((result): Effect.Effect<
|
|
3174
|
+
CallToolResult,
|
|
3175
|
+
unknown,
|
|
3176
|
+
Tool.HandlerServices<Tools[keyof Tools]>
|
|
3177
|
+
> => {
|
|
3178
|
+
if (!result.isFailure) {
|
|
3179
|
+
// An absent result, such as `Schema.Void`, has no structured form.
|
|
3180
|
+
return result.encodedResult === undefined
|
|
3181
|
+
? Effect.succeed(new CallToolResult({ isError: false, content: [] }))
|
|
3182
|
+
: successResult(result.encodedResult)
|
|
3183
|
+
}
|
|
3117
3184
|
// Declared failures return their encoded payload; anything else is classified by origin.
|
|
3118
|
-
|
|
3119
|
-
? Effect.
|
|
3185
|
+
return result.failureOrigin === "handler"
|
|
3186
|
+
? Effect.succeed(
|
|
3187
|
+
new CallToolResult({ isError: true, content: toolResultContent(result.encodedResult) })
|
|
3188
|
+
)
|
|
3189
|
+
: Effect.failCause(Cause.annotate(
|
|
3120
3190
|
Cause.fail(result.result),
|
|
3121
3191
|
Context.make(Toolkit.FailureOrigin, result.failureOrigin ?? "result")
|
|
3122
3192
|
))
|
|
3123
|
-
|
|
3124
|
-
new CallToolResult({
|
|
3125
|
-
isError: result.isFailure,
|
|
3126
|
-
structuredContent: result.isFailure ? undefined : result.encodedResult,
|
|
3127
|
-
content: toolResultContent(result.encodedResult)
|
|
3128
|
-
})
|
|
3129
|
-
)
|
|
3130
|
-
),
|
|
3193
|
+
}),
|
|
3131
3194
|
Effect.catchCause(handleCause)
|
|
3132
3195
|
) as Effect.Effect<CallToolResult, InvalidParams, never>
|
|
3133
3196
|
},
|
|
@@ -3565,6 +3628,166 @@ export const resource: {
|
|
|
3565
3628
|
return (options: any) => Layer.effectDiscard(register(options))
|
|
3566
3629
|
} as any
|
|
3567
3630
|
|
|
3631
|
+
/**
|
|
3632
|
+
* A file of a skill: its text, its bytes, or either with an explicit MIME
|
|
3633
|
+
* type. Without one, the MIME type is inferred from the file extension.
|
|
3634
|
+
*
|
|
3635
|
+
* @stability unstable
|
|
3636
|
+
* @category models
|
|
3637
|
+
* @since 4.1.1
|
|
3638
|
+
*/
|
|
3639
|
+
export type SkillFile = string | Uint8Array | {
|
|
3640
|
+
readonly content: string | Uint8Array
|
|
3641
|
+
readonly mimeType?: string | undefined
|
|
3642
|
+
}
|
|
3643
|
+
|
|
3644
|
+
/**
|
|
3645
|
+
* Options for {@link registerSkill} and {@link skill}.
|
|
3646
|
+
*
|
|
3647
|
+
* **Details**
|
|
3648
|
+
*
|
|
3649
|
+
* `uri` addresses the skill's `SKILL.md`, as
|
|
3650
|
+
* `skill://<skill-path>/SKILL.md`. Its last path segment is the skill's name,
|
|
3651
|
+
* which must equal the frontmatter's `name`. Other schemes work the same way.
|
|
3652
|
+
*
|
|
3653
|
+
* `files` maps paths relative to the skill root to the skill's files,
|
|
3654
|
+
* `SKILL.md` included. Files are served verbatim, and the skill's listed
|
|
3655
|
+
* frontmatter is parsed from `SKILL.md`. Quote frontmatter strings that a YAML
|
|
3656
|
+
* 1.1 parser reads as another type, such as `yes` or `1.0`.
|
|
3657
|
+
*
|
|
3658
|
+
* @stability unstable
|
|
3659
|
+
* @category models
|
|
3660
|
+
* @since 4.1.1
|
|
3661
|
+
*/
|
|
3662
|
+
export interface SkillOptions {
|
|
3663
|
+
readonly uri: string
|
|
3664
|
+
readonly files: { readonly "SKILL.md": SkillFile } & Readonly<Record<string, SkillFile>>
|
|
3665
|
+
readonly annotations?: Context.Context<never> | undefined
|
|
3666
|
+
}
|
|
3667
|
+
|
|
3668
|
+
const skillFileEncoder = new TextEncoder()
|
|
3669
|
+
|
|
3670
|
+
// Bytes are copied so that later changes by the caller cannot alter what is
|
|
3671
|
+
// served under the listed digest. `Buffer#slice` shares memory, so copy with
|
|
3672
|
+
// the `Uint8Array` constructor.
|
|
3673
|
+
const toSkillSourceFile = (path: string, file: SkillFile): McpSkills.SourceFile => {
|
|
3674
|
+
const { content, mimeType } = typeof file === "string" || file instanceof Uint8Array ? { content: file } : file
|
|
3675
|
+
return {
|
|
3676
|
+
path,
|
|
3677
|
+
content: typeof content === "string" ? skillFileEncoder.encode(content) : new Uint8Array(content),
|
|
3678
|
+
mimeType
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
|
|
3682
|
+
/**
|
|
3683
|
+
* Registers an Agent Skill under the MCP Skills extension (SEP-2640).
|
|
3684
|
+
*
|
|
3685
|
+
* **Details**
|
|
3686
|
+
*
|
|
3687
|
+
* Each file is registered as a resource, and the skill is listed by
|
|
3688
|
+
* `skills/list` and `skills/get` with a manifest of every file's SHA-256
|
|
3689
|
+
* digest and size, computed from the bytes served. Every directory of the
|
|
3690
|
+
* skill namespace is readable with `resources/directory/read`. The server
|
|
3691
|
+
* declares the extension once any skill is registered.
|
|
3692
|
+
*
|
|
3693
|
+
* Fails when the skill breaks the extension's rules: a name that does not
|
|
3694
|
+
* match the URI, a `SKILL.md` without YAML frontmatter, frontmatter that is
|
|
3695
|
+
* not valid {@link McpSchema.SkillFrontmatter}, a file path that leaves the
|
|
3696
|
+
* skill, or more than 512 files or 16 MiB in all. It also fails when the
|
|
3697
|
+
* skill's directory contains or lies inside another skill's, or one of its
|
|
3698
|
+
* files is already registered as a resource. Registering a resource at the
|
|
3699
|
+
* URI of a skill's file afterwards is a defect.
|
|
3700
|
+
*
|
|
3701
|
+
* SHA-256 runs on `crypto.subtle`.
|
|
3702
|
+
*
|
|
3703
|
+
* @see {@link skill} for the layer-based skill registration wrapper
|
|
3704
|
+
*
|
|
3705
|
+
* @stability unstable
|
|
3706
|
+
* @category handlers
|
|
3707
|
+
* @since 4.1.1
|
|
3708
|
+
*/
|
|
3709
|
+
export const registerSkill: (options: SkillOptions) => Effect.Effect<void, Cause.IllegalArgumentError, McpServer> =
|
|
3710
|
+
Effect.fnUntraced(function*(options: SkillOptions) {
|
|
3711
|
+
const files = Object.entries(options.files).map(([path, file]) => toSkillSourceFile(path, file))
|
|
3712
|
+
const skill = yield* McpSkills.makeStaticSkill({ uri: options.uri, files }).pipe(
|
|
3713
|
+
Effect.provide(Digest.layerWebCrypto)
|
|
3714
|
+
)
|
|
3715
|
+
|
|
3716
|
+
const registry = yield* McpServer
|
|
3717
|
+
const { addSkillFile, core, globalMiddleware } = internalState.get(registry)!
|
|
3718
|
+
const services = yield* Effect.context<never>()
|
|
3719
|
+
const annotations = options.annotations ?? Context.empty()
|
|
3720
|
+
const { invoke, visible } = makeInvoke(services, globalMiddleware)
|
|
3721
|
+
// Claims the skill's file URIs before they are registered as resources.
|
|
3722
|
+
yield* core.skills.register({
|
|
3723
|
+
entry: skill.entry,
|
|
3724
|
+
isVisible: isEnabled(annotations),
|
|
3725
|
+
visible: visible("skills/list", options.uri),
|
|
3726
|
+
visibleInDirectories: visible("resources/directory/read", options.uri),
|
|
3727
|
+
get: (invocation) =>
|
|
3728
|
+
invoke(Effect.succeed(skill.entry), invocation, {
|
|
3729
|
+
method: "skills/get",
|
|
3730
|
+
target: options.uri,
|
|
3731
|
+
payload: { uri: options.uri }
|
|
3732
|
+
}),
|
|
3733
|
+
directory: (uri) => skill.directories.get(uri)
|
|
3734
|
+
})
|
|
3735
|
+
for (const { content, resource, text } of skill.files) {
|
|
3736
|
+
const { uri, mimeType } = resource
|
|
3737
|
+
yield* addSkillFile({
|
|
3738
|
+
resource: new Resource(resource),
|
|
3739
|
+
annotations,
|
|
3740
|
+
services,
|
|
3741
|
+
handle: Effect.succeed({
|
|
3742
|
+
contents: [text === undefined ? { uri, mimeType, blob: content } : { uri, mimeType, text }]
|
|
3743
|
+
})
|
|
3744
|
+
})
|
|
3745
|
+
}
|
|
3746
|
+
})
|
|
3747
|
+
|
|
3748
|
+
/**
|
|
3749
|
+
* Creates a layer that registers an Agent Skill under the MCP Skills extension
|
|
3750
|
+
* (SEP-2640).
|
|
3751
|
+
*
|
|
3752
|
+
* **Example** (Publishing a skill with a supporting file)
|
|
3753
|
+
*
|
|
3754
|
+
* ```ts
|
|
3755
|
+
* import { Layer } from "effect"
|
|
3756
|
+
* import { McpProtocol, McpServer } from "effect/ai"
|
|
3757
|
+
*
|
|
3758
|
+
* const RefundsSkill = McpServer.skill({
|
|
3759
|
+
* uri: "skill://billing/refunds/SKILL.md",
|
|
3760
|
+
* files: {
|
|
3761
|
+
* "SKILL.md": `---
|
|
3762
|
+
* name: refunds
|
|
3763
|
+
* description: Process refunds using the billing tools
|
|
3764
|
+
* ---
|
|
3765
|
+
*
|
|
3766
|
+
* Read references/policy.md before issuing a refund.
|
|
3767
|
+
* `,
|
|
3768
|
+
* "references/policy.md": "Refunds above CHF 500 require manager approval.\n"
|
|
3769
|
+
* }
|
|
3770
|
+
* })
|
|
3771
|
+
*
|
|
3772
|
+
* const ServerLayer = RefundsSkill.pipe(
|
|
3773
|
+
* Layer.provide(McpServer.layerHttp({
|
|
3774
|
+
* name: "billing",
|
|
3775
|
+
* version: "1.0.0",
|
|
3776
|
+
* path: "/mcp",
|
|
3777
|
+
* protocols: [McpProtocol.v2026_07_28, McpProtocol.v2025_11_25]
|
|
3778
|
+
* }))
|
|
3779
|
+
* )
|
|
3780
|
+
* ```
|
|
3781
|
+
*
|
|
3782
|
+
* @see {@link registerSkill} for the Effect-level skill registration API
|
|
3783
|
+
*
|
|
3784
|
+
* @stability unstable
|
|
3785
|
+
* @category layers
|
|
3786
|
+
* @since 4.1.1
|
|
3787
|
+
*/
|
|
3788
|
+
export const skill = (options: SkillOptions): Layer.Layer<never, Cause.IllegalArgumentError, McpServer> =>
|
|
3789
|
+
Layer.effectDiscard(registerSkill(options))
|
|
3790
|
+
|
|
3568
3791
|
/**
|
|
3569
3792
|
* Registers an MCP prompt from an Effect program.
|
|
3570
3793
|
*
|
package/src/ai/Prompt.ts
CHANGED
|
@@ -486,6 +486,16 @@ export interface FilePartEncoded extends BasePartEncoded<"file", FilePartOptions
|
|
|
486
486
|
*/
|
|
487
487
|
export interface FilePartOptions extends ProviderOptions {}
|
|
488
488
|
|
|
489
|
+
const TaggedURL = Schema.TaggedStruct("URL", { href: Schema.URLFromString })
|
|
490
|
+
|
|
491
|
+
const urlFromTaggedURL = SchemaTransformation.transform<URL, typeof TaggedURL.Type>({
|
|
492
|
+
decode: ({ href }) => href,
|
|
493
|
+
encode: (href) => (({
|
|
494
|
+
_tag: "URL",
|
|
495
|
+
href
|
|
496
|
+
}) as const)
|
|
497
|
+
})
|
|
498
|
+
|
|
489
499
|
/**
|
|
490
500
|
* Schema for validation and encoding of file parts.
|
|
491
501
|
*
|
|
@@ -535,18 +545,13 @@ export const FilePart: Schema.Struct<{
|
|
|
535
545
|
}) as const)
|
|
536
546
|
})
|
|
537
547
|
)),
|
|
538
|
-
Schema.
|
|
539
|
-
Schema.URL,
|
|
540
|
-
SchemaTransformation.transform({
|
|
541
|
-
decode: ({ href }) => href,
|
|
542
|
-
encode: (href) => (({
|
|
543
|
-
_tag: "URL",
|
|
544
|
-
href
|
|
545
|
-
}) as const)
|
|
546
|
-
})
|
|
547
|
-
)),
|
|
548
|
+
TaggedURL.pipe(Schema.decodeTo(Schema.URL, urlFromTaggedURL)),
|
|
548
549
|
Schema.Uint8Array,
|
|
549
|
-
|
|
550
|
+
// Under the JSON and binary codecs a raw `URL` would serialize as a plain
|
|
551
|
+
// string, indistinguishable from the `Schema.String` member. Serialize it
|
|
552
|
+
// through the same `TaggedURL` instance instead, so the codecs see a
|
|
553
|
+
// single `{ _tag: "URL" }` member rather than two
|
|
554
|
+
Schema.URL.annotate({ toCodecJson: () => Schema.link<URL>()(TaggedURL, urlFromTaggedURL) })
|
|
550
555
|
])
|
|
551
556
|
}).annotate({ identifier: "FilePart" })
|
|
552
557
|
|
package/src/ai/Tool.ts
CHANGED
|
@@ -2096,8 +2096,9 @@ export const getStrictMode = <T extends Any>(tool: T): boolean | undefined => Co
|
|
|
2096
2096
|
//
|
|
2097
2097
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
2098
2098
|
|
|
2099
|
-
|
|
2100
|
-
|
|
2099
|
+
// Text that may decode to a forbidden key. `JSON.parse` decodes Unicode
|
|
2100
|
+
// escapes (e.g. `"\u005f_proto__"`), so any `\u` escape also triggers the scan.
|
|
2101
|
+
const suspectRx = /"__proto__"\s*:|"constructor"\s*:|\\u/
|
|
2101
2102
|
|
|
2102
2103
|
function _parse(text: string) {
|
|
2103
2104
|
// Parse normally
|
|
@@ -2108,15 +2109,8 @@ function _parse(text: string) {
|
|
|
2108
2109
|
return obj
|
|
2109
2110
|
}
|
|
2110
2111
|
|
|
2111
|
-
if (
|
|
2112
|
-
suspectProtoRx.test(text) === false &&
|
|
2113
|
-
suspectConstructorRx.test(text) === false
|
|
2114
|
-
) {
|
|
2115
|
-
return obj
|
|
2116
|
-
}
|
|
2117
|
-
|
|
2118
2112
|
// Scan result for proto keys
|
|
2119
|
-
return filter(obj)
|
|
2113
|
+
return suspectRx.test(text) ? filter(obj) : obj
|
|
2120
2114
|
}
|
|
2121
2115
|
|
|
2122
2116
|
function filter(obj: any) {
|
|
@@ -2133,6 +2127,7 @@ function filter(obj: any) {
|
|
|
2133
2127
|
|
|
2134
2128
|
if (
|
|
2135
2129
|
Object.hasOwn(node, "constructor") &&
|
|
2130
|
+
node.constructor !== null &&
|
|
2136
2131
|
Object.hasOwn(node.constructor, "prototype")
|
|
2137
2132
|
) {
|
|
2138
2133
|
throw new SyntaxError("Object contains forbidden prototype property")
|