atproto-mcp 0.3.0 → 0.4.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 +68 -96
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +126 -71
- package/dist/index.js.map +1 -1
- package/dist/tools/implementations/advanced-social-tools.d.ts +326 -47
- package/dist/tools/implementations/advanced-social-tools.d.ts.map +1 -1
- package/dist/tools/implementations/advanced-social-tools.js +299 -100
- package/dist/tools/implementations/advanced-social-tools.js.map +1 -1
- package/dist/tools/implementations/analytics-tools.d.ts +67 -166
- package/dist/tools/implementations/analytics-tools.d.ts.map +1 -1
- package/dist/tools/implementations/analytics-tools.js +129 -488
- package/dist/tools/implementations/analytics-tools.js.map +1 -1
- package/dist/tools/implementations/analyze-account-tool.d.ts +135 -0
- package/dist/tools/implementations/analyze-account-tool.d.ts.map +1 -0
- package/dist/tools/implementations/analyze-account-tool.js +757 -0
- package/dist/tools/implementations/analyze-account-tool.js.map +1 -0
- package/dist/tools/implementations/base-tool.d.ts +57 -0
- package/dist/tools/implementations/base-tool.d.ts.map +1 -1
- package/dist/tools/implementations/base-tool.js +167 -5
- package/dist/tools/implementations/base-tool.js.map +1 -1
- package/dist/tools/implementations/batch-operations-tools.d.ts +47 -117
- package/dist/tools/implementations/batch-operations-tools.d.ts.map +1 -1
- package/dist/tools/implementations/batch-operations-tools.js +123 -222
- package/dist/tools/implementations/batch-operations-tools.js.map +1 -1
- package/dist/tools/implementations/composite-tools.d.ts +287 -8
- package/dist/tools/implementations/composite-tools.d.ts.map +1 -1
- package/dist/tools/implementations/composite-tools.js +402 -71
- package/dist/tools/implementations/composite-tools.js.map +1 -1
- package/dist/tools/implementations/content-discovery-tools.d.ts +207 -75
- package/dist/tools/implementations/content-discovery-tools.d.ts.map +1 -1
- package/dist/tools/implementations/content-discovery-tools.js +240 -208
- package/dist/tools/implementations/content-discovery-tools.js.map +1 -1
- package/dist/tools/implementations/content-management-tools.d.ts +71 -6
- package/dist/tools/implementations/content-management-tools.d.ts.map +1 -1
- package/dist/tools/implementations/content-management-tools.js +126 -50
- package/dist/tools/implementations/content-management-tools.js.map +1 -1
- package/dist/tools/implementations/create-post-tool.d.ts +111 -9
- package/dist/tools/implementations/create-post-tool.d.ts.map +1 -1
- package/dist/tools/implementations/create-post-tool.js +175 -82
- package/dist/tools/implementations/create-post-tool.js.map +1 -1
- package/dist/tools/implementations/create-thread-tool.d.ts +68 -0
- package/dist/tools/implementations/create-thread-tool.d.ts.map +1 -1
- package/dist/tools/implementations/create-thread-tool.js +104 -10
- package/dist/tools/implementations/create-thread-tool.js.map +1 -1
- package/dist/tools/implementations/discover-tool.d.ts +111 -0
- package/dist/tools/implementations/discover-tool.d.ts.map +1 -0
- package/dist/tools/implementations/discover-tool.js +556 -0
- package/dist/tools/implementations/discover-tool.js.map +1 -0
- package/dist/tools/implementations/follow-user-tool.d.ts +69 -6
- package/dist/tools/implementations/follow-user-tool.d.ts.map +1 -1
- package/dist/tools/implementations/follow-user-tool.js +102 -61
- package/dist/tools/implementations/follow-user-tool.js.map +1 -1
- package/dist/tools/implementations/get-author-feed-tool.d.ts +164 -0
- package/dist/tools/implementations/get-author-feed-tool.d.ts.map +1 -0
- package/dist/tools/implementations/get-author-feed-tool.js +147 -0
- package/dist/tools/implementations/get-author-feed-tool.js.map +1 -0
- package/dist/tools/implementations/get-user-profile-tool.d.ts +113 -0
- package/dist/tools/implementations/get-user-profile-tool.d.ts.map +1 -1
- package/dist/tools/implementations/get-user-profile-tool.js +150 -24
- package/dist/tools/implementations/get-user-profile-tool.js.map +1 -1
- package/dist/tools/implementations/index.d.ts +11 -12
- package/dist/tools/implementations/index.d.ts.map +1 -1
- package/dist/tools/implementations/index.js +12 -15
- package/dist/tools/implementations/index.js.map +1 -1
- package/dist/tools/implementations/like-post-tool.d.ts +63 -1
- package/dist/tools/implementations/like-post-tool.d.ts.map +1 -1
- package/dist/tools/implementations/like-post-tool.js +63 -19
- package/dist/tools/implementations/like-post-tool.js.map +1 -1
- package/dist/tools/implementations/media-tools.d.ts +184 -225
- package/dist/tools/implementations/media-tools.d.ts.map +1 -1
- package/dist/tools/implementations/media-tools.js +249 -225
- package/dist/tools/implementations/media-tools.js.map +1 -1
- package/dist/tools/implementations/moderation-tools.d.ts +324 -3
- package/dist/tools/implementations/moderation-tools.d.ts.map +1 -1
- package/dist/tools/implementations/moderation-tools.js +372 -27
- package/dist/tools/implementations/moderation-tools.js.map +1 -1
- package/dist/tools/implementations/reply-to-post-tool.d.ts +44 -2
- package/dist/tools/implementations/reply-to-post-tool.d.ts.map +1 -1
- package/dist/tools/implementations/reply-to-post-tool.js +63 -41
- package/dist/tools/implementations/reply-to-post-tool.js.map +1 -1
- package/dist/tools/implementations/repost-tool.d.ts +77 -1
- package/dist/tools/implementations/repost-tool.d.ts.map +1 -1
- package/dist/tools/implementations/repost-tool.js +149 -21
- package/dist/tools/implementations/repost-tool.js.map +1 -1
- package/dist/tools/implementations/rich-media-tools.d.ts +53 -84
- package/dist/tools/implementations/rich-media-tools.d.ts.map +1 -1
- package/dist/tools/implementations/rich-media-tools.js +77 -190
- package/dist/tools/implementations/rich-media-tools.js.map +1 -1
- package/dist/tools/implementations/search-actors-tool.d.ts +109 -0
- package/dist/tools/implementations/search-actors-tool.d.ts.map +1 -0
- package/dist/tools/implementations/search-actors-tool.js +110 -0
- package/dist/tools/implementations/search-actors-tool.js.map +1 -0
- package/dist/tools/implementations/search-posts-tool.d.ts +113 -5
- package/dist/tools/implementations/search-posts-tool.d.ts.map +1 -1
- package/dist/tools/implementations/search-posts-tool.js +127 -63
- package/dist/tools/implementations/search-posts-tool.js.map +1 -1
- package/dist/tools/implementations/social-graph-tools.d.ts +227 -48
- package/dist/tools/implementations/social-graph-tools.d.ts.map +1 -1
- package/dist/tools/implementations/social-graph-tools.js +268 -148
- package/dist/tools/implementations/social-graph-tools.js.map +1 -1
- package/dist/tools/implementations/timeline-tools.d.ts +91 -4
- package/dist/tools/implementations/timeline-tools.d.ts.map +1 -1
- package/dist/tools/implementations/timeline-tools.js +81 -56
- package/dist/tools/implementations/timeline-tools.js.map +1 -1
- package/dist/tools/index.d.ts +13 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +8 -27
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +14 -5
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -9
- package/dist/types/index.js.map +1 -1
- package/dist/utils/atp-client.d.ts +22 -2
- package/dist/utils/atp-client.d.ts.map +1 -1
- package/dist/utils/atp-client.js +98 -15
- package/dist/utils/atp-client.js.map +1 -1
- package/dist/utils/config.d.ts +12 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +45 -37
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/firehose-client.d.ts +19 -1
- package/dist/utils/firehose-client.d.ts.map +1 -1
- package/dist/utils/firehose-client.js +96 -14
- package/dist/utils/firehose-client.js.map +1 -1
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +12 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/oauth-client.d.ts.map +1 -1
- package/dist/utils/oauth-client.js +6 -2
- package/dist/utils/oauth-client.js.map +1 -1
- package/dist/utils/security.d.ts +8 -0
- package/dist/utils/security.d.ts.map +1 -1
- package/dist/utils/security.js +31 -6
- package/dist/utils/security.js.map +1 -1
- package/dist/utils/url-safety.d.ts.map +1 -1
- package/dist/utils/url-safety.js +37 -0
- package/dist/utils/url-safety.js.map +1 -1
- package/package.json +6 -7
- package/dist/tools/implementations/analyze-engagement-tool.d.ts +0 -105
- package/dist/tools/implementations/analyze-engagement-tool.d.ts.map +0 -1
- package/dist/tools/implementations/analyze-engagement-tool.js +0 -229
- package/dist/tools/implementations/analyze-engagement-tool.js.map +0 -1
- package/dist/tools/implementations/discover-trending-tool.d.ts +0 -107
- package/dist/tools/implementations/discover-trending-tool.d.ts.map +0 -1
- package/dist/tools/implementations/discover-trending-tool.js +0 -284
- package/dist/tools/implementations/discover-trending-tool.js.map +0 -1
- package/dist/tools/implementations/generate-alt-text-tool.d.ts +0 -77
- package/dist/tools/implementations/generate-alt-text-tool.d.ts.map +0 -1
- package/dist/tools/implementations/generate-alt-text-tool.js +0 -128
- package/dist/tools/implementations/generate-alt-text-tool.js.map +0 -1
- package/dist/tools/implementations/oauth-tools.d.ts +0 -108
- package/dist/tools/implementations/oauth-tools.d.ts.map +0 -1
- package/dist/tools/implementations/oauth-tools.js +0 -202
- package/dist/tools/implementations/oauth-tools.js.map +0 -1
- package/dist/tools/implementations/streaming-tools.d.ts +0 -221
- package/dist/tools/implementations/streaming-tools.d.ts.map +0 -1
- package/dist/tools/implementations/streaming-tools.js +0 -445
- package/dist/tools/implementations/streaming-tools.js.map +0 -1
package/README.md
CHANGED
|
@@ -35,10 +35,13 @@ direct access to the AT Protocol ecosystem, enabling seamless interaction with
|
|
|
35
35
|
Bluesky and other AT Protocol-based social networks.
|
|
36
36
|
|
|
37
37
|
**Supports both authenticated and unauthenticated modes** - Start immediately
|
|
38
|
-
with public data access (view profiles,
|
|
39
|
-
authentication for full functionality (search,
|
|
40
|
-
feeds).
|
|
38
|
+
with public data access (view profiles, search accounts, fetch
|
|
39
|
+
follower/following lists), or add authentication for full functionality (search,
|
|
40
|
+
write operations, private data, feeds).
|
|
41
41
|
|
|
42
|
+
> **Zero-config launch**: `npx atproto-mcp` runs the server in unauthenticated
|
|
43
|
+
> public-data mode — no credentials required.
|
|
44
|
+
>
|
|
42
45
|
> **Recent additions**: Batch operations for bulk actions, advanced analytics
|
|
43
46
|
> and insights, intelligent content discovery, and a conversation-context
|
|
44
47
|
> scratchpad resource.
|
|
@@ -91,22 +94,24 @@ server to access AT Protocol functionality.
|
|
|
91
94
|
|
|
92
95
|
### Core Features
|
|
93
96
|
|
|
94
|
-
- **Unauthenticated Mode**:
|
|
95
|
-
profiles
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
- **Zero-config Unauthenticated Mode**: Run `npx atproto-mcp` to access public
|
|
98
|
+
data without any setup - view profiles, search accounts, and fetch
|
|
99
|
+
follower/following lists
|
|
100
|
+
- **Optional Authentication**: Enable full functionality with app passwords for
|
|
101
|
+
write operations, feeds, and private data
|
|
98
102
|
- **Complete AT Protocol Integration**: Full implementation using official
|
|
99
103
|
`@atproto/api`
|
|
100
104
|
- **MCP Server Compliance**: Built with `@modelcontextprotocol/sdk` following
|
|
101
105
|
MCP specification
|
|
102
106
|
- **Type-Safe**: Written in TypeScript with strict type checking
|
|
103
|
-
- **Comprehensive Tools**:
|
|
104
|
-
- **Real-time Support** _(experimental)_: WebSocket firehose scaffolding with
|
|
105
|
-
keyword/user buffer scanning — frame decoding is not yet implemented, so no
|
|
106
|
-
live events are delivered yet
|
|
107
|
+
- **Comprehensive Tools**: 43 MCP tools for social networking operations
|
|
107
108
|
- **Rate Limiting**: Built-in respect for AT Protocol rate limits
|
|
108
109
|
- **Extensible**: Modular architecture for easy customization
|
|
109
110
|
|
|
111
|
+
> **Planned**: OAuth login and real-time firehose streaming are on the roadmap
|
|
112
|
+
> but not yet functional. App-password authentication is the supported auth path
|
|
113
|
+
> today.
|
|
114
|
+
|
|
110
115
|
## Who Is This For?
|
|
111
116
|
|
|
112
117
|
### Primary Audience: LLM Clients
|
|
@@ -151,14 +156,30 @@ should either:
|
|
|
151
156
|
|
|
152
157
|
## Installation
|
|
153
158
|
|
|
159
|
+
Run it with no install and no configuration — `npx atproto-mcp` launches the
|
|
160
|
+
server in unauthenticated public-data mode immediately:
|
|
161
|
+
|
|
154
162
|
```bash
|
|
155
|
-
|
|
163
|
+
npx atproto-mcp
|
|
156
164
|
```
|
|
157
165
|
|
|
158
|
-
Or
|
|
166
|
+
Or install globally:
|
|
159
167
|
|
|
160
168
|
```bash
|
|
161
|
-
|
|
169
|
+
npm install -g atproto-mcp
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Claude Desktop
|
|
173
|
+
|
|
174
|
+
Add this to your Claude Desktop MCP configuration to run the server with zero
|
|
175
|
+
config:
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"mcpServers": {
|
|
180
|
+
"atproto": { "command": "npx", "args": ["-y", "atproto-mcp"] }
|
|
181
|
+
}
|
|
182
|
+
}
|
|
162
183
|
```
|
|
163
184
|
|
|
164
185
|
## Quick Start
|
|
@@ -192,16 +213,18 @@ npx atproto-mcp
|
|
|
192
213
|
|
|
193
214
|
- View user profiles (`get_user_profile` - works without auth, provides
|
|
194
215
|
additional viewer-specific data when authenticated)
|
|
195
|
-
-
|
|
196
|
-
|
|
197
|
-
-
|
|
198
|
-
`
|
|
216
|
+
- Search for accounts by handle or name (`search_actors`)
|
|
217
|
+
- List a user's posts (`get_author_feed`)
|
|
218
|
+
- View follower/following lists (`get_user_connections` with
|
|
219
|
+
`direction: 'followers' | 'follows'` - ENHANCED mode: works without auth,
|
|
220
|
+
enriches the underlying API call when authenticated)
|
|
199
221
|
|
|
200
222
|
**Note:** The following features require authentication:
|
|
201
223
|
|
|
202
224
|
- Searching posts and hashtags (`search_posts`) - **API changed in 2025 to
|
|
203
225
|
require authentication**
|
|
204
|
-
- Browsing feeds and threads (`
|
|
226
|
+
- Browsing feeds and threads (`get_post_context`, `get_custom_feed`,
|
|
227
|
+
`get_timeline`)
|
|
205
228
|
- All write operations (create, like, repost, follow, etc.)
|
|
206
229
|
- Resources (timeline, profile, notifications) - these are listed but require
|
|
207
230
|
authentication to return data (the `conversation-context` scratchpad resource
|
|
@@ -247,7 +270,7 @@ credentials.
|
|
|
247
270
|
|
|
248
271
|
## Available Tools
|
|
249
272
|
|
|
250
|
-
The server provides **
|
|
273
|
+
The server provides **43 MCP tools** across multiple categories. See the
|
|
251
274
|
[complete API documentation](https://cameronrye.github.io/atproto-mcp/api/) for
|
|
252
275
|
detailed information on each tool.
|
|
253
276
|
|
|
@@ -257,34 +280,20 @@ detailed information on each tool.
|
|
|
257
280
|
|
|
258
281
|
- `get_user_profile` - Retrieve basic user information (ENHANCED mode: works
|
|
259
282
|
without auth, provides additional viewer-specific data when authenticated)
|
|
260
|
-
- `
|
|
261
|
-
|
|
262
|
-
- `
|
|
283
|
+
- `get_user_summary` - Get a profile with recent posts and engagement stats in
|
|
284
|
+
one call (ENHANCED mode)
|
|
285
|
+
- `search_actors` - Find accounts by handle or display name (ENHANCED mode)
|
|
286
|
+
- `get_author_feed` - List a specific user's posts (ENHANCED mode)
|
|
287
|
+
- `get_user_connections` - Get follower or following lists via
|
|
288
|
+
`direction: 'followers' | 'follows'` (ENHANCED mode: works without auth,
|
|
263
289
|
enriches the underlying API call when authenticated)
|
|
290
|
+
- `get_post_context` - Get a post with optional thread, author profile,
|
|
291
|
+
engagement metrics, and media (ENHANCED mode)
|
|
264
292
|
|
|
265
293
|
**Rich Media**
|
|
266
294
|
|
|
267
|
-
- `generate_alt_text` - Generate descriptive alt text for images (PUBLIC mode:
|
|
268
|
-
no auth required; experimental — returns an alt-text writing
|
|
269
|
-
template/guidance, does not analyze image pixels)
|
|
270
295
|
- `analyze_image` - Report blob-declared size and MIME type for an image (PUBLIC
|
|
271
296
|
mode: no auth required; does not decode pixels, so no dimensions/aspect ratio)
|
|
272
|
-
- `extract_media_from_post` - Extract media from posts (ENHANCED mode: works
|
|
273
|
-
without auth)
|
|
274
|
-
|
|
275
|
-
**OAuth Management** _(experimental — token exchange not implemented)_
|
|
276
|
-
|
|
277
|
-
> ⚠️ Only the authorization-URL step is functional. The token-exchange steps
|
|
278
|
-
> (`handle_oauth_callback`, `refresh_oauth_tokens`, `revoke_oauth_tokens`) are
|
|
279
|
-
> **not implemented** and return an error. For working authentication, use app
|
|
280
|
-
> passwords (`ATPROTO_IDENTIFIER` + `ATPROTO_PASSWORD`).
|
|
281
|
-
|
|
282
|
-
- `start_oauth_flow` - Generate a PKCE authorization URL (experimental)
|
|
283
|
-
- `handle_oauth_callback` - Complete OAuth flow (not implemented — returns
|
|
284
|
-
error)
|
|
285
|
-
- `refresh_oauth_tokens` - Refresh authentication tokens (not implemented —
|
|
286
|
-
returns error)
|
|
287
|
-
- `revoke_oauth_tokens` - Revoke OAuth tokens (not implemented — returns error)
|
|
288
297
|
|
|
289
298
|
**Note:** As of 2025, the AT Protocol API has changed to require authentication
|
|
290
299
|
for most endpoints that were previously public, including `search_posts`.
|
|
@@ -293,8 +302,9 @@ for most endpoints that were previously public, including `search_posts`.
|
|
|
293
302
|
|
|
294
303
|
**Social Operations**
|
|
295
304
|
|
|
296
|
-
- `create_post` - Create
|
|
297
|
-
|
|
305
|
+
- `create_post` - Create posts with text, auto-detected or explicit richtext
|
|
306
|
+
facets, replies, image/external embeds, and quote posts
|
|
307
|
+
- `create_thread` - Create multi-post threads in one call
|
|
298
308
|
- `reply_to_post` - Reply to existing posts with threading
|
|
299
309
|
- `like_post` / `unlike_post` - Like and unlike posts
|
|
300
310
|
- `repost` / `unrepost` - Repost content with optional quotes
|
|
@@ -304,10 +314,11 @@ for most endpoints that were previously public, including `search_posts`.
|
|
|
304
314
|
|
|
305
315
|
- `search_posts` - Search for posts and content across the network (⚠️ API
|
|
306
316
|
changed in 2025 to require auth)
|
|
307
|
-
- `get_thread` - View post threads and conversations
|
|
308
317
|
- `get_custom_feed` - Access custom feeds
|
|
309
318
|
- `get_timeline` - Retrieve personalized timelines
|
|
310
|
-
- `get_notifications` - Access notification feeds
|
|
319
|
+
- `get_notifications` - Access notification feeds (use `countOnly: true` for a
|
|
320
|
+
cheap unread badge count)
|
|
321
|
+
- `mark_notifications_seen` - Mark notifications as seen up to a timestamp
|
|
311
322
|
|
|
312
323
|
**Content Management**
|
|
313
324
|
|
|
@@ -327,56 +338,25 @@ for most endpoints that were previously public, including `search_posts`.
|
|
|
327
338
|
- `mute_user` / `unmute_user` - Mute and unmute users
|
|
328
339
|
- `block_user` / `unblock_user` - Block and unblock users
|
|
329
340
|
- `report_content` / `report_user` - Report content and users
|
|
330
|
-
|
|
331
|
-
**Real-time Streaming & Intelligence** _(experimental — not yet functional)_
|
|
332
|
-
|
|
333
|
-
> ⚠️ Firehose frame (CAR/DAG-CBOR) decoding is **not implemented** yet, so these
|
|
334
|
-
> tools currently decode no events: `start_streaming` returns a
|
|
335
|
-
> `not_implemented` status, and the buffer-scanning tools always return empty
|
|
336
|
-
> results. The tool descriptions and responses disclose this. Tracked for a
|
|
337
|
-
> future release.
|
|
338
|
-
|
|
339
|
-
- `start_streaming` - Start a real-time event stream (returns `not_implemented`)
|
|
340
|
-
- `stop_streaming` - Stop an event stream subscription
|
|
341
|
-
- `get_streaming_status` - Check streaming status (reports decoding
|
|
342
|
-
availability)
|
|
343
|
-
- `get_recent_events` - Retrieve buffered events (empty until decoding lands)
|
|
344
|
-
- `monitor_keywords` - Scan the event buffer for keywords (empty until decoding
|
|
345
|
-
lands)
|
|
346
|
-
- `track_users` - Scan the event buffer for specific users (empty until decoding
|
|
347
|
-
lands)
|
|
341
|
+
- `analyze_moderation_status` - Check moderation status of content
|
|
348
342
|
|
|
349
343
|
**Batch Operations**
|
|
350
344
|
|
|
351
|
-
- `
|
|
352
|
-
|
|
353
|
-
- `batch_repost` - Repost multiple posts at once (up to 25)
|
|
345
|
+
- `batch_action` - Apply one action across up to 25 targets in a single call via
|
|
346
|
+
`action: 'follow' | 'like' | 'repost'`
|
|
354
347
|
|
|
355
348
|
**Analytics & Insights**
|
|
356
349
|
|
|
357
|
-
- `
|
|
358
|
-
|
|
359
|
-
- `suggest_content_strategy` - Get content strategy recommendations based on
|
|
360
|
-
performance
|
|
350
|
+
- `analyze_account` - Analyze a single account along one dimension via
|
|
351
|
+
`dimension: 'engagement' | 'network' | 'strategy'`
|
|
361
352
|
- `find_influential_users` - Find influential users in a topic area
|
|
362
353
|
|
|
363
354
|
**Content Discovery**
|
|
364
355
|
|
|
365
|
-
- `
|
|
356
|
+
- `discover` - Surface timeline content via `mode: 'trending' | 'recommended'`
|
|
366
357
|
- `find_similar_users` - Find users similar to a given user
|
|
367
|
-
- `recommend_content` - Get personalized content recommendations
|
|
368
358
|
- `discover_communities` - Discover communities around topics
|
|
369
359
|
|
|
370
|
-
**Composite Operations**
|
|
371
|
-
|
|
372
|
-
- `get_user_summary` - Get complete user profile with stats and analysis
|
|
373
|
-
- `get_post_context` - Get post with thread, author, and engagement data
|
|
374
|
-
- `create_thread` - Create multi-post threads in one call
|
|
375
|
-
|
|
376
|
-
**Enhanced Moderation**
|
|
377
|
-
|
|
378
|
-
- `analyze_moderation_status` - Check moderation status of content
|
|
379
|
-
|
|
380
360
|
## Documentation
|
|
381
361
|
|
|
382
362
|
Visit our [documentation site](https://cameronrye.github.io/atproto-mcp) for:
|
|
@@ -400,17 +380,8 @@ export ATPROTO_PASSWORD="your-app-password"
|
|
|
400
380
|
atproto-mcp
|
|
401
381
|
```
|
|
402
382
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
> ⚠️ OAuth token exchange is not implemented, so this cannot complete a login
|
|
406
|
-
> yet. App passwords (above) are the recommended/working method. The variables
|
|
407
|
-
> below configure the experimental authorization-URL generator.
|
|
408
|
-
|
|
409
|
-
```bash
|
|
410
|
-
export ATPROTO_CLIENT_ID="your-client-id"
|
|
411
|
-
export ATPROTO_CLIENT_SECRET="your-client-secret" # optional for public clients
|
|
412
|
-
atproto-mcp --auth oauth
|
|
413
|
-
```
|
|
383
|
+
App passwords are the supported authentication path. OAuth login is planned but
|
|
384
|
+
not yet functional.
|
|
414
385
|
|
|
415
386
|
## Development
|
|
416
387
|
|
|
@@ -508,8 +479,9 @@ npm run test:integration
|
|
|
508
479
|
|
|
509
480
|
**What's tested:**
|
|
510
481
|
|
|
511
|
-
- Public/enhanced tools (`get_user_profile`, `
|
|
512
|
-
authenticated tools (`search_posts`,
|
|
482
|
+
- Public/enhanced tools (`get_user_profile`, `get_user_connections`,
|
|
483
|
+
`get_author_feed`) and authenticated tools (`search_posts`,
|
|
484
|
+
`get_post_context`, `get_custom_feed`)
|
|
513
485
|
- DID and handle resolution
|
|
514
486
|
- Pagination support
|
|
515
487
|
- Error handling
|
package/dist/index.d.ts
CHANGED
|
@@ -36,6 +36,12 @@ export declare class AtpMcpServer {
|
|
|
36
36
|
* Register MCP tools with the server
|
|
37
37
|
*/
|
|
38
38
|
private registerTools;
|
|
39
|
+
/**
|
|
40
|
+
* Normalize an error thrown inside a resource/prompt handler into an McpError:
|
|
41
|
+
* pass an existing McpError through, otherwise log + sanitize and wrap it as an
|
|
42
|
+
* InternalError. Returned (not thrown) so the caller writes `throw this.…`.
|
|
43
|
+
*/
|
|
44
|
+
private toHandlerMcpError;
|
|
39
45
|
/**
|
|
40
46
|
* Register MCP resources with the server
|
|
41
47
|
*/
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAKnE,OAAO,EAAsB,KAAK,gBAAgB,EAAmB,MAAM,kBAAkB,CAAC;AAC9F,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAKnE,OAAO,EAAsB,KAAK,gBAAgB,EAAmB,MAAM,kBAAkB,CAAC;AAC9F,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIlD,OAAO,EAAE,KAAK,mBAAmB,EAAsB,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAwB,eAAe,EAAE,MAAM,qBAAqB,CAAC;AA4D5E;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,eAAe,CAAC,CAAiB;IACzC,OAAO,CAAC,SAAS,CAAqC;IACtD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,cAAc,CAAS;gBAEnB,eAAe,GAAE,OAAO,CAAC,gBAAgB,CAAM;IA0D3D;;;OAGG;IACH,OAAO,CAAC,WAAW;IAwBnB;;OAEG;IACH,OAAO,CAAC,aAAa;IAmJrB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAezB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAgEzB;;OAEG;IACH,OAAO,CAAC,eAAe;IAwDvB;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAWvB;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA6EnC;;OAEG;IACU,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAUlC;;OAEG;YACW,OAAO;IAiDrB;;OAEG;IACI,SAAS,IAAI;QAClB,SAAS,EAAE,OAAO,CAAC;QACnB,eAAe,EAAE,OAAO,CAAC;QACzB,QAAQ,EAAE,iBAAiB,GAAG,cAAc,GAAG,OAAO,CAAC;QACvD,iBAAiB,EAAE,OAAO,CAAC;QAC3B,MAAM,EAAE,gBAAgB,CAAC;KAC1B;IAUD;;OAEG;IACI,YAAY,IAAI,SAAS;IAIhC;;;;;OAKG;IACI,SAAS,IAAI,MAAM;IAI1B;;OAEG;IACI,gBAAgB,IAAI,aAAa;IAIxC;;OAEG;IACI,qBAAqB,IAAI,mBAAmB;IAInD;;OAEG;IACI,kBAAkB,IAAI,eAAe;IAI5C;;OAEG;IACI,gBAAgB,IAAI;QACzB,WAAW,EAAE,mBAAmB,CAAC;QACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAClC,MAAM,EAAE;YACN,SAAS,EAAE,OAAO,CAAC;YACnB,eAAe,EAAE,OAAO,CAAC;YACzB,QAAQ,EAAE,iBAAiB,GAAG,cAAc,GAAG,OAAO,CAAC;YACvD,iBAAiB,EAAE,OAAO,CAAC;YAC3B,MAAM,EAAE,gBAAgB,CAAC;SAC1B,CAAC;KACH;CAOF;AAED;;;;;;;;;;;;GAYG;AACH,eAAe,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -16,11 +16,65 @@ import { AtpClient } from './utils/atp-client.js';
|
|
|
16
16
|
import { Logger } from './utils/logger.js';
|
|
17
17
|
import { ConfigManager } from './utils/config.js';
|
|
18
18
|
import { createTools } from './tools/index.js';
|
|
19
|
-
import { StartStreamingTool } from './tools/implementations/streaming-tools.js';
|
|
20
19
|
import { createResources } from './resources/index.js';
|
|
21
20
|
import { createPrompts } from './prompts/index.js';
|
|
22
21
|
import { PerformanceMonitor } from './utils/performance.js';
|
|
23
22
|
import { SecurityManager } from './utils/security.js';
|
|
23
|
+
/**
|
|
24
|
+
* Pure read tools (no writes to the network). readOnlyHint:true lets clients
|
|
25
|
+
* auto-approve them. Curated explicitly per tool — NOT derived from auth mode,
|
|
26
|
+
* which encodes auth requirement, not destructiveness.
|
|
27
|
+
*/
|
|
28
|
+
const READ_ONLY_TOOLS = new Set([
|
|
29
|
+
'analyze_account',
|
|
30
|
+
'analyze_image',
|
|
31
|
+
'analyze_moderation_status',
|
|
32
|
+
'discover',
|
|
33
|
+
'discover_communities',
|
|
34
|
+
'find_influential_users',
|
|
35
|
+
'find_similar_users',
|
|
36
|
+
'generate_link_preview',
|
|
37
|
+
'get_author_feed',
|
|
38
|
+
'get_custom_feed',
|
|
39
|
+
'get_list',
|
|
40
|
+
'get_notifications',
|
|
41
|
+
'get_post_context',
|
|
42
|
+
'get_timeline',
|
|
43
|
+
'get_user_connections',
|
|
44
|
+
'get_user_profile',
|
|
45
|
+
'get_user_summary',
|
|
46
|
+
'search_actors',
|
|
47
|
+
'search_posts',
|
|
48
|
+
]);
|
|
49
|
+
/**
|
|
50
|
+
* Tools whose effect is irreversible or removes/limits data (deletes, blocks,
|
|
51
|
+
* mutes, reports, removals, token revocation). destructiveHint:true lets clients
|
|
52
|
+
* surface confirmation UI and withhold auto-approval.
|
|
53
|
+
*/
|
|
54
|
+
const DESTRUCTIVE_TOOLS = new Set([
|
|
55
|
+
'block_user',
|
|
56
|
+
'delete_post',
|
|
57
|
+
'mute_user',
|
|
58
|
+
'remove_from_list',
|
|
59
|
+
'report_content',
|
|
60
|
+
'report_user',
|
|
61
|
+
'unfollow_user',
|
|
62
|
+
'unlike_post',
|
|
63
|
+
'unrepost',
|
|
64
|
+
]);
|
|
65
|
+
/**
|
|
66
|
+
* Build the advertised annotations for a tool. openWorldHint is true for every
|
|
67
|
+
* tool (they all reach a live network). A per-tool `schema.annotations` override
|
|
68
|
+
* wins over these defaults.
|
|
69
|
+
*/
|
|
70
|
+
function computeToolAnnotations(method, override) {
|
|
71
|
+
return {
|
|
72
|
+
openWorldHint: true,
|
|
73
|
+
...(READ_ONLY_TOOLS.has(method) ? { readOnlyHint: true } : {}),
|
|
74
|
+
...(DESTRUCTIVE_TOOLS.has(method) ? { destructiveHint: true } : {}),
|
|
75
|
+
...(override ?? {}),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
24
78
|
/**
|
|
25
79
|
* Main server class for AT Protocol MCP Server
|
|
26
80
|
*/
|
|
@@ -58,7 +112,11 @@ export class AtpMcpServer {
|
|
|
58
112
|
this.performanceMonitor = new PerformanceMonitor(this.logger);
|
|
59
113
|
// Initialize security manager
|
|
60
114
|
const securityConfig = {
|
|
61
|
-
|
|
115
|
+
// The blanket HTML/script InputSanitizer is intentionally NOT applied to
|
|
116
|
+
// tool arguments (it would corrupt legitimate post content). Per-field
|
|
117
|
+
// zod validation and url-safety guards are the real defenses, so this flag
|
|
118
|
+
// honestly reports that the object sanitizer does not run on the hot path.
|
|
119
|
+
enableInputSanitization: false,
|
|
62
120
|
enableRateLimit: true,
|
|
63
121
|
enableErrorSanitization: true,
|
|
64
122
|
maxInputLength: 10000,
|
|
@@ -118,6 +176,12 @@ export class AtpMcpServer {
|
|
|
118
176
|
inputSchema: tool.schema.params
|
|
119
177
|
? this.zodToJsonSchema(tool.schema.params)
|
|
120
178
|
: { type: 'object', properties: {} },
|
|
179
|
+
// Advertise an output schema when the tool declares one. This is purely
|
|
180
|
+
// descriptive metadata in the tools/list payload; because tools/call
|
|
181
|
+
// responses are built by this custom handler (not the SDK's high-level
|
|
182
|
+
// registerTool), it does not trigger structuredContent validation.
|
|
183
|
+
...(tool.schema.outputSchema ? { outputSchema: tool.schema.outputSchema } : {}),
|
|
184
|
+
annotations: computeToolAnnotations(tool.schema.method, tool.schema.annotations),
|
|
121
185
|
})),
|
|
122
186
|
}));
|
|
123
187
|
// Build a name -> tool lookup so a SINGLE tools/call handler can dispatch
|
|
@@ -144,6 +208,17 @@ export class AtpMcpServer {
|
|
|
144
208
|
tool: toolName,
|
|
145
209
|
});
|
|
146
210
|
}
|
|
211
|
+
// Build an MCP "tool error" result. Per the MCP spec, errors that occur
|
|
212
|
+
// while a (known) tool runs — including invalid arguments, unavailable
|
|
213
|
+
// tools, and rate limiting — are reported as a result with isError: true,
|
|
214
|
+
// NOT as JSON-RPC protocol errors. This lets the calling model SEE the
|
|
215
|
+
// error text and react (fix arguments, authenticate, back off) instead of
|
|
216
|
+
// receiving an opaque transport failure. Protocol errors are reserved for
|
|
217
|
+
// problems with the request itself (e.g. an unknown tool, handled above).
|
|
218
|
+
const toolError = (message) => ({
|
|
219
|
+
content: [{ type: 'text', text: message }],
|
|
220
|
+
isError: true,
|
|
221
|
+
});
|
|
147
222
|
// Rate-limit tool invocations to guard against runaway loops / abuse.
|
|
148
223
|
// Note: tool arguments are intentionally NOT passed through the HTML/script
|
|
149
224
|
// input sanitizer — that sanitizer strips characters (`<`, `>`, collapses
|
|
@@ -151,51 +226,38 @@ export class AtpMcpServer {
|
|
|
151
226
|
// data. Per-field validation is handled by each tool's zod schema, and
|
|
152
227
|
// outbound URLs/paths are guarded at their call sites (see url-safety).
|
|
153
228
|
if (!this.securityManager.checkRateLimit(`tool:${toolName}`)) {
|
|
154
|
-
|
|
229
|
+
return toolError(`Rate limit exceeded for tool "${toolName}". Please slow down and retry shortly.`);
|
|
230
|
+
}
|
|
231
|
+
// Surface tool availability (e.g. requires authentication) as a result the
|
|
232
|
+
// model can act on, not a protocol error.
|
|
233
|
+
if ('isAvailable' in tool &&
|
|
234
|
+
typeof tool.isAvailable === 'function' &&
|
|
235
|
+
!tool.isAvailable()) {
|
|
236
|
+
const availabilityMessage = 'getAvailabilityMessage' in tool && typeof tool.getAvailabilityMessage === 'function'
|
|
237
|
+
? tool.getAvailabilityMessage()
|
|
238
|
+
: 'Tool not available';
|
|
239
|
+
return toolError(`Tool not available: ${availabilityMessage}`);
|
|
155
240
|
}
|
|
156
241
|
try {
|
|
157
|
-
// Check if tool is available before execution
|
|
158
|
-
if ('isAvailable' in tool && typeof tool.isAvailable === 'function') {
|
|
159
|
-
if (!tool.isAvailable()) {
|
|
160
|
-
const availabilityMessage = 'getAvailabilityMessage' in tool &&
|
|
161
|
-
typeof tool.getAvailabilityMessage === 'function'
|
|
162
|
-
? tool.getAvailabilityMessage()
|
|
163
|
-
: 'Tool not available';
|
|
164
|
-
throw new McpError(ErrorCode.InternalError, `Tool not available: ${availabilityMessage}`, {
|
|
165
|
-
tool: toolName,
|
|
166
|
-
availability: availabilityMessage,
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
242
|
const result = await tool.handler(request.params.arguments || {});
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
// This server intentionally returns all tool results as stringified JSON text
|
|
174
|
-
// rather than using MCP's structured content types. This is a deliberate
|
|
175
|
-
// architectural choice with the following rationale:
|
|
176
|
-
//
|
|
177
|
-
// 1. Consistency: All tools return the same format, making it easier for LLMs
|
|
178
|
-
// to parse and understand responses without needing to handle multiple
|
|
179
|
-
// content type variations.
|
|
243
|
+
// Return BOTH representations:
|
|
180
244
|
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
245
|
+
// - content[].text: pretty-printed JSON, optimized for LLM consumption.
|
|
246
|
+
// LLMs parse formatted JSON text effectively and it is universally
|
|
247
|
+
// supported across all MCP clients, so it stays the primary channel.
|
|
183
248
|
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
249
|
+
// - structuredContent: the same result as a machine-readable object, for
|
|
250
|
+
// non-LLM consumers and tool-chaining clients that would otherwise have
|
|
251
|
+
// to re-parse the pretty-printed string. SDK structuredContent must be a
|
|
252
|
+
// JSON object, so bare arrays/primitives are wrapped under `result`.
|
|
186
253
|
//
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
// arrays, etc.) which could be used instead. However, testing has shown that
|
|
195
|
-
// stringified JSON provides better results for LLM clients in practice.
|
|
196
|
-
//
|
|
197
|
-
// If you need structured content types for programmatic processing, consider
|
|
198
|
-
// parsing the JSON text in your client application.
|
|
254
|
+
// NOTE: we intentionally do NOT declare per-tool `outputSchema` — doing so
|
|
255
|
+
// makes the SDK REQUIRE and validate structuredContent on every call and
|
|
256
|
+
// throw on any non-conforming object. structuredContent here is additive
|
|
257
|
+
// and best-effort.
|
|
258
|
+
const structuredContent = result != null && typeof result === 'object' && !Array.isArray(result)
|
|
259
|
+
? result
|
|
260
|
+
: { result };
|
|
199
261
|
return {
|
|
200
262
|
content: [
|
|
201
263
|
{
|
|
@@ -203,31 +265,39 @@ export class AtpMcpServer {
|
|
|
203
265
|
text: JSON.stringify(result, null, 2),
|
|
204
266
|
},
|
|
205
267
|
],
|
|
268
|
+
structuredContent,
|
|
206
269
|
};
|
|
207
270
|
}
|
|
208
271
|
catch (error) {
|
|
209
272
|
this.logger.error(`Tool ${toolName} execution failed`, error);
|
|
210
|
-
//
|
|
211
|
-
// client receives the accurate JSON-RPC code.
|
|
212
|
-
if (error instanceof McpError) {
|
|
213
|
-
throw error;
|
|
214
|
-
}
|
|
215
|
-
// Invalid input is a client-correctable condition: map it to the
|
|
216
|
-
// spec's InvalidParams (-32602) rather than InternalError (-32603).
|
|
273
|
+
// Invalid arguments are safe to surface verbatim so the model can fix them.
|
|
217
274
|
if (error instanceof ValidationError) {
|
|
218
|
-
|
|
275
|
+
return toolError(`Invalid parameters: ${error.message}`);
|
|
219
276
|
}
|
|
220
277
|
// Sanitize internal error details before returning to the client.
|
|
221
278
|
const sanitized = this.securityManager
|
|
222
279
|
.getErrorSanitizer()
|
|
223
280
|
.sanitizeError(error instanceof Error ? error : new Error(String(error)));
|
|
224
|
-
|
|
225
|
-
tool: toolName,
|
|
226
|
-
});
|
|
281
|
+
return toolError(`Tool execution failed: ${sanitized.message}`);
|
|
227
282
|
}
|
|
228
283
|
});
|
|
229
284
|
this.logger.info(`Registered ${tools.length} MCP tools`);
|
|
230
285
|
}
|
|
286
|
+
/**
|
|
287
|
+
* Normalize an error thrown inside a resource/prompt handler into an McpError:
|
|
288
|
+
* pass an existing McpError through, otherwise log + sanitize and wrap it as an
|
|
289
|
+
* InternalError. Returned (not thrown) so the caller writes `throw this.…`.
|
|
290
|
+
*/
|
|
291
|
+
toHandlerMcpError(error, label, context) {
|
|
292
|
+
this.logger.error(label, error);
|
|
293
|
+
if (error instanceof McpError) {
|
|
294
|
+
return error;
|
|
295
|
+
}
|
|
296
|
+
const sanitized = this.securityManager
|
|
297
|
+
.getErrorSanitizer()
|
|
298
|
+
.sanitizeError(error instanceof Error ? error : new Error(String(error)));
|
|
299
|
+
return new McpError(ErrorCode.InternalError, `${label}: ${sanitized.message}`, context);
|
|
300
|
+
}
|
|
231
301
|
/**
|
|
232
302
|
* Register MCP resources with the server
|
|
233
303
|
*/
|
|
@@ -272,14 +342,7 @@ export class AtpMcpServer {
|
|
|
272
342
|
};
|
|
273
343
|
}
|
|
274
344
|
catch (error) {
|
|
275
|
-
this.
|
|
276
|
-
if (error instanceof McpError) {
|
|
277
|
-
throw error;
|
|
278
|
-
}
|
|
279
|
-
const sanitized = this.securityManager
|
|
280
|
-
.getErrorSanitizer()
|
|
281
|
-
.sanitizeError(error instanceof Error ? error : new Error(String(error)));
|
|
282
|
-
throw new McpError(ErrorCode.InternalError, `Resource read failed: ${sanitized.message}`, {
|
|
345
|
+
throw this.toHandlerMcpError(error, 'Resource read failed', {
|
|
283
346
|
uri: request.params.uri,
|
|
284
347
|
});
|
|
285
348
|
}
|
|
@@ -322,14 +385,9 @@ export class AtpMcpServer {
|
|
|
322
385
|
return { messages };
|
|
323
386
|
}
|
|
324
387
|
catch (error) {
|
|
325
|
-
this.
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
}
|
|
329
|
-
const sanitized = this.securityManager
|
|
330
|
-
.getErrorSanitizer()
|
|
331
|
-
.sanitizeError(error instanceof Error ? error : new Error(String(error)));
|
|
332
|
-
throw new McpError(ErrorCode.InternalError, `Prompt generation failed: ${sanitized.message}`, { name: request.params.name });
|
|
388
|
+
throw this.toHandlerMcpError(error, 'Prompt generation failed', {
|
|
389
|
+
name: request.params.name,
|
|
390
|
+
});
|
|
333
391
|
}
|
|
334
392
|
});
|
|
335
393
|
this.logger.info(`Registered ${prompts.length} MCP prompts`);
|
|
@@ -446,9 +504,6 @@ export class AtpMcpServer {
|
|
|
446
504
|
}
|
|
447
505
|
// Release security manager background timers (rate-limiter cleanup).
|
|
448
506
|
this.securityManager.destroy();
|
|
449
|
-
// Disconnect the shared firehose client (if a streaming tool opened one)
|
|
450
|
-
// so its socket and heartbeat timer do not outlive the server.
|
|
451
|
-
await StartStreamingTool.shutdown();
|
|
452
507
|
}
|
|
453
508
|
catch (error) {
|
|
454
509
|
errors.push(error instanceof Error ? error : new Error(String(error)));
|