Skip to content

Commit 1777aaf

Browse files
authored
add ConstSchema type (#378)
This adds support for creating `{"const": someValue}` schema objects ([spec](https://json-schema.org/understanding-json-schema/reference/const)). These are useful if you want to create a top level `oneOf` object, for instance for meta tools, where you want a `command` field with custom arguments for each command, and you don't want to rely on just descriptions of all the possible arguments and which commands they are valid for.
1 parent 5a9ffc7 commit 1777aaf

File tree

3 files changed

+56
-8
lines changed

3 files changed

+56
-8
lines changed

pkgs/dart_mcp/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
- Does **not** add support for Tasks yet.
2323
- Added support for `EnumSchema` subtypes, matching the spec. This includes
2424
multi select enums and enums with titles. Validation is also supported.
25-
- Added support for validating `const` fields in schemas.
25+
- Added `ConstSchema` type for constant values, with validation.
2626
- **BREAKING**:
2727
- Change many fields of `ResourceLink` to be nullable, and their associated
2828
parameters to be optional. This brings us in line with the specification.

pkgs/dart_mcp/lib/src/api/tools.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,6 +1938,34 @@ extension type ListSchema.fromMap(Map<String, Object?> _value)
19381938
}
19391939
}
19401940

1941+
/// A JSON Schema definition for a constant value.
1942+
extension type ConstSchema.fromMap(Map<String, Object?> _value)
1943+
implements Schema {
1944+
factory ConstSchema({
1945+
String? title,
1946+
String? description,
1947+
required Object? constValue,
1948+
}) => ConstSchema.fromMap({
1949+
Keys.const_: constValue,
1950+
if (title != null) Keys.title: title,
1951+
if (description != null) Keys.description: description,
1952+
});
1953+
1954+
/// The constant value for this schema.
1955+
Object? get constValue {
1956+
if (!_value.containsKey(Keys.const_)) {
1957+
throw ArgumentError('Missing ${Keys.const_} field in $ConstSchema');
1958+
}
1959+
return _value[Keys.const_];
1960+
}
1961+
1962+
/// The human readable title for this constant value.
1963+
String? get title => _value[Keys.title] as String?;
1964+
1965+
/// The description for this constant value.
1966+
String? get description => _value[Keys.description] as String?;
1967+
}
1968+
19411969
HashSet<ValidationError> _createHashSet() {
19421970
return HashSet<ValidationError>(
19431971
equals: (ValidationError a, ValidationError b) {

pkgs/dart_mcp/test/api/tools_test.dart

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,22 @@ void main() {
321321
],
322322
});
323323
});
324+
325+
test('ConstSchema', () {
326+
final schema = ConstSchema(
327+
constValue: 12,
328+
title: 'twelve',
329+
description: 'its the number 12!',
330+
);
331+
expect(schema, {
332+
'const': 12,
333+
'title': 'twelve',
334+
'description': 'its the number 12!',
335+
});
336+
expect(schema.constValue, 12);
337+
expect(schema.title, 'twelve');
338+
expect(schema.description, 'its the number 12!');
339+
});
324340
});
325341

326342
group('Schema Validation Tests (Paths Ignored)', () {
@@ -1708,7 +1724,7 @@ void main() {
17081724
});
17091725

17101726
test('const', () {
1711-
final schema = {'const': 'hello'} as Schema;
1727+
final schema = ConstSchema(constValue: 'hello');
17121728
expectFailuresMatch(schema, 'hello', []);
17131729
expectFailuresMatch(schema, 'world', [
17141730
ValidationError(ValidationErrorType.wrongConstValue, path: const []),
@@ -1759,25 +1775,29 @@ void main() {
17591775
final schema = ObjectSchema(
17601776
properties: {
17611777
'user': ObjectSchema(
1762-
properties: {'name': StringSchema(minLength: 5)},
1778+
properties: {
1779+
'name': StringSchema(minLength: 5),
1780+
'isUser': ConstSchema(constValue: true),
1781+
},
17631782
),
17641783
},
17651784
);
1766-
// 'user.name' is "hi" which fails minLength: 5
1767-
// _validate(StringSchema(minLength:5), "hi") -> [minLengthNotMet]
1768-
// This becomes propertyValueInvalid for 'name'
1769-
// Then this becomes propertyValueInvalid for 'user'
17701785
expectFailuresExact(
17711786
schema,
17721787
{
1773-
'user': {'name': 'hi'},
1788+
'user': {'name': 'hi', 'isUser': false},
17741789
},
17751790
[
17761791
ValidationError(
17771792
ValidationErrorType.minLengthNotMet,
17781793
path: ['user', 'name'],
17791794
details: 'String "hi" is not at least 5 characters long',
17801795
),
1796+
ValidationError(
1797+
ValidationErrorType.wrongConstValue,
1798+
path: ['user', 'isUser'],
1799+
details: 'Value false does not match constant true',
1800+
),
17811801
],
17821802
);
17831803
});

0 commit comments

Comments
 (0)