@cliangdev/conductor 0.13.0 → 0.17.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.
package/dist/mcp/api.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { Blob } from 'node:buffer';
1
2
  async function requestWithStatus(method, urlPath, body, config) {
2
3
  const url = `${config.apiUrl}${urlPath}`;
3
4
  const headers = {
@@ -13,7 +14,9 @@ async function requestWithStatus(method, urlPath, body, config) {
13
14
  const text = await response.text().catch(() => '');
14
15
  throw new Error(`API error ${response.status}: ${text}`);
15
16
  }
16
- if (method === 'DELETE') {
17
+ // 204 as well as DELETE: an endpoint that answers "done, nothing to say" (e.g. confirming an upload)
18
+ // has no body to parse, and calling .json() on it throws.
19
+ if (method === 'DELETE' || response.status === 204) {
17
20
  return { data: undefined, status: response.status };
18
21
  }
19
22
  return { data: (await response.json()), status: response.status };
@@ -49,4 +52,45 @@ export async function apiPut(urlPath, body, config) {
49
52
  export async function apiDelete(urlPath, config) {
50
53
  await request('DELETE', urlPath, undefined, config);
51
54
  }
55
+ /**
56
+ * PUTs raw bytes to an absolute upload URL — a signed bucket URL, or the backend's local passthrough.
57
+ * Deliberately sends no Authorization header: the URL itself is the capability, and a bucket signature is
58
+ * computed over an exact header set, so an extra one invalidates it.
59
+ */
60
+ export async function putBytes(url, contentType, bytes) {
61
+ // Copy into a plain ArrayBuffer: the incoming view may be a slice of a pooled Node Buffer.
62
+ const buffer = new ArrayBuffer(bytes.byteLength);
63
+ new Uint8Array(buffer).set(bytes);
64
+ const response = await fetch(url, {
65
+ method: 'PUT',
66
+ headers: { 'Content-Type': contentType },
67
+ body: buffer,
68
+ });
69
+ if (!response.ok) {
70
+ const text = await response.text().catch(() => '');
71
+ throw new Error(`Upload PUT failed ${response.status}: ${text}`);
72
+ }
73
+ }
74
+ /**
75
+ * POSTs a single file as multipart/form-data. Deliberately does not set Content-Type: fetch has to
76
+ * generate it itself so the multipart boundary matches the body it builds.
77
+ */
78
+ export async function apiPostFile(urlPath, file, config) {
79
+ // Copy into a plain ArrayBuffer: the incoming view may be backed by a pooled Node Buffer, which is
80
+ // both a type mismatch for Blob and a slice of a larger allocation.
81
+ const buffer = new ArrayBuffer(file.bytes.byteLength);
82
+ new Uint8Array(buffer).set(file.bytes);
83
+ const form = new FormData();
84
+ form.append(file.fieldName, new Blob([buffer], { type: file.contentType }), file.filename);
85
+ const response = await fetch(`${config.apiUrl}${urlPath}`, {
86
+ method: 'POST',
87
+ headers: { Authorization: `Bearer ${config.apiKey}` },
88
+ body: form,
89
+ });
90
+ if (!response.ok) {
91
+ const text = await response.text().catch(() => '');
92
+ throw new Error(`API error ${response.status}: ${text}`);
93
+ }
94
+ return (await response.json());
95
+ }
52
96
  //# sourceMappingURL=api.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/mcp/api.ts"],"names":[],"mappings":"AAEA,KAAK,UAAU,iBAAiB,CAC9B,MAAc,EACd,OAAe,EACf,IAAyB,EACzB,MAAc;IAEd,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,MAAM,GAAG,OAAO,EAAE,CAAA;IACxC,MAAM,OAAO,GAA2B;QACtC,aAAa,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE;QACxC,cAAc,EAAE,kBAAkB;KACnC,CAAA;IAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,MAAM;QACN,OAAO;QACP,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;KAC5D,CAAC,CAAA;IAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;QAClD,MAAM,IAAI,KAAK,CAAC,aAAa,QAAQ,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAA;IAC1D,CAAC;IAED,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxB,OAAO,EAAE,IAAI,EAAE,SAAc,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAA;IAC1D,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAA;AACxE,CAAC;AAED,KAAK,UAAU,OAAO,CACpB,MAAc,EACd,OAAe,EACf,IAAyB,EACzB,MAAc;IAEd,OAAO,CAAC,MAAM,iBAAiB,CAAI,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;AACzE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,OAAO,GAAG,YAAY,KAAK,IAAI,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;AACtE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAI,OAAe,EAAE,MAAc;IAC7D,OAAO,OAAO,CAAI,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;AACtD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAI,OAAe,EAAE,IAAa,EAAE,MAAc;IAC7E,OAAO,OAAO,CAAI,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;AAClD,CAAC;AAED;0GAC0G;AAC1G,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAe,EACf,IAAa,EACb,MAAc;IAEd,OAAO,iBAAiB,CAAI,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAI,OAAe,EAAE,IAAa,EAAE,MAAc;IAC9E,OAAO,OAAO,CAAI,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;AACnD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAI,OAAe,EAAE,IAAa,EAAE,MAAc;IAC5E,OAAO,OAAO,CAAI,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAe,EAAE,MAAc;IAC7D,MAAM,OAAO,CAAO,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;AAC3D,CAAC"}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/mcp/api.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";AAgnBA,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CA2clD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";AA05BA,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAupBlD"}
package/dist/mcp/index.js CHANGED
@@ -11,6 +11,8 @@ import { listIntegrationTools, listConnectorCatalog } from './tools/integrations
11
11
  import { listAgents, createAgent, updateAgent, deleteAgent } from './tools/agents.js';
12
12
  import { listSkills, registerSkill } from './tools/skills.js';
13
13
  import { submitKnowledgeSource, readKnowledgeSources, searchKnowledge, readKnowledgePages, writeKnowledgePages, listKnowledgeDomains, suggestKnowledgeDomain, } from './tools/knowledge.js';
14
+ import { listPublishTargets, setPublishTargets, uploadAsset, retryFailedPublishTargets, completeManualPublish, } from './tools/marketing.js';
15
+ import { listProjectDocs, readProjectDoc, writeProjectDoc, moveProjectDoc, deleteProjectDoc, setProjectDocTask, listProjectDocComments, commentOnProjectDoc, respondToProjectDocComment, listProjectDocVersions, restoreProjectDocVersion, uploadProjectDocImage, moveProjectFolder, deleteProjectFolder, } from './tools/project-docs.js';
14
16
  const TOOLS = [
15
17
  // --- Canonical Work Item tools (v2 /work-items surface) ---
16
18
  {
@@ -22,20 +24,24 @@ const TOOLS = [
22
24
  workflow: { type: 'string', description: 'Lifecycle Workflow slug that governs this Work Item (required). Discover with list_workflows({kind:"LIFECYCLE"}).' },
23
25
  type: { type: 'string', description: 'Work Item type, validated against the chosen Workflow\'s allowed types (e.g. PRD, FEATURE_REQUEST, BUG_REPORT)' },
24
26
  title: { type: 'string', description: 'Work Item title' },
25
- description: { type: 'string', description: 'Work Item description (optional)' },
27
+ description: { type: 'string', description: 'Work Item description (optional). On a publishing Workflow this is the caption — the text that goes out to the platform — not a note about the item.' },
28
+ tags: { type: 'array', items: { type: 'string' }, description: 'Freeform labels for grouping work across type, status and Workflow. Stored lower-cased and de-duplicated, so "Autumn" and "autumn" are one tag. Sent whole: omit to leave existing tags alone, send [] to clear them.' },
26
29
  },
27
30
  required: ['workflow', 'type', 'title'],
28
31
  },
29
32
  },
30
33
  {
31
34
  name: 'update_work_item',
32
- description: 'Update an existing Work Item. Canonical tool (targets the v2 work-items API).',
35
+ description: 'Update an existing Work Item\'s title, description, tags, or schedule. On a publishing Workflow the description is the caption that goes out to the platform, not a note. Canonical tool (targets the v2 work-items API). A field you omit is left unchanged. Verify with get_work_item.',
33
36
  inputSchema: {
34
37
  type: 'object',
35
38
  properties: {
36
39
  issueId: { type: 'string', description: 'Work Item ID' },
37
40
  title: { type: 'string', description: 'New title (optional)' },
38
41
  description: { type: 'string', description: 'New description (optional)' },
42
+ scheduledFor: { type: 'string', description: 'ISO-8601 date-time this Work Item is due to fire (optional). On a Post this is when its publish targets go out.' },
43
+ scheduleTimezone: { type: 'string', description: 'IANA zone id the schedule is authored in, e.g. Europe/Berlin (optional). Empty string clears it; an unknown zone is rejected.' },
44
+ tags: { type: 'array', items: { type: 'string' }, description: 'Freeform labels for grouping work across type, status and Workflow. Stored lower-cased and de-duplicated, so "Autumn" and "autumn" are one tag. Sent whole: omit to leave existing tags alone, send [] to clear them.' },
39
45
  },
40
46
  required: ['issueId'],
41
47
  },
@@ -61,6 +67,7 @@ const TOOLS = [
61
67
  type: { type: 'string', description: 'Filter by type (optional)' },
62
68
  status: { type: 'string', description: 'Filter by status (optional)' },
63
69
  workflow: { type: 'string', description: 'Filter by bound Workflow slug (optional, e.g. ENGINEERING)' },
70
+ tag: { type: 'string', description: 'Filter to Work Items carrying this tag (optional). Matched case-insensitively.' },
64
71
  },
65
72
  },
66
73
  },
@@ -552,6 +559,274 @@ const TOOLS = [
552
559
  required: ['slug', 'displayName', 'reason'],
553
560
  },
554
561
  },
562
+ // --- Project Docs (the project's own document tree, not Work Item documents) ---
563
+ {
564
+ name: 'list_project_docs',
565
+ description: "Map the project's Docs section: every document as a \"Folder/Title\" path with its docId, plus each folder with its docCount and lastUpdatedAt so you can judge which parts of the tree carry weight or have gone stale. Pass query to full-text search instead. Call this first to orient before reading or writing project docs.",
566
+ inputSchema: {
567
+ type: 'object',
568
+ properties: {
569
+ folder: { type: 'string', description: 'Limit to one folder path, e.g. "Plans/Q3" (omit for the whole tree)' },
570
+ query: { type: 'string', description: 'Full-text search over titles and content; returns matches with snippets instead of the tree' },
571
+ },
572
+ required: [],
573
+ },
574
+ },
575
+ {
576
+ name: 'read_project_doc',
577
+ description: 'Read one project document by path ("Plans/Q3 Roadmap") or docId. Returns full Markdown content plus taskLines — every checkbox with its 1-based line number and state, which is what set_project_doc_task takes. Pass versionNumber (from list_project_doc_versions) to read an earlier version instead of the current one.',
578
+ inputSchema: {
579
+ type: 'object',
580
+ properties: {
581
+ path: { type: 'string', description: 'Document path, e.g. "Plans/Q3 Roadmap"' },
582
+ docId: { type: 'string', description: 'Document ID — use instead of path when the title contains "/"' },
583
+ versionNumber: { type: 'number', description: 'Read this historical version instead of current content' },
584
+ },
585
+ required: [],
586
+ },
587
+ },
588
+ {
589
+ name: 'write_project_doc',
590
+ description: 'Create or replace a project document at a path, creating any missing folders (upsert). Replaces the whole document, records a version, and marks unresolved line comments stale — to tick a checkbox use set_project_doc_task instead, which does none of that. Verify with read_project_doc.',
591
+ inputSchema: {
592
+ type: 'object',
593
+ properties: {
594
+ path: { type: 'string', description: 'Destination path, e.g. "Plans/Q3 Roadmap" — folder segments are created if missing' },
595
+ content: { type: 'string', description: 'Full Markdown content' },
596
+ },
597
+ required: ['path', 'content'],
598
+ },
599
+ },
600
+ {
601
+ name: 'move_project_doc',
602
+ description: 'Rename a project document and/or move it to another folder, creating missing folders in the destination path. Verify with list_project_docs.',
603
+ inputSchema: {
604
+ type: 'object',
605
+ properties: {
606
+ path: { type: 'string', description: 'Current document path' },
607
+ docId: { type: 'string', description: 'Document ID — use instead of path' },
608
+ newPath: { type: 'string', description: 'New path, e.g. "Archive/Q2 Roadmap"' },
609
+ },
610
+ required: ['newPath'],
611
+ },
612
+ },
613
+ {
614
+ name: 'delete_project_doc',
615
+ description: 'Delete a project document. Irreversible — its version history and comments go with it. Verify with list_project_docs.',
616
+ inputSchema: {
617
+ type: 'object',
618
+ properties: {
619
+ path: { type: 'string', description: 'Document path' },
620
+ docId: { type: 'string', description: 'Document ID — use instead of path' },
621
+ },
622
+ required: [],
623
+ },
624
+ },
625
+ {
626
+ name: 'set_project_doc_task',
627
+ description: 'Check or uncheck one Markdown checkbox in a project document. lineNumber is 1-based against raw content including frontmatter — take it from read_project_doc\'s taskLines. Unlike write_project_doc this records no version and leaves comment anchors valid. Idempotent; returns conflict: true if that line is no longer a task item.',
628
+ inputSchema: {
629
+ type: 'object',
630
+ properties: {
631
+ path: { type: 'string', description: 'Document path' },
632
+ docId: { type: 'string', description: 'Document ID — use instead of path' },
633
+ lineNumber: { type: 'number', description: '1-based line number of the task item (from read_project_doc taskLines)' },
634
+ checked: { type: 'boolean', description: 'true to check the box, false to uncheck' },
635
+ },
636
+ required: ['lineNumber', 'checked'],
637
+ },
638
+ },
639
+ {
640
+ name: 'list_project_doc_comments',
641
+ description: 'Read the comments humans left on a project document — each with its anchored lineNumber, quotedText, lineStale flag and replies. Unresolved only by default. Answer them with respond_to_project_doc_comment.',
642
+ inputSchema: {
643
+ type: 'object',
644
+ properties: {
645
+ path: { type: 'string', description: 'Document path' },
646
+ docId: { type: 'string', description: 'Document ID — use instead of path' },
647
+ includeResolved: { type: 'boolean', description: 'Include already-resolved threads (default false)' },
648
+ },
649
+ required: [],
650
+ },
651
+ },
652
+ {
653
+ name: 'comment_on_project_doc',
654
+ description: 'Leave a comment anchored to one line of a project document. lineNumber is 1-based against raw content including frontmatter. Verify with list_project_doc_comments.',
655
+ inputSchema: {
656
+ type: 'object',
657
+ properties: {
658
+ path: { type: 'string', description: 'Document path' },
659
+ docId: { type: 'string', description: 'Document ID — use instead of path' },
660
+ lineNumber: { type: 'number', description: '1-based line number to anchor the comment to' },
661
+ content: { type: 'string', description: 'Comment text' },
662
+ quotedText: { type: 'string', description: 'Optional snippet of the line being commented on, shown as context' },
663
+ },
664
+ required: ['lineNumber', 'content'],
665
+ },
666
+ },
667
+ {
668
+ name: 'respond_to_project_doc_comment',
669
+ description: 'Answer a comment thread on a project document: post a reply, mark it resolved, or both. Verify with list_project_doc_comments.',
670
+ inputSchema: {
671
+ type: 'object',
672
+ properties: {
673
+ path: { type: 'string', description: 'Document path' },
674
+ docId: { type: 'string', description: 'Document ID — use instead of path' },
675
+ commentId: { type: 'string', description: 'ID of the comment thread (from list_project_doc_comments)' },
676
+ reply: { type: 'string', description: 'Reply text to post on the thread' },
677
+ resolve: { type: 'boolean', description: 'true to mark the thread resolved' },
678
+ },
679
+ required: ['commentId'],
680
+ },
681
+ },
682
+ {
683
+ name: 'list_project_doc_versions',
684
+ description: 'List a project document\'s edit history — versionNumber, who wrote it and when, newest first. Every full-content write adds one; ticking a checkbox does not. Read one with read_project_doc versionNumber, or roll back with restore_project_doc_version.',
685
+ inputSchema: {
686
+ type: 'object',
687
+ properties: {
688
+ path: { type: 'string', description: 'Document path' },
689
+ docId: { type: 'string', description: 'Document ID — use instead of path' },
690
+ },
691
+ required: [],
692
+ },
693
+ },
694
+ {
695
+ name: 'restore_project_doc_version',
696
+ description: 'Roll a project document back to an earlier version (from list_project_doc_versions). Appends the old content as a new version rather than rewinding, so the restore is itself undoable. Verify with read_project_doc.',
697
+ inputSchema: {
698
+ type: 'object',
699
+ properties: {
700
+ path: { type: 'string', description: 'Document path' },
701
+ docId: { type: 'string', description: 'Document ID — use instead of path' },
702
+ versionNumber: { type: 'number', description: 'Version to restore, from list_project_doc_versions' },
703
+ },
704
+ required: ['versionNumber'],
705
+ },
706
+ },
707
+ {
708
+ name: 'upload_project_doc_image',
709
+ description: 'Upload a local image file (png, jpeg, gif, webp) to a project document and get the Markdown snippet to embed. Include that snippet in the content you pass to write_project_doc — the link is stored as a stable reference and re-signed on every read, so it will not expire.',
710
+ inputSchema: {
711
+ type: 'object',
712
+ properties: {
713
+ path: { type: 'string', description: 'Document path the image belongs to' },
714
+ docId: { type: 'string', description: 'Document ID — use instead of path' },
715
+ filePath: { type: 'string', description: 'Path to the image file on this machine' },
716
+ },
717
+ required: ['filePath'],
718
+ },
719
+ },
720
+ {
721
+ name: 'move_project_folder',
722
+ description: 'Rename a docs folder and/or move it under another one, taking its whole subtree with it. Missing folders in the destination path are created. Cannot move a folder into its own subtree. Verify with list_project_docs.',
723
+ inputSchema: {
724
+ type: 'object',
725
+ properties: {
726
+ path: { type: 'string', description: 'Current folder path, e.g. "Plans/Q3"' },
727
+ newPath: { type: 'string', description: 'New folder path, e.g. "Archive/2026/Q3"' },
728
+ },
729
+ required: ['path', 'newPath'],
730
+ },
731
+ },
732
+ {
733
+ name: 'delete_project_folder',
734
+ description: 'Delete a docs folder and its subfolders. Not destructive to content: documents inside are kept and resurface at the project root. Use it to clear away empty or obsolete folders after reorganising. Verify with list_project_docs.',
735
+ inputSchema: {
736
+ type: 'object',
737
+ properties: {
738
+ path: { type: 'string', description: 'Folder path to delete, e.g. "Archive/Old"' },
739
+ },
740
+ required: ['path'],
741
+ },
742
+ },
743
+ {
744
+ name: 'list_publish_targets',
745
+ description: 'Publishing destinations for a project: each connected account with its platform, connectionId, label, lane and credential health. Pass issueId to also get the targets that Work Item publishes to, each with its state, permalink and error — the read-back for set_publish_targets and the status check after a scheduled publish or a retry. TikTok entries carry the creator\'s allowed privacy levels and nickname; they are the only source for a target\'s privacyLevel.',
746
+ inputSchema: {
747
+ type: 'object',
748
+ properties: {
749
+ issueId: { type: 'string', description: 'Work Item ID — include to also return that item\'s selected targets and their publish outcomes (optional)' },
750
+ },
751
+ },
752
+ },
753
+ {
754
+ name: 'set_publish_targets',
755
+ description: 'Choose which connected accounts a Work Item publishes to, with per-target options. Idempotent set-replace: send the complete selection (empty array clears it); already-selected targets keep their state. Discover platform/connectionId pairs with list_publish_targets first and call it again after to verify. Editing the selection on an approved item sends it back for review. Publishing to TikTok also needs the creator\'s consent, which a human records in the Conductor UI — no tool can record or skip it.',
756
+ inputSchema: {
757
+ type: 'object',
758
+ properties: {
759
+ issueId: { type: 'string', description: 'Work Item ID' },
760
+ targets: {
761
+ type: 'array',
762
+ description: 'The complete selection. Empty array clears every target.',
763
+ items: {
764
+ type: 'object',
765
+ properties: {
766
+ platform: { type: 'string', description: 'Platform of the account, from list_publish_targets' },
767
+ connectionId: { type: 'string', description: 'The connected account to publish to, from list_publish_targets. Omit it to select this platform\'s manual destination — the one a human posts by hand — which is offered whether or not the project has connected that platform.' },
768
+ publishOptions: {
769
+ type: 'object',
770
+ description: 'Per-platform options for this target. A TikTok target with no privacyLevel blocks approval.',
771
+ properties: {
772
+ privacyLevel: { type: 'string', description: 'TikTok: one of that account\'s privacyLevelOptions from list_publish_targets' },
773
+ disableComment: { type: 'boolean', description: 'TikTok: turn comments off' },
774
+ disableDuet: { type: 'boolean', description: 'TikTok: turn duets off' },
775
+ disableStitch: { type: 'boolean', description: 'TikTok: turn stitches off' },
776
+ brandContentToggle: { type: 'boolean', description: 'TikTok: paid partnership; TikTok refuses it combined with SELF_ONLY' },
777
+ brandOrganicToggle: { type: 'boolean', description: 'TikTok: promoting the creator\'s own brand' },
778
+ },
779
+ },
780
+ },
781
+ required: ['platform'],
782
+ },
783
+ },
784
+ },
785
+ required: ['issueId', 'targets'],
786
+ },
787
+ },
788
+ {
789
+ name: 'upload_asset',
790
+ description: 'Attach a local file to a Work Item as a file Asset in one call — mints the upload URL, uploads the bytes and confirms. filePath is a path on this machine (this server runs locally). The server enforces the media-type allowlist and size ceiling; a refused file leaves no Asset behind. Video: pass width, height and durationSeconds — they cannot be derived server-side and approval stays blocked without them. Returns the stored Asset as read back after confirmation. Use record_asset instead for a link.',
791
+ inputSchema: {
792
+ type: 'object',
793
+ properties: {
794
+ issueId: { type: 'string', description: 'Work Item ID' },
795
+ filePath: { type: 'string', description: 'Absolute path to the file on this machine' },
796
+ type: { type: 'string', description: 'Asset type, validated against the Workflow\'s asset_types' },
797
+ label: { type: 'string', description: 'Human label (optional — defaults to the filename)' },
798
+ width: { type: 'number', description: 'Pixel width of the media (optional; required in practice for video)' },
799
+ height: { type: 'number', description: 'Pixel height of the media (optional; required in practice for video)' },
800
+ durationSeconds: { type: 'number', description: 'Playback length in seconds (optional; required in practice for video)' },
801
+ },
802
+ required: ['issueId', 'filePath', 'type'],
803
+ },
804
+ },
805
+ {
806
+ name: 'complete_manual_publish',
807
+ description: 'Record that a manual publish target was published by hand, storing the live URL. Only for targets on the MANUAL lane (list_publish_targets shows lane and state; a manual one due to go out is AWAITING_MANUAL) — a target that publishes automatically is refused, because its poller will publish it and report the real outcome. This reports what a human already did outside Conductor; it publishes nothing. Records the same destination Asset an API publish does, so the link surfaces on the calendar and list like any other. Returns the target as read back after recording.',
808
+ inputSchema: {
809
+ type: 'object',
810
+ properties: {
811
+ issueId: { type: 'string', description: 'Work Item ID' },
812
+ targetId: { type: 'string', description: 'The publish target\'s id, from list_publish_targets' },
813
+ permalink: { type: 'string', description: 'Link to the post that actually went out. Required — with no platform to ask, it is the only record this destination was published.' },
814
+ publishedAt: { type: 'string', description: 'ISO-8601 instant it actually went out (optional; defaults to now)' },
815
+ },
816
+ required: ['issueId', 'targetId', 'permalink'],
817
+ },
818
+ },
819
+ {
820
+ name: 'retry_failed_publish_targets',
821
+ description: 'Re-fire only the FAILED publish targets on a Work Item, with a fresh idempotency key. Published targets are untouched and nothing failed is a harmless no-op. Returns the item\'s status and every target. Publishing is asynchronous — call list_publish_targets after to see where each target landed.',
822
+ inputSchema: {
823
+ type: 'object',
824
+ properties: {
825
+ issueId: { type: 'string', description: 'Work Item ID' },
826
+ },
827
+ required: ['issueId'],
828
+ },
829
+ },
555
830
  ];
556
831
  function authErrorResponse() {
557
832
  return {
@@ -624,6 +899,7 @@ export async function runMcpServer() {
624
899
  type: params['type'],
625
900
  title: params['title'],
626
901
  description: params['description'],
902
+ tags: params['tags'],
627
903
  }, config);
628
904
  return successResponse(result);
629
905
  }
@@ -805,6 +1081,9 @@ export async function runMcpServer() {
805
1081
  issueId: params['issueId'],
806
1082
  title: params['title'],
807
1083
  description: params['description'],
1084
+ scheduledFor: params['scheduledFor'],
1085
+ scheduleTimezone: params['scheduleTimezone'],
1086
+ tags: params['tags'],
808
1087
  }, config);
809
1088
  return successResponse(result);
810
1089
  }
@@ -820,6 +1099,7 @@ export async function runMcpServer() {
820
1099
  type: params['type'],
821
1100
  status: params['status'],
822
1101
  workflow: params['workflow'],
1102
+ tag: params['tag'],
823
1103
  }, config);
824
1104
  return successResponse(result);
825
1105
  }
@@ -887,6 +1167,146 @@ export async function runMcpServer() {
887
1167
  }, config);
888
1168
  return successResponse(result);
889
1169
  }
1170
+ // --- Project Docs ---
1171
+ case 'list_project_docs': {
1172
+ const result = await listProjectDocs({
1173
+ folder: params['folder'],
1174
+ query: params['query'],
1175
+ }, config);
1176
+ return successResponse(result);
1177
+ }
1178
+ case 'read_project_doc': {
1179
+ const result = await readProjectDoc({
1180
+ path: params['path'],
1181
+ docId: params['docId'],
1182
+ versionNumber: params['versionNumber'],
1183
+ }, config);
1184
+ return successResponse(result);
1185
+ }
1186
+ case 'write_project_doc': {
1187
+ const result = await writeProjectDoc({
1188
+ path: params['path'],
1189
+ content: params['content'],
1190
+ }, config);
1191
+ return successResponse(result);
1192
+ }
1193
+ case 'move_project_doc': {
1194
+ const result = await moveProjectDoc({
1195
+ path: params['path'],
1196
+ docId: params['docId'],
1197
+ newPath: params['newPath'],
1198
+ }, config);
1199
+ return successResponse(result);
1200
+ }
1201
+ case 'delete_project_doc': {
1202
+ const result = await deleteProjectDoc({
1203
+ path: params['path'],
1204
+ docId: params['docId'],
1205
+ }, config);
1206
+ return successResponse(result);
1207
+ }
1208
+ case 'set_project_doc_task': {
1209
+ const result = await setProjectDocTask({
1210
+ path: params['path'],
1211
+ docId: params['docId'],
1212
+ lineNumber: params['lineNumber'],
1213
+ checked: params['checked'],
1214
+ }, config);
1215
+ return successResponse(result);
1216
+ }
1217
+ case 'list_project_doc_comments': {
1218
+ const result = await listProjectDocComments({
1219
+ path: params['path'],
1220
+ docId: params['docId'],
1221
+ includeResolved: params['includeResolved'],
1222
+ }, config);
1223
+ return successResponse(result);
1224
+ }
1225
+ case 'comment_on_project_doc': {
1226
+ const result = await commentOnProjectDoc({
1227
+ path: params['path'],
1228
+ docId: params['docId'],
1229
+ lineNumber: params['lineNumber'],
1230
+ content: params['content'],
1231
+ quotedText: params['quotedText'],
1232
+ }, config);
1233
+ return successResponse(result);
1234
+ }
1235
+ case 'respond_to_project_doc_comment': {
1236
+ const result = await respondToProjectDocComment({
1237
+ path: params['path'],
1238
+ docId: params['docId'],
1239
+ commentId: params['commentId'],
1240
+ reply: params['reply'],
1241
+ resolve: params['resolve'],
1242
+ }, config);
1243
+ return successResponse(result);
1244
+ }
1245
+ case 'list_project_doc_versions': {
1246
+ const result = await listProjectDocVersions({
1247
+ path: params['path'],
1248
+ docId: params['docId'],
1249
+ }, config);
1250
+ return successResponse(result);
1251
+ }
1252
+ case 'restore_project_doc_version': {
1253
+ const result = await restoreProjectDocVersion({
1254
+ path: params['path'],
1255
+ docId: params['docId'],
1256
+ versionNumber: params['versionNumber'],
1257
+ }, config);
1258
+ return successResponse(result);
1259
+ }
1260
+ case 'move_project_folder': {
1261
+ const result = await moveProjectFolder({
1262
+ path: params['path'],
1263
+ newPath: params['newPath'],
1264
+ }, config);
1265
+ return successResponse(result);
1266
+ }
1267
+ case 'delete_project_folder': {
1268
+ const result = await deleteProjectFolder({ path: params['path'] }, config);
1269
+ return successResponse(result);
1270
+ }
1271
+ case 'list_publish_targets': {
1272
+ return successResponse(await listPublishTargets({ issueId: params['issueId'] }, config));
1273
+ }
1274
+ case 'set_publish_targets': {
1275
+ return successResponse(await setPublishTargets({
1276
+ issueId: params['issueId'],
1277
+ targets: params['targets'],
1278
+ }, config));
1279
+ }
1280
+ case 'upload_asset': {
1281
+ return successResponse(await uploadAsset({
1282
+ issueId: params['issueId'],
1283
+ filePath: params['filePath'],
1284
+ type: params['type'],
1285
+ label: params['label'],
1286
+ width: params['width'],
1287
+ height: params['height'],
1288
+ durationSeconds: params['durationSeconds'],
1289
+ }, config));
1290
+ }
1291
+ case 'complete_manual_publish': {
1292
+ return successResponse(await completeManualPublish({
1293
+ issueId: params['issueId'],
1294
+ targetId: params['targetId'],
1295
+ permalink: params['permalink'],
1296
+ publishedAt: params['publishedAt'],
1297
+ }, config));
1298
+ }
1299
+ case 'retry_failed_publish_targets': {
1300
+ return successResponse(await retryFailedPublishTargets({ issueId: params['issueId'] }, config));
1301
+ }
1302
+ case 'upload_project_doc_image': {
1303
+ const result = await uploadProjectDocImage({
1304
+ path: params['path'],
1305
+ docId: params['docId'],
1306
+ filePath: params['filePath'],
1307
+ }, config);
1308
+ return successResponse(result);
1309
+ }
890
1310
  default:
891
1311
  return errorResponse(`Unknown tool: ${name}`);
892
1312
  }