@pegasusheavy/threads-mcp 1.0.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/.cursorrules/no-summaries.mdc +1 -0
- package/.cursorrules/update-website.mdc +8 -0
- package/.husky/README.md +37 -0
- package/.husky/commit-msg +3 -0
- package/.husky/pre-commit +12 -0
- package/.husky/pre-push +31 -0
- package/CHANGELOG.md +49 -0
- package/LICENSE +22 -0
- package/README.md +336 -0
- package/commitlint.config.js +27 -0
- package/dist/client/enhanced-threads-client.d.ts +49 -0
- package/dist/client/enhanced-threads-client.d.ts.map +1 -0
- package/dist/client/enhanced-threads-client.js +175 -0
- package/dist/client/enhanced-threads-client.js.map +1 -0
- package/dist/client/threads-client.d.ts +23 -0
- package/dist/client/threads-client.d.ts.map +1 -0
- package/dist/client/threads-client.js +208 -0
- package/dist/client/threads-client.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +382 -0
- package/dist/server.js.map +1 -0
- package/dist/types/threads.d.ts +298 -0
- package/dist/types/threads.d.ts.map +1 -0
- package/dist/types/threads.js +76 -0
- package/dist/types/threads.js.map +1 -0
- package/dist/utils/cache.d.ts +42 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +147 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +31 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +99 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/webhook.d.ts +67 -0
- package/dist/utils/webhook.d.ts.map +1 -0
- package/dist/utils/webhook.js +187 -0
- package/dist/utils/webhook.js.map +1 -0
- package/llms.txt +197 -0
- package/package.json +65 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export class RateLimiter {
|
|
2
|
+
tokens;
|
|
3
|
+
lastRefill;
|
|
4
|
+
maxTokens;
|
|
5
|
+
refillRate;
|
|
6
|
+
refillInterval;
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.maxTokens = options.maxTokens;
|
|
9
|
+
this.refillRate = options.refillRate;
|
|
10
|
+
this.refillInterval = options.refillInterval || 1000;
|
|
11
|
+
this.tokens = this.maxTokens;
|
|
12
|
+
this.lastRefill = Date.now();
|
|
13
|
+
}
|
|
14
|
+
refill() {
|
|
15
|
+
const now = Date.now();
|
|
16
|
+
const elapsed = now - this.lastRefill;
|
|
17
|
+
const intervalsElapsed = elapsed / this.refillInterval;
|
|
18
|
+
const tokensToAdd = intervalsElapsed * this.refillRate;
|
|
19
|
+
this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd);
|
|
20
|
+
this.lastRefill = now;
|
|
21
|
+
}
|
|
22
|
+
tryConsume(tokens = 1) {
|
|
23
|
+
this.refill();
|
|
24
|
+
if (this.tokens >= tokens) {
|
|
25
|
+
this.tokens -= tokens;
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
async consume(tokens = 1) {
|
|
31
|
+
while (!this.tryConsume(tokens)) {
|
|
32
|
+
const waitTime = this.getWaitTime(tokens);
|
|
33
|
+
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
getWaitTime(tokens = 1) {
|
|
37
|
+
this.refill();
|
|
38
|
+
if (this.tokens >= tokens) {
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
const tokensNeeded = tokens - this.tokens;
|
|
42
|
+
const intervalsNeeded = Math.ceil(tokensNeeded / this.refillRate);
|
|
43
|
+
return intervalsNeeded * this.refillInterval;
|
|
44
|
+
}
|
|
45
|
+
getTokens() {
|
|
46
|
+
this.refill();
|
|
47
|
+
return this.tokens;
|
|
48
|
+
}
|
|
49
|
+
reset() {
|
|
50
|
+
this.tokens = this.maxTokens;
|
|
51
|
+
this.lastRefill = Date.now();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export class RateLimiterManager {
|
|
55
|
+
limiters;
|
|
56
|
+
constructor() {
|
|
57
|
+
this.limiters = new Map();
|
|
58
|
+
}
|
|
59
|
+
register(endpoint, options) {
|
|
60
|
+
this.limiters.set(endpoint, new RateLimiter(options));
|
|
61
|
+
}
|
|
62
|
+
tryConsume(endpoint, tokens = 1) {
|
|
63
|
+
const limiter = this.limiters.get(endpoint);
|
|
64
|
+
if (!limiter) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
return limiter.tryConsume(tokens);
|
|
68
|
+
}
|
|
69
|
+
async consume(endpoint, tokens = 1) {
|
|
70
|
+
const limiter = this.limiters.get(endpoint);
|
|
71
|
+
if (limiter) {
|
|
72
|
+
await limiter.consume(tokens);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
getWaitTime(endpoint, tokens = 1) {
|
|
76
|
+
const limiter = this.limiters.get(endpoint);
|
|
77
|
+
return limiter ? limiter.getWaitTime(tokens) : 0;
|
|
78
|
+
}
|
|
79
|
+
reset(endpoint) {
|
|
80
|
+
const limiter = this.limiters.get(endpoint);
|
|
81
|
+
if (limiter) {
|
|
82
|
+
limiter.reset();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
resetAll() {
|
|
86
|
+
this.limiters.forEach((limiter) => limiter.reset());
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function RateLimited(limiter, tokens = 1) {
|
|
90
|
+
return function (_target, _propertyKey, descriptor) {
|
|
91
|
+
const originalMethod = descriptor.value;
|
|
92
|
+
descriptor.value = async function (...args) {
|
|
93
|
+
await limiter.consume(tokens);
|
|
94
|
+
return originalMethod.apply(this, args);
|
|
95
|
+
};
|
|
96
|
+
return descriptor;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/utils/rate-limiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
export interface WebhookSubscription {
|
|
3
|
+
id: string;
|
|
4
|
+
url: string;
|
|
5
|
+
events: string[];
|
|
6
|
+
secret?: string;
|
|
7
|
+
active: boolean;
|
|
8
|
+
createdAt: Date;
|
|
9
|
+
lastTriggered?: Date;
|
|
10
|
+
}
|
|
11
|
+
export interface WebhookPayload {
|
|
12
|
+
event: string;
|
|
13
|
+
timestamp: Date;
|
|
14
|
+
data: any;
|
|
15
|
+
subscriptionId: string;
|
|
16
|
+
}
|
|
17
|
+
export interface WebhookDelivery {
|
|
18
|
+
id: string;
|
|
19
|
+
subscriptionId: string;
|
|
20
|
+
event: string;
|
|
21
|
+
payload: WebhookPayload;
|
|
22
|
+
status: 'pending' | 'success' | 'failed';
|
|
23
|
+
attempts: number;
|
|
24
|
+
lastAttempt?: Date;
|
|
25
|
+
response?: {
|
|
26
|
+
status: number;
|
|
27
|
+
body: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export interface WebhookOptions {
|
|
31
|
+
maxRetries?: number;
|
|
32
|
+
retryDelay?: number;
|
|
33
|
+
timeout?: number;
|
|
34
|
+
}
|
|
35
|
+
export declare class WebhookManager extends EventEmitter {
|
|
36
|
+
private subscriptions;
|
|
37
|
+
private deliveries;
|
|
38
|
+
private readonly maxRetries;
|
|
39
|
+
private readonly retryDelay;
|
|
40
|
+
private readonly timeout;
|
|
41
|
+
constructor(options?: WebhookOptions);
|
|
42
|
+
subscribe(url: string, events: string[], secret?: string): WebhookSubscription;
|
|
43
|
+
unsubscribe(subscriptionId: string): boolean;
|
|
44
|
+
updateSubscription(subscriptionId: string, updates: Partial<Pick<WebhookSubscription, 'url' | 'events' | 'secret' | 'active'>>): WebhookSubscription | null;
|
|
45
|
+
getSubscription(subscriptionId: string): WebhookSubscription | undefined;
|
|
46
|
+
getSubscriptions(): WebhookSubscription[];
|
|
47
|
+
getSubscriptionsForEvent(event: string): WebhookSubscription[];
|
|
48
|
+
trigger(event: string, data: any): Promise<WebhookDelivery[]>;
|
|
49
|
+
private deliverWebhook;
|
|
50
|
+
private attemptDelivery;
|
|
51
|
+
private sendWebhook;
|
|
52
|
+
private generateSignature;
|
|
53
|
+
static verifySignature(payload: string, signature: string, secret: string): boolean;
|
|
54
|
+
getDelivery(deliveryId: string): WebhookDelivery | undefined;
|
|
55
|
+
getDeliveriesForSubscription(subscriptionId: string): WebhookDelivery[];
|
|
56
|
+
cleanDeliveries(olderThan: Date): number;
|
|
57
|
+
private generateId;
|
|
58
|
+
private delay;
|
|
59
|
+
stats(): {
|
|
60
|
+
subscriptions: number;
|
|
61
|
+
activeSubscriptions: number;
|
|
62
|
+
deliveries: number;
|
|
63
|
+
successfulDeliveries: number;
|
|
64
|
+
failedDeliveries: number;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=webhook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/utils/webhook.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
export class WebhookManager extends EventEmitter {
|
|
4
|
+
subscriptions;
|
|
5
|
+
deliveries;
|
|
6
|
+
maxRetries;
|
|
7
|
+
retryDelay;
|
|
8
|
+
timeout;
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
super();
|
|
11
|
+
this.subscriptions = new Map();
|
|
12
|
+
this.deliveries = new Map();
|
|
13
|
+
this.maxRetries = options.maxRetries || 3;
|
|
14
|
+
this.retryDelay = options.retryDelay || 1000;
|
|
15
|
+
this.timeout = options.timeout || 5000;
|
|
16
|
+
}
|
|
17
|
+
subscribe(url, events, secret) {
|
|
18
|
+
const subscription = {
|
|
19
|
+
id: this.generateId(),
|
|
20
|
+
url,
|
|
21
|
+
events,
|
|
22
|
+
secret,
|
|
23
|
+
active: true,
|
|
24
|
+
createdAt: new Date(),
|
|
25
|
+
};
|
|
26
|
+
this.subscriptions.set(subscription.id, subscription);
|
|
27
|
+
this.emit('subscription:created', subscription);
|
|
28
|
+
return subscription;
|
|
29
|
+
}
|
|
30
|
+
unsubscribe(subscriptionId) {
|
|
31
|
+
const subscription = this.subscriptions.get(subscriptionId);
|
|
32
|
+
if (!subscription) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
this.subscriptions.delete(subscriptionId);
|
|
36
|
+
this.emit('subscription:deleted', subscription);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
updateSubscription(subscriptionId, updates) {
|
|
40
|
+
const subscription = this.subscriptions.get(subscriptionId);
|
|
41
|
+
if (!subscription) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
Object.assign(subscription, updates);
|
|
45
|
+
this.emit('subscription:updated', subscription);
|
|
46
|
+
return subscription;
|
|
47
|
+
}
|
|
48
|
+
getSubscription(subscriptionId) {
|
|
49
|
+
return this.subscriptions.get(subscriptionId);
|
|
50
|
+
}
|
|
51
|
+
getSubscriptions() {
|
|
52
|
+
return Array.from(this.subscriptions.values());
|
|
53
|
+
}
|
|
54
|
+
getSubscriptionsForEvent(event) {
|
|
55
|
+
return Array.from(this.subscriptions.values()).filter((sub) => sub.active && (sub.events.includes('*') || sub.events.includes(event)));
|
|
56
|
+
}
|
|
57
|
+
async trigger(event, data) {
|
|
58
|
+
const subscriptions = this.getSubscriptionsForEvent(event);
|
|
59
|
+
const deliveries = [];
|
|
60
|
+
for (const subscription of subscriptions) {
|
|
61
|
+
const delivery = await this.deliverWebhook(subscription, event, data);
|
|
62
|
+
deliveries.push(delivery);
|
|
63
|
+
}
|
|
64
|
+
return deliveries;
|
|
65
|
+
}
|
|
66
|
+
async deliverWebhook(subscription, event, data) {
|
|
67
|
+
const payload = {
|
|
68
|
+
event,
|
|
69
|
+
timestamp: new Date(),
|
|
70
|
+
data,
|
|
71
|
+
subscriptionId: subscription.id,
|
|
72
|
+
};
|
|
73
|
+
const delivery = {
|
|
74
|
+
id: this.generateId(),
|
|
75
|
+
subscriptionId: subscription.id,
|
|
76
|
+
event,
|
|
77
|
+
payload,
|
|
78
|
+
status: 'pending',
|
|
79
|
+
attempts: 0,
|
|
80
|
+
};
|
|
81
|
+
this.deliveries.set(delivery.id, delivery);
|
|
82
|
+
await this.attemptDelivery(subscription, delivery);
|
|
83
|
+
return delivery;
|
|
84
|
+
}
|
|
85
|
+
async attemptDelivery(subscription, delivery) {
|
|
86
|
+
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|
|
87
|
+
delivery.attempts = attempt + 1;
|
|
88
|
+
delivery.lastAttempt = new Date();
|
|
89
|
+
try {
|
|
90
|
+
const response = await this.sendWebhook(subscription, delivery.payload);
|
|
91
|
+
delivery.status = 'success';
|
|
92
|
+
delivery.response = response;
|
|
93
|
+
subscription.lastTriggered = new Date();
|
|
94
|
+
this.emit('delivery:success', delivery);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
delivery.status = 'failed';
|
|
99
|
+
delivery.response = {
|
|
100
|
+
status: 0,
|
|
101
|
+
body: error instanceof Error ? error.message : 'Unknown error',
|
|
102
|
+
};
|
|
103
|
+
this.emit('delivery:failed', delivery, error);
|
|
104
|
+
if (attempt < this.maxRetries - 1) {
|
|
105
|
+
await this.delay(this.retryDelay * Math.pow(2, attempt));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async sendWebhook(subscription, payload) {
|
|
111
|
+
const body = JSON.stringify(payload);
|
|
112
|
+
const signature = subscription.secret
|
|
113
|
+
? this.generateSignature(body, subscription.secret)
|
|
114
|
+
: undefined;
|
|
115
|
+
const controller = new AbortController();
|
|
116
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
117
|
+
try {
|
|
118
|
+
const response = await fetch(subscription.url, {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
headers: {
|
|
121
|
+
'Content-Type': 'application/json',
|
|
122
|
+
'User-Agent': 'Threads-MCP-Webhook/1.0',
|
|
123
|
+
...(signature && { 'X-Webhook-Signature': signature }),
|
|
124
|
+
},
|
|
125
|
+
body,
|
|
126
|
+
signal: controller.signal,
|
|
127
|
+
});
|
|
128
|
+
const responseBody = await response.text();
|
|
129
|
+
if (!response.ok) {
|
|
130
|
+
throw new Error(`HTTP ${response.status}: ${responseBody}`);
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
status: response.status,
|
|
134
|
+
body: responseBody,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
clearTimeout(timeoutId);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
generateSignature(payload, secret) {
|
|
142
|
+
return crypto.createHmac('sha256', secret).update(payload).digest('hex');
|
|
143
|
+
}
|
|
144
|
+
static verifySignature(payload, signature, secret) {
|
|
145
|
+
const expectedSignature = crypto
|
|
146
|
+
.createHmac('sha256', secret)
|
|
147
|
+
.update(payload)
|
|
148
|
+
.digest('hex');
|
|
149
|
+
if (signature.length !== expectedSignature.length) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
|
|
153
|
+
}
|
|
154
|
+
getDelivery(deliveryId) {
|
|
155
|
+
return this.deliveries.get(deliveryId);
|
|
156
|
+
}
|
|
157
|
+
getDeliveriesForSubscription(subscriptionId) {
|
|
158
|
+
return Array.from(this.deliveries.values()).filter((delivery) => delivery.subscriptionId === subscriptionId);
|
|
159
|
+
}
|
|
160
|
+
cleanDeliveries(olderThan) {
|
|
161
|
+
let cleaned = 0;
|
|
162
|
+
this.deliveries.forEach((delivery, id) => {
|
|
163
|
+
if (delivery.lastAttempt && delivery.lastAttempt < olderThan) {
|
|
164
|
+
this.deliveries.delete(id);
|
|
165
|
+
cleaned++;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
return cleaned;
|
|
169
|
+
}
|
|
170
|
+
generateId() {
|
|
171
|
+
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
172
|
+
}
|
|
173
|
+
delay(ms) {
|
|
174
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
175
|
+
}
|
|
176
|
+
stats() {
|
|
177
|
+
const deliveryArray = Array.from(this.deliveries.values());
|
|
178
|
+
return {
|
|
179
|
+
subscriptions: this.subscriptions.size,
|
|
180
|
+
activeSubscriptions: Array.from(this.subscriptions.values()).filter((sub) => sub.active).length,
|
|
181
|
+
deliveries: this.deliveries.size,
|
|
182
|
+
successfulDeliveries: deliveryArray.filter((d) => d.status === 'success').length,
|
|
183
|
+
failedDeliveries: deliveryArray.filter((d) => d.status === 'failed').length,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=webhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/utils/webhook.ts"],"names":[],"mappings":"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"}
|
package/llms.txt
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Threads MCP Server
|
|
2
|
+
|
|
3
|
+
> Production-ready Model Context Protocol server for Threads.com API
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Threads MCP Server enables AI assistants like Claude and ChatGPT to interact with the Threads.com API through the Model Context Protocol. Built with TypeScript, featuring rate limiting, caching, and webhooks.
|
|
8
|
+
|
|
9
|
+
## Repository
|
|
10
|
+
|
|
11
|
+
- GitHub: https://github.com/pegasusheavy/threads-mcp
|
|
12
|
+
- Website: https://pegasusheavy.github.io/threads-mcp/
|
|
13
|
+
- License: MIT (Pegasus Heavy Industries LLC)
|
|
14
|
+
|
|
15
|
+
## Key Features
|
|
16
|
+
|
|
17
|
+
- Complete Threads.com API integration
|
|
18
|
+
- Rate limiting with token bucket algorithm
|
|
19
|
+
- In-memory caching with TTL and LRU eviction
|
|
20
|
+
- Webhook support with HMAC signature verification
|
|
21
|
+
- Full TypeScript support with Zod validation
|
|
22
|
+
- 90%+ test coverage with Vitest
|
|
23
|
+
- Claude Desktop integration ready
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install threads-mcp
|
|
29
|
+
# or
|
|
30
|
+
pnpm add threads-mcp
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Configuration
|
|
34
|
+
|
|
35
|
+
Set environment variables:
|
|
36
|
+
```bash
|
|
37
|
+
THREADS_ACCESS_TOKEN="your-access-token"
|
|
38
|
+
THREADS_USER_ID="your-user-id"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Add to Claude Desktop config (`claude_desktop_config.json`):
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"threads": {
|
|
46
|
+
"command": "threads-mcp",
|
|
47
|
+
"env": {
|
|
48
|
+
"THREADS_ACCESS_TOKEN": "your-token",
|
|
49
|
+
"THREADS_USER_ID": "your-user-id"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Available MCP Tools
|
|
57
|
+
|
|
58
|
+
- `threads_get_profile` - Get authenticated user's profile
|
|
59
|
+
- `threads_get_threads` - List user's threads with pagination
|
|
60
|
+
- `threads_get_thread` - Get specific thread by ID
|
|
61
|
+
- `threads_create_thread` - Create text, image, or video threads
|
|
62
|
+
- `threads_reply_to_thread` - Reply to existing threads
|
|
63
|
+
- `threads_get_insights` - Get thread analytics and metrics
|
|
64
|
+
- `threads_get_replies` - Get all replies to a thread
|
|
65
|
+
- `threads_get_conversation` - Get full conversation thread
|
|
66
|
+
|
|
67
|
+
## Usage Examples
|
|
68
|
+
|
|
69
|
+
### Basic Client Usage
|
|
70
|
+
```typescript
|
|
71
|
+
import { ThreadsClient } from 'threads-mcp';
|
|
72
|
+
|
|
73
|
+
const client = new ThreadsClient({
|
|
74
|
+
accessToken: process.env.THREADS_ACCESS_TOKEN,
|
|
75
|
+
userId: process.env.THREADS_USER_ID,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Create a thread
|
|
79
|
+
const thread = await client.createThread({
|
|
80
|
+
text: 'Hello from Threads MCP! 🚀',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Get profile
|
|
84
|
+
const profile = await client.getProfile(['id', 'username', 'threads_profile_picture_url']);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Enhanced Client with Rate Limiting & Caching
|
|
88
|
+
```typescript
|
|
89
|
+
import { EnhancedThreadsClient } from 'threads-mcp';
|
|
90
|
+
|
|
91
|
+
const client = new EnhancedThreadsClient({
|
|
92
|
+
accessToken: process.env.THREADS_ACCESS_TOKEN,
|
|
93
|
+
userId: process.env.THREADS_USER_ID,
|
|
94
|
+
rateLimit: {
|
|
95
|
+
maxRequests: 100,
|
|
96
|
+
windowMs: 60000,
|
|
97
|
+
},
|
|
98
|
+
cache: {
|
|
99
|
+
ttl: 300000,
|
|
100
|
+
maxSize: 100,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Claude Prompts
|
|
106
|
+
|
|
107
|
+
Try these with Claude Desktop after installing:
|
|
108
|
+
- "Show me my Threads profile information"
|
|
109
|
+
- "Post a thread saying 'Hello from Claude! 👋'"
|
|
110
|
+
- "Get analytics for my most recent thread"
|
|
111
|
+
- "Show me my last 10 threads"
|
|
112
|
+
- "Reply to thread ID xyz with 'Great post!'"
|
|
113
|
+
|
|
114
|
+
## Project Structure
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
src/
|
|
118
|
+
├── client/ # API client implementation
|
|
119
|
+
│ ├── threads-client.ts
|
|
120
|
+
│ └── enhanced-threads-client.ts
|
|
121
|
+
├── server.ts # MCP server implementation
|
|
122
|
+
├── types/ # TypeScript types and Zod schemas
|
|
123
|
+
├── utils/ # Rate limiting, caching, webhooks
|
|
124
|
+
└── index.ts # Entry point
|
|
125
|
+
|
|
126
|
+
docs/ # GitHub Pages site (Threads-style)
|
|
127
|
+
examples/ # Usage examples
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Testing
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
pnpm test # Run all tests
|
|
134
|
+
pnpm test:coverage # Generate coverage report
|
|
135
|
+
pnpm build # Build for production
|
|
136
|
+
pnpm lint # Run ESLint
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Technical Stack
|
|
140
|
+
|
|
141
|
+
- **Language**: TypeScript
|
|
142
|
+
- **Runtime**: Node.js
|
|
143
|
+
- **Protocol**: Model Context Protocol (MCP)
|
|
144
|
+
- **Validation**: Zod
|
|
145
|
+
- **HTTP Client**: Axios
|
|
146
|
+
- **Testing**: Vitest
|
|
147
|
+
- **Coverage**: 90%+ (statements, branches, functions, lines)
|
|
148
|
+
|
|
149
|
+
## API Coverage
|
|
150
|
+
|
|
151
|
+
Implements full Threads API including:
|
|
152
|
+
- Profile management
|
|
153
|
+
- Thread creation (text, images, videos)
|
|
154
|
+
- Thread replies and conversations
|
|
155
|
+
- Analytics and insights
|
|
156
|
+
- Media handling
|
|
157
|
+
- Rate limiting compliance
|
|
158
|
+
|
|
159
|
+
## Advanced Features
|
|
160
|
+
|
|
161
|
+
### Rate Limiting
|
|
162
|
+
- Token bucket algorithm
|
|
163
|
+
- Configurable limits per endpoint
|
|
164
|
+
- Automatic retry with backoff
|
|
165
|
+
- Request queuing
|
|
166
|
+
|
|
167
|
+
### Caching
|
|
168
|
+
- In-memory cache with TTL
|
|
169
|
+
- LRU eviction policy
|
|
170
|
+
- Automatic cleanup
|
|
171
|
+
- Cache invalidation
|
|
172
|
+
|
|
173
|
+
### Webhooks
|
|
174
|
+
- Event subscription
|
|
175
|
+
- HMAC-SHA256 signature verification
|
|
176
|
+
- Automatic retries with exponential backoff
|
|
177
|
+
- Event filtering
|
|
178
|
+
|
|
179
|
+
## Documentation
|
|
180
|
+
|
|
181
|
+
- Full API Reference: https://pegasusheavy.github.io/threads-mcp/API.html
|
|
182
|
+
- Usage Examples: https://pegasusheavy.github.io/threads-mcp/EXAMPLES.html
|
|
183
|
+
- GitHub: https://github.com/pegasusheavy/threads-mcp
|
|
184
|
+
|
|
185
|
+
## Contributing
|
|
186
|
+
|
|
187
|
+
See CONTRIBUTING.md for guidelines.
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
MIT License - Copyright (c) 2025 Pegasus Heavy Industries LLC
|
|
192
|
+
|
|
193
|
+
## Contact
|
|
194
|
+
|
|
195
|
+
- GitHub Issues: https://github.com/pegasusheavy/threads-mcp/issues
|
|
196
|
+
- Repository: https://github.com/pegasusheavy/threads-mcp
|
|
197
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pegasusheavy/threads-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A powerful MCP server for Threads.com API integration",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"threads-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"mcp",
|
|
12
|
+
"threads",
|
|
13
|
+
"meta",
|
|
14
|
+
"social-media",
|
|
15
|
+
"api"
|
|
16
|
+
],
|
|
17
|
+
"author": "Pegasus Heavy Industries LLC",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
24
|
+
"axios": "^1.7.9",
|
|
25
|
+
"zod": "^3.24.1"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@commitlint/cli": "^20.2.0",
|
|
29
|
+
"@commitlint/config-conventional": "^20.2.0",
|
|
30
|
+
"@types/node": "^22.10.2",
|
|
31
|
+
"@typescript-eslint/eslint-plugin": "^8.18.2",
|
|
32
|
+
"@typescript-eslint/parser": "^8.18.2",
|
|
33
|
+
"@vitest/coverage-v8": "^2.1.8",
|
|
34
|
+
"eslint": "^9.17.0",
|
|
35
|
+
"husky": "^9.1.7",
|
|
36
|
+
"lint-staged": "^16.2.7",
|
|
37
|
+
"prettier": "^3.4.2",
|
|
38
|
+
"tsx": "^4.19.2",
|
|
39
|
+
"typescript": "^5.7.2",
|
|
40
|
+
"vitest": "^2.1.8"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=18.0.0"
|
|
44
|
+
},
|
|
45
|
+
"lint-staged": {
|
|
46
|
+
"*.ts": [
|
|
47
|
+
"eslint --fix",
|
|
48
|
+
"prettier --write"
|
|
49
|
+
],
|
|
50
|
+
"*.{json,md}": [
|
|
51
|
+
"prettier --write"
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsc",
|
|
56
|
+
"dev": "tsx src/index.ts",
|
|
57
|
+
"test": "vitest run",
|
|
58
|
+
"test:watch": "vitest",
|
|
59
|
+
"test:coverage": "vitest run --coverage",
|
|
60
|
+
"lint": "eslint src --ext .ts",
|
|
61
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
62
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
63
|
+
"format:check": "prettier --check \"src/**/*.ts\""
|
|
64
|
+
}
|
|
65
|
+
}
|