@mseep/mcp-agent-social 1.1.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.
Files changed (165) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -0
  3. package/bin/mcp-agent-social +30 -0
  4. package/dist/api-client.d.ts +31 -0
  5. package/dist/api-client.d.ts.map +1 -0
  6. package/dist/api-client.js +212 -0
  7. package/dist/api-client.js.map +1 -0
  8. package/dist/config.d.ts +19 -0
  9. package/dist/config.d.ts.map +1 -0
  10. package/dist/config.js +79 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/hooks/index.d.ts +38 -0
  13. package/dist/hooks/index.d.ts.map +1 -0
  14. package/dist/hooks/index.js +253 -0
  15. package/dist/hooks/index.js.map +1 -0
  16. package/dist/hooks/types.d.ts +35 -0
  17. package/dist/hooks/types.d.ts.map +1 -0
  18. package/dist/hooks/types.js +4 -0
  19. package/dist/hooks/types.js.map +1 -0
  20. package/dist/http-server.d.ts +38 -0
  21. package/dist/http-server.d.ts.map +1 -0
  22. package/dist/http-server.js +210 -0
  23. package/dist/http-server.js.map +1 -0
  24. package/dist/index.d.ts +2 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +186 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/logger.d.ts +44 -0
  29. package/dist/logger.d.ts.map +1 -0
  30. package/dist/logger.js +281 -0
  31. package/dist/logger.js.map +1 -0
  32. package/dist/metrics.d.ts +47 -0
  33. package/dist/metrics.d.ts.map +1 -0
  34. package/dist/metrics.js +178 -0
  35. package/dist/metrics.js.map +1 -0
  36. package/dist/middleware/error-handler.d.ts +74 -0
  37. package/dist/middleware/error-handler.d.ts.map +1 -0
  38. package/dist/middleware/error-handler.js +218 -0
  39. package/dist/middleware/error-handler.js.map +1 -0
  40. package/dist/middleware/index.d.ts +55 -0
  41. package/dist/middleware/index.d.ts.map +1 -0
  42. package/dist/middleware/index.js +91 -0
  43. package/dist/middleware/index.js.map +1 -0
  44. package/dist/middleware/timeout.d.ts +52 -0
  45. package/dist/middleware/timeout.d.ts.map +1 -0
  46. package/dist/middleware/timeout.js +189 -0
  47. package/dist/middleware/timeout.js.map +1 -0
  48. package/dist/middleware/validator.d.ts +25 -0
  49. package/dist/middleware/validator.d.ts.map +1 -0
  50. package/dist/middleware/validator.js +186 -0
  51. package/dist/middleware/validator.js.map +1 -0
  52. package/dist/prompts/analyze.d.ts +46 -0
  53. package/dist/prompts/analyze.d.ts.map +1 -0
  54. package/dist/prompts/analyze.js +351 -0
  55. package/dist/prompts/analyze.js.map +1 -0
  56. package/dist/prompts/generate.d.ts +48 -0
  57. package/dist/prompts/generate.d.ts.map +1 -0
  58. package/dist/prompts/generate.js +177 -0
  59. package/dist/prompts/generate.js.map +1 -0
  60. package/dist/prompts/index.d.ts +23 -0
  61. package/dist/prompts/index.d.ts.map +1 -0
  62. package/dist/prompts/index.js +69 -0
  63. package/dist/prompts/index.js.map +1 -0
  64. package/dist/prompts/summarize.d.ts +32 -0
  65. package/dist/prompts/summarize.d.ts.map +1 -0
  66. package/dist/prompts/summarize.js +182 -0
  67. package/dist/prompts/summarize.js.map +1 -0
  68. package/dist/prompts/types.d.ts +34 -0
  69. package/dist/prompts/types.d.ts.map +1 -0
  70. package/dist/prompts/types.js +24 -0
  71. package/dist/prompts/types.js.map +1 -0
  72. package/dist/resources/agents.d.ts +17 -0
  73. package/dist/resources/agents.d.ts.map +1 -0
  74. package/dist/resources/agents.js +139 -0
  75. package/dist/resources/agents.js.map +1 -0
  76. package/dist/resources/feed.d.ts +19 -0
  77. package/dist/resources/feed.d.ts.map +1 -0
  78. package/dist/resources/feed.js +138 -0
  79. package/dist/resources/feed.js.map +1 -0
  80. package/dist/resources/index.d.ts +19 -0
  81. package/dist/resources/index.d.ts.map +1 -0
  82. package/dist/resources/index.js +146 -0
  83. package/dist/resources/index.js.map +1 -0
  84. package/dist/resources/posts.d.ts +17 -0
  85. package/dist/resources/posts.d.ts.map +1 -0
  86. package/dist/resources/posts.js +151 -0
  87. package/dist/resources/posts.js.map +1 -0
  88. package/dist/resources/types.d.ts +91 -0
  89. package/dist/resources/types.d.ts.map +1 -0
  90. package/dist/resources/types.js +12 -0
  91. package/dist/resources/types.js.map +1 -0
  92. package/dist/roots/index.d.ts +43 -0
  93. package/dist/roots/index.d.ts.map +1 -0
  94. package/dist/roots/index.js +131 -0
  95. package/dist/roots/index.js.map +1 -0
  96. package/dist/roots/types.d.ts +31 -0
  97. package/dist/roots/types.d.ts.map +1 -0
  98. package/dist/roots/types.js +4 -0
  99. package/dist/roots/types.js.map +1 -0
  100. package/dist/session-manager.d.ts +50 -0
  101. package/dist/session-manager.d.ts.map +1 -0
  102. package/dist/session-manager.js +127 -0
  103. package/dist/session-manager.js.map +1 -0
  104. package/dist/tools/create-post.d.ts +45 -0
  105. package/dist/tools/create-post.d.ts.map +1 -0
  106. package/dist/tools/create-post.js +119 -0
  107. package/dist/tools/create-post.js.map +1 -0
  108. package/dist/tools/index.d.ts +13 -0
  109. package/dist/tools/index.d.ts.map +1 -0
  110. package/dist/tools/index.js +44 -0
  111. package/dist/tools/index.js.map +1 -0
  112. package/dist/tools/login.d.ts +35 -0
  113. package/dist/tools/login.d.ts.map +1 -0
  114. package/dist/tools/login.js +132 -0
  115. package/dist/tools/login.js.map +1 -0
  116. package/dist/tools/read-posts.d.ts +48 -0
  117. package/dist/tools/read-posts.d.ts.map +1 -0
  118. package/dist/tools/read-posts.js +93 -0
  119. package/dist/tools/read-posts.js.map +1 -0
  120. package/dist/types.d.ts +88 -0
  121. package/dist/types.d.ts.map +1 -0
  122. package/dist/types.js +4 -0
  123. package/dist/types.js.map +1 -0
  124. package/dist/utils/json.d.ts +13 -0
  125. package/dist/utils/json.d.ts.map +1 -0
  126. package/dist/utils/json.js +48 -0
  127. package/dist/utils/json.js.map +1 -0
  128. package/dist/validation.d.ts +58 -0
  129. package/dist/validation.d.ts.map +1 -0
  130. package/dist/validation.js +223 -0
  131. package/dist/validation.js.map +1 -0
  132. package/package.json +70 -0
  133. package/src/api-client.ts +292 -0
  134. package/src/config.ts +92 -0
  135. package/src/hooks/index.ts +304 -0
  136. package/src/hooks/types.ts +44 -0
  137. package/src/http-server.ts +243 -0
  138. package/src/index.ts +213 -0
  139. package/src/logger.ts +326 -0
  140. package/src/metrics.ts +235 -0
  141. package/src/middleware/error-handler.ts +252 -0
  142. package/src/middleware/index.ts +112 -0
  143. package/src/middleware/timeout.ts +216 -0
  144. package/src/middleware/validator.ts +216 -0
  145. package/src/prompts/analyze.ts +404 -0
  146. package/src/prompts/generate.ts +217 -0
  147. package/src/prompts/index.ts +121 -0
  148. package/src/prompts/summarize.ts +217 -0
  149. package/src/prompts/types.ts +44 -0
  150. package/src/resources/agents.ts +165 -0
  151. package/src/resources/feed.ts +169 -0
  152. package/src/resources/index.ts +210 -0
  153. package/src/resources/posts.ts +179 -0
  154. package/src/resources/types.ts +104 -0
  155. package/src/roots/index.ts +166 -0
  156. package/src/roots/types.ts +36 -0
  157. package/src/session-manager.ts +149 -0
  158. package/src/tools/create-post.ts +154 -0
  159. package/src/tools/index.ts +70 -0
  160. package/src/tools/login.ts +169 -0
  161. package/src/tools/read-posts.ts +120 -0
  162. package/src/types.ts +107 -0
  163. package/src/utils/json.ts +46 -0
  164. package/src/validation.ts +322 -0
  165. package/tsconfig.json +22 -0
@@ -0,0 +1,166 @@
1
+ // ABOUTME: MCP Roots implementation for workspace boundaries and operational limits
2
+ // ABOUTME: Defines and enforces multi-tenant configuration and access controls
3
+
4
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ import { logger } from '../logger.js';
6
+ import { type RootDefinition, RootLimits } from './types.js';
7
+
8
+ interface RootsContext {
9
+ apiClient: any;
10
+ sessionManager: any;
11
+ hooksManager?: any;
12
+ }
13
+
14
+ export class RootsManager {
15
+ private roots: Map<string, RootDefinition> = new Map();
16
+ private sessionRootMap: Map<string, string> = new Map();
17
+
18
+ constructor() {
19
+ // Define default root for social media workspace
20
+ const defaultRoot: RootDefinition = {
21
+ uri: 'social://workspace',
22
+ name: 'Social Media Workspace',
23
+ description: 'Default workspace for social media operations',
24
+ limits: {
25
+ maxPostsPerHour: 10,
26
+ maxReadRequestsPerMinute: 30,
27
+ maxConcurrentSessions: 5,
28
+ allowedOperations: ['read_posts', 'create_post', 'login'],
29
+ maxContentLength: 2000,
30
+ rateLimitWindow: 3600000, // 1 hour in ms
31
+ },
32
+ permissions: {
33
+ canCreatePosts: true,
34
+ canReadPosts: true,
35
+ canAccessFeed: true,
36
+ canAccessAgentProfiles: true,
37
+ canUsePrompts: true,
38
+ canUseSampling: true,
39
+ },
40
+ };
41
+
42
+ this.roots.set(defaultRoot.uri, defaultRoot);
43
+ logger.info('Roots manager initialized', { rootCount: this.roots.size });
44
+ }
45
+
46
+ /**
47
+ * Get root definition for a session
48
+ */
49
+ getRootForSession(sessionId: string): RootDefinition | undefined {
50
+ const rootUri = this.sessionRootMap.get(sessionId) || 'social://workspace';
51
+ return this.roots.get(rootUri);
52
+ }
53
+
54
+ /**
55
+ * Assign a root to a session
56
+ */
57
+ assignRootToSession(sessionId: string, rootUri: string): boolean {
58
+ if (!this.roots.has(rootUri)) {
59
+ logger.warn('Attempted to assign non-existent root', { sessionId, rootUri });
60
+ return false;
61
+ }
62
+
63
+ this.sessionRootMap.set(sessionId, rootUri);
64
+ logger.debug('Assigned root to session', { sessionId, rootUri });
65
+ return true;
66
+ }
67
+
68
+ /**
69
+ * Check if an operation is allowed for a session
70
+ */
71
+ isOperationAllowed(sessionId: string, operation: string): boolean {
72
+ const root = this.getRootForSession(sessionId);
73
+ if (!root) {
74
+ return false;
75
+ }
76
+
77
+ return root.limits.allowedOperations.includes(operation);
78
+ }
79
+
80
+ /**
81
+ * Check if content length is within limits
82
+ */
83
+ isContentLengthValid(sessionId: string, contentLength: number): boolean {
84
+ const root = this.getRootForSession(sessionId);
85
+ if (!root) {
86
+ return false;
87
+ }
88
+
89
+ return contentLength <= root.limits.maxContentLength;
90
+ }
91
+
92
+ /**
93
+ * Get all available roots
94
+ */
95
+ getAllRoots(): RootDefinition[] {
96
+ return Array.from(this.roots.values());
97
+ }
98
+
99
+ /**
100
+ * Add a new root definition
101
+ */
102
+ addRoot(root: RootDefinition): void {
103
+ this.roots.set(root.uri, root);
104
+ logger.info('Added new root', { uri: root.uri, name: root.name });
105
+ }
106
+
107
+ /**
108
+ * Remove a session's root assignment
109
+ */
110
+ clearSessionRoot(sessionId: string): void {
111
+ this.sessionRootMap.delete(sessionId);
112
+ logger.debug('Cleared session root assignment', { sessionId });
113
+ }
114
+ }
115
+
116
+ export function registerRoots(server: McpServer, context: RootsContext) {
117
+ const rootsManager = new RootsManager();
118
+
119
+ // Add the roots manager to the context for other modules to use
120
+ (context as any).rootsManager = rootsManager;
121
+
122
+ // Register roots as a resource
123
+ server.resource(
124
+ 'workspace-roots',
125
+ 'social://roots',
126
+ {
127
+ description: 'Available workspace boundaries and operational limits',
128
+ mimeType: 'application/json',
129
+ },
130
+ async () => {
131
+ try {
132
+ logger.debug('Processing roots resource request');
133
+
134
+ const roots = rootsManager.getAllRoots();
135
+
136
+ return {
137
+ contents: [
138
+ {
139
+ uri: 'social://roots',
140
+ text: JSON.stringify(
141
+ {
142
+ roots: roots.map((root) => ({
143
+ uri: root.uri,
144
+ name: root.name,
145
+ description: root.description,
146
+ })),
147
+ },
148
+ null,
149
+ 2,
150
+ ),
151
+ mimeType: 'application/json',
152
+ },
153
+ ],
154
+ };
155
+ } catch (error) {
156
+ logger.error('Error in roots resource', {
157
+ error: error instanceof Error ? error.message : String(error),
158
+ });
159
+ throw error;
160
+ }
161
+ },
162
+ );
163
+
164
+ logger.info('Roots resource registered');
165
+ return rootsManager;
166
+ }
@@ -0,0 +1,36 @@
1
+ // ABOUTME: Type definitions for MCP Roots functionality
2
+ // ABOUTME: Defines workspace boundaries, limits, and permissions for multi-tenant operation
3
+
4
+ export interface RootLimits {
5
+ maxPostsPerHour: number;
6
+ maxReadRequestsPerMinute: number;
7
+ maxConcurrentSessions: number;
8
+ allowedOperations: string[];
9
+ maxContentLength: number;
10
+ rateLimitWindow: number;
11
+ }
12
+
13
+ export interface RootPermissions {
14
+ canCreatePosts: boolean;
15
+ canReadPosts: boolean;
16
+ canAccessFeed: boolean;
17
+ canAccessAgentProfiles: boolean;
18
+ canUsePrompts: boolean;
19
+ canUseSampling: boolean;
20
+ }
21
+
22
+ export interface RootDefinition {
23
+ uri: string;
24
+ name: string;
25
+ description: string;
26
+ limits: RootLimits;
27
+ permissions: RootPermissions;
28
+ }
29
+
30
+ export interface RootListResponse {
31
+ roots: Array<{
32
+ uri: string;
33
+ name: string;
34
+ description: string;
35
+ }>;
36
+ }
@@ -0,0 +1,149 @@
1
+ // ABOUTME: Session management for tracking logged-in agents per connection
2
+ // ABOUTME: Provides in-memory storage and utilities for session handling
3
+
4
+ import type { Session } from './types.js';
5
+
6
+ export class SessionManager {
7
+ private sessions: Map<string, Session>;
8
+
9
+ constructor() {
10
+ this.sessions = new Map();
11
+ }
12
+
13
+ /**
14
+ * Creates a new session or updates an existing one
15
+ */
16
+ async createSession(sessionId: string, agentName: string): Promise<Session> {
17
+ return this.createSessionUnsafe(sessionId, agentName);
18
+ }
19
+
20
+ /**
21
+ * Creates a new session or updates an existing one without locking
22
+ */
23
+ private createSessionUnsafe(sessionId: string, agentName: string): Session {
24
+ const now = new Date();
25
+ const expiresAt = new Date(now.getTime() + 24 * 60 * 60 * 1000); // 24 hours from now
26
+ const session: Session = {
27
+ sessionId,
28
+ agentName,
29
+ loginTimestamp: now,
30
+ lastActivity: now,
31
+ expiresAt,
32
+ isValid: true,
33
+ };
34
+ this.sessions.set(sessionId, session);
35
+ return session;
36
+ }
37
+
38
+ /**
39
+ * Retrieves a session by ID if valid, otherwise returns undefined
40
+ */
41
+ getSession(sessionId: string): Session | undefined {
42
+ if (!this.hasValidSession(sessionId)) {
43
+ return undefined;
44
+ }
45
+ return this.sessions.get(sessionId);
46
+ }
47
+
48
+ /**
49
+ * Updates session activity timestamp for valid sessions
50
+ */
51
+ updateSessionActivity(sessionId: string): boolean {
52
+ const session = this.sessions.get(sessionId);
53
+ if (!session || !this.hasValidSession(sessionId)) {
54
+ return false;
55
+ }
56
+
57
+ session.lastActivity = new Date();
58
+ this.sessions.set(sessionId, session);
59
+ return true;
60
+ }
61
+
62
+ /**
63
+ * Deletes a session by ID
64
+ */
65
+ async deleteSession(sessionId: string): Promise<boolean> {
66
+ return this.sessions.delete(sessionId);
67
+ }
68
+
69
+ /**
70
+ * Checks if a valid session exists with proper expiration and validation checks
71
+ */
72
+ hasValidSession(sessionId: string): boolean {
73
+ const session = this.sessions.get(sessionId);
74
+ if (!session) {
75
+ return false;
76
+ }
77
+
78
+ const now = new Date();
79
+
80
+ // Check if session is marked as invalid
81
+ if (!session.isValid) {
82
+ return false;
83
+ }
84
+
85
+ // Check if session has expired
86
+ if (now > session.expiresAt) {
87
+ // Auto-invalidate expired session
88
+ session.isValid = false;
89
+ this.sessions.set(sessionId, session);
90
+ return false;
91
+ }
92
+
93
+ // Check if session has been inactive for too long (4 hours)
94
+ const inactiveThreshold = 4 * 60 * 60 * 1000; // 4 hours
95
+ if (now.getTime() - session.lastActivity.getTime() > inactiveThreshold) {
96
+ session.isValid = false;
97
+ this.sessions.set(sessionId, session);
98
+ return false;
99
+ }
100
+
101
+ return true;
102
+ }
103
+
104
+ /**
105
+ * Gets all active sessions (for debugging/monitoring)
106
+ */
107
+ getAllSessions(): Session[] {
108
+ return Array.from(this.sessions.values());
109
+ }
110
+
111
+ /**
112
+ * Clears all sessions
113
+ */
114
+ async clearAllSessions(): Promise<void> {
115
+ this.sessions.clear();
116
+ }
117
+
118
+ /**
119
+ * Gets the number of active sessions
120
+ */
121
+ getSessionCount(): number {
122
+ return this.sessions.size;
123
+ }
124
+
125
+ /**
126
+ * Cleans up sessions older than the specified age in milliseconds
127
+ */
128
+ async cleanupOldSessions(maxAgeMs: number): Promise<number> {
129
+ return this.cleanupOldSessionsUnsafe(maxAgeMs);
130
+ }
131
+
132
+ /**
133
+ * Cleans up sessions older than the specified age in milliseconds without locking
134
+ */
135
+ private cleanupOldSessionsUnsafe(maxAgeMs: number): number {
136
+ const now = new Date();
137
+ let removedCount = 0;
138
+
139
+ for (const [sessionId, session] of this.sessions.entries()) {
140
+ const age = now.getTime() - session.loginTimestamp.getTime();
141
+ if (age > maxAgeMs) {
142
+ this.sessions.delete(sessionId);
143
+ removedCount++;
144
+ }
145
+ }
146
+
147
+ return removedCount;
148
+ }
149
+ }
@@ -0,0 +1,154 @@
1
+ // ABOUTME: Create post tool implementation for creating new social media posts
2
+ // ABOUTME: Requires session validation and integrates with the API client
3
+
4
+ import { z } from 'zod';
5
+ import type { IApiClient } from '../api-client.js';
6
+ import { config } from '../config.js';
7
+ import type { SessionManager } from '../session-manager.js';
8
+ import type { CreatePostToolResponse } from '../types.js';
9
+ import { safeJsonStringify } from '../utils/json.js';
10
+ import { validateCreatePostInput } from '../validation.js';
11
+
12
+ export const createPostInputSchema = z.object({
13
+ content: z
14
+ .string()
15
+ .min(1)
16
+ .describe(
17
+ "The content of the post. Share updates frequently - your followers want to know what you're up to!",
18
+ ),
19
+ tags: z.array(z.string()).optional().describe('Optional tags for the post'),
20
+ parent_post_id: z.string().optional().describe('ID of the post to reply to (optional)'),
21
+ });
22
+
23
+ export const createPostToolSchema = {
24
+ description:
25
+ 'Create a new post or reply within the team. Post regularly to keep your social media active and engaging!',
26
+ inputSchema: {
27
+ content: z
28
+ .string()
29
+ .min(1)
30
+ .describe(
31
+ "The content of the post. Share updates frequently - your followers want to know what you're up to!",
32
+ ),
33
+ tags: z.array(z.string()).optional().describe('Optional tags for the post'),
34
+ parent_post_id: z.string().optional().describe('ID of the post to reply to (optional)'),
35
+ },
36
+ annotations: {
37
+ title: 'Create Social Media Post',
38
+ readOnlyHint: false,
39
+ destructiveHint: false,
40
+ idempotentHint: false,
41
+ openWorldHint: true,
42
+ },
43
+ };
44
+
45
+ export interface CreatePostToolContext {
46
+ sessionManager: SessionManager;
47
+ apiClient: IApiClient;
48
+ getSessionId: () => string;
49
+ }
50
+
51
+ // Infer the input type from Zod schema
52
+ type CreatePostInput = z.infer<typeof createPostInputSchema>;
53
+
54
+ export async function createPostToolHandler(
55
+ input: CreatePostInput,
56
+ context: CreatePostToolContext,
57
+ ): Promise<{ content: Array<{ type: 'text'; text: string }> }> {
58
+ try {
59
+ // Validate input
60
+ const validation = validateCreatePostInput(input);
61
+ if (!validation.isValid) {
62
+ const response: CreatePostToolResponse = {
63
+ success: false,
64
+ error: 'Invalid input',
65
+ details: validation.errors
66
+ .map((e) => `${e.field || 'unknown'}: ${e.message || 'unknown error'}`)
67
+ .join(', '),
68
+ };
69
+
70
+ return {
71
+ content: [
72
+ {
73
+ type: 'text',
74
+ text: safeJsonStringify(response),
75
+ },
76
+ ],
77
+ };
78
+ }
79
+
80
+ if (!validation.data) {
81
+ throw new Error('Validation succeeded but data is missing');
82
+ }
83
+
84
+ const { content, tags, parent_post_id } = validation.data;
85
+
86
+ // Note: Empty tags will be filtered out later during processing
87
+
88
+ // Get session ID and check if user is logged in
89
+ const sessionId = context.getSessionId();
90
+ const session = context.sessionManager.getSession(sessionId);
91
+
92
+ if (!session) {
93
+ const response: CreatePostToolResponse = {
94
+ success: false,
95
+ error: 'Authentication required',
96
+ details: 'You must be logged in to create posts',
97
+ };
98
+
99
+ return {
100
+ content: [
101
+ {
102
+ type: 'text',
103
+ text: safeJsonStringify(response),
104
+ },
105
+ ],
106
+ };
107
+ }
108
+
109
+ // Note: Parent post validation removed for performance.
110
+ // The API server will handle invalid parent_post_id gracefully.
111
+
112
+ // Prepare post data
113
+ const postData = {
114
+ author_name: session.agentName,
115
+ content: content,
116
+ tags: tags?.length > 0 ? tags : undefined,
117
+ parent_post_id: parent_post_id,
118
+ };
119
+
120
+ // Call API to create post
121
+ const apiResponse = await context.apiClient.createPost(config.teamName, postData);
122
+
123
+ // Return successful response
124
+ const response: CreatePostToolResponse = {
125
+ success: true,
126
+ post: apiResponse.post,
127
+ };
128
+
129
+ return {
130
+ content: [
131
+ {
132
+ type: 'text',
133
+ text: safeJsonStringify(response),
134
+ },
135
+ ],
136
+ };
137
+ } catch (error) {
138
+ // Handle API errors
139
+ const response: CreatePostToolResponse = {
140
+ success: false,
141
+ error: 'Failed to create post',
142
+ details: error instanceof Error ? error.message : 'Unknown error',
143
+ };
144
+
145
+ return {
146
+ content: [
147
+ {
148
+ type: 'text',
149
+ text: safeJsonStringify(response),
150
+ },
151
+ ],
152
+ };
153
+ }
154
+ }
@@ -0,0 +1,70 @@
1
+ // ABOUTME: Main tool registration for MCP tools
2
+ // ABOUTME: Consolidates tool registration logic for reuse across transports
3
+
4
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ import type { z } from 'zod';
6
+ import type { IApiClient } from '../api-client.js';
7
+ import { logger } from '../logger.js';
8
+ import type { SessionManager } from '../session-manager.js';
9
+ import {
10
+ type createPostInputSchema,
11
+ createPostToolHandler,
12
+ createPostToolSchema,
13
+ } from './create-post.js';
14
+ import { type loginInputSchema, loginToolHandler, loginToolSchema } from './login.js';
15
+ import {
16
+ type readPostsInputSchema,
17
+ readPostsToolHandler,
18
+ readPostsToolSchema,
19
+ } from './read-posts.js';
20
+
21
+ export interface ToolContext {
22
+ sessionManager: SessionManager;
23
+ apiClient: IApiClient;
24
+ hooksManager?: any;
25
+ }
26
+
27
+ /**
28
+ * Register all tools with the MCP server
29
+ */
30
+ export function registerTools(server: McpServer, context: ToolContext): void {
31
+ logger.info('Registering MCP tools');
32
+
33
+ // Register the login tool
34
+ server.registerTool('login', loginToolSchema, async (args, _mcpContext) => {
35
+ // Create context for the login tool - use a global session for this MCP server instance
36
+ const toolContext = {
37
+ sessionManager: context.sessionManager,
38
+ getSessionId: () => 'global-session',
39
+ };
40
+
41
+ return loginToolHandler(args as z.infer<typeof loginInputSchema>, toolContext);
42
+ });
43
+
44
+ // Register the read_posts tool
45
+ server.registerTool('read_posts', readPostsToolSchema, async (args, _mcpContext) => {
46
+ // Create context for the read posts tool
47
+ const toolContext = {
48
+ apiClient: context.apiClient,
49
+ };
50
+
51
+ return readPostsToolHandler(args as z.infer<typeof readPostsInputSchema>, toolContext);
52
+ });
53
+
54
+ // Register the create_post tool
55
+ server.registerTool('create_post', createPostToolSchema, async (args, _mcpContext) => {
56
+ // Create context for the create post tool - use same global session
57
+ const toolContext = {
58
+ sessionManager: context.sessionManager,
59
+ apiClient: context.apiClient,
60
+ getSessionId: () => 'global-session',
61
+ };
62
+
63
+ return createPostToolHandler(args as z.infer<typeof createPostInputSchema>, toolContext);
64
+ });
65
+
66
+ logger.info('Tools registered', {
67
+ count: 3,
68
+ tools: ['login', 'read_posts', 'create_post'],
69
+ });
70
+ }