@robota-sdk/agent-transport 3.0.0-beta.74 → 3.0.0-beta.76

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 (197) hide show
  1. package/README.md +10 -10
  2. package/dist/node/headless/index.cjs +1 -1
  3. package/dist/node/headless/index.d.ts +1 -1
  4. package/dist/node/headless/index.js +1 -1
  5. package/dist/node/headless-OnpVk4-k.cjs +15 -0
  6. package/dist/node/{headless-D02zUEGh.js → headless-mRYilLfC.js} +2 -2
  7. package/dist/node/{headless-D02zUEGh.js.map → headless-mRYilLfC.js.map} +1 -1
  8. package/dist/node/{index-DE3-dHqw.d.ts → index-CYl7ksS6.d.ts} +12 -2
  9. package/dist/node/{index-DE3-dHqw.d.ts.map → index-CYl7ksS6.d.ts.map} +1 -1
  10. package/dist/node/{index-WKTgvhlg.d.ts → index-E8Gx4-lc.d.ts} +12 -2
  11. package/dist/node/{index-WKTgvhlg.d.ts.map → index-E8Gx4-lc.d.ts.map} +1 -1
  12. package/dist/node/index.cjs +1 -1
  13. package/dist/node/index.d.ts +2 -7
  14. package/dist/node/index.d.ts.map +1 -1
  15. package/dist/node/index.js +1 -1
  16. package/dist/node/index.js.map +1 -1
  17. package/package.json +7 -75
  18. package/src/headless/HeadlessInteractionChannel.ts +21 -1
  19. package/src/index.ts +1 -5
  20. package/src/transport-registry.ts +0 -9
  21. package/dist/node/headless-BeHAOlIM.cjs +0 -15
  22. package/dist/node/http/index.cjs +0 -1
  23. package/dist/node/http/index.d.ts +0 -2
  24. package/dist/node/http/index.js +0 -1
  25. package/dist/node/http-2Jiuflc1.js +0 -2
  26. package/dist/node/http-2Jiuflc1.js.map +0 -1
  27. package/dist/node/http-CBAvefLw.cjs +0 -1
  28. package/dist/node/index-BQLN_Lc9.d.ts +0 -78
  29. package/dist/node/index-BQLN_Lc9.d.ts.map +0 -1
  30. package/dist/node/index-BnAGE-u9.d.ts +0 -33
  31. package/dist/node/index-BnAGE-u9.d.ts.map +0 -1
  32. package/dist/node/index-BrQ4gGw0.d.ts +0 -213
  33. package/dist/node/index-BrQ4gGw0.d.ts.map +0 -1
  34. package/dist/node/index-CoeBF21y.d.ts +0 -213
  35. package/dist/node/index-CoeBF21y.d.ts.map +0 -1
  36. package/dist/node/index-DHt-2VQ-.d.ts +0 -46
  37. package/dist/node/index-DHt-2VQ-.d.ts.map +0 -1
  38. package/dist/node/index-DMwKN5Le.d.ts +0 -33
  39. package/dist/node/index-DMwKN5Le.d.ts.map +0 -1
  40. package/dist/node/index-IvYaYY6v.d.ts +0 -78
  41. package/dist/node/index-IvYaYY6v.d.ts.map +0 -1
  42. package/dist/node/index-c0M42fsA.d.ts +0 -46
  43. package/dist/node/index-c0M42fsA.d.ts.map +0 -1
  44. package/dist/node/mcp/index.cjs +0 -1
  45. package/dist/node/mcp/index.d.ts +0 -2
  46. package/dist/node/mcp/index.js +0 -1
  47. package/dist/node/mcp-BOglBJNy.cjs +0 -1
  48. package/dist/node/mcp-D3BBVK7C.js +0 -2
  49. package/dist/node/mcp-D3BBVK7C.js.map +0 -1
  50. package/dist/node/rolldown-runtime-CMqjfN_6.cjs +0 -1
  51. package/dist/node/tui/index.cjs +0 -1
  52. package/dist/node/tui/index.d.ts +0 -2
  53. package/dist/node/tui/index.js +0 -1
  54. package/dist/node/tui-Btb1q88j.js +0 -25
  55. package/dist/node/tui-Btb1q88j.js.map +0 -1
  56. package/dist/node/tui-SbUT7Zlt.cjs +0 -24
  57. package/dist/node/ws/index.cjs +0 -1
  58. package/dist/node/ws/index.d.ts +0 -2
  59. package/dist/node/ws/index.js +0 -1
  60. package/dist/node/ws-Dc2RUwVs.js +0 -2
  61. package/dist/node/ws-Dc2RUwVs.js.map +0 -1
  62. package/dist/node/ws-QNMQn5kg.cjs +0 -1
  63. package/src/http/__tests__/http-transport.test.ts +0 -55
  64. package/src/http/__tests__/routes.test.ts +0 -168
  65. package/src/http/http-transport.ts +0 -41
  66. package/src/http/index.ts +0 -4
  67. package/src/http/routes.ts +0 -152
  68. package/src/mcp/__tests__/mcp-server.test.ts +0 -66
  69. package/src/mcp/__tests__/mcp-transport.test.ts +0 -46
  70. package/src/mcp/index.ts +0 -4
  71. package/src/mcp/mcp-server.ts +0 -163
  72. package/src/mcp/mcp-transport.ts +0 -48
  73. package/src/tui/App.tsx +0 -488
  74. package/src/tui/BackgroundTaskPanel.tsx +0 -36
  75. package/src/tui/CjkTextInput.tsx +0 -199
  76. package/src/tui/ConfirmPrompt.tsx +0 -70
  77. package/src/tui/ContextWarningBanner.tsx +0 -34
  78. package/src/tui/ExecutionWorkspaceDetailPane.tsx +0 -64
  79. package/src/tui/ExecutionWorkspaceSwitcher.tsx +0 -187
  80. package/src/tui/InputArea.tsx +0 -310
  81. package/src/tui/InteractivePrompt.tsx +0 -59
  82. package/src/tui/ListPicker.tsx +0 -95
  83. package/src/tui/MenuSelect.tsx +0 -104
  84. package/src/tui/MessageList.tsx +0 -284
  85. package/src/tui/PermissionPrompt.tsx +0 -86
  86. package/src/tui/PluginTUI.tsx +0 -258
  87. package/src/tui/SessionPicker.tsx +0 -68
  88. package/src/tui/SessionStatusBar.tsx +0 -70
  89. package/src/tui/SlashAutocomplete.tsx +0 -110
  90. package/src/tui/StatusBar.tsx +0 -209
  91. package/src/tui/StreamingIndicator.tsx +0 -93
  92. package/src/tui/TextPrompt.tsx +0 -81
  93. package/src/tui/ToolCommandOutput.tsx +0 -39
  94. package/src/tui/ToolDiffBlock.tsx +0 -32
  95. package/src/tui/TransportTUI.tsx +0 -117
  96. package/src/tui/TuiInteractionChannel.ts +0 -483
  97. package/src/tui/UpdateNotice.tsx +0 -14
  98. package/src/tui/UsageSummaryEntry.tsx +0 -39
  99. package/src/tui/WaveText.tsx +0 -44
  100. package/src/tui/__tests__/InteractivePrompt.test.tsx +0 -82
  101. package/src/tui/__tests__/ListPicker.test.tsx +0 -159
  102. package/src/tui/__tests__/MenuSelect.test.tsx +0 -103
  103. package/src/tui/__tests__/PluginTUI.test.tsx +0 -167
  104. package/src/tui/__tests__/SlashAutocomplete.test.tsx +0 -140
  105. package/src/tui/__tests__/TextPrompt.test.tsx +0 -98
  106. package/src/tui/__tests__/TuiInteractionChannel.display-contract.test.ts +0 -239
  107. package/src/tui/__tests__/TuiInteractionChannel.lifecycle.test.ts +0 -297
  108. package/src/tui/__tests__/TuiInteractionChannel.requestAction.test.ts +0 -124
  109. package/src/tui/__tests__/UpdateNotice.test.tsx +0 -15
  110. package/src/tui/__tests__/abort-after-permission.test.tsx +0 -169
  111. package/src/tui/__tests__/abort-streaming-e2e.test.tsx +0 -183
  112. package/src/tui/__tests__/background-task-panel.test.tsx +0 -53
  113. package/src/tui/__tests__/background-task-row-format.test.ts +0 -59
  114. package/src/tui/__tests__/channel-factory-integration.test.ts +0 -138
  115. package/src/tui/__tests__/cjk-text-input-flow.test.ts +0 -109
  116. package/src/tui/__tests__/cjk-text-input.test.ts +0 -191
  117. package/src/tui/__tests__/command-effect-handler.test.ts +0 -127
  118. package/src/tui/__tests__/command-output-summary.test.ts +0 -95
  119. package/src/tui/__tests__/compact-event-bridge.test.ts +0 -20
  120. package/src/tui/__tests__/confirm-permission-flow.test.ts +0 -130
  121. package/src/tui/__tests__/confirm-prompt.test.tsx +0 -87
  122. package/src/tui/__tests__/execution-workspace-switcher.test.tsx +0 -110
  123. package/src/tui/__tests__/execution-workspace-view-model.test.ts +0 -93
  124. package/src/tui/__tests__/fixtures/provider-setup-prompt-driver.tsx +0 -125
  125. package/src/tui/__tests__/input-area-flow.test.ts +0 -164
  126. package/src/tui/__tests__/message-list-rendering.test.tsx +0 -353
  127. package/src/tui/__tests__/prompt-queue.test.tsx +0 -255
  128. package/src/tui/__tests__/provider-setup-pty-e2e.test.ts +0 -233
  129. package/src/tui/__tests__/pty/pty-driver.ts +0 -135
  130. package/src/tui/__tests__/pty/tui-pty.ptytest.ts +0 -61
  131. package/src/tui/__tests__/render-channel-options.test.ts +0 -32
  132. package/src/tui/__tests__/render-markdown.test.ts +0 -72
  133. package/src/tui/__tests__/selection-flow.test.ts +0 -61
  134. package/src/tui/__tests__/session-init-poller.test.ts +0 -102
  135. package/src/tui/__tests__/session-naming.test.ts +0 -64
  136. package/src/tui/__tests__/session-switch-channel.test.tsx +0 -307
  137. package/src/tui/__tests__/slash-routing-effects.test.ts +0 -228
  138. package/src/tui/__tests__/status-activity.test.ts +0 -71
  139. package/src/tui/__tests__/status-bar.test.tsx +0 -158
  140. package/src/tui/__tests__/streaming-indicator.test.tsx +0 -137
  141. package/src/tui/__tests__/text-prompt-flow.test.ts +0 -77
  142. package/src/tui/__tests__/tui-channel-init-failure.test.ts +0 -57
  143. package/src/tui/__tests__/tui-state-manager.test.ts +0 -401
  144. package/src/tui/background-task-row-format.ts +0 -53
  145. package/src/tui/command-interaction.ts +0 -9
  146. package/src/tui/command-output-summary.ts +0 -122
  147. package/src/tui/create-default-tui-cli-adapter.ts +0 -41
  148. package/src/tui/execution-workspace-view-model.ts +0 -123
  149. package/src/tui/flows/cjk-text-input-flow.ts +0 -285
  150. package/src/tui/flows/confirm-prompt-flow.ts +0 -45
  151. package/src/tui/flows/input-area-flow.ts +0 -189
  152. package/src/tui/flows/permission-prompt-flow.ts +0 -85
  153. package/src/tui/flows/selection-flow.ts +0 -126
  154. package/src/tui/flows/session-init-poller.ts +0 -77
  155. package/src/tui/flows/text-prompt-flow.ts +0 -98
  156. package/src/tui/hooks/command-effect-handler.ts +0 -97
  157. package/src/tui/hooks/command-effect-queue.ts +0 -39
  158. package/src/tui/hooks/side-effects-types.ts +0 -35
  159. package/src/tui/hooks/useAutocomplete.ts +0 -87
  160. package/src/tui/hooks/usePluginCallbacks.ts +0 -31
  161. package/src/tui/hooks/usePluginScreenData.ts +0 -85
  162. package/src/tui/hooks/useSideEffects.ts +0 -175
  163. package/src/tui/hooks/useSlashRouting.ts +0 -118
  164. package/src/tui/hooks/useStatusLineSettings.ts +0 -37
  165. package/src/tui/hooks/useTuiChannel.ts +0 -95
  166. package/src/tui/index.ts +0 -14
  167. package/src/tui/interactions/CommandConfirm.tsx +0 -36
  168. package/src/tui/interactions/CommandPicker.tsx +0 -77
  169. package/src/tui/interactions/__tests__/CommandConfirm.test.tsx +0 -124
  170. package/src/tui/interactions/__tests__/CommandPicker.test.tsx +0 -138
  171. package/src/tui/plugin-tui-handlers.ts +0 -163
  172. package/src/tui/render-markdown.ts +0 -130
  173. package/src/tui/render.tsx +0 -117
  174. package/src/tui/session-naming.ts +0 -33
  175. package/src/tui/status-activity.ts +0 -63
  176. package/src/tui/tui-cli-adapter-context.tsx +0 -13
  177. package/src/tui/tui-cli-adapter.ts +0 -25
  178. package/src/tui/tui-state-manager.ts +0 -226
  179. package/src/tui/tui-transport.ts +0 -35
  180. package/src/tui/types.ts +0 -15
  181. package/src/tui/utils/__tests__/edit-diff.test.ts +0 -426
  182. package/src/tui/utils/__tests__/paste-detection.test.ts +0 -116
  183. package/src/tui/utils/__tests__/paste-labels.test.ts +0 -46
  184. package/src/tui/utils/__tests__/tool-call-extractor.test.ts +0 -227
  185. package/src/tui/utils/__tests__/tool-diff-summary.test.ts +0 -104
  186. package/src/tui/utils/edit-diff.ts +0 -153
  187. package/src/tui/utils/paste-labels.ts +0 -9
  188. package/src/tui/utils/tool-call-extractor.ts +0 -92
  189. package/src/tui/utils/tool-diff-summary.ts +0 -75
  190. package/src/ws/__tests__/ws-handler.test.ts +0 -409
  191. package/src/ws/__tests__/ws-transport.test.ts +0 -53
  192. package/src/ws/index.ts +0 -13
  193. package/src/ws/ws-background-messages.ts +0 -170
  194. package/src/ws/ws-handler.ts +0 -280
  195. package/src/ws/ws-protocol.ts +0 -78
  196. package/src/ws/ws-transport-configurable.ts +0 -128
  197. package/src/ws/ws-transport.ts +0 -42
@@ -1,152 +0,0 @@
1
- /**
2
- * HTTP transport adapter — exposes IInteractiveSession over REST API.
3
- *
4
- * Built on Hono for Cloudflare Workers + Node.js + AWS Lambda compatibility.
5
- * Each endpoint maps 1:1 to an IInteractiveSession API method.
6
- */
7
-
8
- import { Hono } from 'hono';
9
- import { streamSSE } from 'hono/streaming';
10
-
11
- import type { IInteractiveSession } from '@robota-sdk/agent-interface-transport';
12
- import type { Context } from 'hono';
13
-
14
- /** Callback that resolves an IInteractiveSession from the request context. */
15
- export type TSessionFactory = (c: Context) => IInteractiveSession | Promise<IInteractiveSession>;
16
-
17
- export interface IAgentRoutesOptions {
18
- /** Resolve an IInteractiveSession per request (e.g., by auth token, session ID). */
19
- sessionFactory: TSessionFactory;
20
- }
21
-
22
- /**
23
- * Create a Hono router with all agent HTTP endpoints.
24
- *
25
- * Usage:
26
- * ```typescript
27
- * const routes = createAgentRoutes({ sessionFactory });
28
- * app.route('/agent', routes); // mount on existing app
29
- * export default routes; // or use standalone (CF Workers)
30
- * ```
31
- */
32
- export function createAgentRoutes(options: IAgentRoutesOptions): Hono {
33
- const { sessionFactory } = options;
34
- const app = new Hono();
35
-
36
- // POST /submit — execute prompt, stream events via SSE
37
- app.post('/submit', async (c) => {
38
- const session = await sessionFactory(c);
39
- const body = await c.req.json<{ prompt: string }>();
40
-
41
- if (!body.prompt || typeof body.prompt !== 'string') {
42
- return c.json({ error: 'prompt is required' }, 400);
43
- }
44
-
45
- return streamSSE(c, async (stream) => {
46
- const cleanup: Array<() => void> = [];
47
-
48
- const subscribe = <T>(event: string, handler: (data: T) => void): void => {
49
- session.on(event as 'text_delta', handler as () => void);
50
- cleanup.push(() => session.off(event as 'text_delta', handler as () => void));
51
- };
52
-
53
- let completed = false;
54
- const done = new Promise<void>((resolve) => {
55
- subscribe('text_delta', (delta: string) => {
56
- stream.writeSSE({ event: 'text_delta', data: JSON.stringify({ delta }) });
57
- });
58
-
59
- subscribe('tool_start', (state) => {
60
- stream.writeSSE({ event: 'tool_start', data: JSON.stringify(state) });
61
- });
62
-
63
- subscribe('tool_end', (state) => {
64
- stream.writeSSE({ event: 'tool_end', data: JSON.stringify(state) });
65
- });
66
-
67
- subscribe('thinking', (isThinking: boolean) => {
68
- stream.writeSSE({ event: 'thinking', data: JSON.stringify({ isThinking }) });
69
- if (!isThinking && completed) {
70
- resolve();
71
- }
72
- });
73
-
74
- subscribe('complete', (result) => {
75
- completed = true;
76
- stream.writeSSE({ event: 'complete', data: JSON.stringify(result) });
77
- });
78
-
79
- subscribe('interrupted', (result) => {
80
- completed = true;
81
- stream.writeSSE({ event: 'interrupted', data: JSON.stringify(result) });
82
- });
83
-
84
- subscribe('error', (error: Error) => {
85
- completed = true;
86
- stream.writeSSE({ event: 'error', data: JSON.stringify({ message: error.message }) });
87
- });
88
- });
89
-
90
- await session.submit(body.prompt);
91
- await done;
92
-
93
- for (const fn of cleanup) fn();
94
- });
95
- });
96
-
97
- // POST /command — execute system command
98
- app.post('/command', async (c) => {
99
- const session = await sessionFactory(c);
100
- const body = await c.req.json<{ name: string; args?: string }>();
101
-
102
- if (!body.name || typeof body.name !== 'string') {
103
- return c.json({ error: 'name is required' }, 400);
104
- }
105
-
106
- const result = await session.executeCommand(body.name, body.args ?? '');
107
- if (!result) {
108
- return c.json({ error: `Unknown command: ${body.name}` }, 404);
109
- }
110
- return c.json(result);
111
- });
112
-
113
- // POST /abort — abort current execution
114
- app.post('/abort', async (c) => {
115
- const session = await sessionFactory(c);
116
- session.abort();
117
- return c.json({ ok: true });
118
- });
119
-
120
- // POST /cancel-queue — cancel queued prompt
121
- app.post('/cancel-queue', async (c) => {
122
- const session = await sessionFactory(c);
123
- session.cancelQueue();
124
- return c.json({ ok: true });
125
- });
126
-
127
- // GET /messages — get message history
128
- app.get('/messages', async (c) => {
129
- const session = await sessionFactory(c);
130
- return c.json(session.getMessages());
131
- });
132
-
133
- // GET /context — get context window state
134
- app.get('/context', async (c) => {
135
- const session = await sessionFactory(c);
136
- return c.json(session.getContextState());
137
- });
138
-
139
- // GET /executing — check if currently executing
140
- app.get('/executing', async (c) => {
141
- const session = await sessionFactory(c);
142
- return c.json({ executing: session.isExecuting() });
143
- });
144
-
145
- // GET /pending — get pending queued prompt
146
- app.get('/pending', async (c) => {
147
- const session = await sessionFactory(c);
148
- return c.json({ pending: session.getPendingPrompt() });
149
- });
150
-
151
- return app;
152
- }
@@ -1,66 +0,0 @@
1
- /**
2
- * Tests for MCP transport adapter.
3
- */
4
-
5
- import { describe, it, expect, vi } from 'vitest';
6
- import { createAgentMcpServer } from '../mcp-server.js';
7
- import type { IInteractiveSession } from '@robota-sdk/agent-interface-transport';
8
-
9
- function createMockSession(commands?: Array<{ name: string; description: string }>) {
10
- return {
11
- submit: vi.fn(),
12
- abort: vi.fn(),
13
- cancelQueue: vi.fn(),
14
- getMessages: vi.fn().mockReturnValue([]),
15
- getContextState: vi
16
- .fn()
17
- .mockReturnValue({ usedTokens: 0, maxTokens: 200000, usedPercentage: 0 }),
18
- isExecuting: vi.fn().mockReturnValue(false),
19
- getPendingPrompt: vi.fn().mockReturnValue(null),
20
- executeCommand: vi.fn().mockResolvedValue({ message: 'done', success: true }),
21
- listCommands: vi.fn().mockReturnValue(
22
- commands ?? [
23
- { name: 'clear', description: 'Clear history' },
24
- { name: 'mode', description: 'Permission mode' },
25
- ],
26
- ),
27
- on: vi.fn(),
28
- off: vi.fn(),
29
- } as unknown as IInteractiveSession;
30
- }
31
-
32
- describe('createAgentMcpServer', () => {
33
- it('creates a server instance', () => {
34
- const server = createAgentMcpServer({
35
- name: 'test-agent',
36
- version: '1.0.0',
37
- session: createMockSession(),
38
- });
39
- expect(server).toBeDefined();
40
- });
41
-
42
- it('exposeCommands=false does not call listCommands', () => {
43
- const session = createMockSession();
44
- createAgentMcpServer({
45
- name: 'test',
46
- version: '1.0.0',
47
- session,
48
- exposeCommands: false,
49
- });
50
- expect(session.listCommands).not.toHaveBeenCalled();
51
- });
52
-
53
- it('exposeCommands=true calls session.listCommands()', () => {
54
- const session = createMockSession([
55
- { name: 'clear', description: 'Clear' },
56
- { name: 'help', description: 'Help' },
57
- ]);
58
- createAgentMcpServer({
59
- name: 'test',
60
- version: '1.0.0',
61
- session,
62
- exposeCommands: true,
63
- });
64
- expect(session.listCommands).toHaveBeenCalled();
65
- });
66
- });
@@ -1,46 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { createMcpTransport } from '../mcp-transport.js';
3
- import type { IInteractiveSession } from '@robota-sdk/agent-interface-transport';
4
-
5
- function createMockSession(): IInteractiveSession {
6
- return {
7
- submit: vi.fn(),
8
- abort: vi.fn(),
9
- cancelQueue: vi.fn(),
10
- getMessages: vi.fn().mockReturnValue([]),
11
- getContextState: vi
12
- .fn()
13
- .mockReturnValue({ usedPercentage: 0, usedTokens: 0, maxTokens: 200000 }),
14
- isExecuting: vi.fn().mockReturnValue(false),
15
- getPendingPrompt: vi.fn().mockReturnValue(null),
16
- executeCommand: vi.fn().mockResolvedValue({ message: 'ok', success: true }),
17
- listCommands: vi.fn().mockReturnValue([]),
18
- on: vi.fn(),
19
- off: vi.fn(),
20
- } as unknown as IInteractiveSession;
21
- }
22
-
23
- describe('createMcpTransport', () => {
24
- it('returns an adapter with name "mcp"', () => {
25
- const transport = createMcpTransport({ name: 'test', version: '1.0.0' });
26
- expect(transport.name).toBe('mcp');
27
- });
28
-
29
- it('throws if start() is called without attach()', async () => {
30
- const transport = createMcpTransport({ name: 'test', version: '1.0.0' });
31
- await expect(transport.start()).rejects.toThrow('No session attached');
32
- });
33
-
34
- it('throws if getServer() is called before start()', () => {
35
- const transport = createMcpTransport({ name: 'test', version: '1.0.0' });
36
- expect(() => transport.getServer()).toThrow('Transport not started');
37
- });
38
-
39
- it('creates an MCP server after attach + start', async () => {
40
- const transport = createMcpTransport({ name: 'test', version: '1.0.0' });
41
- transport.attach(createMockSession() as never);
42
- await transport.start();
43
- const server = transport.getServer();
44
- expect(server).toBeDefined();
45
- });
46
- });
package/src/mcp/index.ts DELETED
@@ -1,4 +0,0 @@
1
- export { createAgentMcpServer } from './mcp-server.js';
2
- export type { IAgentMcpOptions } from './mcp-server.js';
3
- export { createMcpTransport } from './mcp-transport.js';
4
- export type { IMcpTransportOptions } from './mcp-transport.js';
@@ -1,163 +0,0 @@
1
- /**
2
- * MCP transport adapter — exposes IInteractiveSession as an MCP server.
3
- *
4
- * Uses the low-level MCP Server class to avoid TypeScript depth issues
5
- * with McpServer.registerTool() generics. Registers tools/list and
6
- * tools/call handlers directly.
7
- */
8
-
9
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
10
- import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
11
-
12
- import type { IExecutionResult, IInteractiveSession } from '@robota-sdk/agent-interface-transport';
13
-
14
- export interface IAgentMcpOptions {
15
- /** Name for the MCP server. */
16
- name: string;
17
- /** Version string. */
18
- version: string;
19
- /** IInteractiveSession to expose. */
20
- session: IInteractiveSession;
21
- /** If true, register each system command as a separate MCP tool. Default: true. */
22
- exposeCommands?: boolean;
23
- }
24
-
25
- /**
26
- * Create an MCP server that exposes IInteractiveSession over Model Context Protocol.
27
- *
28
- * Usage:
29
- * ```typescript
30
- * const server = createAgentMcpServer({
31
- * name: 'robota-agent',
32
- * version: '1.0.0',
33
- * session: interactiveSession,
34
- * });
35
- *
36
- * import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
37
- * await server.connect(new StdioServerTransport());
38
- * ```
39
- */
40
- export function createAgentMcpServer(options: IAgentMcpOptions): Server {
41
- const { name, version, session, exposeCommands = true } = options;
42
-
43
- const server = new Server({ name, version }, { capabilities: { tools: {} } });
44
-
45
- // Build tool definitions
46
- const tools: Array<{
47
- name: string;
48
- description: string;
49
- inputSchema: Record<string, unknown>;
50
- }> = [
51
- {
52
- name: 'submit',
53
- description: 'Submit a prompt to the AI agent and wait for the response',
54
- inputSchema: {
55
- type: 'object',
56
- properties: {
57
- prompt: { type: 'string', description: 'The prompt to send to the agent' },
58
- },
59
- required: ['prompt'],
60
- },
61
- },
62
- ];
63
-
64
- if (exposeCommands) {
65
- for (const cmd of session.listCommands()) {
66
- tools.push({
67
- name: `command_${cmd.name}`,
68
- description: cmd.description,
69
- inputSchema: {
70
- type: 'object',
71
- properties: {
72
- args: { type: 'string', description: 'Command arguments' },
73
- },
74
- },
75
- });
76
- }
77
- }
78
-
79
- // tools/list handler
80
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
81
- tools,
82
- }));
83
-
84
- // tools/call handler
85
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
86
- const { name: toolName, arguments: toolArgs } = request.params;
87
-
88
- if (toolName === 'submit') {
89
- const prompt = (toolArgs as Record<string, string>)?.prompt;
90
- if (!prompt) {
91
- return {
92
- content: [{ type: 'text', text: 'Error: prompt is required' }],
93
- isError: true,
94
- };
95
- }
96
- const result = await waitForCompletion(session, prompt);
97
- return {
98
- content: [{ type: 'text', text: result.response }],
99
- };
100
- }
101
-
102
- // System commands: command_<name>
103
- if (toolName.startsWith('command_')) {
104
- const cmdName = toolName.slice('command_'.length);
105
- const args = (toolArgs as Record<string, string>)?.args ?? '';
106
- const result = await session.executeCommand(cmdName, args);
107
- return {
108
- content: [
109
- {
110
- type: 'text',
111
- text: result?.message ?? `Unknown command: ${cmdName}`,
112
- },
113
- ],
114
- isError: !result,
115
- };
116
- }
117
-
118
- return {
119
- content: [{ type: 'text', text: `Unknown tool: ${toolName}` }],
120
- isError: true,
121
- };
122
- });
123
-
124
- return server;
125
- }
126
-
127
- /**
128
- * Submit a prompt and wait for the complete/interrupted/error event.
129
- */
130
- function waitForCompletion(
131
- session: IInteractiveSession,
132
- prompt: string,
133
- ): Promise<IExecutionResult> {
134
- return new Promise((resolve, reject) => {
135
- const onComplete = (result: IExecutionResult): void => {
136
- cleanup();
137
- resolve(result);
138
- };
139
- const onInterrupted = (result: IExecutionResult): void => {
140
- cleanup();
141
- resolve(result);
142
- };
143
- const onError = (error: Error): void => {
144
- cleanup();
145
- reject(error);
146
- };
147
-
148
- const cleanup = (): void => {
149
- session.off('complete', onComplete);
150
- session.off('interrupted', onInterrupted);
151
- session.off('error', onError);
152
- };
153
-
154
- session.on('complete', onComplete);
155
- session.on('interrupted', onInterrupted);
156
- session.on('error', onError);
157
-
158
- session.submit(prompt).catch((err) => {
159
- cleanup();
160
- reject(err instanceof Error ? err : new Error(String(err)));
161
- });
162
- });
163
- }
@@ -1,48 +0,0 @@
1
- /**
2
- * ITransportAdapter implementation for MCP transport.
3
- *
4
- * Wraps createAgentMcpServer into the unified ITransportAdapter interface
5
- * while exposing the underlying MCP Server via getServer().
6
- */
7
-
8
- import { createAgentMcpServer } from './mcp-server.js';
9
-
10
- import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
11
- import type { IInteractiveSession, ITransportAdapter } from '@robota-sdk/agent-interface-transport';
12
-
13
- export interface IMcpTransportOptions {
14
- /** Name for the MCP server. */
15
- name: string;
16
- /** Version string. */
17
- version: string;
18
- /** If true, register each system command as a separate MCP tool. Default: true. */
19
- exposeCommands?: boolean;
20
- }
21
-
22
- export function createMcpTransport(
23
- options: IMcpTransportOptions,
24
- ): ITransportAdapter<IInteractiveSession> & { getServer(): Server } {
25
- let session: IInteractiveSession | null = null;
26
- let server: Server | null = null;
27
-
28
- return {
29
- name: 'mcp',
30
- attach(s: IInteractiveSession) {
31
- session = s;
32
- },
33
- async start() {
34
- if (!session) throw new Error('No session attached. Call attach() first.');
35
- server = createAgentMcpServer({ ...options, session });
36
- },
37
- async stop() {
38
- if (server) {
39
- await server.close();
40
- server = null;
41
- }
42
- },
43
- getServer() {
44
- if (!server) throw new Error('Transport not started. Call start() first.');
45
- return server;
46
- },
47
- };
48
- }