@thenavidm/threads-mcp-cli 1.1.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.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1019 -0
  3. package/SKILL.md +203 -0
  4. package/dist/api/client.d.ts +105 -0
  5. package/dist/api/client.js +305 -0
  6. package/dist/api/client.js.map +1 -0
  7. package/dist/api/errors.d.ts +92 -0
  8. package/dist/api/errors.js +195 -0
  9. package/dist/api/errors.js.map +1 -0
  10. package/dist/api/identity.d.ts +33 -0
  11. package/dist/api/identity.js +52 -0
  12. package/dist/api/identity.js.map +1 -0
  13. package/dist/auth/login.d.ts +32 -0
  14. package/dist/auth/login.js +204 -0
  15. package/dist/auth/login.js.map +1 -0
  16. package/dist/auth/store.d.ts +37 -0
  17. package/dist/auth/store.js +88 -0
  18. package/dist/auth/store.js.map +1 -0
  19. package/dist/auth/tokens.d.ts +54 -0
  20. package/dist/auth/tokens.js +96 -0
  21. package/dist/auth/tokens.js.map +1 -0
  22. package/dist/cli.d.ts +59 -0
  23. package/dist/cli.js +444 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/config.d.ts +98 -0
  26. package/dist/config.js +185 -0
  27. package/dist/config.js.map +1 -0
  28. package/dist/content/containers.d.ts +89 -0
  29. package/dist/content/containers.js +210 -0
  30. package/dist/content/containers.js.map +1 -0
  31. package/dist/content/media.d.ts +61 -0
  32. package/dist/content/media.js +125 -0
  33. package/dist/content/media.js.map +1 -0
  34. package/dist/content/text.d.ts +68 -0
  35. package/dist/content/text.js +106 -0
  36. package/dist/content/text.js.map +1 -0
  37. package/dist/doctor.d.ts +14 -0
  38. package/dist/doctor.js +218 -0
  39. package/dist/doctor.js.map +1 -0
  40. package/dist/format/posts.d.ts +41 -0
  41. package/dist/format/posts.js +153 -0
  42. package/dist/format/posts.js.map +1 -0
  43. package/dist/index.d.ts +13 -0
  44. package/dist/index.js +167 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/safety.d.ts +52 -0
  47. package/dist/safety.js +85 -0
  48. package/dist/safety.js.map +1 -0
  49. package/dist/server.d.ts +20 -0
  50. package/dist/server.js +232 -0
  51. package/dist/server.js.map +1 -0
  52. package/dist/tools/accounts.d.ts +27 -0
  53. package/dist/tools/accounts.js +162 -0
  54. package/dist/tools/accounts.js.map +1 -0
  55. package/dist/tools/discover.d.ts +56 -0
  56. package/dist/tools/discover.js +146 -0
  57. package/dist/tools/discover.js.map +1 -0
  58. package/dist/tools/index.d.ts +3 -0
  59. package/dist/tools/index.js +16 -0
  60. package/dist/tools/index.js.map +1 -0
  61. package/dist/tools/insights.d.ts +55 -0
  62. package/dist/tools/insights.js +223 -0
  63. package/dist/tools/insights.js.map +1 -0
  64. package/dist/tools/kit.d.ts +90 -0
  65. package/dist/tools/kit.js +119 -0
  66. package/dist/tools/kit.js.map +1 -0
  67. package/dist/tools/posts.d.ts +170 -0
  68. package/dist/tools/posts.js +312 -0
  69. package/dist/tools/posts.js.map +1 -0
  70. package/dist/tools/read.d.ts +31 -0
  71. package/dist/tools/read.js +95 -0
  72. package/dist/tools/read.js.map +1 -0
  73. package/dist/tools/replies.d.ts +92 -0
  74. package/dist/tools/replies.js +218 -0
  75. package/dist/tools/replies.js.map +1 -0
  76. package/dist/transport/http.d.ts +28 -0
  77. package/dist/transport/http.js +103 -0
  78. package/dist/transport/http.js.map +1 -0
  79. package/package.json +65 -0
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Replies, conversations, and moderation of both.
3
+ *
4
+ * Threads exposes three different reply views and they are not
5
+ * interchangeable, which is the single most common source of confusion here:
6
+ *
7
+ * /{post-id}/replies direct replies to one post, one level deep
8
+ * /{post-id}/conversation the whole tree under one post, flattened
9
+ * /{user-id}/replies every reply you have received, across all posts
10
+ *
11
+ * The third is the one an agent actually wants when asked "what needs
12
+ * answering", and it is the one nothing surfaces by default.
13
+ */
14
+ import { z } from "zod";
15
+ export declare const replyToPost: import("./kit.js").ToolSpec<{
16
+ confirm: z.ZodOptional<z.ZodBoolean>;
17
+ account: z.ZodOptional<z.ZodString>;
18
+ id: z.ZodString;
19
+ text: z.ZodString;
20
+ image_url: z.ZodOptional<z.ZodString>;
21
+ video_url: z.ZodOptional<z.ZodString>;
22
+ alt_text: z.ZodOptional<z.ZodString>;
23
+ }>;
24
+ export declare const getReplies: import("./kit.js").ToolSpec<{
25
+ account: z.ZodOptional<z.ZodString>;
26
+ limit: z.ZodOptional<z.ZodNumber>;
27
+ cursor: z.ZodOptional<z.ZodString>;
28
+ id: z.ZodString;
29
+ reverse: z.ZodOptional<z.ZodBoolean>;
30
+ }>;
31
+ export declare const getConversation: import("./kit.js").ToolSpec<{
32
+ account: z.ZodOptional<z.ZodString>;
33
+ limit: z.ZodOptional<z.ZodNumber>;
34
+ cursor: z.ZodOptional<z.ZodString>;
35
+ id: z.ZodString;
36
+ reverse: z.ZodOptional<z.ZodBoolean>;
37
+ }>;
38
+ export declare const getAllReplies: import("./kit.js").ToolSpec<{
39
+ account: z.ZodOptional<z.ZodString>;
40
+ limit: z.ZodOptional<z.ZodNumber>;
41
+ cursor: z.ZodOptional<z.ZodString>;
42
+ since_hours: z.ZodOptional<z.ZodNumber>;
43
+ }>;
44
+ export declare const hideReply: import("./kit.js").ToolSpec<{
45
+ account: z.ZodOptional<z.ZodString>;
46
+ reply_id: z.ZodString;
47
+ hide: z.ZodOptional<z.ZodBoolean>;
48
+ }>;
49
+ export declare const getPendingReplies: import("./kit.js").ToolSpec<{
50
+ account: z.ZodOptional<z.ZodString>;
51
+ limit: z.ZodOptional<z.ZodNumber>;
52
+ cursor: z.ZodOptional<z.ZodString>;
53
+ }>;
54
+ export declare const managePendingReply: import("./kit.js").ToolSpec<{
55
+ confirm: z.ZodOptional<z.ZodBoolean>;
56
+ account: z.ZodOptional<z.ZodString>;
57
+ reply_id: z.ZodString;
58
+ action: z.ZodEnum<["approve", "ignore"]>;
59
+ }>;
60
+ export declare const REPLY_TOOLS: (import("./kit.js").ToolSpec<{
61
+ confirm: z.ZodOptional<z.ZodBoolean>;
62
+ account: z.ZodOptional<z.ZodString>;
63
+ id: z.ZodString;
64
+ text: z.ZodString;
65
+ image_url: z.ZodOptional<z.ZodString>;
66
+ video_url: z.ZodOptional<z.ZodString>;
67
+ alt_text: z.ZodOptional<z.ZodString>;
68
+ }> | import("./kit.js").ToolSpec<{
69
+ account: z.ZodOptional<z.ZodString>;
70
+ limit: z.ZodOptional<z.ZodNumber>;
71
+ cursor: z.ZodOptional<z.ZodString>;
72
+ id: z.ZodString;
73
+ reverse: z.ZodOptional<z.ZodBoolean>;
74
+ }> | import("./kit.js").ToolSpec<{
75
+ account: z.ZodOptional<z.ZodString>;
76
+ limit: z.ZodOptional<z.ZodNumber>;
77
+ cursor: z.ZodOptional<z.ZodString>;
78
+ since_hours: z.ZodOptional<z.ZodNumber>;
79
+ }> | import("./kit.js").ToolSpec<{
80
+ account: z.ZodOptional<z.ZodString>;
81
+ reply_id: z.ZodString;
82
+ hide: z.ZodOptional<z.ZodBoolean>;
83
+ }> | import("./kit.js").ToolSpec<{
84
+ account: z.ZodOptional<z.ZodString>;
85
+ limit: z.ZodOptional<z.ZodNumber>;
86
+ cursor: z.ZodOptional<z.ZodString>;
87
+ }> | import("./kit.js").ToolSpec<{
88
+ confirm: z.ZodOptional<z.ZodBoolean>;
89
+ account: z.ZodOptional<z.ZodString>;
90
+ reply_id: z.ZodString;
91
+ action: z.ZodEnum<["approve", "ignore"]>;
92
+ }>)[];
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Replies, conversations, and moderation of both.
3
+ *
4
+ * Threads exposes three different reply views and they are not
5
+ * interchangeable, which is the single most common source of confusion here:
6
+ *
7
+ * /{post-id}/replies direct replies to one post, one level deep
8
+ * /{post-id}/conversation the whole tree under one post, flattened
9
+ * /{user-id}/replies every reply you have received, across all posts
10
+ *
11
+ * The third is the one an agent actually wants when asked "what needs
12
+ * answering", and it is the one nothing surfaces by default.
13
+ */
14
+ import { z } from "zod";
15
+ import { accountArg, clamp, confirmArg, defineTool, pageArgs, snippet } from "./kit.js";
16
+ import { REPLY_FIELDS } from "../api/client.js";
17
+ import { cursorOf, dataOf, renderList } from "../format/posts.js";
18
+ import { requireMediaId } from "../api/identity.js";
19
+ import { publish } from "../content/containers.js";
20
+ export const replyToPost = defineTool({
21
+ name: "reply_to",
22
+ title: "Reply to a post",
23
+ description: "Publish a reply to a Threads post, yours or anyone's. Public the moment it runs and counted against the 1,000-reply daily quota, so it needs confirm: true.",
24
+ schema: {
25
+ id: z.string().describe("Numeric id of the post being replied to."),
26
+ text: z.string().describe("The reply. 500 characters."),
27
+ image_url: z.string().optional().describe("Optional public HTTPS image URL."),
28
+ video_url: z.string().optional().describe("Optional public HTTPS video URL."),
29
+ alt_text: z.string().optional(),
30
+ ...accountArg,
31
+ ...confirmArg,
32
+ },
33
+ risk: "destructive",
34
+ summary: (args) => `reply to Threads post ${args.id}: "${snippet(args.text)}"`,
35
+ handler: async (args, ctx) => {
36
+ const account = ctx.account(args.account);
37
+ return publish(ctx.client, account, {
38
+ text: args.text,
39
+ image_url: args.image_url,
40
+ video_url: args.video_url,
41
+ alt_text: args.alt_text,
42
+ reply_to_id: requireMediaId(args.id),
43
+ });
44
+ },
45
+ });
46
+ export const getReplies = defineTool({
47
+ name: "get_replies",
48
+ title: "Read direct replies to a post",
49
+ description: "The direct replies to one post, one level deep. For the whole tree underneath it, including replies to replies, use get_conversation.",
50
+ schema: {
51
+ id: z.string().describe("Numeric id of the post."),
52
+ reverse: z.boolean().optional().describe("Oldest first. Defaults to newest first."),
53
+ ...pageArgs,
54
+ ...accountArg,
55
+ },
56
+ risk: "read",
57
+ handler: async (args, ctx) => {
58
+ const account = ctx.account(args.account);
59
+ const id = requireMediaId(args.id);
60
+ const response = (await ctx.client.call(account, `/${id}/replies`, {
61
+ params: {
62
+ fields: REPLY_FIELDS,
63
+ limit: clamp(args.limit, 25),
64
+ after: args.cursor,
65
+ reverse: args.reverse === true ? "true" : undefined,
66
+ },
67
+ }));
68
+ return renderList(dataOf(response), {
69
+ source: "replies",
70
+ cursor: cursorOf(response),
71
+ meta: { post_id: id },
72
+ }, "reply");
73
+ },
74
+ });
75
+ export const getConversation = defineTool({
76
+ name: "get_conversation",
77
+ title: "Read a whole conversation",
78
+ description: "Every reply under one of your posts, including replies to replies, flattened into one list with each reply naming its parent. Only works on posts you own. This is what to read before deciding what deserves an answer.",
79
+ schema: {
80
+ id: z.string().describe("Numeric id of one of your posts."),
81
+ reverse: z.boolean().optional(),
82
+ ...pageArgs,
83
+ ...accountArg,
84
+ },
85
+ risk: "read",
86
+ handler: async (args, ctx) => {
87
+ const account = ctx.account(args.account);
88
+ const id = requireMediaId(args.id);
89
+ const response = (await ctx.client.call(account, `/${id}/conversation`, {
90
+ params: {
91
+ fields: REPLY_FIELDS,
92
+ limit: clamp(args.limit, 50),
93
+ after: args.cursor,
94
+ reverse: args.reverse === true ? "true" : undefined,
95
+ },
96
+ }));
97
+ return renderList(dataOf(response), {
98
+ source: "conversation",
99
+ cursor: cursorOf(response),
100
+ meta: { root_post_id: id },
101
+ }, "reply");
102
+ },
103
+ });
104
+ export const getAllReplies = defineTool({
105
+ name: "get_all_replies",
106
+ title: "Read every reply across all your posts",
107
+ description: "Every reply you have received, newest first, across every post. This is the inbox view: use it to find what needs answering without walking each post one at a time.",
108
+ schema: {
109
+ since_hours: z
110
+ .number()
111
+ .optional()
112
+ .describe("Only replies from the last N hours. Filtered after fetching, so pair it with a larger limit."),
113
+ ...pageArgs,
114
+ ...accountArg,
115
+ },
116
+ risk: "read",
117
+ handler: async (args, ctx) => {
118
+ const account = ctx.account(args.account);
119
+ const userId = await ctx.client.userId(account);
120
+ const response = (await ctx.client.call(account, `/${userId}/replies`, {
121
+ params: { fields: REPLY_FIELDS, limit: clamp(args.limit, 25), after: args.cursor },
122
+ }));
123
+ let replies = dataOf(response);
124
+ if (args.since_hours) {
125
+ const cutoff = Date.now() - args.since_hours * 3_600_000;
126
+ replies = replies.filter((r) => {
127
+ const at = new Date(String(r.timestamp)).getTime();
128
+ return Number.isNaN(at) || at >= cutoff;
129
+ });
130
+ }
131
+ return renderList(replies, {
132
+ source: "replies",
133
+ cursor: cursorOf(response),
134
+ meta: { scope: "all_posts", ...(args.since_hours ? { since_hours: args.since_hours } : {}) },
135
+ }, "reply");
136
+ },
137
+ });
138
+ export const hideReply = defineTool({
139
+ name: "hide_reply",
140
+ title: "Hide or unhide a reply",
141
+ description: "Hide a reply on one of your posts, or unhide one you hid. Hiding a top-level reply cascades to everything nested under it. Reversible in one call, so this does not need a confirmation.",
142
+ schema: {
143
+ reply_id: z.string().describe("Numeric id of the reply."),
144
+ hide: z.boolean().optional().describe("True to hide, false to unhide. Defaults to true."),
145
+ ...accountArg,
146
+ },
147
+ risk: "write",
148
+ idempotent: true,
149
+ summary: (args) => `${args.hide === false ? "unhide" : "hide"} Threads reply ${args.reply_id}`,
150
+ handler: async (args, ctx) => {
151
+ const account = ctx.account(args.account);
152
+ const id = requireMediaId(args.reply_id);
153
+ const hide = args.hide !== false;
154
+ await ctx.client.call(account, `/${id}/manage_reply`, {
155
+ method: "POST",
156
+ params: { hide: hide ? "true" : "false" },
157
+ });
158
+ return {
159
+ reply_id: id,
160
+ hidden: hide,
161
+ note: hide ? "Nested replies under this one are hidden too." : undefined,
162
+ };
163
+ },
164
+ });
165
+ export const getPendingReplies = defineTool({
166
+ name: "get_pending_replies",
167
+ title: "Read the reply approval queue",
168
+ description: "Replies waiting for approval on posts published with enable_reply_approvals. They are invisible to everyone until approved. Empty unless reply approvals were switched on for the post.",
169
+ schema: { ...pageArgs, ...accountArg },
170
+ risk: "read",
171
+ handler: async (args, ctx) => {
172
+ const account = ctx.account(args.account);
173
+ const userId = await ctx.client.userId(account);
174
+ const response = (await ctx.client.call(account, `/${userId}/pending_replies`, {
175
+ params: {
176
+ fields: `${REPLY_FIELDS},reply_approval_status`,
177
+ limit: clamp(args.limit, 25),
178
+ after: args.cursor,
179
+ },
180
+ }));
181
+ return renderList(dataOf(response), {
182
+ source: "pending_replies",
183
+ cursor: cursorOf(response),
184
+ }, "reply");
185
+ },
186
+ });
187
+ export const managePendingReply = defineTool({
188
+ name: "manage_pending_reply",
189
+ title: "Approve or ignore a pending reply",
190
+ description: "Approve a reply waiting in the approval queue, making it public, or ignore it so it stays hidden. Approving is public, so it needs confirm: true.",
191
+ schema: {
192
+ reply_id: z.string().describe("Numeric id of the pending reply."),
193
+ action: z.enum(["approve", "ignore"]).describe("approve makes it public; ignore leaves it hidden."),
194
+ ...accountArg,
195
+ ...confirmArg,
196
+ },
197
+ risk: "destructive",
198
+ summary: (args) => `${args.action} pending Threads reply ${args.reply_id}`,
199
+ handler: async (args, ctx) => {
200
+ const account = ctx.account(args.account);
201
+ const id = requireMediaId(args.reply_id);
202
+ await ctx.client.call(account, `/${id}/manage_pending_reply`, {
203
+ method: "POST",
204
+ params: { approve: args.action === "approve" ? "true" : "false" },
205
+ });
206
+ return { reply_id: id, action: args.action, public: args.action === "approve" };
207
+ },
208
+ });
209
+ export const REPLY_TOOLS = [
210
+ replyToPost,
211
+ getReplies,
212
+ getConversation,
213
+ getAllReplies,
214
+ hideReply,
215
+ getPendingReplies,
216
+ managePendingReply,
217
+ ];
218
+ //# sourceMappingURL=replies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replies.js","sourceRoot":"","sources":["../../src/tools/replies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxF,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAEnD,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CAAC;IACpC,IAAI,EAAE,UAAU;IAChB,KAAK,EAAE,iBAAiB;IACxB,WAAW,EACT,6JAA6J;IAC/J,MAAM,EAAE;QACN,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0CAA0C,CAAC;QACnE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC;QACvD,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;QAC7E,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;QAC7E,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC/B,GAAG,UAAU;QACb,GAAG,UAAU;KACd;IACD,IAAI,EAAE,aAAa;IACnB,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,yBAAyB,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;IAC9E,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QAC3B,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1C,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE;YAClC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,WAAW,EAAE,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;SACrC,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC;IACnC,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,+BAA+B;IACtC,WAAW,EACT,uIAAuI;IACzI,MAAM,EAAE;QACN,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;QAClD,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;QACnF,GAAG,QAAQ;QACX,GAAG,UAAU;KACd;IACD,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QAC3B,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM,QAAQ,GAAG,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE;YACjE,MAAM,EAAE;gBACN,MAAM,EAAE,YAAY;gBACpB,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;gBAC5B,KAAK,EAAE,IAAI,CAAC,MAAM;gBAClB,OAAO,EAAE,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;aACpD;SACF,CAAC,CAA4B,CAAC;QAE/B,OAAO,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE;YAClC,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC;YAC1B,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;SACtB,EAAE,OAAO,CAAC,CAAC;IACd,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAC;IACxC,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,2BAA2B;IAClC,WAAW,EACT,0NAA0N;IAC5N,MAAM,EAAE;QACN,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;QAC3D,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;QAC/B,GAAG,QAAQ;QACX,GAAG,UAAU;KACd;IACD,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QAC3B,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM,QAAQ,GAAG,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE;YACtE,MAAM,EAAE;gBACN,MAAM,EAAE,YAAY;gBACpB,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;gBAC5B,KAAK,EAAE,IAAI,CAAC,MAAM;gBAClB,OAAO,EAAE,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;aACpD;SACF,CAAC,CAA4B,CAAC;QAE/B,OAAO,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE;YAClC,MAAM,EAAE,cAAc;YACtB,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC;YAC1B,IAAI,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE;SAC3B,EAAE,OAAO,CAAC,CAAC;IACd,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAC;IACtC,IAAI,EAAE,iBAAiB;IACvB,KAAK,EAAE,wCAAwC;IAC/C,WAAW,EACT,sKAAsK;IACxK,MAAM,EAAE;QACN,WAAW,EAAE,CAAC;aACX,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,8FAA8F,CAAC;QAC3G,GAAG,QAAQ;QACX,GAAG,UAAU;KACd;IACD,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QAC3B,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,MAAM,UAAU,EAAE;YACrE,MAAM,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE;SACnF,CAAC,CAA4B,CAAC;QAE/B,IAAI,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/B,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;YACzD,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC7B,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;gBACnD,OAAO,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,MAAM,CAAC;YAC1C,CAAC,CAAC,CAAC;QACL,CAAC;QAED,OAAO,UAAU,CAAC,OAAO,EAAE;YACzB,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC;YAC1B,IAAI,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;SAC7F,EAAE,OAAO,CAAC,CAAC;IACd,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CAAC;IAClC,IAAI,EAAE,YAAY;IAClB,KAAK,EAAE,wBAAwB;IAC/B,WAAW,EACT,0LAA0L;IAC5L,MAAM,EAAE;QACN,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;QACzD,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kDAAkD,CAAC;QACzF,GAAG,UAAU;KACd;IACD,IAAI,EAAE,OAAO;IACb,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,kBAAkB,IAAI,CAAC,QAAQ,EAAE;IAC9F,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QAC3B,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC;QACjC,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE;YACpD,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE;SAC1C,CAAC,CAAC;QACH,OAAO;YACL,QAAQ,EAAE,EAAE;YACZ,MAAM,EAAE,IAAI;YACZ,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,+CAA+C,CAAC,CAAC,CAAC,SAAS;SACzE,CAAC;IACJ,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,UAAU,CAAC;IAC1C,IAAI,EAAE,qBAAqB;IAC3B,KAAK,EAAE,+BAA+B;IACtC,WAAW,EACT,yLAAyL;IAC3L,MAAM,EAAE,EAAE,GAAG,QAAQ,EAAE,GAAG,UAAU,EAAE;IACtC,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QAC3B,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,MAAM,kBAAkB,EAAE;YAC7E,MAAM,EAAE;gBACN,MAAM,EAAE,GAAG,YAAY,wBAAwB;gBAC/C,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;gBAC5B,KAAK,EAAE,IAAI,CAAC,MAAM;aACnB;SACF,CAAC,CAA4B,CAAC;QAE/B,OAAO,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE;YAClC,MAAM,EAAE,iBAAiB;YACzB,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC;SAC3B,EAAE,OAAO,CAAC,CAAC;IACd,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAC;IAC3C,IAAI,EAAE,sBAAsB;IAC5B,KAAK,EAAE,mCAAmC;IAC1C,WAAW,EACT,mJAAmJ;IACrJ,MAAM,EAAE;QACN,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;QACjE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,mDAAmD,CAAC;QACnG,GAAG,UAAU;QACb,GAAG,UAAU;KACd;IACD,IAAI,EAAE,aAAa;IACnB,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,0BAA0B,IAAI,CAAC,QAAQ,EAAE;IAC1E,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QAC3B,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,uBAAuB,EAAE;YAC5D,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE;SAClE,CAAC,CAAC;QACH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;IAClF,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,WAAW;IACX,UAAU;IACV,eAAe;IACf,aAAa;IACb,SAAS;IACT,iBAAiB;IACjB,kBAAkB;CACnB,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * HTTP transport, for running the server somewhere always on.
3
+ *
4
+ * Streamable HTTP, stateless: every request builds its own transport and tears
5
+ * it down. No session map means no session leak, which matters more here than
6
+ * the reconnect support a stateful server would buy.
7
+ *
8
+ * There is a second reason to run this way for Threads specifically. A token
9
+ * only stays alive if something refreshes it inside its 60-day window, and an
10
+ * MCP server launched over stdio only exists while a client has it open. A
11
+ * long-running HTTP instance refreshes on its own and never lets the window
12
+ * close.
13
+ *
14
+ * Bound to 127.0.0.1 by default. A Threads token can post as you, so a server
15
+ * that binds 0.0.0.0 without being asked is a mistake waiting to be made once;
16
+ * THREADS_HTTP_HOST is there for people who mean it.
17
+ */
18
+ import type { BuiltServer } from "../server.js";
19
+ export type HttpOptions = {
20
+ port: number;
21
+ host: string;
22
+ /** When set, every request must send `Authorization: Bearer <token>`. */
23
+ token?: string;
24
+ };
25
+ export declare function httpOptionsFromEnv(argv?: string[]): HttpOptions;
26
+ export declare function startHttpServer(built: BuiltServer, options: HttpOptions): Promise<{
27
+ close: () => Promise<void>;
28
+ }>;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * HTTP transport, for running the server somewhere always on.
3
+ *
4
+ * Streamable HTTP, stateless: every request builds its own transport and tears
5
+ * it down. No session map means no session leak, which matters more here than
6
+ * the reconnect support a stateful server would buy.
7
+ *
8
+ * There is a second reason to run this way for Threads specifically. A token
9
+ * only stays alive if something refreshes it inside its 60-day window, and an
10
+ * MCP server launched over stdio only exists while a client has it open. A
11
+ * long-running HTTP instance refreshes on its own and never lets the window
12
+ * close.
13
+ *
14
+ * Bound to 127.0.0.1 by default. A Threads token can post as you, so a server
15
+ * that binds 0.0.0.0 without being asked is a mistake waiting to be made once;
16
+ * THREADS_HTTP_HOST is there for people who mean it.
17
+ */
18
+ import { createServer } from "node:http";
19
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
20
+ import { daysRemaining } from "../auth/tokens.js";
21
+ export function httpOptionsFromEnv(argv = []) {
22
+ const flag = argv.find((a) => a.startsWith("--port="));
23
+ const port = Number(flag?.split("=")[1] ?? process.env.THREADS_HTTP_PORT ?? 8787);
24
+ return {
25
+ port: Number.isFinite(port) && port > 0 ? port : 8787,
26
+ host: process.env.THREADS_HTTP_HOST || "127.0.0.1",
27
+ token: process.env.THREADS_HTTP_TOKEN || undefined,
28
+ };
29
+ }
30
+ async function readBody(req) {
31
+ const chunks = [];
32
+ for await (const chunk of req)
33
+ chunks.push(chunk);
34
+ const raw = Buffer.concat(chunks).toString("utf8");
35
+ if (!raw)
36
+ return undefined;
37
+ try {
38
+ return JSON.parse(raw);
39
+ }
40
+ catch {
41
+ return undefined;
42
+ }
43
+ }
44
+ /** Constant-time-ish comparison, so the token cannot be guessed byte by byte. */
45
+ function tokenMatches(expected, provided) {
46
+ if (expected.length !== provided.length)
47
+ return false;
48
+ let diff = 0;
49
+ for (let i = 0; i < expected.length; i++)
50
+ diff |= expected.charCodeAt(i) ^ provided.charCodeAt(i);
51
+ return diff === 0;
52
+ }
53
+ async function handle(built, options, req, res) {
54
+ // Health needs no auth: it reports counts, never content.
55
+ if (req.method === "GET" && (req.url === "/health" || req.url === "/healthz")) {
56
+ res.writeHead(200, { "content-type": "application/json" });
57
+ res.end(JSON.stringify({
58
+ status: "ok",
59
+ tools: built.toolCount,
60
+ accounts: built.config.accounts.length,
61
+ read_only: built.config.readOnly,
62
+ tokens: built.config.accounts.map((a) => ({
63
+ username: a.username ?? null,
64
+ days_left: daysRemaining(a) ?? null,
65
+ })),
66
+ }));
67
+ return;
68
+ }
69
+ if (options.token) {
70
+ const header = req.headers.authorization ?? "";
71
+ const provided = header.startsWith("Bearer ") ? header.slice(7) : "";
72
+ if (!provided || !tokenMatches(options.token, provided)) {
73
+ res.writeHead(401, { "content-type": "application/json" });
74
+ res.end(JSON.stringify({ error: "unauthorized" }));
75
+ return;
76
+ }
77
+ }
78
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
79
+ res.on("close", () => void transport.close());
80
+ await built.server.connect(transport);
81
+ await transport.handleRequest(req, res, await readBody(req));
82
+ }
83
+ export async function startHttpServer(built, options) {
84
+ const http = createServer((req, res) => {
85
+ void handle(built, options, req, res).catch((error) => {
86
+ if (!res.headersSent)
87
+ res.writeHead(500, { "content-type": "application/json" });
88
+ res.end(JSON.stringify({
89
+ jsonrpc: "2.0",
90
+ error: { code: -32603, message: error?.message ?? "internal error" },
91
+ id: null,
92
+ }));
93
+ });
94
+ });
95
+ await new Promise((resolve) => http.listen(options.port, options.host, resolve));
96
+ process.stderr.write(`[threads-mcp] HTTP on http://${options.host}:${options.port} (${built.toolCount} tools, ${built.config.accounts.length} account(s))${options.token ? "" : "\n[threads-mcp] No THREADS_HTTP_TOKEN set: this endpoint is unauthenticated."}\n`);
97
+ return {
98
+ close: () => new Promise((resolve) => {
99
+ http.close(() => resolve());
100
+ }),
101
+ };
102
+ }
103
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/transport/http.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@thenavidm/threads-mcp-cli",
3
+ "version": "1.1.0",
4
+ "description": "Threads MCP server and CLI for Claude Code and AI agents. 30 tools for posting, chained threads, carousels, replies and reply approvals, insights, keyword search and profile discovery.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "bin": {
8
+ "threads-mcp": "dist/index.js",
9
+ "threads-cli": "dist/index.js"
10
+ },
11
+ "engines": {
12
+ "node": ">=20"
13
+ },
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "dev": "tsc --watch",
17
+ "start": "node dist/index.js",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "typecheck": "tsc --noEmit",
21
+ "prepublishOnly": "npm run build",
22
+ "build:mcpb": "bash desktop-extension/build.sh"
23
+ },
24
+ "dependencies": {
25
+ "@modelcontextprotocol/sdk": "^1.18.0",
26
+ "zod": "^3.23.8",
27
+ "zod-to-json-schema": "^3.24.1"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^22.10.0",
31
+ "typescript": "^5.7.0",
32
+ "vitest": "^2.1.0"
33
+ },
34
+ "license": "MIT",
35
+ "author": "Navid Moazzez (https://navid.me)",
36
+ "homepage": "https://navid.me",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/thenavidm/threads-mcp-cli.git"
40
+ },
41
+ "bugs": {
42
+ "url": "https://github.com/thenavidm/threads-mcp-cli/issues"
43
+ },
44
+ "keywords": [
45
+ "mcp",
46
+ "model-context-protocol",
47
+ "threads",
48
+ "threads-api",
49
+ "meta",
50
+ "mcp-server",
51
+ "claude",
52
+ "claude-code",
53
+ "ai-agents",
54
+ "llm-tools"
55
+ ],
56
+ "files": [
57
+ "dist",
58
+ "README.md",
59
+ "SKILL.md",
60
+ "LICENSE"
61
+ ],
62
+ "contributors": [
63
+ "Navid Moazzez (https://navid.me)"
64
+ ]
65
+ }