Skip to content

Commit 9692443

Browse files
rafaeljustoclaude
andcommitted
Extract twapi.Date schema override into helpers.WithDateTypeSchema
The twapi.Date JSON-schema override was inlined in the task get/list output schemas, but the same Task struct (with its twapi.Date startDate/ dueDate fields) is also sideloaded into the calendar event list and search responses, which were generating the wrong schema for those nested dates. Extract the override into a reusable helpers.WithDateTypeSchema option and apply it to all four response schemas: task get/list, calendar event list, and search. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d94645e commit 9692443

4 files changed

Lines changed: 43 additions & 19 deletions

File tree

internal/helpers/schema_date.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package helpers
2+
3+
import (
4+
"reflect"
5+
6+
"github.com/google/jsonschema-go/jsonschema"
7+
twapi "github.com/teamwork/twapi-go-sdk"
8+
)
9+
10+
// WithDateTypeSchema registers a JSON-schema override for the twapi.Date type
11+
// on the given generation options. twapi.Date is defined as `type Date
12+
// time.Time`, so the reflection-based generator would otherwise emit a useless
13+
// object schema for time.Time's unexported fields. This override forces it to a
14+
// nullable, date-only string.
15+
//
16+
// Use it whenever generating an output schema from a response type that carries
17+
// (or sideloads) twapi.Date fields:
18+
//
19+
// schema, err = jsonschema.For[Response](helpers.WithDateTypeSchema(&jsonschema.ForOptions{}))
20+
//
21+
// Any other options already set on opts (including pre-existing TypeSchemas
22+
// entries) are preserved. The options value is modified in place and also
23+
// returned for convenient chaining.
24+
func WithDateTypeSchema(opts *jsonschema.ForOptions) *jsonschema.ForOptions {
25+
if opts == nil {
26+
opts = &jsonschema.ForOptions{}
27+
}
28+
if opts.TypeSchemas == nil {
29+
opts.TypeSchemas = make(map[reflect.Type]*jsonschema.Schema)
30+
}
31+
opts.TypeSchemas[reflect.TypeFor[twapi.Date]()] = &jsonschema.Schema{
32+
Types: []string{"null", "string"},
33+
Format: "date",
34+
Description: "Null or date-only date string",
35+
}
36+
return opts
37+
}

internal/twprojects/calendar_events.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ func init() {
2929
var err error
3030

3131
// generate the output schema only once
32-
calendarEventListOutputSchema, err = jsonschema.For[projects.CalendarEventListResponse](&jsonschema.ForOptions{})
32+
calendarEventListOutputSchema, err = jsonschema.For[projects.CalendarEventListResponse](
33+
helpers.WithDateTypeSchema(&jsonschema.ForOptions{}),
34+
)
3335
if err != nil {
3436
panic(fmt.Sprintf("failed to generate JSON schema for CalendarEventListResponse: %v", err))
3537
}

internal/twprojects/search.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ var (
2828
func init() {
2929
var err error
3030

31-
searchOutputSchema, err = jsonschema.For[projects.SearchResponse](&jsonschema.ForOptions{})
31+
searchOutputSchema, err = jsonschema.For[projects.SearchResponse](helpers.WithDateTypeSchema(&jsonschema.ForOptions{}))
3232
if err != nil {
3333
panic(fmt.Sprintf("failed to generate JSON schema for SearchResponse: %v", err))
3434
}

internal/twprojects/tasks.go

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77
"io"
88
"net/http"
9-
"reflect"
109

1110
"github.com/google/jsonschema-go/jsonschema"
1211
"github.com/modelcontextprotocol/go-sdk/mcp"
@@ -38,26 +37,12 @@ func init() {
3837
var err error
3938

4039
// generate the output schemas only once
41-
taskGetOutputSchema, err = jsonschema.For[projects.TaskGetResponse](&jsonschema.ForOptions{
42-
TypeSchemas: map[reflect.Type]*jsonschema.Schema{
43-
reflect.TypeFor[twapi.Date](): {
44-
Types: []string{"null", "string"},
45-
Description: "Null or date-only date string",
46-
},
47-
},
48-
})
40+
taskGetOutputSchema, err = jsonschema.For[projects.TaskGetResponse](helpers.WithDateTypeSchema(&jsonschema.ForOptions{}))
4941
if err != nil {
5042
panic(fmt.Sprintf("failed to generate JSON schema for TaskGetResponse: %v", err))
5143
}
5244
helpers.WithMetaWebLinkSchema(taskGetOutputSchema)
53-
taskListOutputSchema, err = jsonschema.For[projects.TaskListResponse](&jsonschema.ForOptions{
54-
TypeSchemas: map[reflect.Type]*jsonschema.Schema{
55-
reflect.TypeFor[twapi.Date](): {
56-
Types: []string{"null", "string"},
57-
Description: "Null or date-only date string",
58-
},
59-
},
60-
})
45+
taskListOutputSchema, err = jsonschema.For[projects.TaskListResponse](helpers.WithDateTypeSchema(&jsonschema.ForOptions{}))
6146
if err != nil {
6247
panic(fmt.Sprintf("failed to generate JSON schema for TaskListResponse: %v", err))
6348
}

0 commit comments

Comments
 (0)