atproto-mcp 0.5.0 → 0.6.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 CHANGED
@@ -42,8 +42,9 @@ write operations, private data, feeds).
42
42
  > **Zero-config launch**: `npx atproto-mcp` runs the server in unauthenticated
43
43
  > public-data mode — no credentials required.
44
44
  >
45
- > **Recent additions**: Batch operations for bulk actions, advanced analytics
46
- > and insights, and intelligent content discovery.
45
+ > **Recent additions**: Bluesky direct messages, private bookmarks, starter pack
46
+ > discovery, reply/quote controls on posts, parameterized MCP resource
47
+ > templates, and an optional Streamable HTTP transport (`--transport http`).
47
48
 
48
49
  ## Architecture
49
50
 
@@ -86,10 +87,11 @@ server to access AT Protocol functionality.
86
87
  (follow/like/repost up to 25 items at once)
87
88
  - **Analytics & Insights**: Analyze engagement patterns, network connections,
88
89
  and get content strategy recommendations
89
- - **Content Discovery**: Find similar users, trending topics, and influential
90
- voices in your areas of interest
91
- - **Conversation Context**: An MCP resource that acts as a scratchpad for
92
- conversation state (not auto-populated yet)
90
+ - **Content Discovery**: Find similar users, trending topics, starter packs, and
91
+ influential voices in your areas of interest
92
+ - **Direct Messages**: List conversations, read message history, and send
93
+ Bluesky DMs (requires a DM-enabled app password)
94
+ - **Private Bookmarks**: Save, list, and remove private bookmarks on posts
93
95
 
94
96
  ### Core Features
95
97
 
@@ -103,13 +105,16 @@ server to access AT Protocol functionality.
103
105
  - **MCP Server Compliance**: Built with `@modelcontextprotocol/sdk` following
104
106
  MCP specification
105
107
  - **Type-Safe**: Written in TypeScript with strict type checking
106
- - **Comprehensive Tools**: 43 MCP tools for social networking operations
108
+ - **Comprehensive Tools**: 51 MCP tools for social networking operations
107
109
  - **Rate Limiting**: Built-in respect for AT Protocol rate limits
108
110
  - **Extensible**: Modular architecture for easy customization
109
111
 
110
- > **Planned**: OAuth login and real-time firehose streaming are on the roadmap
111
- > but not yet functional. App-password authentication is the supported auth path
112
- > today.
112
+ > **Planned**: OAuth login is on the roadmap but not yet functional —
113
+ > app-password authentication is the supported auth path today. Real-time
114
+ > firehose streaming is **not** planned as MCP tools: a request/response tool
115
+ > cannot honestly expose a continuous stream, and the unused firehose client
116
+ > code has been removed. If streaming ever ships it will be built fresh on
117
+ > [Jetstream](https://docs.bsky.app/blog/jetstream).
113
118
 
114
119
  ## Who Is This For?
115
120
 
@@ -269,7 +274,7 @@ credentials.
269
274
 
270
275
  ## Available Tools
271
276
 
272
- The server provides **43 MCP tools** across multiple categories. See the
277
+ The server provides **51 MCP tools** across multiple categories. See the
273
278
  [complete API documentation](https://cameronrye.github.io/atproto-mcp/api/) for
274
279
  detailed information on each tool.
275
280
 
@@ -288,6 +293,8 @@ detailed information on each tool.
288
293
  enriches the underlying API call when authenticated)
289
294
  - `get_post_context` - Get a post with optional thread, author profile,
290
295
  engagement metrics, and media (ENHANCED mode)
296
+ - `search_starter_packs` / `get_starter_pack` - Search Bluesky starter packs by
297
+ keyword and fetch a pack's details (ENHANCED mode)
291
298
 
292
299
  **Rich Media**
293
300
 
@@ -319,6 +326,18 @@ for most endpoints that were previously public, including `search_posts`.
319
326
  cheap unread badge count)
320
327
  - `mark_notifications_seen` - Mark notifications as seen up to a timestamp
321
328
 
329
+ **Direct Messages**
330
+
331
+ - `list_conversations` - List your Bluesky DM conversations
332
+ - `get_conversation_messages` - Read a conversation's message history
333
+ - `send_direct_message` - Send a DM (requires an app password created with
334
+ "Allow access to your direct messages" enabled)
335
+
336
+ **Bookmarks**
337
+
338
+ - `add_bookmark` / `remove_bookmark` - Privately bookmark and un-bookmark posts
339
+ - `get_bookmarks` - List your private bookmarks
340
+
322
341
  **Content Management**
323
342
 
324
343
  - `upload_image` / `upload_video` - Upload media content
@@ -523,10 +542,13 @@ This project is licensed under the MIT License.
523
542
 
524
543
  ## Deployment
525
544
 
526
- This is a **stdio MCP server**: it is normally launched by an MCP client (e.g.
527
- Claude Desktop) via `npx atproto-mcp` and communicates over stdin/stdout. It
528
- does not listen on a network port, so there is no HTTP endpoint to expose or
529
- scale.
545
+ By default this is a **stdio MCP server**: it is normally launched by an MCP
546
+ client (e.g. Claude Desktop) via `npx atproto-mcp` and communicates over
547
+ stdin/stdout, binding no network port. Alternatively, `--transport http` serves
548
+ the MCP **Streamable HTTP** transport at `http://<host>:<port>/mcp` (default
549
+ binding `127.0.0.1:3000`, loopback only); exposing it beyond loopback (e.g.
550
+ `--host 0.0.0.0`) is the operator's responsibility to secure. stdio remains the
551
+ default and the recommended setup for MCP clients.
530
552
 
531
553
  ### Built-in safeguards
532
554
 
package/dist/cli.d.ts CHANGED
@@ -2,6 +2,22 @@
2
2
  /**
3
3
  * Command-line interface for the AT Protocol MCP Server
4
4
  */
5
+ import { type IMcpServerConfig } from './types/index.js';
6
+ import { type McpTransportKind } from './index.js';
7
+ /**
8
+ * Result of CLI argument parsing: configuration overrides plus the selected
9
+ * transport. The transport is deliberately NOT part of IMcpServerConfig — it
10
+ * is a process-level startup choice, passed to AtpMcpServer.start().
11
+ */
12
+ export interface ICliArgs {
13
+ config: Partial<IMcpServerConfig>;
14
+ transport: McpTransportKind;
15
+ }
16
+ /**
17
+ * Parse command line arguments. `argv` defaults to the process arguments and
18
+ * is injectable for tests.
19
+ */
20
+ export declare function parseCliArgs(argv?: string[]): ICliArgs;
5
21
  /**
6
22
  * Main CLI function
7
23
  */
package/dist/cli.js CHANGED
@@ -66,6 +66,11 @@ function loadEnvFile() {
66
66
  * CLI argument definitions
67
67
  */
68
68
  const CLI_OPTIONS = {
69
+ transport: {
70
+ type: 'string',
71
+ short: 't',
72
+ description: 'Transport: stdio|http (default: stdio)',
73
+ },
69
74
  port: {
70
75
  type: 'string',
71
76
  short: 'p',
@@ -113,13 +118,18 @@ AT Protocol MCP Server - Comprehensive interface for LLMs to interact with AT Pr
113
118
 
114
119
  Usage: atproto-mcp [options]
115
120
 
116
- Transport: this server communicates over stdio (for MCP clients such as Claude
117
- Desktop). It does not listen on a TCP port; --port/--host are accepted but
118
- currently have no effect.
121
+ Transport: by default this server communicates over stdio (for MCP clients
122
+ such as Claude Desktop). With --transport http it serves the MCP Streamable
123
+ HTTP transport at http://<host>:<port>/mcp instead. The default binding is the
124
+ loopback interface (127.0.0.1), so only local clients can connect; binding any
125
+ other host (e.g. --host 0.0.0.0) exposes the server to the network, and
126
+ securing that exposure (firewalling, reverse proxy, authentication) is the
127
+ operator's responsibility.
119
128
 
120
129
  Options:
121
- -p, --port <number> Server port (reserved; stdio transport ignores it)
122
- -H, --host <string> Server host (reserved; stdio transport ignores it)
130
+ -t, --transport <mode> Transport: stdio|http (default: stdio)
131
+ -p, --port <number> HTTP port for --transport http (default: 3000; stdio ignores it)
132
+ -H, --host <string> HTTP bind host for --transport http (default: 127.0.0.1, loopback; stdio ignores it)
123
133
  -s, --service <url> AT Protocol service URL (default: https://bsky.social)
124
134
  -a, --auth <method> Authentication method: app-password|oauth (optional)
125
135
  -l, --log-level <level> Log level: debug|info|warn|error (default: info)
@@ -155,8 +165,8 @@ Examples:
155
165
  # Start in unauthenticated mode (works immediately!)
156
166
  atproto-mcp
157
167
 
158
- # Start with custom port and debug logging
159
- atproto-mcp --port 8080 --log-level debug
168
+ # Serve the Streamable HTTP transport on loopback port 8080
169
+ atproto-mcp --transport http --port 8080 --log-level debug
160
170
 
161
171
  # Enable authentication with app password
162
172
  export ATPROTO_IDENTIFIER="your-handle.bsky.social"
@@ -185,11 +195,13 @@ function showVersion() {
185
195
  }
186
196
  }
187
197
  /**
188
- * Parse command line arguments
198
+ * Parse command line arguments. `argv` defaults to the process arguments and
199
+ * is injectable for tests.
189
200
  */
190
- function parseCliArgs() {
201
+ export function parseCliArgs(argv = process.argv.slice(2)) {
191
202
  try {
192
203
  const { values } = parseArgs({
204
+ args: argv,
193
205
  options: CLI_OPTIONS,
194
206
  allowPositionals: false,
195
207
  });
@@ -212,6 +224,15 @@ function parseCliArgs() {
212
224
  throw new ConfigurationError(`Invalid log level: ${values['log-level']}. Must be one of: debug, info, warn, error`);
213
225
  }
214
226
  }
227
+ // Validate the transport selection. stdio stays the default; http serves
228
+ // the Streamable HTTP transport using the --port/--host binding.
229
+ let transport = 'stdio';
230
+ if (values.transport != null && values.transport !== '') {
231
+ if (values.transport !== 'stdio' && values.transport !== 'http') {
232
+ throw new ConfigurationError(`Invalid transport: ${values.transport}. Must be 'stdio' or 'http'`);
233
+ }
234
+ transport = values.transport;
235
+ }
215
236
  // Build configuration from CLI arguments
216
237
  const config = {};
217
238
  if (values.port != null && values.port !== '') {
@@ -247,7 +268,7 @@ function parseCliArgs() {
247
268
  }
248
269
  config.atproto = atproto;
249
270
  }
250
- return config;
271
+ return { config, transport };
251
272
  }
252
273
  catch (error) {
253
274
  if (error instanceof ConfigurationError) {
@@ -267,7 +288,7 @@ async function main() {
267
288
  // visible to ConfigManager and the rest of the server.
268
289
  loadEnvFile();
269
290
  // Parse command line arguments
270
- const cliConfig = parseCliArgs();
291
+ const { config: cliConfig, transport } = parseCliArgs();
271
292
  // Create and start server
272
293
  const server = new AtpMcpServer(cliConfig);
273
294
  // Setup graceful shutdown handlers
@@ -298,7 +319,7 @@ async function main() {
298
319
  process.exit(1);
299
320
  });
300
321
  // Start the server
301
- await server.start();
322
+ await server.start({ transport });
302
323
  // Keep the process running
303
324
  logger.info('AT Protocol MCP Server is running. Press Ctrl+C to stop.');
304
325
  }
package/dist/index.d.ts CHANGED
@@ -12,6 +12,23 @@ import { AtpClient } from './utils/atp-client.js';
12
12
  import { ConfigManager } from './utils/config.js';
13
13
  import { type IPerformanceMetrics } from './utils/performance.js';
14
14
  import { SecurityManager } from './utils/security.js';
15
+ /**
16
+ * Transports the server can speak. stdio is the default (MCP clients such as
17
+ * Claude Desktop spawn the process and own its stdin/stdout); http serves the
18
+ * MCP Streamable HTTP transport on a TCP port at /mcp.
19
+ */
20
+ export type McpTransportKind = 'stdio' | 'http';
21
+ /**
22
+ * Options for {@link AtpMcpServer.start}. port/host apply to the http
23
+ * transport only and override the configured values (config.port/config.host),
24
+ * which lets tests bind an ephemeral port (0) that the config schema does not
25
+ * allow.
26
+ */
27
+ export interface IServerStartOptions {
28
+ transport?: McpTransportKind;
29
+ port?: number;
30
+ host?: string;
31
+ }
15
32
  /**
16
33
  * Main server class for AT Protocol MCP Server
17
34
  */
@@ -24,9 +41,21 @@ export declare class AtpMcpServer {
24
41
  private securityManager;
25
42
  private metricsInterval?;
26
43
  private transport;
44
+ private httpServer;
45
+ private httpSessions;
46
+ private httpAllowedHosts;
27
47
  private isRunning;
28
48
  private isShuttingDown;
29
49
  constructor(configOverrides?: Partial<IMcpServerConfig>);
50
+ /**
51
+ * Construct an MCP Server instance with every handler registered.
52
+ *
53
+ * This is the single construction path for ALL transports: the constructor
54
+ * builds the stdio server through it, and the http transport builds one
55
+ * fresh Server per session through it (each Streamable HTTP session needs
56
+ * its own Server because the SDK Protocol binds 1:1 to a transport).
57
+ */
58
+ private createMcpServer;
30
59
  /**
31
60
  * Set up the MCP server with basic handlers
32
61
  * Register tools, resources, and prompts with the MCP server
@@ -50,6 +79,18 @@ export declare class AtpMcpServer {
50
79
  * Register MCP prompts with the server
51
80
  */
52
81
  private registerPrompts;
82
+ /**
83
+ * Register the completion/complete handler (declared via the `completions`
84
+ * capability).
85
+ *
86
+ * - ref/prompt: serves candidate values for enumerable prompt arguments
87
+ * (each prompt declares its own candidates); free-text arguments complete
88
+ * to an empty list, never an error. Unknown prompt names are invalid params.
89
+ * - ref/resource: serves the {actor} variable of the resource templates with
90
+ * the authenticated user's handle when a session exists, else empty.
91
+ * Unknown templates/arguments complete to an empty list.
92
+ */
93
+ private registerCompletions;
53
94
  /**
54
95
  * Convert Zod schema to JSON Schema for MCP compatibility
55
96
  *
@@ -58,9 +99,59 @@ export declare class AtpMcpServer {
58
99
  */
59
100
  private zodToJsonSchema;
60
101
  /**
61
- * Start the MCP server
102
+ * Start the MCP server.
103
+ *
104
+ * By default the server speaks MCP over stdio. Pass
105
+ * `{ transport: 'http' }` to serve the Streamable HTTP transport instead:
106
+ * a node:http server routes POST/GET/DELETE on /mcp through per-session
107
+ * StreamableHTTPServerTransport instances, each backed by a fresh MCP
108
+ * Server built via the same construction path as the stdio server.
109
+ */
110
+ start(options?: IServerStartOptions): Promise<void>;
111
+ /**
112
+ * Start the Streamable HTTP transport: a node:http server (no express
113
+ * dependency) that routes /mcp through per-session transports.
114
+ *
115
+ * Binding defaults to the configured host, with 'localhost' pinned to the
116
+ * IPv4 loopback 127.0.0.1 so the bind address (and the DNS-rebinding
117
+ * allowlist) is deterministic across platforms whose resolvers disagree
118
+ * about ::1 vs 127.0.0.1. Exposing the server beyond loopback (e.g.
119
+ * --host 0.0.0.0) is the operator's responsibility to secure.
120
+ */
121
+ private startHttpTransport;
122
+ /**
123
+ * Route a single HTTP request. Only /mcp is served; the SDK transport does
124
+ * the MCP-level work (method dispatch, session validation, SSE streaming).
125
+ */
126
+ private handleHttpRequest;
127
+ /**
128
+ * Create a Streamable HTTP session: a stateful transport (server-minted
129
+ * session id) wired to a fresh MCP Server from the shared factory. The
130
+ * session registers itself in httpSessions once the SDK accepts the
131
+ * initialize request, and removes itself when the transport closes (DELETE,
132
+ * client disconnect, or shutdown).
133
+ */
134
+ private createHttpSession;
135
+ /**
136
+ * Read and parse a JSON request body, bounding its size. Responds (413/400)
137
+ * and resolves to undefined when the body is unusable; the caller must stop
138
+ * processing the request in that case.
139
+ */
140
+ private readJsonBody;
141
+ /**
142
+ * Write a JSON-RPC-shaped HTTP error response (the same shape the SDK
143
+ * transport uses for its own protocol-level rejections).
144
+ */
145
+ private writeJsonRpcError;
146
+ /**
147
+ * The address the http transport is bound to, or null when the http
148
+ * transport is not running. Exposed so callers (and tests binding port 0)
149
+ * can discover the actual ephemeral port.
62
150
  */
63
- start(): Promise<void>;
151
+ getHttpAddress(): {
152
+ host: string;
153
+ port: number;
154
+ } | null;
64
155
  /**
65
156
  * Stop the MCP server
66
157
  */