miadi 1.0.14

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 (53) hide show
  1. package/.env.example +28 -0
  2. package/ARCHITECTURE.md +290 -0
  3. package/CLAUDE.md +269 -0
  4. package/GEMINI.md +80 -0
  5. package/MCP_CONNECTOR_READY.md +219 -0
  6. package/MCP_LEARNING_NOTES.md +178 -0
  7. package/MCP_REBUILD_PLAN.md +159 -0
  8. package/MCP_REMOTE_SERVER_SPEC.md +373 -0
  9. package/MIA.md +344 -0
  10. package/MIETTE.md +195 -0
  11. package/README.md +264 -0
  12. package/REMOTE_MCP_TRANSFORMATION_GUIDE.md +384 -0
  13. package/STATUS.md +191 -0
  14. package/TOOL_SELECTION_PLAN.md +340 -0
  15. package/WAKE_UP_SUMMARY.md +102 -0
  16. package/__PUBLISH.sh +1 -0
  17. package/book/_/ledgers/ledger_miadi_mcp_analysis_250730.md +0 -0
  18. package/conversations/2507301433.claude.issue.11.2025-07-30-this-mcp-is-not-working-another-instance-of-yours.txt +756 -0
  19. package/conversations/2507301601.cursor.reverse_engineer_mcp_service_for.md +808 -0
  20. package/conversations/2508050125.llmcon.claude.MIADI_TOOLS-implement-what-is-in-toolselectionplanmd.txt +1235 -0
  21. package/conversations/2508051939.llmcon.claude.issue-14.TransitionToPlanningIT.implement-what-is-in-toolselectionplanmd.txt +1424 -0
  22. package/conversations/2508082352.llmcon.claude.MCP-Remote-Take-II.txt +1658 -0
  23. package/dist/index-remote.js +54736 -0
  24. package/dist/index.js +32363 -0
  25. package/mcp.sample.json +14 -0
  26. package/openapi.yml +2161 -0
  27. package/package.json +56 -0
  28. package/research/MCP_Research_Perplexity_2508060045.md +410 -0
  29. package/samples/README.md +2 -0
  30. package/scripts/ngrokserve.sh +6 -0
  31. package/scripts/start-remote.sh +141 -0
  32. package/scripts/start-with-ngrok.sh +140 -0
  33. package/src/api-client.ts +254 -0
  34. package/src/index-remote.ts +406 -0
  35. package/src/index-simple.ts +232 -0
  36. package/src/index.ts +510 -0
  37. package/src/tool-registry.ts +223 -0
  38. package/src/tools/ai-tools.ts +69 -0
  39. package/src/tools/capability-tools.ts +79 -0
  40. package/src/tools/forge-tools.ts +51 -0
  41. package/src/tools/memory-tools.ts +137 -0
  42. package/src/tools/session-tools.ts +135 -0
  43. package/src/tools/workflow-tools.ts +65 -0
  44. package/src/types.ts +291 -0
  45. package/src/utils.ts +279 -0
  46. package/tests/quick-test.sh +116 -0
  47. package/tests/run-all-tests.sh +167 -0
  48. package/tests/test-agent-capabilities.sh +364 -0
  49. package/tests/test-connectivity.sh +90 -0
  50. package/tests/test-memory-operations.sh +236 -0
  51. package/tests/test-session-management.sh +320 -0
  52. package/tests/test-tool-discovery.sh +151 -0
  53. package/tsconfig.json +24 -0
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Tool Registry System for Miadi MCP Server
3
+ *
4
+ * Provides environment variable-based tool selection to allow selective exposure
5
+ * of MCP tools based on deployment requirements, security constraints, and use cases.
6
+ */
7
+
8
+ import { z } from "zod";
9
+
10
+ /**
11
+ * Definition of a tool that can be registered with the MCP server
12
+ */
13
+ export interface ToolDefinition {
14
+ name: string;
15
+ category: string;
16
+ description: string;
17
+ schema: Record<string, z.ZodType<any>>;
18
+ handler: (args: any) => Promise<any>;
19
+ dependencies?: string[];
20
+ }
21
+
22
+ /**
23
+ * Configuration for tool registry behavior
24
+ */
25
+ export interface ToolRegistryConfig {
26
+ enabledCategories?: string[];
27
+ enabledTools?: string[];
28
+ disabledCategories?: string[];
29
+ disabledTools?: string[];
30
+ defaultEnabled?: boolean;
31
+ }
32
+
33
+ /**
34
+ * Category mappings for tool organization
35
+ */
36
+ export const TOOL_CATEGORIES = {
37
+ memory: [
38
+ 'miadi-get-memory', 'miadi-store-memory', 'miadi-update-memory-ttl',
39
+ 'miadi-get-memory-meta', 'miadi-scan-keys', 'miadi-gather-memory-values',
40
+ 'miadi-collect-memory', 'miadi-view-key-content', 'miadi-search-cluster'
41
+ ],
42
+ session: [
43
+ 'miadi-start-session', 'miadi-get-current-session', 'miadi-switch-mode',
44
+ 'miadi-switch-persona', 'miadi-end-session', 'miadi-list-sessions'
45
+ ],
46
+ capability: [
47
+ 'miadi-resolve-capabilities', 'miadi-get-agent-info', 'miadi-detect-cues'
48
+ ],
49
+ ai: ['miadi-openai-request', 'miadi-ai-request'],
50
+ workflow: [
51
+ 'miadi-register-agent', 'miadi-get-agent-events', 'miadi-get-workflow-howto'
52
+ ],
53
+ forge: [
54
+ 'miadi-get-forge-state', 'miadi-update-forge-state', 'miadi-get-glyph-map'
55
+ ]
56
+ };
57
+
58
+ /**
59
+ * Registry for managing MCP tool registration with environment-based selection
60
+ */
61
+ export class ToolRegistry {
62
+ private tools: Map<string, ToolDefinition> = new Map();
63
+ private config: ToolRegistryConfig;
64
+
65
+ constructor(config: ToolRegistryConfig) {
66
+ this.config = config;
67
+ this.validateConfig();
68
+ }
69
+
70
+ /**
71
+ * Register a tool with the registry
72
+ */
73
+ register(tool: ToolDefinition): void {
74
+ this.tools.set(tool.name, tool);
75
+ }
76
+
77
+ /**
78
+ * Check if a specific tool should be enabled based on configuration
79
+ */
80
+ isToolEnabled(toolName: string): boolean {
81
+ const tool = this.tools.get(toolName);
82
+ if (!tool) return false;
83
+
84
+ // Check category-level enablement
85
+ const categoryEnabled = this.isCategoryEnabled(tool.category);
86
+ const toolSpecificallyDisabled = this.config.disabledTools?.includes(toolName);
87
+ const toolSpecificallyEnabled = this.config.enabledTools?.includes(toolName);
88
+
89
+ // Priority: specific tool settings > category settings > default
90
+ if (toolSpecificallyEnabled) return true;
91
+ if (toolSpecificallyDisabled) return false;
92
+ return categoryEnabled;
93
+ }
94
+
95
+ /**
96
+ * Check if a category should be enabled based on configuration
97
+ */
98
+ private isCategoryEnabled(category: string): boolean {
99
+ // If specific tools are enabled but no categories are specified,
100
+ // default to false for category-based enablement
101
+ if (this.config.enabledTools && !this.config.enabledCategories) {
102
+ return false;
103
+ }
104
+
105
+ if (this.config.enabledCategories) {
106
+ return this.config.enabledCategories.includes(category);
107
+ }
108
+ if (this.config.disabledCategories) {
109
+ return !this.config.disabledCategories.includes(category);
110
+ }
111
+ return this.config.defaultEnabled ?? true;
112
+ }
113
+
114
+ /**
115
+ * Get all enabled tools
116
+ */
117
+ getEnabledTools(): ToolDefinition[] {
118
+ return Array.from(this.tools.values()).filter(tool =>
119
+ this.isToolEnabled(tool.name)
120
+ );
121
+ }
122
+
123
+ /**
124
+ * Get all registered tools (regardless of enabled status)
125
+ */
126
+ getAllTools(): ToolDefinition[] {
127
+ return Array.from(this.tools.values());
128
+ }
129
+
130
+ /**
131
+ * Validate the registry configuration
132
+ */
133
+ validateConfig(): void {
134
+ const validCategories = Object.keys(TOOL_CATEGORIES);
135
+
136
+ const checkCategories = (categories?: string[], context: string = '') => {
137
+ categories?.forEach(cat => {
138
+ if (!validCategories.includes(cat)) {
139
+ console.error(`⚠️ Unknown category in ${context}: ${cat}`);
140
+ console.error(` Valid categories: ${validCategories.join(', ')}`);
141
+ }
142
+ });
143
+ };
144
+
145
+ checkCategories(this.config.enabledCategories, 'enabledCategories');
146
+ checkCategories(this.config.disabledCategories, 'disabledCategories');
147
+
148
+ // Check for conflicting configuration
149
+ if (this.config.enabledCategories && this.config.disabledCategories) {
150
+ const conflict = this.config.enabledCategories.find(cat =>
151
+ this.config.disabledCategories?.includes(cat)
152
+ );
153
+ if (conflict) {
154
+ console.error(`⚠️ Category '${conflict}' is both enabled and disabled`);
155
+ }
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Register all enabled tools with the MCP server
161
+ */
162
+ registerAllEnabledTools(server: any): void {
163
+ const enabledTools = this.getEnabledTools();
164
+ const totalTools = this.tools.size;
165
+
166
+ console.error(`🔧 Tool Registry Configuration:`);
167
+ console.error(` Total tools available: ${totalTools}`);
168
+ console.error(` Tools to register: ${enabledTools.length}`);
169
+
170
+ if (enabledTools.length < totalTools) {
171
+ const disabledCount = totalTools - enabledTools.length;
172
+ console.error(` Tools disabled: ${disabledCount}`);
173
+ }
174
+
175
+ // Group enabled tools by category for logging
176
+ const enabledByCategory: Record<string, string[]> = {};
177
+ enabledTools.forEach(tool => {
178
+ if (!enabledByCategory[tool.category]) {
179
+ enabledByCategory[tool.category] = [];
180
+ }
181
+ enabledByCategory[tool.category].push(tool.name);
182
+ });
183
+
184
+ console.error(`📋 Enabled tool categories:`);
185
+ Object.entries(enabledByCategory).forEach(([category, tools]) => {
186
+ console.error(` ${category}: ${tools.length} tools`);
187
+ });
188
+
189
+ enabledTools.forEach(tool => {
190
+ server.tool(tool.name, tool.description, tool.schema, tool.handler);
191
+ });
192
+
193
+ console.error(`✅ ${enabledTools.length} tools registered successfully`);
194
+ }
195
+
196
+ /**
197
+ * Get configuration summary for debugging
198
+ */
199
+ getConfigSummary(): string {
200
+ const lines = ['Tool Registry Configuration:'];
201
+
202
+ if (this.config.enabledCategories) {
203
+ lines.push(` Enabled Categories: ${this.config.enabledCategories.join(', ')}`);
204
+ }
205
+ if (this.config.disabledCategories) {
206
+ lines.push(` Disabled Categories: ${this.config.disabledCategories.join(', ')}`);
207
+ }
208
+ if (this.config.enabledTools) {
209
+ lines.push(` Enabled Tools: ${this.config.enabledTools.join(', ')}`);
210
+ }
211
+ if (this.config.disabledTools) {
212
+ lines.push(` Disabled Tools: ${this.config.disabledTools.join(', ')}`);
213
+ }
214
+
215
+ lines.push(` Default Enabled: ${this.config.defaultEnabled ?? true}`);
216
+
217
+ const enabledCount = this.getEnabledTools().length;
218
+ const totalCount = this.tools.size;
219
+ lines.push(` Result: ${enabledCount}/${totalCount} tools enabled`);
220
+
221
+ return lines.join('\n');
222
+ }
223
+ }
@@ -0,0 +1,69 @@
1
+ import { miadiApi } from '../api-client';
2
+ import {
3
+ OpenAIRequest,
4
+ OpenAIResponse,
5
+ AIRequest,
6
+ AIResponse
7
+ } from '../types';
8
+
9
+ export const aiTools = {
10
+ /**
11
+ * Make a request to OpenAI API through Miadi system
12
+ */
13
+ openaiRequest: async (
14
+ prompt: string,
15
+ modelId?: string,
16
+ maxTokens?: number,
17
+ temperature?: number,
18
+ stream?: boolean
19
+ ): Promise<OpenAIResponse> => {
20
+ try {
21
+ if (!prompt) {
22
+ throw new Error('Prompt is required');
23
+ }
24
+
25
+ const requestBody: OpenAIRequest = {
26
+ prompt
27
+ };
28
+
29
+ if (modelId) requestBody.modelId = modelId;
30
+ if (maxTokens !== undefined) requestBody.maxTokens = maxTokens;
31
+ if (temperature !== undefined) requestBody.temperature = temperature;
32
+ if (stream !== undefined) requestBody.stream = stream;
33
+
34
+ return await miadiApi.openaiRequest(requestBody);
35
+ } catch (error: any) {
36
+ throw new Error(`Failed to make OpenAI request: ${error.message}`);
37
+ }
38
+ },
39
+
40
+ /**
41
+ * Make a generic AI request through Miadi system
42
+ */
43
+ aiRequest: async (
44
+ prompt: string,
45
+ modelId?: string,
46
+ maxTokens?: number,
47
+ temperature?: number
48
+ ): Promise<AIResponse> => {
49
+ try {
50
+ if (!prompt) {
51
+ throw new Error('Prompt is required');
52
+ }
53
+
54
+ const requestBody: AIRequest = {
55
+ prompt
56
+ };
57
+
58
+ if (modelId) requestBody.modelId = modelId;
59
+ if (maxTokens !== undefined) requestBody.maxTokens = maxTokens;
60
+ if (temperature !== undefined) requestBody.temperature = temperature;
61
+
62
+ return await miadiApi.aiRequest(requestBody);
63
+ } catch (error: any) {
64
+ throw new Error(`Failed to make AI request: ${error.message}`);
65
+ }
66
+ }
67
+ };
68
+
69
+ export default aiTools;
@@ -0,0 +1,79 @@
1
+ import { miadiApi } from '../api-client';
2
+ import {
3
+ CapabilityResolveRequest,
4
+ CapabilityResolveResponse,
5
+ AgentInfoResponse,
6
+ CueDetectRequest,
7
+ CueDetectResponse,
8
+ ContextVariable,
9
+ AgentSession
10
+ } from '../types';
11
+
12
+ export const capabilityTools = {
13
+ /**
14
+ * Resolve capabilities for persona/mode combination
15
+ */
16
+ resolveCapabilities: async (
17
+ persona: string,
18
+ mode: string,
19
+ context?: ContextVariable[]
20
+ ): Promise<CapabilityResolveResponse> => {
21
+ try {
22
+ if (!persona || !mode) {
23
+ throw new Error('Persona and mode are required');
24
+ }
25
+
26
+ const requestBody: CapabilityResolveRequest = {
27
+ persona,
28
+ mode
29
+ };
30
+
31
+ if (context) requestBody.context = context;
32
+
33
+ return await miadiApi.resolveCapabilities(requestBody);
34
+ } catch (error: any) {
35
+ throw new Error(`Failed to resolve capabilities: ${error.message}`);
36
+ }
37
+ },
38
+
39
+ /**
40
+ * Get comprehensive agent system information
41
+ */
42
+ getAgentInfo: async (): Promise<AgentInfoResponse> => {
43
+ try {
44
+ return await miadiApi.getAgentInfo();
45
+ } catch (error: any) {
46
+ throw new Error(`Failed to get agent info: ${error.message}`);
47
+ }
48
+ },
49
+
50
+ /**
51
+ * Detect mode/persona switch cues from natural language text
52
+ */
53
+ detectCues: async (
54
+ text: string,
55
+ currentSession?: AgentSession,
56
+ availableModes?: string[],
57
+ availablePersonas?: string[]
58
+ ): Promise<CueDetectResponse> => {
59
+ try {
60
+ if (!text) {
61
+ throw new Error('Text is required for cue detection');
62
+ }
63
+
64
+ const requestBody: CueDetectRequest = {
65
+ text
66
+ };
67
+
68
+ if (currentSession) requestBody.currentSession = currentSession;
69
+ if (availableModes) requestBody.availableModes = availableModes;
70
+ if (availablePersonas) requestBody.availablePersonas = availablePersonas;
71
+
72
+ return await miadiApi.detectCues(requestBody);
73
+ } catch (error: any) {
74
+ throw new Error(`Failed to detect cues: ${error.message}`);
75
+ }
76
+ }
77
+ };
78
+
79
+ export default capabilityTools;
@@ -0,0 +1,51 @@
1
+ import { miadiApi } from '../api-client';
2
+ import {
3
+ ForgeStateRequest,
4
+ ForgeStateResponse,
5
+ ForgeStateUpdateResponse
6
+ } from '../types';
7
+
8
+ export const forgeTools = {
9
+ /**
10
+ * Get the current forge state
11
+ */
12
+ getForgeState: async (): Promise<ForgeStateResponse> => {
13
+ try {
14
+ return await miadiApi.getForgeState();
15
+ } catch (error: any) {
16
+ throw new Error(`Failed to get forge state: ${error.message}`);
17
+ }
18
+ },
19
+
20
+ /**
21
+ * Update the forge state (open/close)
22
+ */
23
+ updateForgeState: async (state: boolean): Promise<ForgeStateUpdateResponse> => {
24
+ try {
25
+ if (typeof state !== 'boolean') {
26
+ throw new Error('State must be a boolean value (true for open, false for closed)');
27
+ }
28
+
29
+ const requestBody: ForgeStateRequest = {
30
+ state
31
+ };
32
+
33
+ return await miadiApi.updateForgeState(requestBody);
34
+ } catch (error: any) {
35
+ throw new Error(`Failed to update forge state: ${error.message}`);
36
+ }
37
+ },
38
+
39
+ /**
40
+ * Get glyph map information
41
+ */
42
+ getGlyphMap: async (): Promise<any> => {
43
+ try {
44
+ return await miadiApi.getGlyphMap();
45
+ } catch (error: any) {
46
+ throw new Error(`Failed to get glyph map: ${error.message}`);
47
+ }
48
+ }
49
+ };
50
+
51
+ export default forgeTools;
@@ -0,0 +1,137 @@
1
+ import { miadiApi } from '../api-client';
2
+ import { GetMemoryResponse, StoreMemoryRequest, StoreMemoryResponse, ScanResponse, GatherMemoryResponse } from '../types';
3
+
4
+ export const memoryTools = {
5
+ /**
6
+ * Retrieve memory data from Redis storage
7
+ */
8
+ getMemory: async (
9
+ key?: string,
10
+ list?: boolean,
11
+ pattern?: string,
12
+ type?: 'auto' | 'string' | 'hash' | 'list' | 'set' | 'zset'
13
+ ): Promise<GetMemoryResponse> => {
14
+ try {
15
+ return await miadiApi.getMemory(key, list, pattern, type);
16
+ } catch (error: any) {
17
+ throw new Error(`Failed to retrieve memory: ${error.message}`);
18
+ }
19
+ },
20
+
21
+ /**
22
+ * Store data in memory with optional TTL and type
23
+ */
24
+ storeMemory: async (
25
+ key: string,
26
+ value: string,
27
+ ttl?: number,
28
+ type?: 'string' | 'hash' | 'list'
29
+ ): Promise<StoreMemoryResponse> => {
30
+ try {
31
+ const requestBody: StoreMemoryRequest = { key, value };
32
+ if (ttl !== undefined) requestBody.ttl = ttl;
33
+ if (type) requestBody.type = type;
34
+
35
+ return await miadiApi.storeMemory(requestBody);
36
+ } catch (error: any) {
37
+ throw new Error(`Failed to store memory: ${error.message}`);
38
+ }
39
+ },
40
+
41
+ /**
42
+ * Update TTL for an existing memory key
43
+ */
44
+ updateMemoryTTL: async (key: string, ttl: number): Promise<any> => {
45
+ try {
46
+ return await miadiApi.updateMemoryTTL(key, ttl);
47
+ } catch (error: any) {
48
+ throw new Error(`Failed to update memory TTL: ${error.message}`);
49
+ }
50
+ },
51
+
52
+ /**
53
+ * Get metadata for a memory key
54
+ */
55
+ getMemoryMeta: async (key: string): Promise<any> => {
56
+ try {
57
+ return await miadiApi.getMemoryMeta(key);
58
+ } catch (error: any) {
59
+ throw new Error(`Failed to get memory metadata: ${error.message}`);
60
+ }
61
+ },
62
+
63
+ /**
64
+ * Scan Redis keys using pattern matching or regex
65
+ */
66
+ scanKeys: async (
67
+ pattern?: string,
68
+ regex?: string,
69
+ limit?: number
70
+ ): Promise<ScanResponse> => {
71
+ try {
72
+ return await miadiApi.scanKeys(pattern, regex, limit);
73
+ } catch (error: any) {
74
+ throw new Error(`Failed to scan keys: ${error.message}`);
75
+ }
76
+ },
77
+
78
+ /**
79
+ * Gather memory values using pattern matching
80
+ */
81
+ gatherMemoryValues: async (
82
+ pattern?: string,
83
+ regex?: string,
84
+ limit?: number,
85
+ scanLimit?: number
86
+ ): Promise<GatherMemoryResponse> => {
87
+ try {
88
+ return await miadiApi.gatherMemoryValues(pattern, regex, limit, scanLimit);
89
+ } catch (error: any) {
90
+ throw new Error(`Failed to gather memory values: ${error.message}`);
91
+ }
92
+ },
93
+
94
+ /**
95
+ * Collect specific memory values by keys
96
+ */
97
+ collectMemory: async (keys: string[]): Promise<any> => {
98
+ try {
99
+ if (!keys || keys.length === 0) {
100
+ throw new Error('Keys array is required and cannot be empty');
101
+ }
102
+ return await miadiApi.collectMemory(keys);
103
+ } catch (error: any) {
104
+ throw new Error(`Failed to collect memory: ${error.message}`);
105
+ }
106
+ },
107
+
108
+ /**
109
+ * View key content for cluster operations
110
+ */
111
+ viewKeyContent: async (key: string): Promise<any> => {
112
+ try {
113
+ if (!key) {
114
+ throw new Error('Key is required');
115
+ }
116
+ return await miadiApi.viewKeyContent(key);
117
+ } catch (error: any) {
118
+ throw new Error(`Failed to view key content: ${error.message}`);
119
+ }
120
+ },
121
+
122
+ /**
123
+ * Search cluster for keys
124
+ */
125
+ searchCluster: async (term: string): Promise<{ keys: string[] }> => {
126
+ try {
127
+ if (!term) {
128
+ throw new Error('Search term is required');
129
+ }
130
+ return await miadiApi.searchCluster(term);
131
+ } catch (error: any) {
132
+ throw new Error(`Failed to search cluster: ${error.message}`);
133
+ }
134
+ }
135
+ };
136
+
137
+ export default memoryTools;
@@ -0,0 +1,135 @@
1
+ import { miadiApi } from '../api-client';
2
+ import {
3
+ SessionStartRequest,
4
+ SessionStartResponse,
5
+ SessionCurrentResponse,
6
+ SessionSwitchModeRequest,
7
+ SessionSwitchPersonaRequest,
8
+ SessionListResponse,
9
+ ContextVariable
10
+ } from '../types';
11
+
12
+ export const sessionTools = {
13
+ /**
14
+ * Start a new agent session with specified persona and mode
15
+ */
16
+ startSession: async (
17
+ persona: string,
18
+ mode: string,
19
+ userId?: string,
20
+ contextOverrides?: ContextVariable[]
21
+ ): Promise<SessionStartResponse> => {
22
+ try {
23
+ if (!persona || !mode) {
24
+ throw new Error('Persona and mode are required');
25
+ }
26
+
27
+ const requestBody: SessionStartRequest = {
28
+ persona,
29
+ mode
30
+ };
31
+
32
+ if (userId) requestBody.userId = userId;
33
+ if (contextOverrides) requestBody.contextOverrides = contextOverrides;
34
+
35
+ return await miadiApi.startSession(requestBody);
36
+ } catch (error: any) {
37
+ throw new Error(`Failed to start session: ${error.message}`);
38
+ }
39
+ },
40
+
41
+ /**
42
+ * Get current session details
43
+ */
44
+ getCurrentSession: async (sessionId?: string): Promise<SessionCurrentResponse> => {
45
+ try {
46
+ return await miadiApi.getCurrentSession(sessionId);
47
+ } catch (error: any) {
48
+ throw new Error(`Failed to get current session: ${error.message}`);
49
+ }
50
+ },
51
+
52
+ /**
53
+ * Switch mode in an existing session
54
+ */
55
+ switchMode: async (
56
+ sessionId: string,
57
+ newMode: string,
58
+ contextOverrides?: ContextVariable[]
59
+ ): Promise<SessionCurrentResponse> => {
60
+ try {
61
+ if (!sessionId || !newMode) {
62
+ throw new Error('Session ID and new mode are required');
63
+ }
64
+
65
+ const requestBody: SessionSwitchModeRequest = {
66
+ sessionId,
67
+ newMode
68
+ };
69
+
70
+ if (contextOverrides) requestBody.contextOverrides = contextOverrides;
71
+
72
+ return await miadiApi.switchMode(requestBody);
73
+ } catch (error: any) {
74
+ throw new Error(`Failed to switch mode: ${error.message}`);
75
+ }
76
+ },
77
+
78
+ /**
79
+ * Switch persona in an existing session
80
+ */
81
+ switchPersona: async (
82
+ sessionId: string,
83
+ newPersona: string,
84
+ preserveMode?: boolean
85
+ ): Promise<SessionCurrentResponse> => {
86
+ try {
87
+ if (!sessionId || !newPersona) {
88
+ throw new Error('Session ID and new persona are required');
89
+ }
90
+
91
+ const requestBody: SessionSwitchPersonaRequest = {
92
+ sessionId,
93
+ newPersona
94
+ };
95
+
96
+ if (preserveMode !== undefined) requestBody.preserveMode = preserveMode;
97
+
98
+ return await miadiApi.switchPersona(requestBody);
99
+ } catch (error: any) {
100
+ throw new Error(`Failed to switch persona: ${error.message}`);
101
+ }
102
+ },
103
+
104
+ /**
105
+ * End an active session
106
+ */
107
+ endSession: async (sessionId: string): Promise<{ success: boolean; message: string; timestamp: string }> => {
108
+ try {
109
+ if (!sessionId) {
110
+ throw new Error('Session ID is required');
111
+ }
112
+
113
+ return await miadiApi.endSession(sessionId);
114
+ } catch (error: any) {
115
+ throw new Error(`Failed to end session: ${error.message}`);
116
+ }
117
+ },
118
+
119
+ /**
120
+ * List active sessions with optional filtering
121
+ */
122
+ listSessions: async (
123
+ userId?: string,
124
+ limit?: number,
125
+ offset?: number
126
+ ): Promise<SessionListResponse> => {
127
+ try {
128
+ return await miadiApi.listSessions(userId, limit, offset);
129
+ } catch (error: any) {
130
+ throw new Error(`Failed to list sessions: ${error.message}`);
131
+ }
132
+ }
133
+ };
134
+
135
+ export default sessionTools;