atproto-mcp 0.4.0 → 0.5.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 +5 -6
- package/dist/cli.d.ts +8 -1
- package/dist/cli.js +25 -7
- package/dist/health-check.d.ts +0 -1
- package/dist/health-check.js +2 -2
- package/dist/index.d.ts +0 -1
- package/dist/index.js +142 -64
- package/dist/prompts/index.d.ts +12 -4
- package/dist/prompts/index.js +21 -11
- package/dist/resources/base.d.ts +0 -1
- package/dist/resources/base.js +0 -1
- package/dist/resources/conversation-context-resource.d.ts +7 -8
- package/dist/resources/conversation-context-resource.js +10 -12
- package/dist/resources/index.d.ts +1 -3
- package/dist/resources/index.js +6 -6
- package/dist/tools/implementations/advanced-social-tools.d.ts +0 -1
- package/dist/tools/implementations/advanced-social-tools.js +13 -2
- package/dist/tools/implementations/analytics-tools.d.ts +13 -2
- package/dist/tools/implementations/analytics-tools.js +12 -8
- package/dist/tools/implementations/analyze-account-tool.d.ts +452 -1
- package/dist/tools/implementations/analyze-account-tool.js +397 -9
- package/dist/tools/implementations/base-tool.d.ts +49 -7
- package/dist/tools/implementations/base-tool.js +49 -14
- package/dist/tools/implementations/batch-operations-tools.d.ts +22 -8
- package/dist/tools/implementations/batch-operations-tools.js +131 -28
- package/dist/tools/implementations/composite-tools.d.ts +0 -1
- package/dist/tools/implementations/composite-tools.js +0 -1
- package/dist/tools/implementations/content-discovery-tools.d.ts +8 -3
- package/dist/tools/implementations/content-discovery-tools.js +100 -67
- package/dist/tools/implementations/content-management-tools.d.ts +91 -9
- package/dist/tools/implementations/content-management-tools.js +51 -32
- package/dist/tools/implementations/create-post-tool.d.ts +399 -11
- package/dist/tools/implementations/create-post-tool.js +116 -33
- package/dist/tools/implementations/create-thread-tool.d.ts +1 -2
- package/dist/tools/implementations/create-thread-tool.js +17 -1
- package/dist/tools/implementations/discover-tool.d.ts +235 -1
- package/dist/tools/implementations/discover-tool.js +238 -21
- package/dist/tools/implementations/follow-user-tool.d.ts +0 -1
- package/dist/tools/implementations/follow-user-tool.js +0 -1
- package/dist/tools/implementations/get-author-feed-tool.d.ts +0 -1
- package/dist/tools/implementations/get-author-feed-tool.js +0 -1
- package/dist/tools/implementations/get-user-profile-tool.d.ts +0 -1
- package/dist/tools/implementations/get-user-profile-tool.js +0 -1
- package/dist/tools/implementations/index.d.ts +0 -1
- package/dist/tools/implementations/index.js +0 -1
- package/dist/tools/implementations/like-post-tool.d.ts +9 -3
- package/dist/tools/implementations/like-post-tool.js +15 -6
- package/dist/tools/implementations/media-tools.d.ts +90 -8
- package/dist/tools/implementations/media-tools.js +292 -60
- package/dist/tools/implementations/moderation-tools.d.ts +0 -1
- package/dist/tools/implementations/moderation-tools.js +0 -1
- package/dist/tools/implementations/reply-to-post-tool.d.ts +5 -2
- package/dist/tools/implementations/reply-to-post-tool.js +15 -2
- package/dist/tools/implementations/repost-tool.d.ts +3 -2
- package/dist/tools/implementations/repost-tool.js +5 -13
- package/dist/tools/implementations/rich-media-tools.d.ts +13 -15
- package/dist/tools/implementations/rich-media-tools.js +2 -17
- package/dist/tools/implementations/search-actors-tool.d.ts +0 -1
- package/dist/tools/implementations/search-actors-tool.js +0 -1
- package/dist/tools/implementations/search-posts-tool.d.ts +3 -32
- package/dist/tools/implementations/search-posts-tool.js +0 -60
- package/dist/tools/implementations/social-graph-tools.d.ts +1 -3
- package/dist/tools/implementations/social-graph-tools.js +5 -3
- package/dist/tools/implementations/timeline-tools.d.ts +0 -28
- package/dist/tools/implementations/timeline-tools.js +0 -74
- package/dist/tools/index.d.ts +0 -1
- package/dist/tools/index.js +0 -1
- package/dist/types/index.d.ts +30 -12
- package/dist/types/index.js +6 -3
- package/dist/utils/atp-client.d.ts +15 -2
- package/dist/utils/atp-client.js +64 -11
- package/dist/utils/config.d.ts +3 -2
- package/dist/utils/config.js +10 -7
- package/dist/utils/firehose-client.d.ts +0 -1
- package/dist/utils/firehose-client.js +0 -1
- package/dist/utils/logger.d.ts +8 -1
- package/dist/utils/logger.js +17 -4
- package/dist/utils/oauth-client.d.ts +0 -1
- package/dist/utils/oauth-client.js +4 -5
- package/dist/utils/performance.d.ts +0 -1
- package/dist/utils/performance.js +0 -1
- package/dist/utils/security.d.ts +0 -1
- package/dist/utils/security.js +0 -1
- package/dist/utils/url-safety.d.ts +0 -1
- package/dist/utils/url-safety.js +0 -1
- package/package.json +11 -6
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/health-check.d.ts.map +0 -1
- package/dist/health-check.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/prompts/index.d.ts.map +0 -1
- package/dist/prompts/index.js.map +0 -1
- package/dist/resources/base.d.ts.map +0 -1
- package/dist/resources/base.js.map +0 -1
- package/dist/resources/conversation-context-resource.d.ts.map +0 -1
- package/dist/resources/conversation-context-resource.js.map +0 -1
- package/dist/resources/index.d.ts.map +0 -1
- package/dist/resources/index.js.map +0 -1
- package/dist/test/integration-config.d.ts +0 -60
- package/dist/test/integration-config.d.ts.map +0 -1
- package/dist/test/integration-config.js +0 -93
- package/dist/test/integration-config.js.map +0 -1
- package/dist/test/setup.d.ts +0 -78
- package/dist/test/setup.d.ts.map +0 -1
- package/dist/test/setup.js +0 -138
- package/dist/test/setup.js.map +0 -1
- package/dist/tools/implementations/advanced-social-tools.d.ts.map +0 -1
- package/dist/tools/implementations/advanced-social-tools.js.map +0 -1
- package/dist/tools/implementations/analytics-tools.d.ts.map +0 -1
- package/dist/tools/implementations/analytics-tools.js.map +0 -1
- package/dist/tools/implementations/analyze-account-tool.d.ts.map +0 -1
- package/dist/tools/implementations/analyze-account-tool.js.map +0 -1
- package/dist/tools/implementations/base-tool.d.ts.map +0 -1
- package/dist/tools/implementations/base-tool.js.map +0 -1
- package/dist/tools/implementations/batch-operations-tools.d.ts.map +0 -1
- package/dist/tools/implementations/batch-operations-tools.js.map +0 -1
- package/dist/tools/implementations/composite-tools.d.ts.map +0 -1
- package/dist/tools/implementations/composite-tools.js.map +0 -1
- package/dist/tools/implementations/content-discovery-tools.d.ts.map +0 -1
- package/dist/tools/implementations/content-discovery-tools.js.map +0 -1
- package/dist/tools/implementations/content-management-tools.d.ts.map +0 -1
- package/dist/tools/implementations/content-management-tools.js.map +0 -1
- package/dist/tools/implementations/create-post-tool.d.ts.map +0 -1
- package/dist/tools/implementations/create-post-tool.js.map +0 -1
- package/dist/tools/implementations/create-thread-tool.d.ts.map +0 -1
- package/dist/tools/implementations/create-thread-tool.js.map +0 -1
- package/dist/tools/implementations/discover-tool.d.ts.map +0 -1
- package/dist/tools/implementations/discover-tool.js.map +0 -1
- package/dist/tools/implementations/follow-user-tool.d.ts.map +0 -1
- package/dist/tools/implementations/follow-user-tool.js.map +0 -1
- package/dist/tools/implementations/get-author-feed-tool.d.ts.map +0 -1
- package/dist/tools/implementations/get-author-feed-tool.js.map +0 -1
- package/dist/tools/implementations/get-user-profile-tool.d.ts.map +0 -1
- package/dist/tools/implementations/get-user-profile-tool.js.map +0 -1
- package/dist/tools/implementations/index.d.ts.map +0 -1
- package/dist/tools/implementations/index.js.map +0 -1
- package/dist/tools/implementations/like-post-tool.d.ts.map +0 -1
- package/dist/tools/implementations/like-post-tool.js.map +0 -1
- package/dist/tools/implementations/media-tools.d.ts.map +0 -1
- package/dist/tools/implementations/media-tools.js.map +0 -1
- package/dist/tools/implementations/moderation-tools.d.ts.map +0 -1
- package/dist/tools/implementations/moderation-tools.js.map +0 -1
- package/dist/tools/implementations/reply-to-post-tool.d.ts.map +0 -1
- package/dist/tools/implementations/reply-to-post-tool.js.map +0 -1
- package/dist/tools/implementations/repost-tool.d.ts.map +0 -1
- package/dist/tools/implementations/repost-tool.js.map +0 -1
- package/dist/tools/implementations/rich-media-tools.d.ts.map +0 -1
- package/dist/tools/implementations/rich-media-tools.js.map +0 -1
- package/dist/tools/implementations/search-actors-tool.d.ts.map +0 -1
- package/dist/tools/implementations/search-actors-tool.js.map +0 -1
- package/dist/tools/implementations/search-posts-tool.d.ts.map +0 -1
- package/dist/tools/implementations/search-posts-tool.js.map +0 -1
- package/dist/tools/implementations/social-graph-tools.d.ts.map +0 -1
- package/dist/tools/implementations/social-graph-tools.js.map +0 -1
- package/dist/tools/implementations/timeline-tools.d.ts.map +0 -1
- package/dist/tools/implementations/timeline-tools.js.map +0 -1
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/utils/atp-client.d.ts.map +0 -1
- package/dist/utils/atp-client.js.map +0 -1
- package/dist/utils/config.d.ts.map +0 -1
- package/dist/utils/config.js.map +0 -1
- package/dist/utils/firehose-client.d.ts.map +0 -1
- package/dist/utils/firehose-client.js.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/oauth-client.d.ts.map +0 -1
- package/dist/utils/oauth-client.js.map +0 -1
- package/dist/utils/performance.d.ts.map +0 -1
- package/dist/utils/performance.js.map +0 -1
- package/dist/utils/security.d.ts.map +0 -1
- package/dist/utils/security.js.map +0 -1
- package/dist/utils/url-safety.d.ts.map +0 -1
- package/dist/utils/url-safety.js.map +0 -1
package/README.md
CHANGED
|
@@ -43,8 +43,7 @@ write operations, private data, feeds).
|
|
|
43
43
|
> public-data mode — no credentials required.
|
|
44
44
|
>
|
|
45
45
|
> **Recent additions**: Batch operations for bulk actions, advanced analytics
|
|
46
|
-
> and insights, intelligent content discovery
|
|
47
|
-
> scratchpad resource.
|
|
46
|
+
> and insights, and intelligent content discovery.
|
|
48
47
|
|
|
49
48
|
## Architecture
|
|
50
49
|
|
|
@@ -227,10 +226,10 @@ config:
|
|
|
227
226
|
`get_timeline`)
|
|
228
227
|
- All write operations (create, like, repost, follow, etc.)
|
|
229
228
|
- Resources (timeline, profile, notifications) - these are listed but require
|
|
230
|
-
authentication to return data
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
229
|
+
authentication to return data
|
|
230
|
+
|
|
231
|
+
Prompts (content composition, reply templates) are pure text templates and work
|
|
232
|
+
without authentication.
|
|
234
233
|
|
|
235
234
|
**Important:** All tools, resources, and prompts are listed by the MCP server
|
|
236
235
|
regardless of authentication state. Most tools and resources that require
|
package/dist/cli.d.ts
CHANGED
|
@@ -6,5 +6,12 @@
|
|
|
6
6
|
* Main CLI function
|
|
7
7
|
*/
|
|
8
8
|
declare function main(): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Detect whether a module is the process entry point. Node realpath-resolves
|
|
11
|
+
* import.meta.url for the main module, but argv[1] stays the literal invoked
|
|
12
|
+
* path — and npm installs bins as symlinks — so a naive string comparison
|
|
13
|
+
* against `file://${argv[1]}` breaks symlinked, relative, and space-containing
|
|
14
|
+
* paths. Compare realpath-resolved file URLs instead.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isMainModule(importMetaUrl: string, argv1: string | undefined): boolean;
|
|
9
17
|
export { main as runCli };
|
|
10
|
-
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* Command-line interface for the AT Protocol MCP Server
|
|
4
4
|
*/
|
|
5
5
|
import { parseArgs } from 'node:util';
|
|
6
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
6
|
+
import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
7
7
|
import { dirname, join } from 'node:path';
|
|
8
|
-
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
9
9
|
import { ConfigurationError } from './types/index.js';
|
|
10
10
|
import { AtpMcpServer } from './index.js';
|
|
11
11
|
import { LogLevel, Logger } from './utils/logger.js';
|
|
@@ -73,7 +73,7 @@ const CLI_OPTIONS = {
|
|
|
73
73
|
},
|
|
74
74
|
host: {
|
|
75
75
|
type: 'string',
|
|
76
|
-
short: '
|
|
76
|
+
short: 'H',
|
|
77
77
|
description: 'Server host (default: localhost)',
|
|
78
78
|
},
|
|
79
79
|
service: {
|
|
@@ -93,6 +93,7 @@ const CLI_OPTIONS = {
|
|
|
93
93
|
},
|
|
94
94
|
help: {
|
|
95
95
|
type: 'boolean',
|
|
96
|
+
short: 'h',
|
|
96
97
|
description: 'Show help message',
|
|
97
98
|
},
|
|
98
99
|
version: {
|
|
@@ -118,11 +119,11 @@ currently have no effect.
|
|
|
118
119
|
|
|
119
120
|
Options:
|
|
120
121
|
-p, --port <number> Server port (reserved; stdio transport ignores it)
|
|
121
|
-
-
|
|
122
|
+
-H, --host <string> Server host (reserved; stdio transport ignores it)
|
|
122
123
|
-s, --service <url> AT Protocol service URL (default: https://bsky.social)
|
|
123
124
|
-a, --auth <method> Authentication method: app-password|oauth (optional)
|
|
124
125
|
-l, --log-level <level> Log level: debug|info|warn|error (default: info)
|
|
125
|
-
|
|
126
|
+
-h, --help Show this help message
|
|
126
127
|
-v, --version Show version information
|
|
127
128
|
|
|
128
129
|
🔓 Unauthenticated Mode (Default):
|
|
@@ -311,12 +312,29 @@ async function main() {
|
|
|
311
312
|
process.exit(1);
|
|
312
313
|
}
|
|
313
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* Detect whether a module is the process entry point. Node realpath-resolves
|
|
317
|
+
* import.meta.url for the main module, but argv[1] stays the literal invoked
|
|
318
|
+
* path — and npm installs bins as symlinks — so a naive string comparison
|
|
319
|
+
* against `file://${argv[1]}` breaks symlinked, relative, and space-containing
|
|
320
|
+
* paths. Compare realpath-resolved file URLs instead.
|
|
321
|
+
*/
|
|
322
|
+
export function isMainModule(importMetaUrl, argv1) {
|
|
323
|
+
if (argv1 == null || argv1 === '') {
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
try {
|
|
327
|
+
return importMetaUrl === pathToFileURL(realpathSync(argv1)).href;
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
314
333
|
// Run CLI if this file is executed directly
|
|
315
|
-
if (import.meta.url
|
|
334
|
+
if (isMainModule(import.meta.url, process.argv[1])) {
|
|
316
335
|
main().catch(error => {
|
|
317
336
|
console.error('Fatal error:', error);
|
|
318
337
|
process.exit(1);
|
|
319
338
|
});
|
|
320
339
|
}
|
|
321
340
|
export { main as runCli };
|
|
322
|
-
//# sourceMappingURL=cli.js.map
|
package/dist/health-check.d.ts
CHANGED
package/dist/health-check.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* connection counts of the running server — a fresh process cannot observe those
|
|
11
11
|
* and reporting them would be misleading.
|
|
12
12
|
*/
|
|
13
|
+
import { isMainModule } from './cli.js';
|
|
13
14
|
import { AtpMcpServer } from './index.js';
|
|
14
15
|
function healthCheck() {
|
|
15
16
|
try {
|
|
@@ -47,7 +48,6 @@ function healthCheck() {
|
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
// Run health check if this script is executed directly
|
|
50
|
-
if (import.meta.url
|
|
51
|
+
if (isMainModule(import.meta.url, process.argv[1])) {
|
|
51
52
|
void healthCheck();
|
|
52
53
|
}
|
|
53
|
-
//# sourceMappingURL=health-check.js.map
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
10
10
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
11
|
-
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
11
|
+
import { ErrorCode, ListResourceTemplatesRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js';
|
|
12
12
|
import { z } from 'zod';
|
|
13
13
|
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
14
14
|
import { ConfigurationError, ValidationError } from './types/index.js';
|
|
@@ -21,57 +21,108 @@ import { createPrompts } from './prompts/index.js';
|
|
|
21
21
|
import { PerformanceMonitor } from './utils/performance.js';
|
|
22
22
|
import { SecurityManager } from './utils/security.js';
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* MCP spec error code for "Resource not found" (resources/read with an unknown
|
|
25
|
+
* URI). Not part of the SDK's ErrorCode enum, which only covers the generic
|
|
26
|
+
* JSON-RPC codes.
|
|
27
27
|
*/
|
|
28
|
-
const
|
|
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
|
-
]);
|
|
28
|
+
const RESOURCE_NOT_FOUND_ERROR_CODE = -32002;
|
|
49
29
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
30
|
+
* Explicit per-tool annotation hints, advertised verbatim in tools/list.
|
|
31
|
+
*
|
|
32
|
+
* Per MCP spec defaults, clients assume the worst case for any hint a server
|
|
33
|
+
* omits on a non-read-only tool (destructiveHint: true, idempotentHint:
|
|
34
|
+
* false), so every write tool carries both hints explicitly.
|
|
35
|
+
*
|
|
36
|
+
* - readOnlyHint: pure reads (no writes to the network). Curated explicitly
|
|
37
|
+
* per tool — NOT derived from auth mode, which encodes auth requirement,
|
|
38
|
+
* not destructiveness. destructive/idempotent hints are only meaningful for
|
|
39
|
+
* write tools and are omitted on read-only entries.
|
|
40
|
+
* - destructiveHint: the tool may delete or overwrite existing data/state
|
|
41
|
+
* (record deletes, list removals, profile overwrites, blocks, irreversible
|
|
42
|
+
* moderation reports). Purely additive, reversible writes carry an explicit
|
|
43
|
+
* false.
|
|
44
|
+
* - idempotentHint: repeating the call with identical arguments has no
|
|
45
|
+
* additional effect. Claimed only where verified — an implementation-level
|
|
46
|
+
* dedup/no-op path, or set/clear semantics of the underlying XRPC endpoint.
|
|
53
47
|
*/
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
48
|
+
const TOOL_ANNOTATIONS = {
|
|
49
|
+
// Pure read tools.
|
|
50
|
+
analyze_account: { readOnlyHint: true },
|
|
51
|
+
analyze_image: { readOnlyHint: true },
|
|
52
|
+
analyze_moderation_status: { readOnlyHint: true },
|
|
53
|
+
discover: { readOnlyHint: true },
|
|
54
|
+
discover_communities: { readOnlyHint: true },
|
|
55
|
+
find_influential_users: { readOnlyHint: true },
|
|
56
|
+
find_similar_users: { readOnlyHint: true },
|
|
57
|
+
generate_link_preview: { readOnlyHint: true },
|
|
58
|
+
get_author_feed: { readOnlyHint: true },
|
|
59
|
+
get_custom_feed: { readOnlyHint: true },
|
|
60
|
+
get_list: { readOnlyHint: true },
|
|
61
|
+
get_notifications: { readOnlyHint: true },
|
|
62
|
+
get_post_context: { readOnlyHint: true },
|
|
63
|
+
get_timeline: { readOnlyHint: true },
|
|
64
|
+
get_user_connections: { readOnlyHint: true },
|
|
65
|
+
get_user_profile: { readOnlyHint: true },
|
|
66
|
+
get_user_summary: { readOnlyHint: true },
|
|
67
|
+
search_actors: { readOnlyHint: true },
|
|
68
|
+
search_posts: { readOnlyHint: true },
|
|
69
|
+
// Additive, reversible writes. Idempotency notes name the verified
|
|
70
|
+
// dedup/no-op path in the implementation.
|
|
71
|
+
// add_to_list has no dedup: duplicate listitem records are possible.
|
|
72
|
+
add_to_list: { destructiveHint: false, idempotentHint: false },
|
|
73
|
+
// batch_action only supports follow/like/repost (no quote text), and every
|
|
74
|
+
// path dedups per target via authoritative viewer state.
|
|
75
|
+
batch_action: { destructiveHint: false, idempotentHint: true },
|
|
76
|
+
create_list: { destructiveHint: false, idempotentHint: false },
|
|
77
|
+
create_post: { destructiveHint: false, idempotentHint: false },
|
|
78
|
+
create_thread: { destructiveHint: false, idempotentHint: false },
|
|
79
|
+
// Dedups via viewer.following.
|
|
80
|
+
follow_user: { destructiveHint: false, idempotentHint: true },
|
|
81
|
+
// Dedups via viewer.like.
|
|
82
|
+
like_post: { destructiveHint: false, idempotentHint: true },
|
|
83
|
+
// seenAt defaults to "now", so repeated calls advance the seen marker.
|
|
84
|
+
mark_notifications_seen: { destructiveHint: false, idempotentHint: false },
|
|
85
|
+
// app.bsky.graph.muteActor sets server-side state; repeating it is a no-op.
|
|
86
|
+
mute_user: { destructiveHint: false, idempotentHint: true },
|
|
87
|
+
reply_to_post: { destructiveHint: false, idempotentHint: false },
|
|
88
|
+
// Plain reposts dedup via viewer.repost, but quote text creates a new post
|
|
89
|
+
// on every call, so the tool as a whole is not idempotent.
|
|
90
|
+
repost: { destructiveHint: false, idempotentHint: false },
|
|
91
|
+
upload_image: { destructiveHint: false, idempotentHint: false },
|
|
92
|
+
upload_video: { destructiveHint: false, idempotentHint: false },
|
|
93
|
+
// Destructive writes: may delete or overwrite data/state. These hints let
|
|
94
|
+
// clients surface confirmation UI and withhold auto-approval.
|
|
95
|
+
// Blocking imposes hard bidirectional restrictions and has no dedup
|
|
96
|
+
// (duplicate block records are possible).
|
|
97
|
+
block_user: { destructiveHint: true, idempotentHint: false },
|
|
98
|
+
delete_post: { destructiveHint: true, idempotentHint: false },
|
|
99
|
+
// "Not in list" resolves to an explicit success:false no-op.
|
|
100
|
+
remove_from_list: { destructiveHint: true, idempotentHint: true },
|
|
101
|
+
// Reports are irreversible moderation actions against third parties; each
|
|
102
|
+
// call files a new report.
|
|
103
|
+
report_content: { destructiveHint: true, idempotentHint: false },
|
|
104
|
+
report_user: { destructiveHint: true, idempotentHint: false },
|
|
105
|
+
// "Not blocked" resolves to an explicit success:false no-op.
|
|
106
|
+
unblock_user: { destructiveHint: true, idempotentHint: true },
|
|
107
|
+
unfollow_user: { destructiveHint: true, idempotentHint: false },
|
|
108
|
+
unlike_post: { destructiveHint: true, idempotentHint: false },
|
|
109
|
+
// app.bsky.graph.unmuteActor clears server-side state; repeating is a no-op.
|
|
110
|
+
unmute_user: { destructiveHint: true, idempotentHint: true },
|
|
111
|
+
unrepost: { destructiveHint: true, idempotentHint: false },
|
|
112
|
+
// Overwrites profile fields; the read-merge-write (CAS-guarded) converges
|
|
113
|
+
// to the same record for identical arguments.
|
|
114
|
+
update_profile: { destructiveHint: true, idempotentHint: true },
|
|
115
|
+
};
|
|
65
116
|
/**
|
|
66
117
|
* Build the advertised annotations for a tool. openWorldHint is true for every
|
|
67
118
|
* tool (they all reach a live network). A per-tool `schema.annotations` override
|
|
68
|
-
* wins over these defaults.
|
|
119
|
+
* wins over these defaults. Tools missing from TOOL_ANNOTATIONS fall back to
|
|
120
|
+
* the MCP client-side worst-case defaults (destructive, non-idempotent).
|
|
69
121
|
*/
|
|
70
122
|
function computeToolAnnotations(method, override) {
|
|
71
123
|
return {
|
|
72
124
|
openWorldHint: true,
|
|
73
|
-
...(
|
|
74
|
-
...(DESTRUCTIVE_TOOLS.has(method) ? { destructiveHint: true } : {}),
|
|
125
|
+
...(TOOL_ANNOTATIONS[method] ?? {}),
|
|
75
126
|
...(override ?? {}),
|
|
76
127
|
};
|
|
77
128
|
}
|
|
@@ -176,10 +227,12 @@ export class AtpMcpServer {
|
|
|
176
227
|
inputSchema: tool.schema.params
|
|
177
228
|
? this.zodToJsonSchema(tool.schema.params)
|
|
178
229
|
: { type: 'object', properties: {} },
|
|
179
|
-
// Advertise
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
230
|
+
// Advertise the tool's declared output schema. This is a binding
|
|
231
|
+
// contract, not decoration: spec-compliant clients (including the
|
|
232
|
+
// official SDK's Client.callTool) validate every tools/call
|
|
233
|
+
// structuredContent against this schema and hard-fail on mismatch,
|
|
234
|
+
// so each outputSchema literal must track its tool's actual return
|
|
235
|
+
// shape (see the structuredContent note in the tools/call handler).
|
|
183
236
|
...(tool.schema.outputSchema ? { outputSchema: tool.schema.outputSchema } : {}),
|
|
184
237
|
annotations: computeToolAnnotations(tool.schema.method, tool.schema.annotations),
|
|
185
238
|
})),
|
|
@@ -251,10 +304,13 @@ export class AtpMcpServer {
|
|
|
251
304
|
// to re-parse the pretty-printed string. SDK structuredContent must be a
|
|
252
305
|
// JSON object, so bare arrays/primitives are wrapped under `result`.
|
|
253
306
|
//
|
|
254
|
-
// NOTE:
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
//
|
|
307
|
+
// NOTE: per-tool `outputSchema` IS advertised in tools/list (see
|
|
308
|
+
// registerTools above), and SDK clients validate structuredContent
|
|
309
|
+
// against it, failing the call on any drift. structuredContent is
|
|
310
|
+
// therefore a contract, not best-effort: tools that declare an
|
|
311
|
+
// outputSchema must return a conforming object. (Server-side, this
|
|
312
|
+
// custom handler performs no validation of its own — the SDK only
|
|
313
|
+
// auto-validates when tools are registered via registerTool.)
|
|
258
314
|
const structuredContent = result != null && typeof result === 'object' && !Array.isArray(result)
|
|
259
315
|
? result
|
|
260
316
|
: { result };
|
|
@@ -311,6 +367,13 @@ export class AtpMcpServer {
|
|
|
311
367
|
mimeType: resource.mimeType,
|
|
312
368
|
})),
|
|
313
369
|
}));
|
|
370
|
+
// Register resources/templates/list handler. The declared resources
|
|
371
|
+
// capability invites clients to probe this method; without a handler the
|
|
372
|
+
// SDK answers -32601 Method not found. No URI templates are offered (all
|
|
373
|
+
// resources have fixed URIs), so the list is empty.
|
|
374
|
+
this.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
|
|
375
|
+
resourceTemplates: [],
|
|
376
|
+
}));
|
|
314
377
|
// Register resources/read handler
|
|
315
378
|
this.server.setRequestHandler(z.object({
|
|
316
379
|
method: z.literal('resources/read'),
|
|
@@ -321,7 +384,9 @@ export class AtpMcpServer {
|
|
|
321
384
|
try {
|
|
322
385
|
const resource = resources.find(r => r.uri === request.params.uri);
|
|
323
386
|
if (!resource) {
|
|
324
|
-
|
|
387
|
+
// The MCP spec reserves -32002 for "Resource not found"; the SDK's
|
|
388
|
+
// ErrorCode enum does not (yet) name it, so use the literal.
|
|
389
|
+
throw new McpError(RESOURCE_NOT_FOUND_ERROR_CODE, `Resource not found: ${request.params.uri}`, {
|
|
325
390
|
uri: request.params.uri,
|
|
326
391
|
});
|
|
327
392
|
}
|
|
@@ -331,13 +396,22 @@ export class AtpMcpServer {
|
|
|
331
396
|
throw new McpError(ErrorCode.InternalError, `Resource not available: ${request.params.uri}`, { uri: request.params.uri });
|
|
332
397
|
}
|
|
333
398
|
const content = await resource.read();
|
|
399
|
+
// Per the MCP resource content schema, each item is either text
|
|
400
|
+
// contents or base64 blob contents. Binary payloads must be passed
|
|
401
|
+
// through as a blob, not coerced to empty text.
|
|
334
402
|
return {
|
|
335
403
|
contents: [
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
404
|
+
content.blob
|
|
405
|
+
? {
|
|
406
|
+
uri: content.uri,
|
|
407
|
+
mimeType: content.mimeType,
|
|
408
|
+
blob: Buffer.from(content.blob).toString('base64'),
|
|
409
|
+
}
|
|
410
|
+
: {
|
|
411
|
+
uri: content.uri,
|
|
412
|
+
mimeType: content.mimeType,
|
|
413
|
+
text: content.text ?? '',
|
|
414
|
+
},
|
|
341
415
|
],
|
|
342
416
|
};
|
|
343
417
|
}
|
|
@@ -376,11 +450,10 @@ export class AtpMcpServer {
|
|
|
376
450
|
name: request.params.name,
|
|
377
451
|
});
|
|
378
452
|
}
|
|
379
|
-
//
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
}
|
|
453
|
+
// No availability/auth gate here: prompts are pure text templates
|
|
454
|
+
// that never touch the AT Protocol client. Required arguments are
|
|
455
|
+
// enforced inside prompt.get(), which throws an InvalidParams
|
|
456
|
+
// McpError that toHandlerMcpError passes through verbatim.
|
|
384
457
|
const messages = await prompt.get(request.params.arguments ?? {});
|
|
385
458
|
return { messages };
|
|
386
459
|
}
|
|
@@ -465,8 +538,14 @@ export class AtpMcpServer {
|
|
|
465
538
|
}
|
|
466
539
|
catch (error) {
|
|
467
540
|
this.logger.error('Failed to start AT Protocol MCP Server', error);
|
|
468
|
-
// Cleanup on failure
|
|
469
|
-
|
|
541
|
+
// Cleanup on failure — but never let a failing cleanup mask the
|
|
542
|
+
// original startup error, which is the one the caller must see.
|
|
543
|
+
try {
|
|
544
|
+
await this.cleanup();
|
|
545
|
+
}
|
|
546
|
+
catch (cleanupError) {
|
|
547
|
+
this.logger.error('Cleanup after failed startup also failed', cleanupError);
|
|
548
|
+
}
|
|
470
549
|
if (error instanceof ConfigurationError) {
|
|
471
550
|
throw error;
|
|
472
551
|
}
|
|
@@ -600,4 +679,3 @@ export class AtpMcpServer {
|
|
|
600
679
|
* await server.start();
|
|
601
680
|
*/
|
|
602
681
|
export default AtpMcpServer;
|
|
603
|
-
//# sourceMappingURL=index.js.map
|
package/dist/prompts/index.d.ts
CHANGED
|
@@ -34,9 +34,18 @@ export declare abstract class BasePrompt implements IMcpPrompt {
|
|
|
34
34
|
protected logger: Logger;
|
|
35
35
|
constructor(atpClient: AtpClient, loggerName: string);
|
|
36
36
|
/**
|
|
37
|
-
* Check if the prompt is available
|
|
37
|
+
* Check if the prompt is available.
|
|
38
|
+
*
|
|
39
|
+
* Prompts are pure text templates: they never call the AT Protocol client,
|
|
40
|
+
* so they are available regardless of authentication state.
|
|
38
41
|
*/
|
|
39
42
|
isAvailable(): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Read a declared-required string argument, rejecting missing/blank values
|
|
45
|
+
* with the spec invalid-params error instead of silently substituting a
|
|
46
|
+
* placeholder.
|
|
47
|
+
*/
|
|
48
|
+
protected requireStringArg(args: Record<string, unknown> | undefined, name: string): string;
|
|
40
49
|
/**
|
|
41
50
|
* Generate the prompt content
|
|
42
51
|
*/
|
|
@@ -47,7 +56,7 @@ export declare abstract class BasePrompt implements IMcpPrompt {
|
|
|
47
56
|
*/
|
|
48
57
|
export declare class ContentCompositionPrompt extends BasePrompt {
|
|
49
58
|
readonly name = "content_composition";
|
|
50
|
-
readonly description = "Generate engaging social media post content with proper formatting and hashtags.
|
|
59
|
+
readonly description = "Generate engaging social media post content with proper formatting and hashtags. Pure text template; works without authentication.";
|
|
51
60
|
readonly arguments: {
|
|
52
61
|
name: string;
|
|
53
62
|
description: string;
|
|
@@ -61,7 +70,7 @@ export declare class ContentCompositionPrompt extends BasePrompt {
|
|
|
61
70
|
*/
|
|
62
71
|
export declare class ReplyTemplatePrompt extends BasePrompt {
|
|
63
72
|
readonly name = "reply_template";
|
|
64
|
-
readonly description = "Generate thoughtful reply templates for different types of posts.
|
|
73
|
+
readonly description = "Generate thoughtful reply templates for different types of posts. Pure text template; works without authentication.";
|
|
65
74
|
readonly arguments: {
|
|
66
75
|
name: string;
|
|
67
76
|
description: string;
|
|
@@ -74,4 +83,3 @@ export declare class ReplyTemplatePrompt extends BasePrompt {
|
|
|
74
83
|
* Create all MCP prompts for AT Protocol content assistance
|
|
75
84
|
*/
|
|
76
85
|
export declare function createPrompts(atpClient: AtpClient): BasePrompt[];
|
|
77
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/prompts/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP Prompts for AT Protocol content creation assistance
|
|
3
3
|
*/
|
|
4
|
+
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
4
5
|
import { Logger } from '../utils/logger.js';
|
|
5
6
|
/**
|
|
6
7
|
* Base class for MCP prompts
|
|
@@ -13,15 +14,25 @@ export class BasePrompt {
|
|
|
13
14
|
this.logger = new Logger(loggerName);
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
16
|
-
* Check if the prompt is available
|
|
17
|
+
* Check if the prompt is available.
|
|
18
|
+
*
|
|
19
|
+
* Prompts are pure text templates: they never call the AT Protocol client,
|
|
20
|
+
* so they are available regardless of authentication state.
|
|
17
21
|
*/
|
|
18
22
|
isAvailable() {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Read a declared-required string argument, rejecting missing/blank values
|
|
27
|
+
* with the spec invalid-params error instead of silently substituting a
|
|
28
|
+
* placeholder.
|
|
29
|
+
*/
|
|
30
|
+
requireStringArg(args, name) {
|
|
31
|
+
const value = args?.[name];
|
|
32
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
33
|
+
throw new McpError(ErrorCode.InvalidParams, `Missing required argument "${name}" for prompt "${this.name}"`, { prompt: this.name, argument: name });
|
|
24
34
|
}
|
|
35
|
+
return value;
|
|
25
36
|
}
|
|
26
37
|
}
|
|
27
38
|
/**
|
|
@@ -29,7 +40,7 @@ export class BasePrompt {
|
|
|
29
40
|
*/
|
|
30
41
|
export class ContentCompositionPrompt extends BasePrompt {
|
|
31
42
|
name = 'content_composition';
|
|
32
|
-
description = 'Generate engaging social media post content with proper formatting and hashtags.
|
|
43
|
+
description = 'Generate engaging social media post content with proper formatting and hashtags. Pure text template; works without authentication.';
|
|
33
44
|
arguments = [
|
|
34
45
|
{
|
|
35
46
|
name: 'topic',
|
|
@@ -56,7 +67,7 @@ export class ContentCompositionPrompt extends BasePrompt {
|
|
|
56
67
|
super(atpClient, 'ContentCompositionPrompt');
|
|
57
68
|
}
|
|
58
69
|
async get(args) {
|
|
59
|
-
const topic = args
|
|
70
|
+
const topic = this.requireStringArg(args, 'topic');
|
|
60
71
|
const tone = args?.['tone'] ?? 'casual';
|
|
61
72
|
const length = args?.['length'] ?? 'medium';
|
|
62
73
|
const includeHashtags = args?.['include_hashtags'] !== false;
|
|
@@ -104,7 +115,7 @@ Please provide the post text ready to publish.`,
|
|
|
104
115
|
*/
|
|
105
116
|
export class ReplyTemplatePrompt extends BasePrompt {
|
|
106
117
|
name = 'reply_template';
|
|
107
|
-
description = 'Generate thoughtful reply templates for different types of posts.
|
|
118
|
+
description = 'Generate thoughtful reply templates for different types of posts. Pure text template; works without authentication.';
|
|
108
119
|
arguments = [
|
|
109
120
|
{
|
|
110
121
|
name: 'original_post',
|
|
@@ -126,7 +137,7 @@ export class ReplyTemplatePrompt extends BasePrompt {
|
|
|
126
137
|
super(atpClient, 'ReplyTemplatePrompt');
|
|
127
138
|
}
|
|
128
139
|
async get(args) {
|
|
129
|
-
const originalPost = args
|
|
140
|
+
const originalPost = this.requireStringArg(args, 'original_post');
|
|
130
141
|
const replyType = args?.['reply_type'] ?? 'supportive';
|
|
131
142
|
const relationship = args?.['relationship'] ?? 'stranger';
|
|
132
143
|
const replyTypeGuidance = {
|
|
@@ -191,4 +202,3 @@ export function createPrompts(atpClient) {
|
|
|
191
202
|
logger.info(`Created ${prompts.length} AT Protocol MCP prompts`);
|
|
192
203
|
return prompts;
|
|
193
204
|
}
|
|
194
|
-
//# sourceMappingURL=index.js.map
|
package/dist/resources/base.d.ts
CHANGED
package/dist/resources/base.js
CHANGED
|
@@ -41,14 +41,14 @@ interface IConversationContext {
|
|
|
41
41
|
/**
|
|
42
42
|
* Conversation Context Resource
|
|
43
43
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* - Active threads being followed
|
|
47
|
-
* - Users that have been mentioned
|
|
48
|
-
* - Search queries performed
|
|
49
|
-
* - Recent actions taken
|
|
44
|
+
* In-memory store for conversation state (discussed posts, active threads,
|
|
45
|
+
* mentioned users, searches, recent actions), exposed as an MCP resource.
|
|
50
46
|
*
|
|
51
|
-
*
|
|
47
|
+
* NOT REGISTERED with the MCP server (see createResources): MCP has no
|
|
48
|
+
* client-write mechanism for resources, and no tool currently calls the
|
|
49
|
+
* static add* methods, so the resource would always read as empty. The class
|
|
50
|
+
* is retained so a future change can populate it from tool handlers and
|
|
51
|
+
* re-register it.
|
|
52
52
|
*/
|
|
53
53
|
export declare class ConversationContextResource extends BaseResource {
|
|
54
54
|
readonly uri = "atproto://conversation-context";
|
|
@@ -110,4 +110,3 @@ export declare class ConversationContextResource extends BaseResource {
|
|
|
110
110
|
static getContext(): IConversationContext;
|
|
111
111
|
}
|
|
112
112
|
export {};
|
|
113
|
-
//# sourceMappingURL=conversation-context-resource.d.ts.map
|
|
@@ -5,22 +5,21 @@ import { BaseResource } from './base.js';
|
|
|
5
5
|
/**
|
|
6
6
|
* Conversation Context Resource
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* - Active threads being followed
|
|
11
|
-
* - Users that have been mentioned
|
|
12
|
-
* - Search queries performed
|
|
13
|
-
* - Recent actions taken
|
|
8
|
+
* In-memory store for conversation state (discussed posts, active threads,
|
|
9
|
+
* mentioned users, searches, recent actions), exposed as an MCP resource.
|
|
14
10
|
*
|
|
15
|
-
*
|
|
11
|
+
* NOT REGISTERED with the MCP server (see createResources): MCP has no
|
|
12
|
+
* client-write mechanism for resources, and no tool currently calls the
|
|
13
|
+
* static add* methods, so the resource would always read as empty. The class
|
|
14
|
+
* is retained so a future change can populate it from tool handlers and
|
|
15
|
+
* re-register it.
|
|
16
16
|
*/
|
|
17
17
|
export class ConversationContextResource extends BaseResource {
|
|
18
18
|
uri = 'atproto://conversation-context';
|
|
19
19
|
name = 'Conversation Context';
|
|
20
|
-
description = '
|
|
21
|
-
'users, recent actions).
|
|
22
|
-
'
|
|
23
|
-
'"not tracked", not "nothing happened".';
|
|
20
|
+
description = 'Server-side scratchpad of conversation state (recently discussed posts, active threads, ' +
|
|
21
|
+
'mentioned users, recent actions). Populated only by server-side tool integrations; in the ' +
|
|
22
|
+
'current release nothing writes to it, so it always reads as empty arrays.';
|
|
24
23
|
mimeType = 'application/json';
|
|
25
24
|
static context = {
|
|
26
25
|
recentlyDiscussedPosts: [],
|
|
@@ -177,4 +176,3 @@ export class ConversationContextResource extends BaseResource {
|
|
|
177
176
|
return this.context;
|
|
178
177
|
}
|
|
179
178
|
}
|
|
180
|
-
//# sourceMappingURL=conversation-context-resource.js.map
|
|
@@ -37,10 +37,8 @@ export declare class NotificationsResource extends BaseResource {
|
|
|
37
37
|
constructor(atpClient: AtpClient);
|
|
38
38
|
read(): Promise<IResourceContent>;
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
export { ConversationContextResource };
|
|
40
|
+
export { ConversationContextResource } from './conversation-context-resource.js';
|
|
42
41
|
/**
|
|
43
42
|
* Create all MCP resources for AT Protocol data
|
|
44
43
|
*/
|
|
45
44
|
export declare function createResources(atpClient: AtpClient): BaseResource[];
|
|
46
|
-
//# sourceMappingURL=index.d.ts.map
|