@exileum/meta-mcp 6.0.0 → 8.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/README.md +44 -18
- package/dist/constants/fields.d.ts +6 -0
- package/dist/constants/fields.d.ts.map +1 -0
- package/dist/constants/fields.js +25 -0
- package/dist/constants/fields.js.map +1 -0
- package/dist/http-transport.d.ts +20 -0
- package/dist/http-transport.d.ts.map +1 -0
- package/dist/http-transport.js +228 -0
- package/dist/http-transport.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +77 -82
- package/dist/index.js.map +1 -1
- package/dist/prompts/index.d.ts +56 -0
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +264 -41
- package/dist/prompts/index.js.map +1 -1
- package/dist/register-all.d.ts +4 -0
- package/dist/register-all.d.ts.map +1 -0
- package/dist/register-all.js +41 -0
- package/dist/register-all.js.map +1 -0
- package/dist/resources/instagram.d.ts.map +1 -1
- package/dist/resources/instagram.js +13 -4
- package/dist/resources/instagram.js.map +1 -1
- package/dist/resources/threads.d.ts.map +1 -1
- package/dist/resources/threads.js +13 -4
- package/dist/resources/threads.js.map +1 -1
- package/dist/services/meta-client.d.ts +76 -3
- package/dist/services/meta-client.d.ts.map +1 -1
- package/dist/services/meta-client.js +330 -46
- package/dist/services/meta-client.js.map +1 -1
- package/dist/shutdown.d.ts +18 -0
- package/dist/shutdown.d.ts.map +1 -0
- package/dist/shutdown.js +60 -0
- package/dist/shutdown.js.map +1 -0
- package/dist/tools/annotations.d.ts +6 -0
- package/dist/tools/annotations.d.ts.map +1 -0
- package/dist/tools/annotations.js +27 -0
- package/dist/tools/annotations.js.map +1 -0
- package/dist/tools/instagram/comments.d.ts.map +1 -1
- package/dist/tools/instagram/comments.js +75 -50
- package/dist/tools/instagram/comments.js.map +1 -1
- package/dist/tools/instagram/hashtags.d.ts +1 -0
- package/dist/tools/instagram/hashtags.d.ts.map +1 -1
- package/dist/tools/instagram/hashtags.js +54 -36
- package/dist/tools/instagram/hashtags.js.map +1 -1
- package/dist/tools/instagram/media.d.ts.map +1 -1
- package/dist/tools/instagram/media.js +53 -34
- package/dist/tools/instagram/media.js.map +1 -1
- package/dist/tools/instagram/mentions.d.ts.map +1 -1
- package/dist/tools/instagram/mentions.js +22 -17
- package/dist/tools/instagram/mentions.js.map +1 -1
- package/dist/tools/instagram/messaging.d.ts.map +1 -1
- package/dist/tools/instagram/messaging.js +74 -50
- package/dist/tools/instagram/messaging.js.map +1 -1
- package/dist/tools/instagram/profile.d.ts +2 -0
- package/dist/tools/instagram/profile.d.ts.map +1 -1
- package/dist/tools/instagram/profile.js +63 -43
- package/dist/tools/instagram/profile.js.map +1 -1
- package/dist/tools/instagram/publishing.d.ts.map +1 -1
- package/dist/tools/instagram/publishing.js +202 -139
- package/dist/tools/instagram/publishing.js.map +1 -1
- package/dist/tools/meta/auth.d.ts.map +1 -1
- package/dist/tools/meta/auth.js +63 -21
- package/dist/tools/meta/auth.js.map +1 -1
- package/dist/tools/threads/insights.d.ts.map +1 -1
- package/dist/tools/threads/insights.js +22 -15
- package/dist/tools/threads/insights.js.map +1 -1
- package/dist/tools/threads/media.d.ts.map +1 -1
- package/dist/tools/threads/media.js +42 -70
- package/dist/tools/threads/media.js.map +1 -1
- package/dist/tools/threads/mentions.d.ts.map +1 -1
- package/dist/tools/threads/mentions.js +15 -16
- package/dist/tools/threads/mentions.js.map +1 -1
- package/dist/tools/threads/profile.d.ts +2 -0
- package/dist/tools/threads/profile.d.ts.map +1 -1
- package/dist/tools/threads/profile.js +18 -3
- package/dist/tools/threads/profile.js.map +1 -1
- package/dist/tools/threads/publishing.d.ts.map +1 -1
- package/dist/tools/threads/publishing.js +260 -157
- package/dist/tools/threads/publishing.js.map +1 -1
- package/dist/tools/threads/replies.d.ts.map +1 -1
- package/dist/tools/threads/replies.js +55 -45
- package/dist/tools/threads/replies.js.map +1 -1
- package/dist/utils/container.d.ts +16 -4
- package/dist/utils/container.d.ts.map +1 -1
- package/dist/utils/container.js +36 -6
- package/dist/utils/container.js.map +1 -1
- package/dist/utils/errors.d.ts +23 -3
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +32 -2
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/logger.d.ts +28 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +32 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/params.d.ts +3 -0
- package/dist/utils/params.d.ts.map +1 -0
- package/dist/utils/params.js +14 -0
- package/dist/utils/params.js.map +1 -0
- package/dist/utils/progress.d.ts +42 -0
- package/dist/utils/progress.d.ts.map +1 -0
- package/dist/utils/progress.js +35 -0
- package/dist/utils/progress.js.map +1 -0
- package/dist/utils/response.d.ts +11 -0
- package/dist/utils/response.d.ts.map +1 -0
- package/dist/utils/response.js +6 -0
- package/dist/utils/response.js.map +1 -0
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -71,9 +71,25 @@ npm run build
|
|
|
71
71
|
| `META_APP_SECRET` | For token/webhook tools | Meta App Secret |
|
|
72
72
|
| `META_API_VERSION` | Optional | Meta Graph API version for Instagram and Facebook endpoints — defaults to `v25.0` (verified 2026-05-06). Override only when Meta deprecates a version before meta-mcp ships a new release. Format: `vMAJOR.MINOR` (e.g., `v26.0`); malformed values fall back to the default with a stderr warning. OAuth token endpoints are unversioned and unaffected by this setting |
|
|
73
73
|
| `THREADS_API_VERSION` | Optional | Threads API version — defaults to `v1.0` (verified 2026-05-06). Threads runs a separate single-major-version track and is not bumped in lockstep with the Graph API. Same `vMAJOR.MINOR` format and fallback behavior as `META_API_VERSION` |
|
|
74
|
+
| `MCP_TRANSPORT` | Optional | Transport to serve MCP over — `stdio` (default) or `http`. See [HTTP Transport](#http-transport) |
|
|
75
|
+
| `MCP_HTTP_PORT` | Optional (http) | TCP port for the HTTP transport — defaults to `3000`. Must be an integer 1–65535 |
|
|
76
|
+
| `MCP_HTTP_HOST` | Optional (http) | Bind address for the HTTP transport — defaults to `127.0.0.1` (loopback only). Set to `0.0.0.0` to accept connections from other hosts (e.g. in Docker), but only behind a TLS/auth reverse proxy |
|
|
77
|
+
| `MCP_HTTP_ALLOWED_HOSTS` | Optional (http) | Comma-separated `host:port` allowlist for DNS-rebinding protection. Defaults to loopback variants at the bound port; set this when binding to a non-loopback host so legitimate requests pass the Host check |
|
|
74
78
|
|
|
75
79
|
The server validates these at startup. Malformed values for `INSTAGRAM_USER_ID`, `THREADS_USER_ID`, or `META_APP_ID` cause the process to exit with `Invalid meta-mcp configuration: …`. Setting only one half of a credential pair (e.g., `INSTAGRAM_ACCESS_TOKEN` without `INSTAGRAM_USER_ID`) prints a stderr warning and continues; related tool invocations still fail at call time.
|
|
76
80
|
|
|
81
|
+
## HTTP Transport
|
|
82
|
+
|
|
83
|
+
By default the server speaks MCP over **stdio** — the right choice for local clients (Claude Desktop, Claude Code, etc.). Set `MCP_TRANSPORT=http` to instead serve the SDK's **Streamable HTTP** transport, which enables remote/web-based MCP clients, cloud deployments, and multiple concurrent client sessions.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
MCP_TRANSPORT=http MCP_HTTP_PORT=3000 npx @exileum/meta-mcp
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The server then listens on `http://127.0.0.1:3000/mcp`: `POST` to send messages, `GET` for the server→client SSE stream, `DELETE` to end a session. Each client gets an isolated session keyed by the `Mcp-Session-Id` header, so multiple clients can connect at once.
|
|
90
|
+
|
|
91
|
+
**Security.** The transport binds to `127.0.0.1` (loopback) by default and enables DNS-rebinding protection scoped to localhost, so it is not reachable off-host out of the box. To expose it to other machines (e.g. a container), set `MCP_HTTP_HOST=0.0.0.0` and run it **behind a reverse proxy that terminates TLS and handles authentication** — the server itself performs no auth. When bound to a non-loopback address, set `MCP_HTTP_ALLOWED_HOSTS` to the `host:port` values clients will use so the Host-header check passes (otherwise rebinding protection is disabled with a stderr warning).
|
|
92
|
+
|
|
77
93
|
## Account Requirements
|
|
78
94
|
|
|
79
95
|
| Platform | Account Type | Notes |
|
|
@@ -90,8 +106,13 @@ The server validates these at startup. Malformed values for `INSTAGRAM_USER_ID`,
|
|
|
90
106
|
- **Meta**: Token exchange/refresh/debug, webhook management
|
|
91
107
|
- **2 resources**: Instagram profile, Threads profile
|
|
92
108
|
- **2 prompts**: Cross-platform content publishing, analytics report
|
|
93
|
-
- Rate limit tracking via `x-app-usage` header
|
|
109
|
+
- Rate limit tracking via `x-app-usage` header — and **automatic client-side throttling** at 80% (1s slowdown) / 90% (5s backoff) so a burst of tool calls stays under Meta's per-app quota
|
|
110
|
+
- **Automatic retry** for transient Meta API failures (HTTP `429`/`500`/`502`/`503`/`504`, network errors, `fetch` timeouts) with exponential backoff and `Retry-After` honoring; tunable via `MetaClient`'s `maxRetries` option (default 3, set to 0 to disable)
|
|
94
111
|
- **Structured error responses** with `error_type` (`auth`, `validation`, `rate_limit`, `server`, `network`, `internal`), HTTP status, Meta API code/subcode/type, and a `remediation` hint where actionable — see [`CHANGELOG.md`](./CHANGELOG.md) for the JSON shape
|
|
112
|
+
- **MCP server `instructions`** sent during `initialize` so clients know required env vars, the two-step publish flow, expected video processing times, and the `_rateLimit` envelope without re-reading the README
|
|
113
|
+
- **MCP `notifications/progress`** emitted while polling container status during publishing — attach a `progressToken` to `ig_publish_*` / `threads_publish_image|video|carousel` calls and the server reports each poll attempt
|
|
114
|
+
- **Structured MCP logging** via the `notifications/message` channel (the server declares the `logging` capability) — each API call logs `debug` (method + path, never the token-bearing URL), terminal failures log `error` (status/code/sanitized message), rate-limit pressure logs `warning`, and `DELETE`/publish operations log an `info` audit line. Clients can raise the floor with `logging/setLevel` (default emits all levels, including `debug`)
|
|
115
|
+
- **Optional HTTP transport** — set `MCP_TRANSPORT=http` to serve the MCP Streamable HTTP transport (stateful multi-session, localhost-bound by default) instead of stdio, for remote/cloud deployments — see [HTTP Transport](#http-transport)
|
|
95
116
|
|
|
96
117
|
## Tools
|
|
97
118
|
|
|
@@ -171,21 +192,22 @@ The server validates these at startup. Malformed values for `INSTAGRAM_USER_ID`,
|
|
|
171
192
|
|------|-------------|
|
|
172
193
|
| `ig_get_conversations` | List DM conversations |
|
|
173
194
|
| `ig_get_messages` | Get messages in a conversation |
|
|
174
|
-
| `ig_send_message` | Send a DM |
|
|
195
|
+
| `ig_send_message` | Send a DM (optional `messaging_type` = `RESPONSE`/`UPDATE`/`MESSAGE_TAG` and `tag` = `HUMAN_AGENT` for the 7-day human-agent window) |
|
|
175
196
|
| `ig_get_message` | Get message details |
|
|
176
197
|
|
|
177
|
-
### Threads — Publishing (
|
|
198
|
+
### Threads — Publishing (9)
|
|
178
199
|
|
|
179
200
|
| Tool | Description |
|
|
180
201
|
|------|-------------|
|
|
181
|
-
| `threads_publish_text` | Publish a text post in a single API call (`auto_publish_text=true`, default; set `auto_publish=false` for the legacy two-step flow). Supports polls, GIFs, link attachments, topic tags, quote posts, spoiler flag, cross-share to IG Stories, geo-gating via `allowlisted_country_codes`, text attachments up to 10K chars with styling |
|
|
182
|
-
| `threads_publish_image` | Publish an image post (supports alt_text, topic tags, spoiler flag, cross-share to IG Stories, geo-gating via `allowlisted_country_codes`) |
|
|
183
|
-
| `threads_publish_video` | Publish a video post (supports alt_text, topic tags, spoiler flag, cross-share to IG Stories, geo-gating via `allowlisted_country_codes`) |
|
|
184
|
-
| `threads_publish_carousel` | Publish a carousel (2-20 items, supports alt_text per item, cross-share to IG Stories, geo-gating via `allowlisted_country_codes` on the parent container) |
|
|
202
|
+
| `threads_publish_text` | Publish a text post in a single API call (`auto_publish_text=true`, default; set `auto_publish=false` for the legacy two-step flow). Supports polls, GIFs, link attachments, topic tags, quote posts, spoiler flag, cross-share to IG Stories, geo-gating via `allowlisted_country_codes`, location tagging via `location_id`, text attachments up to 10K chars with styling |
|
|
203
|
+
| `threads_publish_image` | Publish an image post (supports alt_text, topic tags, spoiler flag, cross-share to IG Stories, geo-gating via `allowlisted_country_codes`, location tagging via `location_id`) |
|
|
204
|
+
| `threads_publish_video` | Publish a video post (supports alt_text, topic tags, spoiler flag, cross-share to IG Stories, geo-gating via `allowlisted_country_codes`, location tagging via `location_id`) |
|
|
205
|
+
| `threads_publish_carousel` | Publish a carousel (2-20 items, supports alt_text per item, cross-share to IG Stories, geo-gating via `allowlisted_country_codes` and location tagging via `location_id` on the parent container) |
|
|
185
206
|
| `threads_delete_post` | Delete a post (max 100/day) |
|
|
186
207
|
| `threads_get_container_status` | Check container processing status (unpublished containers only) |
|
|
187
208
|
| `threads_get_publishing_limit` | Check remaining publishing quota (250 posts/day) |
|
|
188
209
|
| `threads_repost` | Repost an existing thread to your profile (requires `threads_content_publish`) |
|
|
210
|
+
| `threads_search_locations` | Search Threads-supported locations by query (`q`) or coordinates (`latitude`+`longitude`) to obtain a `location_id` for the four `threads_publish_*` tools (requires `threads_location_tagging` permission) |
|
|
189
211
|
|
|
190
212
|
### Threads — Media & Search (3)
|
|
191
213
|
|
|
@@ -228,14 +250,14 @@ The server validates these at startup. Malformed values for `INSTAGRAM_USER_ID`,
|
|
|
228
250
|
| Resource URI | Description |
|
|
229
251
|
|-------------|-------------|
|
|
230
252
|
| `meta-mcp://instagram/profile` | Instagram account profile data |
|
|
231
|
-
| `meta-mcp://threads/profile` | Threads account profile data (includes is_verified) |
|
|
253
|
+
| `meta-mcp://threads/profile` | Threads account profile data (includes is_verified and is_eligible_for_geo_gating) |
|
|
232
254
|
|
|
233
255
|
## Prompts
|
|
234
256
|
|
|
235
|
-
| Prompt | Description |
|
|
236
|
-
|
|
237
|
-
| `content_publish` | Cross-post content to Instagram and Threads |
|
|
238
|
-
| `analytics_report` | Generate combined analytics report |
|
|
257
|
+
| Prompt | Description | Arguments (all optional) |
|
|
258
|
+
|--------|-------------|--------------------------|
|
|
259
|
+
| `content_publish` | Cross-post content to Instagram and Threads | `platform` (`instagram` \| `threads` \| `both`), `content_type` (`text` \| `image` \| `video` \| `carousel`), `media_url`, `caption` |
|
|
260
|
+
| `analytics_report` | Generate combined analytics report | `platform` (`instagram` \| `threads` \| `both`), `time_range` (`7d` \| `30d` \| `90d`), `focus` (`engagement` \| `growth` \| `content`) |
|
|
239
261
|
|
|
240
262
|
## Setup Guide
|
|
241
263
|
|
|
@@ -331,11 +353,15 @@ Access tokens expire after ~60 days. Refresh before expiration (token must be at
|
|
|
331
353
|
&access_token=CURRENT_LONG_LIVED_TOKEN
|
|
332
354
|
```
|
|
333
355
|
|
|
356
|
+
When you rotate a token through `meta_refresh_token` or `meta_exchange_token`, the new token is **automatically applied in-memory** to the running MCP server — subsequent tool calls use it immediately, no server restart needed. The new token is still returned in the response so you can persist it in your environment for the next process restart. A single `[meta-mcp] <Platform> access token updated in-memory after <tool>…` line is logged to stderr when this happens.
|
|
357
|
+
|
|
334
358
|
Check token status anytime with `meta_debug_token`.
|
|
335
359
|
|
|
336
360
|
## Troubleshooting
|
|
337
361
|
|
|
338
|
-
Tool failures return `isError: true` with a JSON body in `content[0].text` matching the envelope documented in [`CHANGELOG.md`](./CHANGELOG.md): `{ error: true, error_type, http_status, code, subcode, type, message, remediation, fbtrace_id, raw }`. The fastest path to a fix is to read `error_type` and the Meta API `code`, then jump to the matching subsection below. The full code reference is the [Meta Graph API error handling guide](https://developers.facebook.com/docs/graph-api/guides/error-handling/).
|
|
362
|
+
Tool failures return `isError: true` with a JSON body in `content[0].text` matching the envelope documented in [`CHANGELOG.md`](./CHANGELOG.md): `{ error: true, error_type, http_status, code, subcode, type, step, container_id, message, remediation, fbtrace_id, raw }`. The fastest path to a fix is to read `error_type` and the Meta API `code`, then jump to the matching subsection below. The full code reference is the [Meta Graph API error handling guide](https://developers.facebook.com/docs/graph-api/guides/error-handling/).
|
|
363
|
+
|
|
364
|
+
On the publish tools (`ig_publish_*`, `threads_publish_*`, `threads_reply`), errors also include `step` (`container creation` / `processing` / `publishing`, plus `child container creation` / `child processing` / `parent container creation` / `parent processing` on carousels) and `container_id` when one was created. The `message` mirrors them: `"Publish photo failed at processing (container: 17889615324): Container processing timed out after 30s"`. Use these to decide whether to retry the publish, clean up an orphaned container, or treat the existing container as still reusable.
|
|
339
365
|
|
|
340
366
|
### `error_type: "auth"` — expired, revoked, or under-scoped token
|
|
341
367
|
|
|
@@ -355,12 +381,12 @@ What to do:
|
|
|
355
381
|
|
|
356
382
|
### `error_type: "rate_limit"` — application or user quota exhausted
|
|
357
383
|
|
|
358
|
-
Triggered by Meta API codes `4`, `17`, `32`, `341`, `613`, the business-use-case range `80001`–`80008`, or HTTP `429`. Includes any `OAuthException` with code `4` / `17` (these are surfaced as `error_type: "rate_limit"`, **not** `"auth"`, despite the type field).
|
|
384
|
+
Triggered by Meta API codes `4`, `17`, `32`, `341`, `613`, the business-use-case range `80001`–`80008`, or HTTP `429`. Includes any `OAuthException` with code `4` / `17` (these are surfaced as `error_type: "rate_limit"`, **not** `"auth"`, despite the type field). `MetaClient` automatically retries HTTP `429` up to 3 times with exponential backoff and honors any `Retry-After` header — a `rate_limit` error reaching the caller means the retry budget was exhausted.
|
|
359
385
|
|
|
360
386
|
What to do:
|
|
361
387
|
|
|
362
|
-
1. Inspect the `_rateLimit` field on prior successful tool responses. `callCount`, `totalCpuTime`, and `totalTime` come from Meta's `x-app-usage` header; when
|
|
363
|
-
2.
|
|
388
|
+
1. Inspect the `_rateLimit` field on prior successful tool responses. `callCount`, `totalCpuTime`, and `totalTime` come from Meta's `x-app-usage` header; when any approaches `100` you are near the per-app threshold.
|
|
389
|
+
2. meta-mcp already self-throttles once `max(callCount, totalCpuTime, totalTime)` crosses 80% (1s slowdown) or 90% (5s backoff) — watch for the `warning`-level MCP log message (`logger: "meta-client"`, with `usage_pct` and `delay_ms`) the server emits before each throttled call. Profile reads (`ig_get_profile`, `threads_get_profile`, and the matching `meta-mcp://*/profile` resources) and hashtag-name lookups (`ig_search_hashtag`) are also cached in-process for 5 minutes / 7 days respectively, with cache hits skipping the network entirely. If you are still hitting `rate_limit` errors despite all that, reduce request volume further.
|
|
364
390
|
3. Threads has hard daily quotas (250 publishes, 100 deletes) — query the remaining quota with `threads_get_publishing_limit` before bulk operations.
|
|
365
391
|
|
|
366
392
|
### `error_type: "validation"` — bad parameter, wrong ID, or unsupported field
|
|
@@ -375,8 +401,8 @@ Triggered by Meta API codes `100`, `200`, `803`, or any unmapped 4xx HTTP status
|
|
|
375
401
|
|
|
376
402
|
### Other categories
|
|
377
403
|
|
|
378
|
-
- `error_type: "server"` (codes `1`, `2`, HTTP 5xx) — transient Meta outage
|
|
379
|
-
- `error_type: "network"` — `fetch` timed out or failed before reaching Meta.
|
|
404
|
+
- `error_type: "server"` (codes `1`, `2`, HTTP 5xx) — transient Meta outage. `MetaClient` already retried `500`/`502`/`503`/`504` up to 3 times with exponential backoff before surfacing this; check [metastatus.com](https://metastatus.com/) if it persists.
|
|
405
|
+
- `error_type: "network"` — `fetch` timed out or failed before reaching Meta. `MetaClient` already retried thrown network errors up to 3 times; verify outbound connectivity if the error keeps reappearing.
|
|
380
406
|
- `error_type: "internal"` — unexpected condition that did not map to a Meta error code. The `raw` field carries the sanitized original message; `access_token`, `client_secret`, and `input_token` values are scrubbed to `***` before reporting.
|
|
381
407
|
|
|
382
408
|
## API Stability
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const IG_PROFILE_FIELDS = "id,name,username,biography,followers_count,follows_count,media_count,profile_picture_url,website";
|
|
2
|
+
export declare const IG_MEDIA_FIELDS = "id,caption,media_type,media_url,permalink,thumbnail_url,timestamp,like_count,comments_count";
|
|
3
|
+
export declare const IG_HASHTAG_MEDIA_FIELDS = "id,caption,media_type,media_url,permalink,timestamp,like_count,comments_count";
|
|
4
|
+
export declare const THREADS_PROFILE_FIELDS = "id,username,name,threads_profile_picture_url,threads_biography,is_verified,is_eligible_for_geo_gating";
|
|
5
|
+
export declare const THREADS_MEDIA_FIELDS: string;
|
|
6
|
+
//# sourceMappingURL=fields.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../src/constants/fields.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,qGACsE,CAAC;AAErG,eAAO,MAAM,eAAe,gGACmE,CAAC;AAIhG,eAAO,MAAM,uBAAuB,kFAC6C,CAAC;AAElF,eAAO,MAAM,sBAAsB,0GACsE,CAAC;AAE1G,eAAO,MAAM,oBAAoB,QAiBtB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const IG_PROFILE_FIELDS = "id,name,username,biography,followers_count,follows_count,media_count,profile_picture_url,website";
|
|
2
|
+
export const IG_MEDIA_FIELDS = "id,caption,media_type,media_url,permalink,thumbnail_url,timestamp,like_count,comments_count";
|
|
3
|
+
// thumbnail_url is intentionally omitted — the Instagram Hashtag Search API
|
|
4
|
+
// does not return it on hashtag media results, only on user-owned media.
|
|
5
|
+
export const IG_HASHTAG_MEDIA_FIELDS = "id,caption,media_type,media_url,permalink,timestamp,like_count,comments_count";
|
|
6
|
+
export const THREADS_PROFILE_FIELDS = "id,username,name,threads_profile_picture_url,threads_biography,is_verified,is_eligible_for_geo_gating";
|
|
7
|
+
export const THREADS_MEDIA_FIELDS = [
|
|
8
|
+
"id",
|
|
9
|
+
"media_product_type",
|
|
10
|
+
"media_type",
|
|
11
|
+
"media_url",
|
|
12
|
+
"permalink",
|
|
13
|
+
"text",
|
|
14
|
+
"timestamp",
|
|
15
|
+
"shortcode",
|
|
16
|
+
"is_quote_post",
|
|
17
|
+
"has_replies",
|
|
18
|
+
"reply_audience",
|
|
19
|
+
"topic_tag",
|
|
20
|
+
"link_attachment_url",
|
|
21
|
+
"poll_attachment{option_a,option_b,option_c,option_d,option_a_votes_percentage,option_b_votes_percentage,option_c_votes_percentage,option_d_votes_percentage,total_votes,expiration_timestamp}",
|
|
22
|
+
"gif_url",
|
|
23
|
+
"alt_text",
|
|
24
|
+
].join(",");
|
|
25
|
+
//# sourceMappingURL=fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fields.js","sourceRoot":"","sources":["../../src/constants/fields.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAC5B,kGAAkG,CAAC;AAErG,MAAM,CAAC,MAAM,eAAe,GAC1B,6FAA6F,CAAC;AAEhG,4EAA4E;AAC5E,yEAAyE;AACzE,MAAM,CAAC,MAAM,uBAAuB,GAClC,+EAA+E,CAAC;AAElF,MAAM,CAAC,MAAM,sBAAsB,GACjC,uGAAuG,CAAC;AAE1G,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,IAAI;IACJ,oBAAoB;IACpB,YAAY;IACZ,WAAW;IACX,WAAW;IACX,MAAM;IACN,WAAW;IACX,WAAW;IACX,eAAe;IACf,aAAa;IACb,gBAAgB;IAChB,WAAW;IACX,qBAAqB;IACrB,+LAA+L;IAC/L,SAAS;IACT,UAAU;CACX,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
export declare const DEFAULT_HTTP_HOST = "127.0.0.1";
|
|
3
|
+
export declare const DEFAULT_HTTP_PORT = 3000;
|
|
4
|
+
export declare const MCP_ENDPOINT = "/mcp";
|
|
5
|
+
export interface HttpTransportConfig {
|
|
6
|
+
host: string;
|
|
7
|
+
port: number;
|
|
8
|
+
allowedHosts: string[] | undefined;
|
|
9
|
+
}
|
|
10
|
+
export interface StartHttpTransportOptions extends HttpTransportConfig {
|
|
11
|
+
createServer: () => McpServer;
|
|
12
|
+
log?: (msg: string) => void;
|
|
13
|
+
}
|
|
14
|
+
export interface HttpTransportHandle {
|
|
15
|
+
port: number;
|
|
16
|
+
close(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export declare function parseHttpTransportConfig(env: NodeJS.ProcessEnv): HttpTransportConfig;
|
|
19
|
+
export declare function startHttpTransport(options: StartHttpTransportOptions): Promise<HttpTransportHandle>;
|
|
20
|
+
//# sourceMappingURL=http-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-transport.d.ts","sourceRoot":"","sources":["../src/http-transport.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIzE,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAC7C,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,YAAY,SAAS,CAAC;AAKnC,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IAEb,YAAY,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CACpC;AAED,MAAM,WAAW,yBAA0B,SAAQ,mBAAmB;IAEpE,YAAY,EAAE,MAAM,SAAS,CAAC;IAC9B,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,mBAAmB,CAsBpF;AAED,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,mBAAmB,CAAC,CAoI9B"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
export const DEFAULT_HTTP_HOST = "127.0.0.1";
|
|
6
|
+
export const DEFAULT_HTTP_PORT = 3000;
|
|
7
|
+
export const MCP_ENDPOINT = "/mcp";
|
|
8
|
+
// Node's http server imposes no body limit; cap reads to avoid memory exhaustion.
|
|
9
|
+
const MAX_BODY_BYTES = 4 * 1024 * 1024;
|
|
10
|
+
export function parseHttpTransportConfig(env) {
|
|
11
|
+
const host = env.MCP_HTTP_HOST?.trim() || DEFAULT_HTTP_HOST;
|
|
12
|
+
let port = DEFAULT_HTTP_PORT;
|
|
13
|
+
const rawPort = env.MCP_HTTP_PORT?.trim();
|
|
14
|
+
if (rawPort) {
|
|
15
|
+
// Number() (unlike parseInt) rejects "3000abc" as NaN.
|
|
16
|
+
const parsed = Number(rawPort);
|
|
17
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
|
|
18
|
+
throw new Error(`MCP_HTTP_PORT must be an integer between 1 and 65535 (got "${rawPort}")`);
|
|
19
|
+
}
|
|
20
|
+
port = parsed;
|
|
21
|
+
}
|
|
22
|
+
const rawAllowed = env.MCP_HTTP_ALLOWED_HOSTS?.trim();
|
|
23
|
+
const allowedHosts = rawAllowed
|
|
24
|
+
? rawAllowed.split(",").map((h) => h.trim()).filter(Boolean)
|
|
25
|
+
: undefined;
|
|
26
|
+
return { host, port, allowedHosts };
|
|
27
|
+
}
|
|
28
|
+
export async function startHttpTransport(options) {
|
|
29
|
+
const { host, createServer: createMcpServer } = options;
|
|
30
|
+
const log = options.log ?? ((msg) => console.error(msg));
|
|
31
|
+
const sessions = new Map();
|
|
32
|
+
// Resolved after listen() so DNS-rebinding allowlists can include the actual
|
|
33
|
+
// bound port (matters when port is 0, e.g. in tests).
|
|
34
|
+
let allowedHosts = [];
|
|
35
|
+
let dnsRebindingProtection = false;
|
|
36
|
+
const httpServer = createServer((req, res) => {
|
|
37
|
+
void handleRequest(req, res).catch((err) => {
|
|
38
|
+
log(`[meta-mcp] HTTP handler error — ${errMessage(err)}`);
|
|
39
|
+
sendJsonRpcError(res, 500, -32603, "Internal server error");
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
async function handleRequest(req, res) {
|
|
43
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? host}`);
|
|
44
|
+
if (url.pathname !== MCP_ENDPOINT) {
|
|
45
|
+
sendJsonRpcError(res, 404, -32601, `Not found: ${url.pathname}`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const method = req.method ?? "GET";
|
|
49
|
+
const sessionId = headerValue(req.headers["mcp-session-id"]);
|
|
50
|
+
if (method === "POST") {
|
|
51
|
+
let body;
|
|
52
|
+
try {
|
|
53
|
+
body = await readJsonBody(req);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
// Fixed messages only — never echo exception text back to a remote client.
|
|
57
|
+
if (err instanceof PayloadTooLargeError) {
|
|
58
|
+
sendJsonRpcError(res, 413, -32600, "Request body too large");
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
sendJsonRpcError(res, 400, -32700, "Invalid JSON in request body");
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (sessionId) {
|
|
66
|
+
const transport = sessions.get(sessionId);
|
|
67
|
+
if (!transport) {
|
|
68
|
+
sendJsonRpcError(res, 404, -32001, "Session not found");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
await transport.handleRequest(req, res, body);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (isInitializeRequest(body)) {
|
|
75
|
+
const transport = new StreamableHTTPServerTransport({
|
|
76
|
+
sessionIdGenerator: () => randomUUID(),
|
|
77
|
+
enableDnsRebindingProtection: dnsRebindingProtection,
|
|
78
|
+
allowedHosts,
|
|
79
|
+
onsessioninitialized: (sid) => {
|
|
80
|
+
sessions.set(sid, transport);
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
// Drop the session on close (DELETE or client drop) so the map can't leak.
|
|
84
|
+
transport.onclose = () => {
|
|
85
|
+
const sid = transport.sessionId;
|
|
86
|
+
if (sid)
|
|
87
|
+
sessions.delete(sid);
|
|
88
|
+
};
|
|
89
|
+
try {
|
|
90
|
+
await createMcpServer().connect(transport);
|
|
91
|
+
await transport.handleRequest(req, res, body);
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
// Close so onclose evicts a half-initialized session from the map.
|
|
95
|
+
await transport.close().catch(() => undefined);
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
sendJsonRpcError(res, 400, -32000, "Bad Request: missing or invalid session ID");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (method === "GET" || method === "DELETE") {
|
|
104
|
+
if (!sessionId) {
|
|
105
|
+
sendJsonRpcError(res, 400, -32000, "Bad Request: missing session ID");
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const transport = sessions.get(sessionId);
|
|
109
|
+
if (!transport) {
|
|
110
|
+
sendJsonRpcError(res, 404, -32001, "Session not found");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
await transport.handleRequest(req, res);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
sendJsonRpcError(res, 405, -32601, `Method not allowed: ${method}`);
|
|
117
|
+
}
|
|
118
|
+
await new Promise((resolve, reject) => {
|
|
119
|
+
const onListenError = (err) => reject(err);
|
|
120
|
+
httpServer.once("error", onListenError);
|
|
121
|
+
httpServer.listen(options.port, host, () => {
|
|
122
|
+
httpServer.removeListener("error", onListenError);
|
|
123
|
+
resolve();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
const actualPort = httpServer.address().port;
|
|
127
|
+
if (options.allowedHosts && options.allowedHosts.length > 0) {
|
|
128
|
+
allowedHosts = options.allowedHosts;
|
|
129
|
+
dnsRebindingProtection = true;
|
|
130
|
+
}
|
|
131
|
+
else if (isLoopbackHost(host)) {
|
|
132
|
+
allowedHosts = loopbackHosts(actualPort);
|
|
133
|
+
dnsRebindingProtection = true;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// Bound to a routable interface (e.g. 0.0.0.0 in Docker) with no allowlist —
|
|
137
|
+
// a Host allowlist can't be derived, so leave protection off and tell the
|
|
138
|
+
// operator to front it with a proxy or set MCP_HTTP_ALLOWED_HOSTS.
|
|
139
|
+
log(`[meta-mcp] Warning: HTTP transport bound to non-loopback host ${host} without ` +
|
|
140
|
+
`MCP_HTTP_ALLOWED_HOSTS — DNS-rebinding protection is disabled. Run behind a ` +
|
|
141
|
+
`reverse proxy and/or set MCP_HTTP_ALLOWED_HOSTS.`);
|
|
142
|
+
}
|
|
143
|
+
log(`[meta-mcp] HTTP transport listening on http://${host}:${actualPort}${MCP_ENDPOINT}`);
|
|
144
|
+
return {
|
|
145
|
+
port: actualPort,
|
|
146
|
+
close: () => closeHttpTransport(httpServer, sessions),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
async function closeHttpTransport(httpServer, sessions) {
|
|
150
|
+
// Close transports first: each open SSE stream holds a socket that
|
|
151
|
+
// httpServer.close() would otherwise wait on forever.
|
|
152
|
+
for (const transport of sessions.values()) {
|
|
153
|
+
try {
|
|
154
|
+
await transport.close();
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// best-effort — keep tearing down the rest
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
sessions.clear();
|
|
161
|
+
httpServer.closeIdleConnections();
|
|
162
|
+
await new Promise((resolve, reject) => {
|
|
163
|
+
httpServer.close((err) => (err ? reject(err) : resolve()));
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
class PayloadTooLargeError extends Error {
|
|
167
|
+
}
|
|
168
|
+
function readJsonBody(req) {
|
|
169
|
+
return new Promise((resolve, reject) => {
|
|
170
|
+
const chunks = [];
|
|
171
|
+
let size = 0;
|
|
172
|
+
let settled = false;
|
|
173
|
+
const settle = (fn) => {
|
|
174
|
+
if (settled)
|
|
175
|
+
return;
|
|
176
|
+
settled = true;
|
|
177
|
+
fn();
|
|
178
|
+
};
|
|
179
|
+
req.on("data", (chunk) => {
|
|
180
|
+
size += chunk.length;
|
|
181
|
+
if (size > MAX_BODY_BYTES) {
|
|
182
|
+
// Reject and stop buffering, but keep draining the socket so the 413
|
|
183
|
+
// response writes cleanly — destroying req here would kill res too.
|
|
184
|
+
settle(() => reject(new PayloadTooLargeError(`Request body exceeds ${MAX_BODY_BYTES} bytes`)));
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
chunks.push(chunk);
|
|
188
|
+
});
|
|
189
|
+
req.on("end", () => {
|
|
190
|
+
settle(() => {
|
|
191
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
192
|
+
if (!raw) {
|
|
193
|
+
resolve(undefined);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
resolve(JSON.parse(raw));
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
reject(new SyntaxError("Invalid JSON in request body"));
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
req.on("error", (err) => settle(() => reject(err)));
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
function sendJsonRpcError(res, status, code, message) {
|
|
208
|
+
if (res.headersSent || res.writableEnded || res.destroyed)
|
|
209
|
+
return;
|
|
210
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
211
|
+
res.end(JSON.stringify({ jsonrpc: "2.0", error: { code, message }, id: null }));
|
|
212
|
+
}
|
|
213
|
+
function headerValue(value) {
|
|
214
|
+
if (Array.isArray(value))
|
|
215
|
+
return value[0];
|
|
216
|
+
return value;
|
|
217
|
+
}
|
|
218
|
+
function isLoopbackHost(host) {
|
|
219
|
+
// The whole 127.0.0.0/8 range is loopback, plus localhost and IPv6 ::1.
|
|
220
|
+
return host === "localhost" || host === "::1" || /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host);
|
|
221
|
+
}
|
|
222
|
+
function loopbackHosts(port) {
|
|
223
|
+
return [`127.0.0.1:${port}`, `localhost:${port}`, `[::1]:${port}`];
|
|
224
|
+
}
|
|
225
|
+
function errMessage(err) {
|
|
226
|
+
return err instanceof Error ? err.message : String(err);
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=http-transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-transport.js","sourceRoot":"","sources":["../src/http-transport.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAKA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAwEpE,wBAAgB,mBAAmB,IAAI,SAAS,CAU/C"}
|
package/dist/index.js
CHANGED
|
@@ -1,79 +1,72 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
2
3
|
import { createRequire } from "node:module";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
3
5
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
7
|
import { loadConfig } from "./config.js";
|
|
8
|
+
import { parseHttpTransportConfig, startHttpTransport, } from "./http-transport.js";
|
|
6
9
|
import { MetaClient } from "./services/meta-client.js";
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
import { registerIgPublishingTools } from "./tools/instagram/publishing.js";
|
|
11
|
-
import { registerIgMediaTools } from "./tools/instagram/media.js";
|
|
12
|
-
import { registerIgCommentTools } from "./tools/instagram/comments.js";
|
|
13
|
-
import { registerIgProfileTools } from "./tools/instagram/profile.js";
|
|
14
|
-
import { registerIgHashtagTools } from "./tools/instagram/hashtags.js";
|
|
15
|
-
import { registerIgMentionTools } from "./tools/instagram/mentions.js";
|
|
16
|
-
import { registerIgMessagingTools } from "./tools/instagram/messaging.js";
|
|
17
|
-
// Threads tools
|
|
18
|
-
import { registerThreadsPublishingTools } from "./tools/threads/publishing.js";
|
|
19
|
-
import { registerThreadsMediaTools } from "./tools/threads/media.js";
|
|
20
|
-
import { registerThreadsReplyTools } from "./tools/threads/replies.js";
|
|
21
|
-
import { registerThreadsProfileTools } from "./tools/threads/profile.js";
|
|
22
|
-
import { registerThreadsInsightTools } from "./tools/threads/insights.js";
|
|
23
|
-
import { registerThreadsMentionsTools } from "./tools/threads/mentions.js";
|
|
24
|
-
// Resources & Prompts
|
|
25
|
-
import { registerInstagramResources } from "./resources/instagram.js";
|
|
26
|
-
import { registerThreadsResources } from "./resources/threads.js";
|
|
27
|
-
import { registerPrompts } from "./prompts/index.js";
|
|
10
|
+
import { registerAll } from "./register-all.js";
|
|
11
|
+
import { setupFatalErrorHandlers, setupShutdownHandlers } from "./shutdown.js";
|
|
12
|
+
import { createMcpLogger } from "./utils/logger.js";
|
|
28
13
|
const require = createRequire(import.meta.url);
|
|
29
14
|
const { version: SERVER_VERSION } = require("../package.json");
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
15
|
+
const SERVER_INSTRUCTIONS = [
|
|
16
|
+
"Meta MCP server for managing Instagram and Threads via the Meta Graph API.",
|
|
17
|
+
"Instagram tools require INSTAGRAM_ACCESS_TOKEN and INSTAGRAM_USER_ID; Threads tools require THREADS_ACCESS_TOKEN and THREADS_USER_ID.",
|
|
18
|
+
"Token-rotation tools (meta_exchange_token, meta_refresh_token) additionally need META_APP_ID and META_APP_SECRET.",
|
|
19
|
+
"Most publishing tools follow a two-step flow internally: create a container, wait for processing (up to 30s for images, up to 5 minutes for videos), then publish — exposed as a single MCP tool call.",
|
|
20
|
+
"When the client sets a progressToken on a publish call, the server emits notifications/progress events while polling container status.",
|
|
21
|
+
"Tool responses include a _rateLimit field when the Meta API returns rate-limit headers; check it to throttle subsequent calls.",
|
|
22
|
+
].join(" ");
|
|
23
|
+
// Server is built before the client so the client can be handed a logger that
|
|
24
|
+
// emits to the MCP `notifications/message` channel. `capabilities.logging`
|
|
25
|
+
// must be declared or sendLoggingMessage is a silent no-op (#62).
|
|
26
|
+
function buildServer(config) {
|
|
27
|
+
const server = new McpServer({
|
|
28
|
+
name: "meta-mcp",
|
|
29
|
+
version: SERVER_VERSION,
|
|
30
|
+
}, {
|
|
31
|
+
instructions: SERVER_INSTRUCTIONS,
|
|
32
|
+
capabilities: { logging: {} },
|
|
33
|
+
});
|
|
34
|
+
const client = new MetaClient(config, { logger: createMcpLogger(server, "meta-client") });
|
|
35
|
+
registerAll(server, client);
|
|
36
|
+
return server;
|
|
41
37
|
}
|
|
42
|
-
const client = new MetaClient(config);
|
|
43
|
-
// Register tools
|
|
44
|
-
registerMetaAuthTools(server, client);
|
|
45
|
-
registerIgPublishingTools(server, client);
|
|
46
|
-
registerIgMediaTools(server, client);
|
|
47
|
-
registerIgCommentTools(server, client);
|
|
48
|
-
registerIgProfileTools(server, client);
|
|
49
|
-
registerIgHashtagTools(server, client);
|
|
50
|
-
registerIgMentionTools(server, client);
|
|
51
|
-
registerIgMessagingTools(server, client);
|
|
52
|
-
registerThreadsPublishingTools(server, client);
|
|
53
|
-
registerThreadsMediaTools(server, client);
|
|
54
|
-
registerThreadsReplyTools(server, client);
|
|
55
|
-
registerThreadsProfileTools(server, client);
|
|
56
|
-
registerThreadsInsightTools(server, client);
|
|
57
|
-
registerThreadsMentionsTools(server, client);
|
|
58
|
-
// Register resources
|
|
59
|
-
registerInstagramResources(server, client);
|
|
60
|
-
registerThreadsResources(server, client);
|
|
61
|
-
// Register prompts
|
|
62
|
-
registerPrompts(server);
|
|
63
38
|
async function main() {
|
|
64
|
-
|
|
65
|
-
|
|
39
|
+
let config;
|
|
40
|
+
let httpConfig = null;
|
|
41
|
+
try {
|
|
42
|
+
config = loadConfig();
|
|
43
|
+
const kind = (process.env.MCP_TRANSPORT ?? "stdio").trim().toLowerCase();
|
|
44
|
+
if (kind === "http") {
|
|
45
|
+
httpConfig = parseHttpTransportConfig(process.env);
|
|
46
|
+
}
|
|
47
|
+
else if (kind !== "stdio") {
|
|
48
|
+
throw new Error(`MCP_TRANSPORT must be "stdio" or "http" (got "${kind}")`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
if (httpConfig) {
|
|
56
|
+
const handle = await startHttpTransport({
|
|
57
|
+
...httpConfig,
|
|
58
|
+
createServer: () => buildServer(config),
|
|
59
|
+
});
|
|
60
|
+
setupShutdownHandlers(handle);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const server = buildServer(config);
|
|
64
|
+
await server.connect(new StdioServerTransport());
|
|
65
|
+
setupShutdownHandlers(server);
|
|
66
|
+
}
|
|
66
67
|
}
|
|
67
|
-
main().catch((err) => {
|
|
68
|
-
console.error("Fatal error:", err);
|
|
69
|
-
process.exit(1);
|
|
70
|
-
});
|
|
71
68
|
// ── Smithery Sandbox ──
|
|
72
69
|
export function createSandboxServer() {
|
|
73
|
-
const sandbox = new McpServer({
|
|
74
|
-
name: "meta-mcp",
|
|
75
|
-
version: SERVER_VERSION,
|
|
76
|
-
});
|
|
77
70
|
const mockConfig = {
|
|
78
71
|
appId: "",
|
|
79
72
|
appSecret: "",
|
|
@@ -82,24 +75,26 @@ export function createSandboxServer() {
|
|
|
82
75
|
threadsAccessToken: "",
|
|
83
76
|
threadsUserId: "",
|
|
84
77
|
};
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
78
|
+
return buildServer(mockConfig);
|
|
79
|
+
}
|
|
80
|
+
// Guard keeps `import { createSandboxServer }` and test imports side-effect-free —
|
|
81
|
+
// without it, importing this module would always run main() and connect stdio.
|
|
82
|
+
function isInvokedAsCli() {
|
|
83
|
+
const entry = process.argv[1];
|
|
84
|
+
if (!entry)
|
|
85
|
+
return false;
|
|
86
|
+
try {
|
|
87
|
+
return import.meta.url === pathToFileURL(realpathSync(entry)).href;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (isInvokedAsCli()) {
|
|
94
|
+
setupFatalErrorHandlers();
|
|
95
|
+
main().catch((err) => {
|
|
96
|
+
console.error("Fatal error:", err);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
});
|
|
104
99
|
}
|
|
105
100
|
//# sourceMappingURL=index.js.map
|