@motiblog/mcp 0.1.1 → 0.1.2

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 CHANGED
@@ -92,17 +92,18 @@ OAuth lands later — per-project keys are the MVP contract per `docs/product/ag
92
92
 
93
93
  ## Publishing to npm
94
94
 
95
- The package is publish-ready but **not yet published**. Until it is, leave the
96
- "no published npm package" line on `/mcp` and in `llms.txt` alone an agent
97
- that reads an install instruction and hits a 404 is worse off than one told
98
- the truth.
95
+ Published as [`@motiblog/mcp`](https://www.npmjs.com/package/@motiblog/mcp)
96
+ under the `motiblog` org. Consumers run it with:
99
97
 
100
- Prerequisites, all of which are a human's job:
98
+ ```bash
99
+ claude mcp add motiblog --env MOTIBLOG_API_KEY=<key> -- npx -y @motiblog/mcp
100
+ ```
101
101
 
102
- 1. An npm account, and membership of an org named `motiblog` (the `@motiblog`
103
- scope). A free org is enough scoped packages publish publicly with
104
- `access: public`, already set in `publishConfig`.
105
- 2. `npm login` on the machine doing the publish.
102
+ Prerequisites for whoever publishes: membership of the `motiblog` org,
103
+ `npm login`, and **2FA enabled**npm rejects publishes without it
104
+ (`403 … Two-factor authentication or granular access token with bypass 2fa
105
+ enabled is required`). Either pass `--otp=<code>` or use a granular access
106
+ token with *Bypass 2FA* in `~/.npmrc`.
106
107
 
107
108
  Then, from `apps/mcp`:
108
109
 
@@ -118,7 +119,14 @@ tar -tzf motiblog-mcp-*.tgz # dist/ + README.md, nothing else
118
119
  tar -xzOf motiblog-mcp-*.tgz package/package.json | grep -E '"main"|"bin"'
119
120
  ```
120
121
 
121
- Two things that must stay true, and both are easy to break:
122
+ Three things that must stay true, and all are easy to break:
123
+
124
+ - **The `mcp` bin alias.** `npx -y @motiblog/mcp` resolves the bin whose key
125
+ matches the package's *unscoped* name. Published 0.1.0 had only
126
+ `motiblog-mcp` and `motiblog-mcp-http`, so the obvious command failed with
127
+ "could not determine executable to run" — fixed in 0.1.1 by aliasing `mcp`
128
+ to the stdio entry. Test the bare `npx -y @motiblog/mcp` after any change to
129
+ `publishConfig.bin`, not just the long form.
122
130
 
123
131
  - **`@motiblog/shared` is bundled, not depended on.** It is a workspace
124
132
  package that will never exist on npm, so `tsup.config.ts` lists it under
package/dist/http.js CHANGED
@@ -492,7 +492,7 @@ var toolDefinitions = [
492
492
  name: "get_digest",
493
493
  description: "One-call morning check-in for a project: article counts by lifecycle status, the REVIEW queue (what is waiting to be published, with word counts), posts published in the last 7 days, content plans due in the next 7 days, the latest published post, quota usage, and the project's current autonomy level (L1 = nothing ships without agent review; L2 = clean articles auto-publish). Start every check-in here.",
494
494
  inputSchema: { ...projectIdArg },
495
- annotations: { readOnlyHint: true },
495
+ annotations: { title: "Morning Digest", readOnlyHint: true, destructiveHint: false },
496
496
  async handler(args, ctx) {
497
497
  const projectId = resolveProjectId(ctx, args.project_id);
498
498
  const [project, articles, plans] = await Promise.all([
@@ -509,7 +509,7 @@ var toolDefinitions = [
509
509
  name: "list_projects",
510
510
  description: "List every MotiBlog project the API key can access, with settings that matter to agents: requireApproval (REVIEW vs APPROVED after generation), autoPublish, factCheckStrict, selfHostedBlog, and article quota usage.",
511
511
  inputSchema: {},
512
- annotations: { readOnlyHint: true },
512
+ annotations: { title: "List Projects", readOnlyHint: true, destructiveHint: false },
513
513
  async handler(_args, ctx) {
514
514
  return json(await ctx.client.listProjects());
515
515
  }
@@ -518,7 +518,7 @@ var toolDefinitions = [
518
518
  name: "get_project",
519
519
  description: "Get one MotiBlog project in detail: pipeline settings (aiModel, language, internalLinks, banner), positioning inputs (businessProfile, productTruth, manualPositioning) and quota state.",
520
520
  inputSchema: { ...projectIdArg },
521
- annotations: { readOnlyHint: true },
521
+ annotations: { title: "Project Details", readOnlyHint: true, destructiveHint: false },
522
522
  async handler(args, ctx) {
523
523
  return json(await ctx.client.getProject(resolveProjectId(ctx, args.project_id)));
524
524
  }
@@ -528,6 +528,7 @@ var toolDefinitions = [
528
528
  name: "start_pipeline",
529
529
  description: "Start the autonomous pipeline for a project: site crawl/analysis \u2192 topic selection \u2192 article generation for scheduled plans. Returns immediately; poll get_pipeline_status or list_articles(status=GENERATING) to follow progress.",
530
530
  inputSchema: { ...projectIdArg },
531
+ annotations: { title: "Start Content Pipeline", readOnlyHint: false, destructiveHint: false },
531
532
  async handler(args, ctx) {
532
533
  const projectId = resolveProjectId(ctx, args.project_id);
533
534
  return json(await ctx.client.pipelineStart(projectId));
@@ -537,7 +538,7 @@ var toolDefinitions = [
537
538
  name: "get_pipeline_status",
538
539
  description: "Get current pipeline run state for a project: whether a run is active, its steps, and recent run history. Use after start_pipeline or generate_article.",
539
540
  inputSchema: { ...projectIdArg },
540
- annotations: { readOnlyHint: true },
541
+ annotations: { title: "Pipeline Status", readOnlyHint: true, destructiveHint: false },
541
542
  async handler(args, ctx) {
542
543
  return json(
543
544
  await ctx.client.pipelineStatus(resolveProjectId(ctx, args.project_id))
@@ -552,6 +553,7 @@ var toolDefinitions = [
552
553
  topic: import_zod.z.string().min(1).describe('The proposed topic/title, e.g. "How headless CMSs serve AI agents"'),
553
554
  ...projectIdArg
554
555
  },
556
+ annotations: { title: "Suggest Topics", readOnlyHint: false, destructiveHint: false },
555
557
  async handler(args, ctx) {
556
558
  const projectId = resolveProjectId(ctx, args.project_id);
557
559
  return json(await ctx.client.createContentPlanFromGap(projectId, String(args.topic)));
@@ -561,7 +563,7 @@ var toolDefinitions = [
561
563
  name: "list_content_plans",
562
564
  description: "List the content plan queue for a project with each entry status: DRAFT (proposed), APPROVED (cleared for generation), IN_PROGRESS, COMPLETED.",
563
565
  inputSchema: { ...projectIdArg },
564
- annotations: { readOnlyHint: true },
566
+ annotations: { title: "List Content Plans", readOnlyHint: true, destructiveHint: false },
565
567
  async handler(args, ctx) {
566
568
  return json(await ctx.client.listContentPlans(resolveProjectId(ctx, args.project_id)));
567
569
  }
@@ -573,6 +575,7 @@ var toolDefinitions = [
573
575
  plan_id: import_zod.z.string().describe("Content plan entry id (from list_content_plans)"),
574
576
  ...projectIdArg
575
577
  },
578
+ annotations: { title: "Approve Content Plan", readOnlyHint: false, destructiveHint: false },
576
579
  async handler(args, ctx) {
577
580
  const projectId = resolveProjectId(ctx, args.project_id);
578
581
  return json(await ctx.client.approveContentPlan(projectId, String(args.plan_id)));
@@ -585,6 +588,7 @@ var toolDefinitions = [
585
588
  plan_id: import_zod.z.string().describe("Content plan entry id"),
586
589
  ...projectIdArg
587
590
  },
591
+ annotations: { title: "Regenerate Content Plan", readOnlyHint: false, destructiveHint: true },
588
592
  async handler(args, ctx) {
589
593
  const projectId = resolveProjectId(ctx, args.project_id);
590
594
  return json(await ctx.client.regenerateContentPlan(projectId, String(args.plan_id)));
@@ -597,6 +601,7 @@ var toolDefinitions = [
597
601
  plan_id: import_zod.z.string().describe("Content plan entry id (must be APPROVED)"),
598
602
  ...projectIdArg
599
603
  },
604
+ annotations: { title: "Generate Article", readOnlyHint: false, destructiveHint: false },
600
605
  async handler(args, ctx) {
601
606
  const projectId = resolveProjectId(ctx, args.project_id);
602
607
  return json(await ctx.client.generateArticleFromPlan(projectId, String(args.plan_id)));
@@ -610,7 +615,7 @@ var toolDefinitions = [
610
615
  end_date: import_zod.z.string().describe("ISO date, e.g. 2026-09-30"),
611
616
  ...projectIdArg
612
617
  },
613
- annotations: { readOnlyHint: true },
618
+ annotations: { title: "Content Calendar", readOnlyHint: true, destructiveHint: false },
614
619
  async handler(args, ctx) {
615
620
  const projectId = resolveProjectId(ctx, args.project_id);
616
621
  return json(
@@ -630,7 +635,7 @@ var toolDefinitions = [
630
635
  status: import_zod.z.enum(ARTICLE_STATUSES).optional().describe("Filter by lifecycle status (default REVIEW)"),
631
636
  ...projectIdArg
632
637
  },
633
- annotations: { readOnlyHint: true },
638
+ annotations: { title: "Review Queue", readOnlyHint: true, destructiveHint: false },
634
639
  async handler(args, ctx) {
635
640
  const projectId = resolveProjectId(ctx, args.project_id);
636
641
  const status = typeof args.status === "string" ? args.status : "REVIEW";
@@ -646,7 +651,7 @@ var toolDefinitions = [
646
651
  include_logs: import_zod.z.boolean().optional().describe("Include pipeline phase logs (default false)"),
647
652
  ...projectIdArg
648
653
  },
649
- annotations: { readOnlyHint: true },
654
+ annotations: { title: "Get Article", readOnlyHint: true, destructiveHint: false },
650
655
  async handler(args, ctx) {
651
656
  const projectId = resolveProjectId(ctx, args.project_id);
652
657
  const article = await ctx.client.getArticle(projectId, String(args.article_id));
@@ -668,6 +673,7 @@ var toolDefinitions = [
668
673
  status: import_zod.z.enum(["DRAFT", "REVIEW", "APPROVED"]).optional(),
669
674
  ...projectIdArg
670
675
  },
676
+ annotations: { title: "Update Article", readOnlyHint: false, destructiveHint: true },
671
677
  async handler(args, ctx) {
672
678
  const projectId = resolveProjectId(ctx, args.project_id);
673
679
  const patch = {};
@@ -686,6 +692,12 @@ var toolDefinitions = [
686
692
  publish_now: import_zod.z.boolean().optional().describe("Also trigger publishing now (default false)"),
687
693
  ...projectIdArg
688
694
  },
695
+ annotations: {
696
+ title: "Approve & Publish",
697
+ readOnlyHint: false,
698
+ destructiveHint: true,
699
+ openWorldHint: true
700
+ },
689
701
  async handler(args, ctx) {
690
702
  const projectId = resolveProjectId(ctx, args.project_id);
691
703
  const articleId = String(args.article_id);
@@ -704,6 +716,7 @@ var toolDefinitions = [
704
716
  clear_schedule: import_zod.z.boolean().optional().describe("Clear any scheduled date instead of setting one"),
705
717
  ...projectIdArg
706
718
  },
719
+ annotations: { title: "Schedule Publication", readOnlyHint: false, destructiveHint: false },
707
720
  async handler(args, ctx) {
708
721
  const projectId = resolveProjectId(ctx, args.project_id);
709
722
  const value = args.clear_schedule === true ? null : args.scheduled_for ?? null;
@@ -718,7 +731,7 @@ var toolDefinitions = [
718
731
  article_id: import_zod.z.string().describe("Article id"),
719
732
  ...projectIdArg
720
733
  },
721
- annotations: { destructiveHint: true },
734
+ annotations: { title: "Regenerate Article", readOnlyHint: false, destructiveHint: true },
722
735
  async handler(args, ctx) {
723
736
  const projectId = resolveProjectId(ctx, args.project_id);
724
737
  return json(await ctx.client.regenerateArticle(projectId, String(args.article_id)));
@@ -732,6 +745,7 @@ var toolDefinitions = [
732
745
  chapter_index: import_zod.z.number().int().min(0).describe("Zero-based chapter index"),
733
746
  ...projectIdArg
734
747
  },
748
+ annotations: { title: "Regenerate Chapter", readOnlyHint: false, destructiveHint: true },
735
749
  async handler(args, ctx) {
736
750
  const projectId = resolveProjectId(ctx, args.project_id);
737
751
  return json(
@@ -750,7 +764,7 @@ var toolDefinitions = [
750
764
  article_id: import_zod.z.string().describe("Article id"),
751
765
  ...projectIdArg
752
766
  },
753
- annotations: { readOnlyHint: true },
767
+ annotations: { title: "Pipeline Logs", readOnlyHint: true, destructiveHint: false },
754
768
  async handler(args, ctx) {
755
769
  const projectId = resolveProjectId(ctx, args.project_id);
756
770
  return json(await ctx.client.getPipelineLogs(projectId, String(args.article_id)));
@@ -760,7 +774,7 @@ var toolDefinitions = [
760
774
  name: "list_refresh_suggestions",
761
775
  description: "Across ALL your projects: published articles whose Search Console stats suggest they are decaying and would benefit from a refresh. Feed these back into planning via suggest_topics or update_article.",
762
776
  inputSchema: {},
763
- annotations: { readOnlyHint: true },
777
+ annotations: { title: "Refresh Suggestions", readOnlyHint: true, destructiveHint: false },
764
778
  async handler(_args, ctx) {
765
779
  return json(await ctx.client.listRefreshSuggestions());
766
780
  }
@@ -770,7 +784,7 @@ var toolDefinitions = [
770
784
  name: "list_integrations",
771
785
  description: "List configured publishing targets (integrations) for a project: WEBHOOK, WORDPRESS, GHOST, WEBFLOW, SHOPIFY, DEVTO, SANITY, CUSTOM_API \u2014 with enabled state.",
772
786
  inputSchema: { ...projectIdArg },
773
- annotations: { readOnlyHint: true },
787
+ annotations: { title: "List Integrations", readOnlyHint: true, destructiveHint: false },
774
788
  async handler(args, ctx) {
775
789
  const projectId = resolveProjectId(ctx, args.project_id);
776
790
  const integrations = await ctx.client.listIntegrations(projectId);
@@ -787,6 +801,12 @@ var toolDefinitions = [
787
801
  method: import_zod.z.enum(["POST", "PUT", "PATCH"]).optional().describe("HTTP method (default POST)"),
788
802
  ...projectIdArg
789
803
  },
804
+ annotations: {
805
+ title: "Create Webhook Integration",
806
+ readOnlyHint: false,
807
+ destructiveHint: false,
808
+ openWorldHint: true
809
+ },
790
810
  async handler(args, ctx) {
791
811
  const projectId = resolveProjectId(ctx, args.project_id);
792
812
  const config = { url: String(args.url) };
@@ -810,6 +830,12 @@ var toolDefinitions = [
810
830
  mode: import_zod.z.enum(["ping", "full"]).optional(),
811
831
  ...projectIdArg
812
832
  },
833
+ annotations: {
834
+ title: "Test Integration",
835
+ readOnlyHint: false,
836
+ destructiveHint: false,
837
+ openWorldHint: true
838
+ },
813
839
  async handler(args, ctx) {
814
840
  const projectId = resolveProjectId(ctx, args.project_id);
815
841
  return json(
@@ -829,6 +855,12 @@ var toolDefinitions = [
829
855
  integration_id: import_zod.z.string().describe("Target integration id"),
830
856
  ...projectIdArg
831
857
  },
858
+ annotations: {
859
+ title: "Publish to CMS",
860
+ readOnlyHint: false,
861
+ destructiveHint: true,
862
+ openWorldHint: true
863
+ },
832
864
  async handler(args, ctx) {
833
865
  const projectId = resolveProjectId(ctx, args.project_id);
834
866
  return json(
@@ -848,6 +880,12 @@ var toolDefinitions = [
848
880
  integration_id: import_zod.z.string(),
849
881
  ...projectIdArg
850
882
  },
883
+ annotations: {
884
+ title: "Retry Publish",
885
+ readOnlyHint: false,
886
+ destructiveHint: true,
887
+ openWorldHint: true
888
+ },
851
889
  async handler(args, ctx) {
852
890
  const projectId = resolveProjectId(ctx, args.project_id);
853
891
  return json(
@@ -867,7 +905,7 @@ var toolDefinitions = [
867
905
  limit: import_zod.z.number().int().min(1).max(200).optional(),
868
906
  ...projectIdArg
869
907
  },
870
- annotations: { readOnlyHint: true },
908
+ annotations: { title: "Publish Logs", readOnlyHint: true, destructiveHint: false },
871
909
  async handler(args, ctx) {
872
910
  const projectId = resolveProjectId(ctx, args.project_id);
873
911
  return json(
@@ -884,7 +922,7 @@ var toolDefinitions = [
884
922
  name: "list_keywords",
885
923
  description: "List tracked keywords for a project (search volume, difficulty, intent, status). Keywords drive topic relevance in generation.",
886
924
  inputSchema: { ...projectIdArg },
887
- annotations: { readOnlyHint: true },
925
+ annotations: { title: "List Keywords", readOnlyHint: true, destructiveHint: false },
888
926
  async handler(args, ctx) {
889
927
  return json(await ctx.client.listKeywords(resolveProjectId(ctx, args.project_id)));
890
928
  }
@@ -899,6 +937,7 @@ var toolDefinitions = [
899
937
  intent: import_zod.z.enum(["INFORMATIONAL", "NAVIGATIONAL", "TRANSACTIONAL", "COMMERCIAL"]).optional(),
900
938
  ...projectIdArg
901
939
  },
940
+ annotations: { title: "Add Keyword", readOnlyHint: false, destructiveHint: false },
902
941
  async handler(args, ctx) {
903
942
  const projectId = resolveProjectId(ctx, args.project_id);
904
943
  return json(
@@ -918,7 +957,7 @@ var toolDefinitions = [
918
957
  active_only: import_zod.z.boolean().optional().describe("Only active facts (default false = all)"),
919
958
  ...projectIdArg
920
959
  },
921
- annotations: { readOnlyHint: true },
960
+ annotations: { title: "List Product Facts", readOnlyHint: true, destructiveHint: false },
922
961
  async handler(args, ctx) {
923
962
  const projectId = resolveProjectId(ctx, args.project_id);
924
963
  return json(
@@ -936,6 +975,7 @@ var toolDefinitions = [
936
975
  active: import_zod.z.boolean().optional().describe("Set active flag when updating"),
937
976
  ...projectIdArg
938
977
  },
978
+ annotations: { title: "Supply Product Fact", readOnlyHint: false, destructiveHint: false },
939
979
  async handler(args, ctx) {
940
980
  const projectId = resolveProjectId(ctx, args.project_id);
941
981
  const category = Array.isArray(args.category) ? args.category.map(String) : void 0;
@@ -966,6 +1006,12 @@ var toolDefinitions = [
966
1006
  limit: import_zod.z.number().int().min(1).max(500).optional().describe("Max articles to export (default 100)"),
967
1007
  ...projectIdArg
968
1008
  },
1009
+ annotations: {
1010
+ title: "Export Blog to Files",
1011
+ readOnlyHint: false,
1012
+ destructiveHint: true,
1013
+ openWorldHint: true
1014
+ },
969
1015
  async handler(args, ctx) {
970
1016
  const projectId = resolveProjectId(ctx, args.project_id);
971
1017
  const status = typeof args.status === "string" ? args.status : "PUBLISHED";
@@ -1002,10 +1048,12 @@ function buildServer(config) {
1002
1048
  register(
1003
1049
  tool.name,
1004
1050
  {
1005
- title: tool.name,
1051
+ // The human-readable title, not the wire name — a picker showing
1052
+ // "approve_publication" tells a person nothing about what it does.
1053
+ title: tool.annotations.title,
1006
1054
  description: tool.description,
1007
1055
  inputSchema: tool.inputSchema,
1008
- ...tool.annotations ? { annotations: tool.annotations } : {}
1056
+ annotations: tool.annotations
1009
1057
  },
1010
1058
  async (args) => {
1011
1059
  let result;