@rolino/mcp 0.3.0 → 0.5.0-beta.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.
@@ -6,6 +6,21 @@ import { stat } from "fs/promises";
6
6
  import { basename, extname, resolve } from "path";
7
7
  import {
8
8
  ActorSchema,
9
+ AgentBlogArticleDetailSchema,
10
+ AgentBlogArticleSchema,
11
+ AgentBlogDraftUpdateSchema,
12
+ AgentBlogJobSchema,
13
+ AgentBlogConnectionExecuteSchema,
14
+ AgentBlogConnectionPreviewSchema,
15
+ AgentBlogConnectionStatusSchema,
16
+ AgentBlogPlanItemSchema,
17
+ AgentBlogPlanSchema,
18
+ AgentBlogPublishPreviewSchema,
19
+ AgentBlogPublishingProviderSchema,
20
+ AgentBlogPublishingDestinationSchema,
21
+ AgentBlogDeliveryAttemptSchema,
22
+ AgentBlogRevalidationTestSchema,
23
+ AgentBlogSetupStatusSchema,
9
24
  CalendarListDataSchema,
10
25
  DraftPostInputSchema,
11
26
  DraftPostUpdateInputSchema,
@@ -24,7 +39,15 @@ import {
24
39
  ProjectListDataSchema,
25
40
  ProjectSchema,
26
41
  ProviderDeliveryOptionsProviderSchema,
27
- ProviderDeliveryOptionsSchema
42
+ ProviderDeliveryOptionsSchema,
43
+ SeoEntityIdSchema,
44
+ SeoExpectedImpactSchema,
45
+ SeoOpportunityKindSchema,
46
+ SeoOpportunityListDataSchema,
47
+ SeoOpportunitySchema,
48
+ SeoReportCompletenessSchema,
49
+ SeoReportListDataSchema,
50
+ SeoReportSchema
28
51
  } from "@rolino/contracts";
29
52
  import {
30
53
  RolinoApiError,
@@ -36,7 +59,7 @@ import * as z from "zod/v4";
36
59
  // package.json
37
60
  var package_default = {
38
61
  name: "@rolino/mcp",
39
- version: "0.3.0",
62
+ version: "0.5.0-beta.0",
40
63
  description: "Model Context Protocol server for Rolino",
41
64
  type: "module",
42
65
  license: "MIT",
@@ -95,9 +118,9 @@ var package_default = {
95
118
  dependencies: {
96
119
  "@hono/node-server": "2.0.12",
97
120
  "@modelcontextprotocol/sdk": "^1.30.0",
98
- "@rolino/contracts": "0.3.0",
99
- "@rolino/local-auth": "0.3.0",
100
- "@rolino/sdk": "0.3.0",
121
+ "@rolino/contracts": "0.5.0-beta.0",
122
+ "@rolino/local-auth": "0.5.0-beta.0",
123
+ "@rolino/sdk": "0.5.0-beta.0",
101
124
  zod: "^4.4.3"
102
125
  },
103
126
  devDependencies: {
@@ -146,12 +169,20 @@ var ProviderDeliveryOptionsToolSchema = z.object({
146
169
  stitch: z.object({ available: z.boolean() }).strict()
147
170
  }).strict().optional(),
148
171
  maxVideoDurationMs: z.number().int().positive().nullable().optional(),
149
- supportedPostTypes: z.array(z.enum(["TEXT", "SINGLE_IMAGE", "MULTI_IMAGE"])).optional(),
172
+ supportedPostTypes: z.array(z.enum(["TEXT", "SINGLE_IMAGE", "MULTI_IMAGE", "VIDEO"])).optional(),
150
173
  image: z.object({
151
174
  maxItems: z.number().int().positive(),
152
175
  mimeTypes: z.array(z.enum(["image/jpeg", "image/png"])),
153
176
  maxPixelsExclusive: z.number().int().positive(),
154
177
  maxBytes: z.number().int().positive()
178
+ }).strict().optional(),
179
+ video: z.object({
180
+ maxItems: z.number().int().positive(),
181
+ mimeTypes: z.array(z.literal("video/mp4")),
182
+ minBytes: z.number().int().positive(),
183
+ maxBytes: z.number().int().positive(),
184
+ minDurationMs: z.number().int().positive(),
185
+ maxDurationMs: z.number().int().positive()
155
186
  }).strict().optional()
156
187
  }).strict().superRefine((value, context) => {
157
188
  if (!ProviderDeliveryOptionsSchema.safeParse(value).success) {
@@ -166,7 +197,7 @@ var mcpDraftInputShape = {
166
197
  ...DraftPostInputSchema.shape,
167
198
  platforms: DraftPostInputSchema.shape.platforms,
168
199
  mediaAssetIds: DraftPostInputSchema.shape.mediaAssetIds.describe(
169
- "Existing project media asset IDs. YouTube requires exactly one stored video; Bluesky accepts up to four stored JPEG, PNG, or WebP images."
200
+ "Existing project media asset IDs. YouTube requires exactly one stored video; LinkedIn accepts one MP4 video or up to ten JPEG/PNG images; Bluesky accepts up to four stored JPEG, PNG, or WebP images."
170
201
  ),
171
202
  captionOverrides: DraftPostInputSchema.shape.captionOverrides.describe(
172
203
  "Optional destination text. YOUTUBE is the video description, BLUESKY is limited to 300 graphemes, and LINKEDIN is limited to 3,000 characters; when omitted, Rolino uses the shared caption."
@@ -175,7 +206,7 @@ var mcpDraftInputShape = {
175
206
  "TikTok delivery choices. First call refresh_delivery_options, then supply an explicit mode. Direct publishing requires an exact returned visibility plus yes/no choices for comments, duet, stitch, commercial content, own-brand promotion, third-party promotion, and AI-generated content. Set reviewed only after reviewing the account-specific options; this is distinct from draft mutation consent."
176
207
  ),
177
208
  youtubeSettings: DraftPostInputSchema.shape.youtubeSettings.describe(
178
- "Required with YOUTUBE: explicit title, numeric categoryId, privacyStatus, madeForKids declaration, synthetic-media disclosure, subscriber-notification choice, and tags. Unaudited Google API projects may be restricted to Private uploads."
209
+ "Required with YOUTUBE: explicit title, numeric categoryId, privacyStatus, madeForKids declaration, synthetic-media disclosure, subscriber-notification choice, and tags. Immediate publishing uses the exact PUBLIC, UNLISTED, or PRIVATE visibility and creates no schedule. Future scheduling requires PUBLIC, while Rolino uploads privately for preparation. Unaudited Google API projects may be restricted to Private uploads."
179
210
  )
180
211
  };
181
212
  var mcpDraftUpdateInputShape = {
@@ -197,6 +228,8 @@ var mcpScheduleExecuteOutputSchema = z.object({
197
228
  provider: PostSchedulePendingSchema.shape.provider.optional(),
198
229
  mutationId: PostSchedulePendingSchema.shape.mutationId.optional(),
199
230
  operation: PostSchedulePendingSchema.shape.operation.optional(),
231
+ phase: PostSchedulePendingSchema.shape.phase.optional(),
232
+ videoUploaded: PostSchedulePendingSchema.shape.videoUploaded.optional(),
200
233
  message: PostSchedulePendingSchema.shape.message.optional()
201
234
  }).passthrough();
202
235
  function jsonResult(structuredContent) {
@@ -296,6 +329,18 @@ var publishExecuteAnnotations = {
296
329
  idempotentHint: true,
297
330
  openWorldHint: true
298
331
  };
332
+ var destructiveIdempotentAnnotations = {
333
+ readOnlyHint: false,
334
+ destructiveHint: true,
335
+ idempotentHint: true,
336
+ openWorldHint: false
337
+ };
338
+ var revalidationTestAnnotations = {
339
+ readOnlyHint: false,
340
+ destructiveHint: false,
341
+ idempotentHint: true,
342
+ openWorldHint: true
343
+ };
299
344
  function defaultClient(options) {
300
345
  return new RolinoClient({
301
346
  baseUrl: options.baseUrl ?? "https://getrolino.com",
@@ -306,6 +351,10 @@ function defaultClient(options) {
306
351
  }
307
352
  function createRolinoMcpServer(options = {}) {
308
353
  const api = options.client ?? defaultClient(options);
354
+ const blogApi = () => {
355
+ if (!api.blog) throw new TypeError("This Rolino client does not support Blog Studio.");
356
+ return api.blog;
357
+ };
309
358
  const server = new McpServer({
310
359
  name: "rolino",
311
360
  version: ROLINO_MCP_VERSION,
@@ -511,7 +560,7 @@ function createRolinoMcpServer(options = {}) {
511
560
  });
512
561
  server.registerTool("get_post_readiness", {
513
562
  title: "Get Rolino post readiness",
514
- description: "Evaluate one saved post against its destinations, media, captions, settings, permissions, token timing, and cached provider health. YouTube checks cover one stored video, explicit metadata and declarations, description limits, Private-only audit restrictions, and queue-aware lead time. This is a read-only evaluation and does not refresh providers or publish content.",
563
+ description: "Evaluate one saved post against its destinations, media, captions, settings, permissions, token timing, and cached provider health. YouTube checks cover one stored video, explicit metadata and declarations, description limits, Private-only audit restrictions, and queue-aware lead time. LinkedIn checks allow text, JPEG/PNG images, or exactly one MP4 within its size and duration limits. This is a read-only evaluation and does not refresh providers or publish content.",
515
564
  inputSchema: {
516
565
  projectId: z.string().min(1).describe("Exact Rolino project ID."),
517
566
  postId: z.string().min(1).describe("Exact Rolino post ID.")
@@ -527,7 +576,7 @@ function createRolinoMcpServer(options = {}) {
527
576
  });
528
577
  server.registerTool("preview_post_schedule", {
529
578
  title: "Preview a Rolino post schedule",
530
- description: "Validate one exact future schedule against the current post version and live provider health. A YouTube schedule must target Public and its preview explains that confirmed execution starts a private upload immediately for early processing. This contacts configured providers and creates a five-minute, single-use confirmation, but does not schedule or publish the post. Inspect the complete result before calling execute_post_schedule with identical values.",
579
+ description: "Validate one exact future schedule against the current post version and live provider health. A YouTube schedule must target Public. Its preview states that preparation uploads the video privately now; CONFIRMING_SCHEDULE means the upload is complete and YouTube is acknowledging the future Public publish time, not that the video is public. This contacts configured providers and creates a five-minute, single-use confirmation, but does not schedule or publish the post. Inspect the complete result before calling execute_post_schedule with identical values.",
531
580
  inputSchema: {
532
581
  projectId: z.string().min(1).describe("Exact Rolino project ID."),
533
582
  postId: z.string().min(1).describe("Exact Rolino post ID."),
@@ -550,7 +599,7 @@ function createRolinoMcpServer(options = {}) {
550
599
  });
551
600
  server.registerTool("execute_post_schedule", {
552
601
  title: "Execute a confirmed Rolino post schedule",
553
- description: "Schedule a post only after preview_post_schedule. Pass the unchanged time, timezone, post version, and returned one-time confirmation token. For YouTube, confirmed execution begins or resumes the private upload immediately and may return PENDING until a remote schedule change is confirmed; it never means the video already published. Rolino rechecks readiness and provider health, rejects stale or mismatched confirmation, and uses the stable idempotency key for safe retries. Scheduling causes future external publishing and is consequential.",
602
+ description: "Schedule a post only after preview_post_schedule. Pass the unchanged time, timezone, post version, and returned one-time confirmation token. For YouTube, confirmed execution begins or resumes a private upload immediately and may return PENDING with phase REMOTE_SCHEDULE_CONFIRMATION after upload while YouTube acknowledges the future Public publish time. PENDING never means the video is public. Rolino rechecks readiness and provider health, rejects stale or mismatched confirmation, and uses the stable idempotency key for safe retries. Scheduling causes future external publishing and is consequential.",
554
603
  inputSchema: {
555
604
  projectId: z.string().min(1).describe("Exact Rolino project ID."),
556
605
  postId: z.string().min(1).describe("Exact Rolino post ID."),
@@ -582,12 +631,12 @@ function createRolinoMcpServer(options = {}) {
582
631
  });
583
632
  server.registerTool("preview_post_publish", {
584
633
  title: "Preview immediate Rolino post publishing",
585
- description: "Validate exact destinations against the current post version, safe retry state, readiness, and live provider health. YouTube readiness requires one stored video and explicit metadata; the configured visibility is exact, while unaudited Google API projects may be restricted to Private. This contacts configured providers and creates a five-minute, single-use confirmation, but does not publish. Inspect the complete result before calling execute_post_publish with identical destinations.",
634
+ description: "Validate exact destinations for immediate publishing against the current post version, safe retry state, readiness, and live provider health. YouTube readiness requires one stored video and explicit metadata. Immediate YouTube publishing uses the configured PUBLIC, UNLISTED, or PRIVATE visibility and never creates a schedule; the preview exposes this as deliveryMode IMMEDIATE. Unaudited Google API projects may be restricted to Private. This contacts configured providers and creates a five-minute, single-use confirmation, but does not publish. Inspect the complete result before calling execute_post_publish with identical destinations.",
586
635
  inputSchema: {
587
636
  projectId: z.string().min(1).describe("Exact Rolino project ID."),
588
637
  postId: z.string().min(1).describe("Exact Rolino post ID."),
589
638
  expectedVersion: z.number().int().positive().describe("Current version returned by get_post."),
590
- destinations: PostPublishInputSchema.shape.destinations.describe("Exact Instagram, TikTok, YouTube, and/or Bluesky destinations to publish now.")
639
+ destinations: PostPublishInputSchema.shape.destinations.describe("Exact Instagram, TikTok, YouTube, Bluesky, and/or LinkedIn destinations to publish now.")
591
640
  },
592
641
  outputSchema: PostPublishPreviewSchema,
593
642
  annotations: publishPreviewAnnotations
@@ -605,7 +654,7 @@ function createRolinoMcpServer(options = {}) {
605
654
  });
606
655
  server.registerTool("execute_post_publish", {
607
656
  title: "Execute confirmed immediate Rolino post publishing",
608
- description: "Begin external publishing only after preview_post_publish. Pass the unchanged destinations, post version, and returned one-time confirmation token. YouTube execution starts a resumable upload and remains PREPARING until provider-confirmed upload and processing complete; no queued or local state is reported as published. Rolino rechecks readiness and safe retry state, durably queues the exact destinations, rejects ambiguous prior outcomes, and uses the stable idempotency key to prevent duplicate execution. This is immediately consequential.",
657
+ description: "Begin external publishing only after preview_post_publish. Pass the unchanged destinations, post version, and returned one-time confirmation token. Immediate YouTube publishing preserves the previewed PUBLIC, UNLISTED, or PRIVATE visibility and never creates or confirms a schedule. Video execution remains PREPARING for YouTube and LinkedIn until provider-confirmed upload and processing complete; no queued or local state is reported as published. Rolino rechecks readiness and safe retry state, durably queues the exact destinations, rejects ambiguous prior outcomes, and uses the stable idempotency key to prevent duplicate execution. This is immediately consequential.",
609
658
  inputSchema: {
610
659
  projectId: z.string().min(1).describe("Exact Rolino project ID."),
611
660
  postId: z.string().min(1).describe("Exact Rolino post ID."),
@@ -637,7 +686,7 @@ function createRolinoMcpServer(options = {}) {
637
686
  });
638
687
  server.registerTool("list_integration_health", {
639
688
  title: "List Rolino publishing integration health",
640
- description: "List secret-safe cached health for every enabled publishing connection (Instagram, TikTok, YouTube, or Bluesky) in one exact project. This tool does not contact external providers or reveal social credentials.",
689
+ description: "List secret-safe cached health for every enabled publishing connection (Instagram, TikTok, YouTube, Bluesky, or LinkedIn) in one exact project. This tool does not contact external providers or reveal social credentials.",
641
690
  inputSchema: {
642
691
  projectId: z.string().min(1).describe("Exact Rolino project ID.")
643
692
  },
@@ -652,7 +701,7 @@ function createRolinoMcpServer(options = {}) {
652
701
  });
653
702
  server.registerTool("refresh_delivery_options", {
654
703
  title: "Refresh provider delivery options",
655
- description: "Contact one connected publishing provider and return secret-safe, account-specific choices needed to prepare a compliant draft. TikTok returns account-specific video choices; LinkedIn returns its verified member and image policy. This refreshes cached provider health but never creates, schedules, or publishes a post.",
704
+ description: "Contact one connected publishing provider and return secret-safe, account-specific choices needed to prepare a compliant draft. TikTok returns account-specific video choices; LinkedIn returns its verified member plus image and native-video policy. This refreshes cached provider health but never creates, schedules, or publishes a post.",
656
705
  inputSchema: {
657
706
  projectId: z.string().min(1).describe("Exact Rolino project ID."),
658
707
  provider: ProviderDeliveryOptionsProviderSchema.describe("Provider whose delivery choices should be refreshed.")
@@ -669,6 +718,379 @@ function createRolinoMcpServer(options = {}) {
669
718
  return errorResult(error);
670
719
  }
671
720
  });
721
+ server.registerTool("list_seo_opportunities", {
722
+ title: "List active SEO tasks",
723
+ description: "Read a bounded list of active Ready and Research SEO tasks for one exact project. Each result contains one complete versioned task, decision, format, deliverables, evidence, provenance, separate topic-relevance and action-readiness confidence, limits, and no-publish rules. Rejected findings are never active tasks. This closed-world tool cannot run analysis, change settings, delete data, enable reports, send email, publish, generate media, or call an external provider.",
724
+ inputSchema: {
725
+ projectId: SeoEntityIdSchema.describe("Exact Rolino project ID."),
726
+ limit: z.number().int().min(1).max(50).default(20).describe("Maximum opportunities to return, from 1 to 50."),
727
+ cursor: z.string().min(1).max(512).regex(/^[A-Za-z0-9_-]+$/).optional().describe("Opaque cursor returned by a previous list_seo_opportunities call."),
728
+ kind: SeoOpportunityKindSchema.optional().describe("Optional exact opportunity kind."),
729
+ expectedImpact: SeoExpectedImpactSchema.optional().describe("Optional HIGH, MEDIUM, or LOW impact filter.")
730
+ },
731
+ outputSchema: SeoOpportunityListDataSchema,
732
+ annotations: readOnlyAnnotations
733
+ }, async ({ projectId, limit, cursor, kind, expectedImpact }) => {
734
+ try {
735
+ if (!api.seo) throw new TypeError("This Rolino client does not support SEO reading.");
736
+ return jsonResult(await api.seo.opportunities.list(projectId, { limit, cursor, kind, expectedImpact }));
737
+ } catch (error) {
738
+ return errorResult(error);
739
+ }
740
+ });
741
+ server.registerTool("get_seo_opportunity", {
742
+ title: "Read one complete SEO task",
743
+ description: "Read one canonical versioned SEO task by exact project and opportunity ID. It includes the Ready or Research decision, selected format, exact deliverables, safe evidence, provenance, separate topic-relevance and action-readiness confidence, targeting, source dates, limitations, success metric, and no-publish rules. This closed-world tool is read-only and cannot generate content, publish, or call an external provider.",
744
+ inputSchema: {
745
+ projectId: SeoEntityIdSchema.describe("Exact Rolino project ID."),
746
+ opportunityId: SeoEntityIdSchema.describe("Exact Rolino SEO opportunity ID.")
747
+ },
748
+ outputSchema: SeoOpportunitySchema,
749
+ annotations: readOnlyAnnotations
750
+ }, async ({ projectId, opportunityId }) => {
751
+ try {
752
+ if (!api.seo) throw new TypeError("This Rolino client does not support SEO reading.");
753
+ return jsonResult(await api.seo.opportunities.get(projectId, opportunityId));
754
+ } catch (error) {
755
+ return errorResult(error);
756
+ }
757
+ });
758
+ server.registerTool("list_seo_reports", {
759
+ title: "List weekly SEO reports",
760
+ description: "Read a bounded list of stored weekly SEO report summaries for one exact project. This closed-world tool is read-only and cannot generate or deliver reports.",
761
+ inputSchema: {
762
+ projectId: SeoEntityIdSchema.describe("Exact Rolino project ID."),
763
+ limit: z.number().int().min(1).max(26).default(10).describe("Maximum reports to return, from 1 to 26."),
764
+ cursor: z.string().min(1).max(512).regex(/^[A-Za-z0-9_-]+$/).optional().describe("Opaque cursor returned by a previous list_seo_reports call."),
765
+ completeness: SeoReportCompletenessSchema.optional().describe("Optional COMPLETE or PARTIAL filter.")
766
+ },
767
+ outputSchema: SeoReportListDataSchema,
768
+ annotations: readOnlyAnnotations
769
+ }, async ({ projectId, limit, cursor, completeness }) => {
770
+ try {
771
+ if (!api.seo) throw new TypeError("This Rolino client does not support SEO reading.");
772
+ return jsonResult(await api.seo.reports.list(projectId, { limit, cursor, completeness }));
773
+ } catch (error) {
774
+ return errorResult(error);
775
+ }
776
+ });
777
+ server.registerTool("get_seo_report", {
778
+ title: "Read one weekly SEO report",
779
+ description: "Read one unchanged weekly SEO report snapshot by exact project and report ID. It groups complete Ready tasks, Research tasks, and bounded rejected findings. This closed-world tool is read-only and cannot generate content, change, or deliver a report.",
780
+ inputSchema: {
781
+ projectId: SeoEntityIdSchema.describe("Exact Rolino project ID."),
782
+ reportId: SeoEntityIdSchema.describe("Exact Rolino weekly SEO report ID.")
783
+ },
784
+ outputSchema: SeoReportSchema,
785
+ annotations: readOnlyAnnotations
786
+ }, async ({ projectId, reportId }) => {
787
+ try {
788
+ if (!api.seo) throw new TypeError("This Rolino client does not support SEO reading.");
789
+ return jsonResult(await api.seo.reports.get(projectId, reportId));
790
+ } catch (error) {
791
+ return errorResult(error);
792
+ }
793
+ });
794
+ server.registerTool("list_blog_plans", {
795
+ title: "List Blog Studio plans",
796
+ description: "Read the bounded 30-day Blog Studio plans for one exact project. This does not read the social calendar.",
797
+ inputSchema: { projectId: SeoEntityIdSchema.describe("Exact Rolino project ID.") },
798
+ outputSchema: z.object({ plans: z.array(AgentBlogPlanSchema) }),
799
+ annotations: readOnlyAnnotations
800
+ }, async ({ projectId }) => {
801
+ try {
802
+ return jsonResult(await blogApi().plans.list(projectId));
803
+ } catch (error) {
804
+ return errorResult(error);
805
+ }
806
+ });
807
+ server.registerTool("get_blog_setup_status", {
808
+ title: "Get Blog setup status",
809
+ description: "Requires blog:read. Call first to inspect action-oriented website, import, plan, delivery, and revalidation readiness. This never returns credentials, secrets, prompts, or draft content.",
810
+ inputSchema: { projectId: SeoEntityIdSchema },
811
+ outputSchema: AgentBlogSetupStatusSchema,
812
+ annotations: readOnlyAnnotations
813
+ }, async ({ projectId }) => {
814
+ try {
815
+ return jsonResult(await blogApi().setup.status(projectId));
816
+ } catch (error) {
817
+ return errorResult(error);
818
+ }
819
+ });
820
+ server.registerTool("initialize_blog_site", {
821
+ title: "Initialize and import Blog site",
822
+ description: "Requires blog:manage. Initialize the exact project website from Brand Settings and queue a bounded import. Poll the returned job; do not retry blindly. This cannot publish.",
823
+ inputSchema: { projectId: SeoEntityIdSchema, idempotencyKey: z.string().min(8).max(200) },
824
+ outputSchema: AgentBlogJobSchema,
825
+ annotations: createDraftAnnotations
826
+ }, async ({ projectId, idempotencyKey }) => {
827
+ try {
828
+ return jsonResult(await blogApi().site.import(projectId, idempotencyKey));
829
+ } catch (error) {
830
+ return errorResult(error);
831
+ }
832
+ });
833
+ server.registerTool("retry_blog_site_import", {
834
+ title: "Retry Blog site import",
835
+ description: "Requires blog:manage. Requeue only a failed bounded website import with a stable idempotency key. Poll the returned job.",
836
+ inputSchema: { projectId: SeoEntityIdSchema, idempotencyKey: z.string().min(8).max(200) },
837
+ outputSchema: AgentBlogJobSchema,
838
+ annotations: createDraftAnnotations
839
+ }, async ({ projectId, idempotencyKey }) => {
840
+ try {
841
+ return jsonResult(await blogApi().site.retry(projectId, idempotencyKey));
842
+ } catch (error) {
843
+ return errorResult(error);
844
+ }
845
+ });
846
+ server.registerTool("create_blog_plan", {
847
+ title: "Create Blog cadence plan",
848
+ description: "Requires blog:manage. Queue an evidence-backed 30-day plan only after import is ready. Ask the user for cadence first and poll the returned job. This never accepts items or publishes.",
849
+ inputSchema: { projectId: SeoEntityIdSchema, startsOn: z.string().regex(/^\d{4}-\d{2}-\d{2}$/), weekdays: z.array(z.number().int().min(0).max(6)).min(1).max(7), timeZone: z.string().min(1), idempotencyKey: z.string().min(8).max(200) },
850
+ outputSchema: z.object({ planId: z.string(), job: AgentBlogJobSchema }),
851
+ annotations: createDraftAnnotations
852
+ }, async ({ projectId, startsOn, weekdays, timeZone, idempotencyKey }) => {
853
+ try {
854
+ return jsonResult(await blogApi().plans.create(projectId, { startsOn, weekdays, timeZone }, idempotencyKey));
855
+ } catch (error) {
856
+ return errorResult(error);
857
+ }
858
+ });
859
+ server.registerTool("retry_blog_plan", {
860
+ title: "Retry failed Blog plan",
861
+ description: "Requires blog:manage. Retry one exact failed plan generation with idempotency and poll the returned job.",
862
+ inputSchema: { projectId: SeoEntityIdSchema, planId: SeoEntityIdSchema, idempotencyKey: z.string().min(8).max(200) },
863
+ outputSchema: AgentBlogJobSchema,
864
+ annotations: createDraftAnnotations
865
+ }, async ({ projectId, planId, idempotencyKey }) => {
866
+ try {
867
+ return jsonResult(await blogApi().plans.retry(projectId, planId, idempotencyKey));
868
+ } catch (error) {
869
+ return errorResult(error);
870
+ }
871
+ });
872
+ server.registerTool("list_blog_plan_items", {
873
+ title: "List Blog plan items and evidence",
874
+ description: "Requires blog:read. Present this safe frozen evidence to the user before accepting or dismissing any item. Provider payloads and prompts are excluded.",
875
+ inputSchema: { projectId: SeoEntityIdSchema, planId: SeoEntityIdSchema },
876
+ outputSchema: z.object({ items: z.array(AgentBlogPlanItemSchema) }),
877
+ annotations: readOnlyAnnotations
878
+ }, async ({ projectId, planId }) => {
879
+ try {
880
+ return jsonResult(await blogApi().plans.items.list(projectId, planId));
881
+ } catch (error) {
882
+ return errorResult(error);
883
+ }
884
+ });
885
+ server.registerTool("set_blog_plan_item_state", {
886
+ title: "Accept or dismiss Blog plan item",
887
+ description: "Requires blog:write. Apply an optimistic, idempotent ACCEPTED or DISMISSED transition only after showing the evidence. Dismissal is marked destructive. This cannot publish.",
888
+ inputSchema: { projectId: SeoEntityIdSchema, planId: SeoEntityIdSchema, itemId: SeoEntityIdSchema, state: z.enum(["ACCEPTED", "DISMISSED"]), expectedState: z.string().min(1), expectedVersion: z.number().int().positive(), idempotencyKey: z.string().min(8).max(200) },
889
+ outputSchema: AgentBlogPlanItemSchema,
890
+ annotations: destructiveIdempotentAnnotations
891
+ }, async ({ projectId, planId, itemId, ...input }) => {
892
+ try {
893
+ return jsonResult(await blogApi().plans.items.setState(projectId, planId, itemId, input));
894
+ } catch (error) {
895
+ return errorResult(error);
896
+ }
897
+ });
898
+ server.registerTool("create_blog_article_from_plan_item", {
899
+ title: "Create editable Blog article",
900
+ description: "Requires blog:write. Create an editable draft from one accepted plan item and queue generation. Publication remains a separate approved-revision preview and execute operation.",
901
+ inputSchema: { projectId: SeoEntityIdSchema, planId: SeoEntityIdSchema, itemId: SeoEntityIdSchema, expectedVersion: z.number().int().positive(), idempotencyKey: z.string().min(8).max(200) },
902
+ outputSchema: z.object({ articleId: z.string(), job: AgentBlogJobSchema.nullable() }),
903
+ annotations: createDraftAnnotations
904
+ }, async ({ projectId, planId, itemId, expectedVersion, idempotencyKey }) => {
905
+ try {
906
+ return jsonResult(await blogApi().plans.items.createArticle(projectId, planId, itemId, { expectedState: "ACCEPTED", expectedVersion, idempotencyKey }));
907
+ } catch (error) {
908
+ return errorResult(error);
909
+ }
910
+ });
911
+ server.registerTool("get_blog_connection_status", {
912
+ title: "Get Blog delivery connection status",
913
+ description: "Requires blog:read. Read configured and honestly verified delivery/revalidation readiness. It never returns stored tokens or secrets.",
914
+ inputSchema: { projectId: SeoEntityIdSchema },
915
+ outputSchema: AgentBlogConnectionStatusSchema,
916
+ annotations: readOnlyAnnotations
917
+ }, async ({ projectId }) => {
918
+ try {
919
+ return jsonResult(await blogApi().connection.status(projectId));
920
+ } catch (error) {
921
+ return errorResult(error);
922
+ }
923
+ });
924
+ const connectionInputSchema = { projectId: SeoEntityIdSchema, endpoint: z.string().url().nullable(), credentialAction: z.enum(["KEEP", "CREATE", "ROTATE", "REVOKE"]), credentialId: z.string().min(1).nullable(), secretAction: z.enum(["KEEP", "CREATE", "ROTATE"]) };
925
+ server.registerTool("preview_blog_connection_setup", {
926
+ title: "Preview Blog connection setup",
927
+ description: "Requires blog:manage. Preview the exact site, endpoint, credential action, secret action, and effects before provisioning. No delivery token or webhook secret is returned by preview.",
928
+ inputSchema: connectionInputSchema,
929
+ outputSchema: AgentBlogConnectionPreviewSchema,
930
+ annotations: createDraftAnnotations
931
+ }, async ({ projectId, ...input }) => {
932
+ try {
933
+ return jsonResult(await blogApi().connection.preview(projectId, input));
934
+ } catch (error) {
935
+ return errorResult(error);
936
+ }
937
+ });
938
+ server.registerTool("execute_blog_connection_setup", {
939
+ title: "Execute confirmed Blog connection setup",
940
+ description: "Requires blog:manage, the exact preview confirmation, and idempotency. A successful first response can return a delivery token or webhook secret once; write them only to server-side environment configuration. A replay never reveals them again. This cannot publish.",
941
+ inputSchema: { ...connectionInputSchema, confirmationToken: z.string().min(1), idempotencyKey: z.string().min(8).max(200) },
942
+ outputSchema: AgentBlogConnectionExecuteSchema,
943
+ annotations: destructiveIdempotentAnnotations
944
+ }, async ({ projectId, ...input }) => {
945
+ try {
946
+ return jsonResult(await blogApi().connection.execute(projectId, input));
947
+ } catch (error) {
948
+ return errorResult(error);
949
+ }
950
+ });
951
+ server.registerTool("test_blog_revalidation", {
952
+ title: "Test signed Blog revalidation",
953
+ description: "Requires blog:manage. Make one explicit, SSRF-protected open-world signed request to the configured same-host endpoint and persist only a safe status code.",
954
+ inputSchema: { projectId: SeoEntityIdSchema, idempotencyKey: z.string().min(8).max(200) },
955
+ outputSchema: AgentBlogRevalidationTestSchema,
956
+ annotations: revalidationTestAnnotations
957
+ }, async ({ projectId, idempotencyKey }) => {
958
+ try {
959
+ return jsonResult(await blogApi().connection.test(projectId, idempotencyKey));
960
+ } catch (error) {
961
+ return errorResult(error);
962
+ }
963
+ });
964
+ server.registerTool("list_blog_publishing_providers", {
965
+ title: "List Blog publishing providers",
966
+ description: "List only providers available to this rollout with safe capabilities and official documentation. Requires blog:manage.",
967
+ inputSchema: { projectId: SeoEntityIdSchema },
968
+ outputSchema: z.object({ providers: z.array(AgentBlogPublishingProviderSchema) }),
969
+ annotations: readOnlyAnnotations
970
+ }, async ({ projectId }) => {
971
+ try {
972
+ return jsonResult(await blogApi().publishing.providers(projectId));
973
+ } catch (error) {
974
+ return errorResult(error);
975
+ }
976
+ });
977
+ server.registerTool("list_blog_publishing_destinations", {
978
+ title: "List Blog publishing destinations",
979
+ description: "List safe destination readiness without credentials or private provider data.",
980
+ inputSchema: { projectId: SeoEntityIdSchema },
981
+ outputSchema: z.object({ destinations: z.array(AgentBlogPublishingDestinationSchema) }),
982
+ annotations: readOnlyAnnotations
983
+ }, async ({ projectId }) => {
984
+ try {
985
+ return jsonResult(await blogApi().publishing.destinations(projectId));
986
+ } catch (error) {
987
+ return errorResult(error);
988
+ }
989
+ });
990
+ server.registerTool("list_blog_delivery_attempts", {
991
+ title: "List Blog delivery attempts",
992
+ description: "List safe per-destination delivery states and remote URLs. Draft content and provider responses are not returned.",
993
+ inputSchema: { projectId: SeoEntityIdSchema, articleId: SeoEntityIdSchema.optional() },
994
+ outputSchema: z.object({ deliveries: z.array(AgentBlogDeliveryAttemptSchema) }),
995
+ annotations: readOnlyAnnotations
996
+ }, async ({ projectId, articleId }) => {
997
+ try {
998
+ return jsonResult(await blogApi().publishing.deliveries(projectId, articleId));
999
+ } catch (error) {
1000
+ return errorResult(error);
1001
+ }
1002
+ });
1003
+ server.registerTool("list_blog_articles", {
1004
+ title: "List Blog Studio articles",
1005
+ description: "Read Blog Studio article workflow state for one exact project. Draft content requires blog:read.",
1006
+ inputSchema: { projectId: SeoEntityIdSchema.describe("Exact Rolino project ID.") },
1007
+ outputSchema: z.object({ articles: z.array(AgentBlogArticleSchema) }),
1008
+ annotations: readOnlyAnnotations
1009
+ }, async ({ projectId }) => {
1010
+ try {
1011
+ return jsonResult(await blogApi().articles.list(projectId));
1012
+ } catch (error) {
1013
+ return errorResult(error);
1014
+ }
1015
+ });
1016
+ server.registerTool("get_blog_article", {
1017
+ title: "Read one Blog Studio article",
1018
+ description: "Read one exact Blog Studio draft and its immutable revision history.",
1019
+ inputSchema: { projectId: SeoEntityIdSchema, articleId: SeoEntityIdSchema },
1020
+ outputSchema: AgentBlogArticleDetailSchema,
1021
+ annotations: readOnlyAnnotations
1022
+ }, async ({ projectId, articleId }) => {
1023
+ try {
1024
+ return jsonResult(await blogApi().articles.get(projectId, articleId));
1025
+ } catch (error) {
1026
+ return errorResult(error);
1027
+ }
1028
+ });
1029
+ server.registerTool("update_blog_draft", {
1030
+ title: "Update one Blog Studio draft",
1031
+ description: "Save a complete Blog draft with optimistic conflict protection. This creates a new immutable revision and cannot publish.",
1032
+ inputSchema: { projectId: SeoEntityIdSchema, articleId: SeoEntityIdSchema, draft: AgentBlogDraftUpdateSchema },
1033
+ outputSchema: AgentBlogArticleDetailSchema,
1034
+ annotations: createDraftAnnotations
1035
+ }, async ({ projectId, articleId, draft }) => {
1036
+ try {
1037
+ return jsonResult(await blogApi().articles.updateDraft(projectId, articleId, draft));
1038
+ } catch (error) {
1039
+ return errorResult(error);
1040
+ }
1041
+ });
1042
+ server.registerTool("generate_blog_article", {
1043
+ title: "Request Blog article generation",
1044
+ description: "Queue durable generation for one Blog article. It returns a job and never waits for provider work in the request.",
1045
+ inputSchema: { projectId: SeoEntityIdSchema, articleId: SeoEntityIdSchema, idempotencyKey: z.string().min(8).max(200) },
1046
+ outputSchema: AgentBlogJobSchema,
1047
+ annotations: createDraftAnnotations
1048
+ }, async ({ projectId, articleId, idempotencyKey }) => {
1049
+ try {
1050
+ return jsonResult(await blogApi().articles.generate(projectId, articleId, idempotencyKey));
1051
+ } catch (error) {
1052
+ return errorResult(error);
1053
+ }
1054
+ });
1055
+ server.registerTool("get_blog_job", {
1056
+ title: "Read one Blog job",
1057
+ description: "Read truthful durable job state without exposing prompts, provider details, or secrets.",
1058
+ inputSchema: { projectId: SeoEntityIdSchema, jobId: SeoEntityIdSchema },
1059
+ outputSchema: AgentBlogJobSchema,
1060
+ annotations: readOnlyAnnotations
1061
+ }, async ({ projectId, jobId }) => {
1062
+ try {
1063
+ return jsonResult(await blogApi().jobs.get(projectId, jobId));
1064
+ } catch (error) {
1065
+ return errorResult(error);
1066
+ }
1067
+ });
1068
+ server.registerTool("preview_blog_publish", {
1069
+ title: "Preview approved Blog publication",
1070
+ description: "Preview an exact user-approved revision and issue a short-lived bound confirmation. This does not publish.",
1071
+ inputSchema: { projectId: SeoEntityIdSchema, articleId: SeoEntityIdSchema, revisionId: SeoEntityIdSchema, destinationIds: z.array(SeoEntityIdSchema).min(1).max(20).optional() },
1072
+ outputSchema: AgentBlogPublishPreviewSchema,
1073
+ annotations: publishPreviewAnnotations
1074
+ }, async ({ projectId, articleId, revisionId, destinationIds }) => {
1075
+ try {
1076
+ return jsonResult(await blogApi().articles.previewPublish(projectId, articleId, revisionId, void 0, destinationIds));
1077
+ } catch (error) {
1078
+ return errorResult(error);
1079
+ }
1080
+ });
1081
+ server.registerTool("execute_blog_publish", {
1082
+ title: "Publish approved Blog revision",
1083
+ description: "Publish only the same exact user-approved revision from preview. Requires blog:publish, a confirmation token, and an idempotency key.",
1084
+ inputSchema: { projectId: SeoEntityIdSchema, articleId: SeoEntityIdSchema, revisionId: SeoEntityIdSchema, destinationIds: z.array(SeoEntityIdSchema).min(1).max(20).optional(), confirmationToken: z.string().min(1), idempotencyKey: z.string().min(8).max(200) },
1085
+ outputSchema: z.object({ publicationId: z.string(), articleId: z.string(), revisionId: z.string(), publicUrl: z.string().url(), publishedAt: z.string().datetime() }),
1086
+ annotations: publishExecuteAnnotations
1087
+ }, async ({ projectId, articleId, revisionId, destinationIds, confirmationToken, idempotencyKey }) => {
1088
+ try {
1089
+ return jsonResult(await blogApi().articles.executePublish(projectId, articleId, { revisionId, destinationIds, confirmationToken, idempotencyKey }));
1090
+ } catch (error) {
1091
+ return errorResult(error);
1092
+ }
1093
+ });
672
1094
  server.registerTool("list_calendar_events", {
673
1095
  title: "List Rolino calendar events",
674
1096
  description: "List scheduled posts and their enabled destinations in one exact project, including YouTube posts whose private early preparation has started. By default the bounded window spans 30 days in the past through 90 days ahead; provide both from and to for another range and paginate with nextCursor.",
@@ -708,4 +1130,4 @@ export {
708
1130
  createRolinoMcpServer,
709
1131
  startStdioServer
710
1132
  };
711
- //# sourceMappingURL=chunk-L6II7ZEX.js.map
1133
+ //# sourceMappingURL=chunk-IW4JXMD2.js.map