@twitterapis/mcp 0.6.9 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +23 -4
- package/package.json +2 -2
- package/src/index.js +24 -7
- package/src/query.js +32 -0
- package/src/tools.js +177 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.2 (2026-08-14)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **3 new compat tools for tweet monitoring** (task #87): `twitter_x_user_stream_add_user`, `twitter_x_user_stream_remove_user`, `twitter_x_user_stream_list_users` -- drop-in equivalents of `twitter_monitor_create`/`twitter_monitor_delete`/`twitter_monitor_list` using an alternate request/response envelope shape, for migrating an existing integration built against that shape without a rewrite. Free per call, same underlying monitor system, same safety checks as the native tools. The catalog is now **74 tools: 45 reads and 29 write actions**.
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **The generator's `toolPathFor` only special-cased `/account/*` as un-prefixed** (billing reads mounted at the API root rather than under `/twitter/`); the 3 new compat routes live at `/oapi/x_user_stream/*`, the same shape of gap, now handled identically.
|
|
12
|
+
|
|
13
|
+
## 0.7.1 (2026-08-14)
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **10 new tools for account monitoring + webhooks** (task #49, backend build plan Phase 5): watch an X account for new posts and get them pushed to your own HTTPS endpoint instead of polling. `twitter_monitor_create`, `twitter_monitor_list`, `twitter_monitor_update`, `twitter_monitor_delete`, `twitter_monitor_health`, `twitter_monitor_deliveries`, `twitter_monitor_webhook_create`, `twitter_monitor_webhook_list`, `twitter_monitor_webhook_delete`, `twitter_monitor_webhook_test`. Free per call (account administration, not a metered Twitter read); needs only your API key, no linked X session. The catalog is now **71 tools: 44 reads and 27 write actions**.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **The generator (`scripts/gen-tools.mjs`) could not represent a DELETE route, or two HTTP methods on the same REST path**, which is exactly the shape the monitor/webhook endpoints need (`/monitor/{id}` is POST to update and DELETE to remove; `/monitor` and `/webhook` are each GET to list and POST to create). The endpoint table was keyed by path alone (a second method on the same path silently overwrote the first) and skipped every method that wasn't `get`/`post` outright, so a vendored `delete` operation never reached the catalog at all. Endpoints are now keyed by `(method, path)`; an override targeting an ambiguous path sets `method: "..."` to say which one. A `{name}` URL-template segment (this API's spec declares no formal `in: "path"` parameter for one) is synthesized as a required arg and threaded through as the tool's `pathParams`, which the runtime (`src/index.js`, via the new `resolvePathParams` in `src/query.js`) substitutes into the URL instead of sending as a query-string or JSON-body field. Regression-tested against a synthetic route table in `test/gen-tools-endpoints.mjs`, independent of the real spec.
|
|
22
|
+
|
|
23
|
+
## 0.7.0 (2026-08-11)
|
|
24
|
+
|
|
25
|
+
### Removed
|
|
26
|
+
|
|
27
|
+
- **`twitter_users_by_ids` removed from the tool list.** X refuses the batch `UsersByRestIds` lookup for the pooled cookie sessions this package's REST backend reads through (confirmed by instrumenting the request and verifying a token was actually attached before it was rejected, not just repeated 403s). The REST endpoint itself stays live and returns an honest `503 endpoint_unavailable` rather than being deleted, but a tool the model can call and always get a hard failure from is worse than no tool at all, so it is out of the catalog. Use `twitter_user_info_by_id` instead: same user object, one id per call. The catalog is now **61 tools: 40 reads and 21 write actions**.
|
|
28
|
+
|
|
3
29
|
## 0.6.9 (2026-08-10)
|
|
4
30
|
|
|
5
31
|
### Added
|
package/README.md
CHANGED
|
@@ -91,9 +91,9 @@ Restart Claude Desktop. The `twitter_*` tools appear in the tool picker.
|
|
|
91
91
|
|
|
92
92
|
## Tools
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
74 tools: 45 reads and 29 write actions. Most user endpoints accept `username` (handle without @) **or** `user_id` (`twitter_user_likes` and `twitter_user_tweets_complete` require `user_id`); tweet endpoints accept `id` **or** `url`; paginated endpoints return a `cursor` you pass back to get the next page. Two of the reads are free account/billing lookups (`twitter_account_me`, `twitter_account_payments`); the 13 monitoring tools are also free (account administration, not metered reads).
|
|
95
95
|
|
|
96
|
-
Public reads (search, profiles, tweets, followers, likes) work with just your API key. The **account-only** reads (bookmarks, DMs, home timeline, followers-you-know) and **
|
|
96
|
+
Public reads (search, profiles, tweets, followers, likes) work with just your API key. The **account-only** reads (bookmarks, DMs, home timeline, followers-you-know) and **most write actions** act AS an authenticated X account, so they need a session linked to your key first (returns HTTP 409 until then). Link a session either by registering your x.com cookies (`twitter_customer_session`) or by logging in with a username/password (`twitter_user_login`). Alternatively, pass **per-call inline credentials** on any of those tools (`auth_token` + `ct0`, with optional `proxy_url` / `user_agent`) to act AS that account for a single call without pre-registering a session, so one API key can act as many accounts. For write actions, set `proxy_url` to a residential proxy, since X soft-blocks writes that egress from datacenter IPs. Each write tool is annotated `readOnlyHint: false`; reversing actions (delete, unfollow, unlike, unretweet, unbookmark, monitor/webhook delete) are annotated `destructiveHint: true` so MCP clients can prompt before running them. The **monitoring** tools (see below) are the one exception: they administer your twitterapis.com account, not an X session, so they need only your API key, no linked session and no inline credentials.
|
|
97
97
|
|
|
98
98
|
### Reads
|
|
99
99
|
|
|
@@ -103,7 +103,6 @@ Public reads (search, profiles, tweets, followers, likes) work with just your AP
|
|
|
103
103
|
| `twitter_user_search` | Find user accounts by name or keyword |
|
|
104
104
|
| `twitter_user_info` | Full profile by handle (bio, counts, verification, location) |
|
|
105
105
|
| `twitter_user_info_by_id` | Full profile by numeric user id |
|
|
106
|
-
| `twitter_users_by_ids` | Up to 100 numeric user ids resolved to full profiles in one call |
|
|
107
106
|
| `twitter_user_about` | A user's structured About object (category, professional/business labels, verification + identity-verification flags, joined date, and X's 'About this account' transparency panel) |
|
|
108
107
|
| `twitter_user_affiliates` | Accounts affiliated with an organization profile |
|
|
109
108
|
| `twitter_check_follow_relationship` | Follow relationship between two user ids (who follows whom) |
|
|
@@ -167,6 +166,26 @@ Public reads (search, profiles, tweets, followers, likes) work with just your AP
|
|
|
167
166
|
|
|
168
167
|
See also `twitter_article_get` and `twitter_article_list` above.
|
|
169
168
|
|
|
169
|
+
### Monitoring _(webhook delivery of new posts; free, not metered)_
|
|
170
|
+
|
|
171
|
+
Watch an X account for new posts and get them pushed to your own HTTPS endpoint, HMAC-signed, instead of polling. Register a webhook first, then create a monitor; every new post from a watched handle is delivered to every active webhook on your account (or a restricted subset via `webhook_ids`). Monitor/webhook CRUD is account administration, not a metered Twitter read, so every tool below is free.
|
|
172
|
+
|
|
173
|
+
| Tool | What it does |
|
|
174
|
+
|---|---|
|
|
175
|
+
| `twitter_monitor_create` | Start watching an X account (`handle`) for new posts |
|
|
176
|
+
| `twitter_monitor_list` | List every monitor on your account |
|
|
177
|
+
| `twitter_monitor_update` | Pause/resume a monitor or change its `webhook_ids` restriction |
|
|
178
|
+
| `twitter_monitor_delete` | Stop and remove a monitor (irreversible) |
|
|
179
|
+
| `twitter_monitor_health` | One monitor's status, degradation flag, poll interval, cursor position |
|
|
180
|
+
| `twitter_monitor_deliveries` | Recent delivery events across every monitor, with detection + delivery latency |
|
|
181
|
+
| `twitter_x_user_stream_add_user` | Compat drop-in for `twitter_monitor_create` using an x_user_stream-shaped envelope |
|
|
182
|
+
| `twitter_x_user_stream_remove_user` | Compat drop-in for `twitter_monitor_delete` using an x_user_stream-shaped envelope |
|
|
183
|
+
| `twitter_x_user_stream_list_users` | Compat drop-in for `twitter_monitor_list` using an x_user_stream-shaped envelope |
|
|
184
|
+
| `twitter_monitor_webhook_create` | Register an HTTPS delivery URL; returns the HMAC signing secret **once** |
|
|
185
|
+
| `twitter_monitor_webhook_list` | List every webhook registered on your account |
|
|
186
|
+
| `twitter_monitor_webhook_delete` | Soft-delete a webhook by id (irreversible from the caller's side) |
|
|
187
|
+
| `twitter_monitor_webhook_test` | Send one signed test event to a webhook right now, synchronously |
|
|
188
|
+
|
|
170
189
|
### Session setup
|
|
171
190
|
|
|
172
191
|
Link an X account to your key once, so the account-only reads and write actions act as it (or pass per-call `auth_token`/`ct0` instead).
|
|
@@ -257,7 +276,7 @@ Calls are billed to your twitterapis.com account. Almost every endpoint is $0.00
|
|
|
257
276
|
|
|
258
277
|
**Do I need an X (Twitter) developer account?** No. Get an API key at [twitterapis.com/signup](https://www.twitterapis.com/signup); there is no application or approval step.
|
|
259
278
|
|
|
260
|
-
**Is it read-only?** No.
|
|
279
|
+
**Is it read-only?** No. 45 read tools work with just your API key; 29 write actions (post, like, retweet, follow, DM, media upload, article create/edit/publish/delete, monitor/webhook create/update/delete) act as a linked X account or per-call inline credentials, except monitor/webhook CRUD, which is account administration and needs only your API key.
|
|
261
280
|
|
|
262
281
|
**Which clients are supported?** Claude Desktop, Cursor, Windsurf, and VS Code (Copilot agent mode), or any Model Context Protocol client.
|
|
263
282
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twitterapis/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Official MCP server for twitterapis.com, the Twitter/X API (search, users, followers, tweets, threads, lists, likes, bookmarks, DMs) plus write actions (post/like/retweet/follow) as native tools for Claude, Cursor, and any MCP client.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"build": "node scripts/gen-tools.mjs --write",
|
|
27
27
|
"build:check": "node scripts/gen-tools.mjs --check",
|
|
28
28
|
"openapi:refresh": "node scripts/openapi-refresh.mjs",
|
|
29
|
-
"test": "node scripts/gen-tools.mjs --check && node test/catalog-identity.mjs && node test/tools.test.mjs && node test/smoke.mjs && node test/openapi-parity.mjs && node test/readme-parity.mjs && node test/firewall.mjs && node test/registry-manifests.mjs",
|
|
29
|
+
"test": "node scripts/gen-tools.mjs --check && node test/gen-tools-endpoints.mjs && node test/catalog-identity.mjs && node test/tools.test.mjs && node test/smoke.mjs && node test/openapi-parity.mjs && node test/readme-parity.mjs && node test/firewall.mjs && node test/registry-manifests.mjs",
|
|
30
30
|
"prepublishOnly": "npm test && node test/publish-provenance.mjs",
|
|
31
31
|
"check:openapi-parity": "node test/openapi-parity.mjs",
|
|
32
32
|
"check:firewall": "node test/firewall.mjs",
|
package/src/index.js
CHANGED
|
@@ -24,7 +24,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
24
24
|
// places and drifted: the package shipped 0.5.0 while the MCP handshake and the
|
|
25
25
|
// outbound user-agent both still advertised 0.3.0.
|
|
26
26
|
const VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
27
|
-
import { TOOLS, buildQuery } from "./tools.js";
|
|
27
|
+
import { TOOLS, buildQuery, resolvePathParams, MissingPathParamError } from "./tools.js";
|
|
28
28
|
|
|
29
29
|
const API_KEY = process.env.TWITTERAPIS_KEY;
|
|
30
30
|
const BASE_URL = (
|
|
@@ -44,9 +44,26 @@ if (!API_KEY) {
|
|
|
44
44
|
// from the query string, so the same buildQuery path serves both and only the
|
|
45
45
|
// HTTP method differs. A few POST endpoints (customer/session, user_login,
|
|
46
46
|
// media/upload) instead read a JSON request body; those tools set jsonBody:true
|
|
47
|
-
// and callEndpoint sends the args in the body rather than the query string.
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
// and callEndpoint sends the args in the body rather than the query string. A
|
|
48
|
+
// handful of monitoring endpoints (/monitor/{id}, /webhook/{id}, ...) carry a
|
|
49
|
+
// REST path parameter instead: those tools set pathParams (the arg names to
|
|
50
|
+
// substitute into the URL template) and callEndpoint splices them into path
|
|
51
|
+
// before building the query string or body, so a pathParams arg never leaks
|
|
52
|
+
// into either.
|
|
53
|
+
async function callEndpoint(path, args, method = "GET", jsonBody = false, pathParams = []) {
|
|
54
|
+
// Fill {name} URL segments from args and strip those keys, so a pathParams arg
|
|
55
|
+
// (e.g. a monitor/webhook id) never also leaks into the query string or JSON
|
|
56
|
+
// body. A missing value fails loudly rather than shipping a request that still
|
|
57
|
+
// contains the literal "{id}" against the API.
|
|
58
|
+
let resolvedPath, all;
|
|
59
|
+
try {
|
|
60
|
+
({ path: resolvedPath, args: all } = resolvePathParams(path, pathParams, args));
|
|
61
|
+
} catch (err) {
|
|
62
|
+
if (err instanceof MissingPathParamError) {
|
|
63
|
+
return { isError: true, content: [{ type: "text", text: err.message }] };
|
|
64
|
+
}
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
50
67
|
|
|
51
68
|
const headers = {
|
|
52
69
|
// The API accepts either header; send both for maximum compatibility.
|
|
@@ -64,7 +81,7 @@ async function callEndpoint(path, args, method = "GET", jsonBody = false) {
|
|
|
64
81
|
// and user_login the credentials ARE the payload the handler reads from the
|
|
65
82
|
// body, so they must NOT be diverted into x-* headers the way per-call inline
|
|
66
83
|
// creds are on the query-string tools.
|
|
67
|
-
url = `${BASE_URL}${
|
|
84
|
+
url = `${BASE_URL}${resolvedPath}`;
|
|
68
85
|
headers["content-type"] = "application/json";
|
|
69
86
|
reqBody = JSON.stringify(all);
|
|
70
87
|
} else {
|
|
@@ -75,7 +92,7 @@ async function callEndpoint(path, args, method = "GET", jsonBody = false) {
|
|
|
75
92
|
// session is used. Lets a single key act as many accounts.
|
|
76
93
|
const { auth_token, ct0, user_agent, proxy_url, ...rest } = all;
|
|
77
94
|
const q = buildQuery(rest);
|
|
78
|
-
url = `${BASE_URL}${
|
|
95
|
+
url = `${BASE_URL}${resolvedPath}${q ? `?${q}` : ""}`;
|
|
79
96
|
if (auth_token && ct0) {
|
|
80
97
|
headers["x-auth-token"] = auth_token;
|
|
81
98
|
headers["x-ct0"] = ct0;
|
|
@@ -138,7 +155,7 @@ for (const tool of TOOLS) {
|
|
|
138
155
|
server.registerTool(
|
|
139
156
|
tool.name,
|
|
140
157
|
{ description: tool.description, inputSchema: tool.shape, annotations },
|
|
141
|
-
async (args) => callEndpoint(tool.path, args, method, Boolean(tool.jsonBody)),
|
|
158
|
+
async (args) => callEndpoint(tool.path, args, method, Boolean(tool.jsonBody), tool.pathParams || []),
|
|
142
159
|
);
|
|
143
160
|
}
|
|
144
161
|
|
package/src/query.js
CHANGED
|
@@ -14,3 +14,35 @@ export function buildQuery(args) {
|
|
|
14
14
|
}
|
|
15
15
|
return qs.toString();
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
// Thrown by resolvePathParams when a tool's pathParams arg is missing/empty. The
|
|
19
|
+
// caller decides how to surface it (index.js turns it into an MCP tool error);
|
|
20
|
+
// this module stays a pure mechanical helper with no knowledge of MCP shapes.
|
|
21
|
+
export class MissingPathParamError extends Error {
|
|
22
|
+
constructor(name, path) {
|
|
23
|
+
super(`Missing required path parameter "${name}" for ${path}.`);
|
|
24
|
+
this.name = "MissingPathParamError";
|
|
25
|
+
this.paramName = name;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Substitutes {name} URL-template segments (e.g. "/monitor/{id}") from `args`,
|
|
30
|
+
// for the handful of REST endpoints that carry a path parameter instead of a
|
|
31
|
+
// query/body one (monitor/webhook CRUD by id). Returns the resolved path plus
|
|
32
|
+
// args with every consumed key REMOVED, so a pathParams arg never also leaks
|
|
33
|
+
// into the query string or JSON body downstream. Throws MissingPathParamError
|
|
34
|
+
// rather than silently shipping a request that still contains the literal
|
|
35
|
+
// "{id}" against the API.
|
|
36
|
+
export function resolvePathParams(path, pathParams, args) {
|
|
37
|
+
const rest = { ...(args || {}) };
|
|
38
|
+
let resolved = path;
|
|
39
|
+
for (const name of pathParams || []) {
|
|
40
|
+
const value = rest[name];
|
|
41
|
+
delete rest[name];
|
|
42
|
+
if (value === undefined || value === null || value === "") {
|
|
43
|
+
throw new MissingPathParamError(name, path);
|
|
44
|
+
}
|
|
45
|
+
resolved = resolved.replace(`{${name}}`, encodeURIComponent(String(value)));
|
|
46
|
+
}
|
|
47
|
+
return { path: resolved, args: rest };
|
|
48
|
+
}
|
package/src/tools.js
CHANGED
|
@@ -8,18 +8,22 @@
|
|
|
8
8
|
// file in memory and fails if it does not match what is committed, so a hand edit
|
|
9
9
|
// here is caught rather than shipped.
|
|
10
10
|
//
|
|
11
|
-
// Catalog:
|
|
11
|
+
// Catalog: 74 tools (45 reads, 29 writes).
|
|
12
12
|
//
|
|
13
13
|
// Each tool maps 1:1 to a REST endpoint at https://api.twitterapis.com. Tool arg
|
|
14
14
|
// names map 1:1 to endpoint query params (every endpoint, including the POST
|
|
15
15
|
// write actions, reads its params from the query string), except the per-call
|
|
16
|
-
// inline credentials, which travel as x-* request headers,
|
|
17
|
-
// jsonBody tools, whose fields travel in a JSON request body
|
|
18
|
-
//
|
|
19
|
-
//
|
|
16
|
+
// inline credentials, which travel as x-* request headers, the 4
|
|
17
|
+
// jsonBody tools, whose fields travel in a JSON request body, and any arg listed
|
|
18
|
+
// in pathParams, which is substituted into the URL path (e.g. {id}) instead. A
|
|
19
|
+
// tool with `method: "POST"` or `method: "DELETE"` is a write that acts on
|
|
20
|
+
// behalf of the authenticated account behind your API key; reads are GET and
|
|
21
|
+
// default when `method` is omitted.
|
|
20
22
|
//
|
|
21
23
|
// write:true -> action mutates account/Twitter state (readOnlyHint:false)
|
|
22
24
|
// destructive:true -> action removes/reverses state (delete, un-follow/like/RT/bookmark)
|
|
25
|
+
// pathParams -> arg names substituted into the URL template, not sent as
|
|
26
|
+
// query-string or body fields (e.g. ["id"] for /monitor/{id})
|
|
23
27
|
import { z } from "zod";
|
|
24
28
|
|
|
25
29
|
export const TOOLS = [
|
|
@@ -82,17 +86,6 @@ export const TOOLS = [
|
|
|
82
86
|
),
|
|
83
87
|
},
|
|
84
88
|
},
|
|
85
|
-
{
|
|
86
|
-
name: "twitter_users_by_ids",
|
|
87
|
-
path: "/twitter/users/by_ids",
|
|
88
|
-
description:
|
|
89
|
-
"Resolve up to 100 numeric user ids into full profiles in ONE call. Same user object as twitter_user_info_by_id, returned as a list. Use this whenever you hold several ids and would otherwise loop twitter_user_info_by_id, for example hydrating the authors of a batch of tweets. Ids that no longer resolve (suspended or deleted accounts) are omitted rather than returned as nulls; compare the requested and resolved counts in the response, or diff the returned ids against the ones you sent, to see which were dropped. Sending more than 100 ids is rejected rather than truncated, so a short list always means those accounts are gone, never that the request was clipped.",
|
|
90
|
-
shape: {
|
|
91
|
-
user_ids: z.string().describe(
|
|
92
|
-
"Comma-separated numeric Twitter/X user ids, up to 100 (e.g. '44196397,745273'). Duplicates are collapsed and billed once.",
|
|
93
|
-
),
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
89
|
{
|
|
97
90
|
name: "twitter_user_about",
|
|
98
91
|
path: "/twitter/user/user_about",
|
|
@@ -1372,8 +1365,173 @@ export const TOOLS = [
|
|
|
1372
1365
|
),
|
|
1373
1366
|
},
|
|
1374
1367
|
},
|
|
1368
|
+
{
|
|
1369
|
+
name: "twitter_monitor_create",
|
|
1370
|
+
path: "/twitter/monitor",
|
|
1371
|
+
method: "POST",
|
|
1372
|
+
write: true,
|
|
1373
|
+
description:
|
|
1374
|
+
"Start watching an X account for new posts. Every new post from that handle is HMAC-signed and delivered to your registered webhook(s) on a shared poll interval (see twitter_monitor_webhook_create to register a delivery URL first). Free: monitor creation is account administration, not a metered read. Returns the new monitor's id, plus its normalized handle, status, and poll_interval_ms.",
|
|
1375
|
+
shape: {
|
|
1376
|
+
handle: z.string().min(1).describe(
|
|
1377
|
+
"The X username to watch, without the leading @ (e.g. 'elonmusk').",
|
|
1378
|
+
),
|
|
1379
|
+
webhook_ids: z.string().optional().describe(
|
|
1380
|
+
"Optional. Comma-separated webhook id(s) from twitter_monitor_webhook_create to restrict this monitor's deliveries to. Omit to deliver to every active webhook on the account (the default).",
|
|
1381
|
+
),
|
|
1382
|
+
},
|
|
1383
|
+
},
|
|
1384
|
+
{
|
|
1385
|
+
name: "twitter_monitor_list",
|
|
1386
|
+
path: "/twitter/monitor",
|
|
1387
|
+
description:
|
|
1388
|
+
"List every monitor on your account: id, subject (its from:<handle> query), kind, status ('active' or 'paused'), degraded flag, events_possibly_missed, webhook_ids restriction, and created_at. Takes no arguments.",
|
|
1389
|
+
shape: {},
|
|
1390
|
+
},
|
|
1391
|
+
{
|
|
1392
|
+
name: "twitter_monitor_update",
|
|
1393
|
+
path: "/twitter/monitor/{id}",
|
|
1394
|
+
method: "POST",
|
|
1395
|
+
write: true,
|
|
1396
|
+
pathParams: ["id"],
|
|
1397
|
+
description:
|
|
1398
|
+
"Partially update an existing monitor: pause or resume it via status, change which webhooks receive its events via webhook_ids, or both in the same call (applied atomically). Resuming a paused monitor re-runs the same capacity and per-account cap checks as creating a new one, since it adds load back to the shared pool. Free per call. Both fields are optional; omit either to leave it unchanged.",
|
|
1399
|
+
shape: {
|
|
1400
|
+
id: z.string().describe(
|
|
1401
|
+
"The monitor's id, from twitter_monitor_create or twitter_monitor_list.",
|
|
1402
|
+
),
|
|
1403
|
+
status: z.enum(["active","paused"]).optional().describe(
|
|
1404
|
+
"'paused' to pause the monitor, 'active' to resume it. Omit to leave status unchanged.",
|
|
1405
|
+
),
|
|
1406
|
+
webhook_ids: z.string().optional().describe(
|
|
1407
|
+
"Optional. Comma-separated webhook id(s) to restrict delivery to. Pass an empty string to clear the restriction back to 'deliver to every active webhook'. Omit entirely to leave it unchanged.",
|
|
1408
|
+
),
|
|
1409
|
+
},
|
|
1410
|
+
},
|
|
1411
|
+
{
|
|
1412
|
+
name: "twitter_monitor_delete",
|
|
1413
|
+
path: "/twitter/monitor/{id}",
|
|
1414
|
+
method: "DELETE",
|
|
1415
|
+
write: true,
|
|
1416
|
+
destructive: true,
|
|
1417
|
+
pathParams: ["id"],
|
|
1418
|
+
description:
|
|
1419
|
+
"Stop and remove a monitor by id. Irreversible: create a new monitor with twitter_monitor_create if you want to watch that handle again. Delivery history referencing this monitor is retained, not cascade-deleted. Free per call.",
|
|
1420
|
+
shape: {
|
|
1421
|
+
id: z.string().describe(
|
|
1422
|
+
"The monitor's id, from twitter_monitor_create or twitter_monitor_list.",
|
|
1423
|
+
),
|
|
1424
|
+
},
|
|
1425
|
+
},
|
|
1426
|
+
{
|
|
1427
|
+
name: "twitter_monitor_health",
|
|
1428
|
+
path: "/twitter/monitor/{id}/health",
|
|
1429
|
+
pathParams: ["id"],
|
|
1430
|
+
description:
|
|
1431
|
+
"Read one monitor's current status, degradation flag, poll interval, possibly-missed-event count, and cursor position (last_tweet_id, last_poll_at), for building your own health dashboard. Free per call.",
|
|
1432
|
+
shape: {
|
|
1433
|
+
id: z.string().describe(
|
|
1434
|
+
"The monitor's id, from twitter_monitor_create or twitter_monitor_list.",
|
|
1435
|
+
),
|
|
1436
|
+
},
|
|
1437
|
+
},
|
|
1438
|
+
{
|
|
1439
|
+
name: "twitter_monitor_deliveries",
|
|
1440
|
+
path: "/twitter/monitor/deliveries",
|
|
1441
|
+
description:
|
|
1442
|
+
"List your most recent monitor delivery events across every monitor, most recent first: id, monitor_id, tweet_id, status, tweet_created_at, and the real measured latency (detected_lag_ms, from X's own post timestamp to enqueue; delivery_lag_ms, the separate queue-to-webhook-POST time; total_lag_ms). Free per call.",
|
|
1443
|
+
shape: {
|
|
1444
|
+
limit: z.number().int().min(1).max(200).optional().describe(
|
|
1445
|
+
"Max delivery events to return, 1 to 200. Defaults to 50 when omitted.",
|
|
1446
|
+
),
|
|
1447
|
+
},
|
|
1448
|
+
},
|
|
1449
|
+
{
|
|
1450
|
+
name: "twitter_x_user_stream_add_user",
|
|
1451
|
+
path: "/oapi/x_user_stream/add_user_to_monitor_tweet",
|
|
1452
|
+
method: "POST",
|
|
1453
|
+
write: true,
|
|
1454
|
+
description:
|
|
1455
|
+
"Compat drop-in for twitter_monitor_create using an x_user_stream-shaped request/response envelope: watch an X account for new posts, translated onto the same underlying monitor system. Free per call. Prefer twitter_monitor_create for new integrations; this exists for migrating an existing x_user_stream-shaped integration without a rewrite.",
|
|
1456
|
+
shape: {
|
|
1457
|
+
x_user_name: z.string().describe(
|
|
1458
|
+
"The X username to watch, without the @.",
|
|
1459
|
+
),
|
|
1460
|
+
},
|
|
1461
|
+
},
|
|
1462
|
+
{
|
|
1463
|
+
name: "twitter_x_user_stream_remove_user",
|
|
1464
|
+
path: "/oapi/x_user_stream/remove_user_to_monitor_tweet",
|
|
1465
|
+
method: "POST",
|
|
1466
|
+
write: true,
|
|
1467
|
+
destructive: true,
|
|
1468
|
+
description:
|
|
1469
|
+
"Compat drop-in for twitter_monitor_delete using an x_user_stream-shaped envelope: stop watching an account. Irreversible. Free per call.",
|
|
1470
|
+
shape: {
|
|
1471
|
+
id_for_user: z.string().describe(
|
|
1472
|
+
"The monitor id, from twitter_x_user_stream_list_users. Same value as a twitter_monitor_* tool's monitor id.",
|
|
1473
|
+
),
|
|
1474
|
+
},
|
|
1475
|
+
},
|
|
1476
|
+
{
|
|
1477
|
+
name: "twitter_x_user_stream_list_users",
|
|
1478
|
+
path: "/oapi/x_user_stream/get_user_to_monitor_tweet",
|
|
1479
|
+
description:
|
|
1480
|
+
"Compat drop-in for twitter_monitor_list using an x_user_stream-shaped envelope: list every account you are currently tweet-monitoring. Honest field mapping, not fabricated: x_user_id is always null (this API stores no numeric Twitter user id) and is_monitor_profile is always 0 (profile-change monitoring is not a capability this API has). Free per call.",
|
|
1481
|
+
shape: {},
|
|
1482
|
+
},
|
|
1483
|
+
{
|
|
1484
|
+
name: "twitter_monitor_webhook_create",
|
|
1485
|
+
path: "/twitter/webhook",
|
|
1486
|
+
method: "POST",
|
|
1487
|
+
write: true,
|
|
1488
|
+
description:
|
|
1489
|
+
"Register an HTTPS endpoint to receive signed monitor events. The HMAC signing secret is returned ONLY in this response, store it immediately: it cannot be retrieved again, and it is what you use to verify the X-TwitterAPIs-Signature header on every delivery. Free per call.",
|
|
1490
|
+
shape: {
|
|
1491
|
+
url: z.string().min(1).describe(
|
|
1492
|
+
"Your https delivery endpoint, e.g. 'https://example.com/webhooks/twitterapis'. Private, loopback, link-local, and metadata IPs are refused, re-checked at every delivery, not just at registration.",
|
|
1493
|
+
),
|
|
1494
|
+
},
|
|
1495
|
+
},
|
|
1496
|
+
{
|
|
1497
|
+
name: "twitter_monitor_webhook_list",
|
|
1498
|
+
path: "/twitter/webhook",
|
|
1499
|
+
description:
|
|
1500
|
+
"List every webhook registered on your account: id, url, status ('active' delivers, 'disabled' means the endpoint returned a 410 Gone and needs re-registering to reactivate), and created_at. The signing secret is never returned here, only at creation. Takes no arguments.",
|
|
1501
|
+
shape: {},
|
|
1502
|
+
},
|
|
1503
|
+
{
|
|
1504
|
+
name: "twitter_monitor_webhook_delete",
|
|
1505
|
+
path: "/twitter/webhook/{id}",
|
|
1506
|
+
method: "DELETE",
|
|
1507
|
+
write: true,
|
|
1508
|
+
destructive: true,
|
|
1509
|
+
pathParams: ["id"],
|
|
1510
|
+
description:
|
|
1511
|
+
"Soft-delete a webhook by id: it stops receiving deliveries immediately and disappears from twitter_monitor_webhook_list, but delivery history referencing it is retained rather than cascade-deleted. Irreversible from the caller's side (register a new webhook with twitter_monitor_webhook_create to resume delivery). Free per call.",
|
|
1512
|
+
shape: {
|
|
1513
|
+
id: z.string().describe(
|
|
1514
|
+
"The webhook's id, from twitter_monitor_webhook_create or twitter_monitor_webhook_list.",
|
|
1515
|
+
),
|
|
1516
|
+
},
|
|
1517
|
+
},
|
|
1518
|
+
{
|
|
1519
|
+
name: "twitter_monitor_webhook_test",
|
|
1520
|
+
path: "/twitter/webhook/{id}/test",
|
|
1521
|
+
method: "POST",
|
|
1522
|
+
write: true,
|
|
1523
|
+
pathParams: ["id"],
|
|
1524
|
+
description:
|
|
1525
|
+
"Send one HMAC-signed test event to this webhook's URL right now and return the outcome synchronously: delivered (true if your endpoint returned a 2xx within the delivery timeout), status_code, and error. Unlike a real monitor event, a test send is never queued, retried, or dead-lettered, it is a one-shot diagnostic to confirm your endpoint and signature verification both work before relying on the webhook. Free per call.",
|
|
1526
|
+
shape: {
|
|
1527
|
+
id: z.string().describe(
|
|
1528
|
+
"The webhook's id, from twitter_monitor_webhook_create or twitter_monitor_webhook_list.",
|
|
1529
|
+
),
|
|
1530
|
+
},
|
|
1531
|
+
},
|
|
1375
1532
|
];
|
|
1376
1533
|
|
|
1377
|
-
// The query-string builder
|
|
1378
|
-
//
|
|
1379
|
-
|
|
1534
|
+
// The query-string builder and the path-param substitution helper are
|
|
1535
|
+
// hand-written logic, not catalog data, so they live in their own module and
|
|
1536
|
+
// are re-exported here to keep this file's one import path.
|
|
1537
|
+
export { buildQuery, resolvePathParams, MissingPathParamError } from "./query.js";
|