You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* feat(mcp): support taxonomies field on content_create/content_update (emdash-cms#953)
Extends MCP content_create and content_update (and the matching REST
POST/PUT bodies) with a `taxonomies` field of shape
`{ [taxonomyName]: [termSlug, ...] }`. Term slugs are resolved in the
entry's locale via `TaxonomyRepository.findBySlug` and persisted through
`setTermsForEntry` in the same transaction as the content write, so
crash recovery leaves either both changes or neither.
- Unknown slug or wrong-shape input surfaces a VALIDATION_ERROR from the
handler; the entire content write rolls back.
- Update semantics match `setTermsForEntry`: only the taxonomies named in
the payload are touched, and an empty array clears one taxonomy.
- Reuses the exact code path the `/terms/{taxonomy}` REST route uses so
the two entry points can't drift, including the `invalidateTermCache`
side-effect for the hasAny hydration cache.
Prior behaviour: `taxonomies` was silently accepted and ignored, forcing
agents to make N follow-up REST calls per taxonomy after every create.
* fix(mcp): address review feedback on taxonomies field
- Point content_create's taxonomies description at taxonomy_list_terms
(the real tool) instead of the non-existent term_list, so agents can
self-serve slug lookups.
- Rewrite the stale hasAnyTermAssignments cache comment on the helper's
invalidateTermCache call to name what actually gets bumped (the
taxonomy object cache).
Copy file name to clipboardExpand all lines: packages/core/src/mcp/server.ts
+19Lines changed: 19 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -660,6 +660,12 @@ export function createMcpServer(): McpServer {
660
660
.describe(
661
661
"Bylines to credit. Each entry references an existing byline by id (see byline_list / byline_create) with an optional roleLabel. The first entry becomes the primary byline.",
662
662
),
663
+
taxonomies: z
664
+
.record(z.string(),z.array(z.string()))
665
+
.optional()
666
+
.describe(
667
+
"Taxonomy term assignments as { taxonomyName: [termSlug, ...] }. Term slugs are resolved in the entry's locale. Call taxonomy_list to see available taxonomies and taxonomy_list_terms to look up slugs. Missing keys leave that taxonomy unchanged.",
668
+
),
663
669
}),
664
670
annotations: {destructiveHint: false},
665
671
},
@@ -698,6 +704,7 @@ export function createMcpServer(): McpServer {
698
704
locale: args.locale,
699
705
translationOf: args.translationOf,
700
706
bylines: args.bylines,
707
+
taxonomies: args.taxonomies,
701
708
});
702
709
if(!result.success)returnunwrap(result);
703
710
constitemId=extractContentId(result.data);
@@ -715,6 +722,7 @@ export function createMcpServer(): McpServer {
715
722
locale: args.locale,
716
723
translationOf: args.translationOf,
717
724
bylines: args.bylines,
725
+
taxonomies: args.taxonomies,
718
726
}),
719
727
);
720
728
},
@@ -771,6 +779,12 @@ export function createMcpServer(): McpServer {
771
779
.describe(
772
780
"Replace the byline list for this item. The first entry becomes the primary byline. Pass an empty array to clear all bylines.",
773
781
),
782
+
taxonomies: z
783
+
.record(z.string(),z.array(z.string()))
784
+
.optional()
785
+
.describe(
786
+
"Replace taxonomy term assignments as { taxonomyName: [termSlug, ...] }. Term slugs are resolved in the entry's locale. Only named taxonomies are touched; other taxonomies are left unchanged. Pass an empty array to clear a taxonomy.",
787
+
),
774
788
publishedAt: z.iso
775
789
.datetime({offset: true,message: "must be an ISO 8601 datetime"})
776
790
.nullish()
@@ -823,6 +837,7 @@ export function createMcpServer(): McpServer {
0 commit comments