agent-planner-mcp 0.3.1 → 0.5.1
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/AGENT_GUIDE.md +257 -0
- package/LICENSE +21 -0
- package/README.md +128 -247
- package/SKILL.md +438 -0
- package/package.json +24 -7
- package/src/api-client.js +506 -115
- package/src/index.js +60 -27
- package/src/integrations/search-integration.js +3 -5
- package/src/server-http.js +569 -0
- package/src/session-manager.js +223 -0
- package/src/setup.js +1 -1
- package/src/tools/search-wrapper.js +12 -6
- package/src/tools.js +1983 -159
- package/claude-code/AUTONOMOUS_EXECUTION_GUIDE.md +0 -335
- package/claude-code/commands/README.md +0 -112
- package/claude-code/commands/create-plan.md +0 -174
- package/claude-code/commands/execute-plan.md +0 -202
- package/claude-code/commands/plan-status.md +0 -145
- package/claude-code/settings.template.json +0 -12
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Manager for MCP HTTP Server
|
|
3
|
+
*
|
|
4
|
+
* Manages client sessions for the Streamable HTTP transport.
|
|
5
|
+
* Each session tracks:
|
|
6
|
+
* - Unique session ID (Mcp-Session-Id header)
|
|
7
|
+
* - Initialization state
|
|
8
|
+
* - Client capabilities
|
|
9
|
+
* - Creation and last activity timestamps
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { randomUUID } = require('crypto');
|
|
13
|
+
|
|
14
|
+
class SessionManager {
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
// In-memory session storage
|
|
17
|
+
this.sessions = new Map();
|
|
18
|
+
|
|
19
|
+
// Configuration
|
|
20
|
+
this.sessionTimeout = options.sessionTimeout || 30 * 60 * 1000; // 30 minutes default
|
|
21
|
+
this.cleanupInterval = options.cleanupInterval || 5 * 60 * 1000; // 5 minutes default
|
|
22
|
+
|
|
23
|
+
// Start periodic cleanup
|
|
24
|
+
this.startCleanup();
|
|
25
|
+
|
|
26
|
+
console.error('SessionManager initialized');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create a new session
|
|
31
|
+
* @returns {string} Session ID
|
|
32
|
+
*/
|
|
33
|
+
createSession() {
|
|
34
|
+
const sessionId = randomUUID();
|
|
35
|
+
|
|
36
|
+
const session = {
|
|
37
|
+
id: sessionId,
|
|
38
|
+
initialized: false,
|
|
39
|
+
clientCapabilities: null,
|
|
40
|
+
createdAt: Date.now(),
|
|
41
|
+
lastActivityAt: Date.now()
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
this.sessions.set(sessionId, session);
|
|
45
|
+
|
|
46
|
+
console.error(`Session created: ${sessionId}`);
|
|
47
|
+
return sessionId;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get a session by ID
|
|
52
|
+
* @param {string} sessionId - Session ID
|
|
53
|
+
* @returns {Object|null} Session object or null if not found
|
|
54
|
+
*/
|
|
55
|
+
getSession(sessionId) {
|
|
56
|
+
if (!sessionId) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const session = this.sessions.get(sessionId);
|
|
61
|
+
|
|
62
|
+
if (!session) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Update last activity
|
|
67
|
+
session.lastActivityAt = Date.now();
|
|
68
|
+
|
|
69
|
+
return session;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Mark a session as initialized
|
|
74
|
+
* @param {string} sessionId - Session ID
|
|
75
|
+
* @param {Object} clientCapabilities - Client capabilities from initialize request
|
|
76
|
+
*/
|
|
77
|
+
initializeSession(sessionId, clientCapabilities) {
|
|
78
|
+
const session = this.sessions.get(sessionId);
|
|
79
|
+
|
|
80
|
+
if (!session) {
|
|
81
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
session.initialized = true;
|
|
85
|
+
session.clientCapabilities = clientCapabilities;
|
|
86
|
+
session.lastActivityAt = Date.now();
|
|
87
|
+
|
|
88
|
+
console.error(`Session initialized: ${sessionId}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Store a per-session API client
|
|
93
|
+
* @param {string} sessionId - Session ID
|
|
94
|
+
* @param {Object} apiClient - API client instance bound to user's token
|
|
95
|
+
*/
|
|
96
|
+
setApiClient(sessionId, apiClient) {
|
|
97
|
+
const session = this.sessions.get(sessionId);
|
|
98
|
+
if (session) {
|
|
99
|
+
session.apiClient = apiClient;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get the per-session API client
|
|
105
|
+
* @param {string} sessionId - Session ID
|
|
106
|
+
* @returns {Object|null} API client or null
|
|
107
|
+
*/
|
|
108
|
+
getApiClient(sessionId) {
|
|
109
|
+
const session = this.sessions.get(sessionId);
|
|
110
|
+
return session?.apiClient || null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Check if a session is initialized
|
|
115
|
+
* @param {string} sessionId - Session ID
|
|
116
|
+
* @returns {boolean} True if initialized
|
|
117
|
+
*/
|
|
118
|
+
isInitialized(sessionId) {
|
|
119
|
+
const session = this.sessions.get(sessionId);
|
|
120
|
+
return session && session.initialized;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Delete a session
|
|
125
|
+
* @param {string} sessionId - Session ID
|
|
126
|
+
* @returns {boolean} True if session was deleted
|
|
127
|
+
*/
|
|
128
|
+
deleteSession(sessionId) {
|
|
129
|
+
const deleted = this.sessions.delete(sessionId);
|
|
130
|
+
|
|
131
|
+
if (deleted) {
|
|
132
|
+
console.error(`Session deleted: ${sessionId}`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return deleted;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Clean up expired sessions
|
|
140
|
+
*/
|
|
141
|
+
cleanupExpiredSessions() {
|
|
142
|
+
const now = Date.now();
|
|
143
|
+
let cleanedCount = 0;
|
|
144
|
+
|
|
145
|
+
for (const [sessionId, session] of this.sessions.entries()) {
|
|
146
|
+
const age = now - session.lastActivityAt;
|
|
147
|
+
|
|
148
|
+
if (age > this.sessionTimeout) {
|
|
149
|
+
this.sessions.delete(sessionId);
|
|
150
|
+
cleanedCount++;
|
|
151
|
+
console.error(`Session expired: ${sessionId} (inactive for ${Math.round(age / 1000)}s)`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (cleanedCount > 0) {
|
|
156
|
+
console.error(`Cleaned up ${cleanedCount} expired sessions`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Start periodic cleanup of expired sessions
|
|
162
|
+
*/
|
|
163
|
+
startCleanup() {
|
|
164
|
+
this.cleanupTimer = setInterval(() => {
|
|
165
|
+
this.cleanupExpiredSessions();
|
|
166
|
+
}, this.cleanupInterval);
|
|
167
|
+
|
|
168
|
+
// Prevent the timer from keeping the process alive
|
|
169
|
+
this.cleanupTimer.unref();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Stop periodic cleanup
|
|
174
|
+
*/
|
|
175
|
+
stopCleanup() {
|
|
176
|
+
if (this.cleanupTimer) {
|
|
177
|
+
clearInterval(this.cleanupTimer);
|
|
178
|
+
this.cleanupTimer = null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get session statistics
|
|
184
|
+
* @returns {Object} Statistics object
|
|
185
|
+
*/
|
|
186
|
+
getStats() {
|
|
187
|
+
const now = Date.now();
|
|
188
|
+
const stats = {
|
|
189
|
+
total: this.sessions.size,
|
|
190
|
+
initialized: 0,
|
|
191
|
+
uninitialized: 0,
|
|
192
|
+
sessions: []
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
for (const [sessionId, session] of this.sessions.entries()) {
|
|
196
|
+
if (session.initialized) {
|
|
197
|
+
stats.initialized++;
|
|
198
|
+
} else {
|
|
199
|
+
stats.uninitialized++;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
stats.sessions.push({
|
|
203
|
+
id: sessionId,
|
|
204
|
+
initialized: session.initialized,
|
|
205
|
+
age: Math.round((now - session.createdAt) / 1000),
|
|
206
|
+
idleTime: Math.round((now - session.lastActivityAt) / 1000)
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return stats;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Destroy the session manager
|
|
215
|
+
*/
|
|
216
|
+
destroy() {
|
|
217
|
+
this.stopCleanup();
|
|
218
|
+
this.sessions.clear();
|
|
219
|
+
console.error('SessionManager destroyed');
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
module.exports = { SessionManager };
|
package/src/setup.js
CHANGED
|
@@ -124,7 +124,7 @@ function createEnvFile(config) {
|
|
|
124
124
|
API_URL=${config.apiUrl}
|
|
125
125
|
USER_API_TOKEN=${config.token}
|
|
126
126
|
MCP_SERVER_NAME=planning-system
|
|
127
|
-
MCP_SERVER_VERSION
|
|
127
|
+
MCP_SERVER_VERSION=${require('../package.json').version}
|
|
128
128
|
NODE_ENV=production
|
|
129
129
|
`;
|
|
130
130
|
|
|
@@ -136,7 +136,7 @@ async function globalSearch(query) {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
// Check if response has categorized results (plans, nodes, etc.)
|
|
139
|
-
const categories = ['plans', 'nodes', 'comments', 'logs'
|
|
139
|
+
const categories = ['plans', 'nodes', 'comments', 'logs'];
|
|
140
140
|
const allResults = [];
|
|
141
141
|
|
|
142
142
|
categories.forEach(category => {
|
|
@@ -156,9 +156,11 @@ async function globalSearch(query) {
|
|
|
156
156
|
return allResults;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
// Generic handler for any object with arrays
|
|
160
|
-
Object.keys(response).
|
|
161
|
-
|
|
159
|
+
// Generic handler for any object with arrays - log warning about unexpected format
|
|
160
|
+
const unknownKeys = Object.keys(response).filter(key => Array.isArray(response[key]) && key !== 'results');
|
|
161
|
+
if (unknownKeys.length > 0) {
|
|
162
|
+
console.error(`[search-wrapper] Warning: Unexpected response format with keys: ${unknownKeys.join(', ')}. Normalizing results.`);
|
|
163
|
+
unknownKeys.forEach(key => {
|
|
162
164
|
response[key].forEach(item => {
|
|
163
165
|
allResults.push({
|
|
164
166
|
...item,
|
|
@@ -167,8 +169,12 @@ async function globalSearch(query) {
|
|
|
167
169
|
source: 'global_search'
|
|
168
170
|
});
|
|
169
171
|
});
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (allResults.length === 0 && Object.keys(response).length > 0) {
|
|
176
|
+
console.error(`[search-wrapper] Warning: Could not extract results from response. Keys: ${Object.keys(response).join(', ')}`);
|
|
177
|
+
}
|
|
172
178
|
|
|
173
179
|
return allResults;
|
|
174
180
|
} catch (error) {
|