@robota-sdk/agent-transport-mcp 3.0.0-beta.63 → 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.
- package/README.md +3 -63
- package/dist/node/index.cjs +1 -158
- package/dist/node/index.d.ts +24 -36
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +2 -130
- package/dist/node/index.js.map +1 -0
- package/package.json +18 -15
- package/src/__tests__/mcp-server.test.ts +66 -0
- package/src/__tests__/mcp-transport.test.ts +46 -0
- package/src/index.ts +4 -0
- package/src/mcp-server.ts +163 -0
- package/src/mcp-transport.ts +48 -0
- package/dist/node/index.d.cts +0 -58
package/README.md
CHANGED
|
@@ -1,69 +1,9 @@
|
|
|
1
1
|
# @robota-sdk/agent-transport-mcp
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
pnpm add @robota-sdk/agent-transport-mcp
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Usage
|
|
3
|
+
Model Context Protocol (MCP) server transport for the Robota SDK.
|
|
12
4
|
|
|
13
5
|
```typescript
|
|
14
|
-
import { createAgentMcpServer } from '@robota-sdk/agent-transport-mcp';
|
|
15
|
-
import type { InteractiveSession } from '@robota-sdk/agent-sdk';
|
|
16
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
17
|
-
|
|
18
|
-
const mcpServer = createAgentMcpServer({
|
|
19
|
-
name: 'robota-agent',
|
|
20
|
-
version: '1.0.0',
|
|
21
|
-
session: interactiveSession,
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
// Connect via stdio (subprocess / Claude Desktop)
|
|
25
|
-
await mcpServer.connect(new StdioServerTransport());
|
|
6
|
+
import { createMcpTransport, createAgentMcpServer } from '@robota-sdk/agent-transport-mcp';
|
|
26
7
|
```
|
|
27
8
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Each tool maps directly to an `InteractiveSession` operation or a system command.
|
|
31
|
-
|
|
32
|
-
| Tool Name | Input | Description |
|
|
33
|
-
| ----------------- | -------------------- | --------------------------------------- |
|
|
34
|
-
| `submit` | `{ prompt: string }` | Submit a prompt, wait for full response |
|
|
35
|
-
| `command_help` | `{ args?: string }` | Show available commands |
|
|
36
|
-
| `command_clear` | `{ args?: string }` | Clear conversation history |
|
|
37
|
-
| `command_compact` | `{ args?: string }` | Compress the context window |
|
|
38
|
-
| `command_mode` | `{ args?: string }` | Show or change permission mode |
|
|
39
|
-
| `command_model` | `{ args?: string }` | Change the active AI model |
|
|
40
|
-
| `command_context` | `{ args?: string }` | Show context window info |
|
|
41
|
-
| ... | ... | One tool per registered system command |
|
|
42
|
-
|
|
43
|
-
The `submit` tool waits for the session to reach `complete` or `interrupted` state before returning, so MCP clients receive a single text response rather than a stream.
|
|
44
|
-
|
|
45
|
-
## ITransportAdapter
|
|
46
|
-
|
|
47
|
-
The MCP transport implements the `ITransportAdapter` interface from `@robota-sdk/agent-sdk`:
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
50
|
-
import { createMcpTransport } from '@robota-sdk/agent-transport-mcp';
|
|
51
|
-
import type { ITransportAdapter } from '@robota-sdk/agent-sdk';
|
|
52
|
-
|
|
53
|
-
const transport: ITransportAdapter = createMcpTransport({
|
|
54
|
-
name: 'robota-agent',
|
|
55
|
-
version: '1.0.0',
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
transport.attach(interactiveSession);
|
|
59
|
-
await transport.start();
|
|
60
|
-
|
|
61
|
-
// Access the underlying MCP server
|
|
62
|
-
const server = transport.getServer();
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## Dependencies
|
|
66
|
-
|
|
67
|
-
- `@robota-sdk/agent-sdk` — `InteractiveSession`
|
|
68
|
-
- `@modelcontextprotocol/sdk` — MCP server implementation
|
|
69
|
-
- `zod` — input schema definitions
|
|
9
|
+
See [docs/SPEC.md](./docs/SPEC.md) for the full contract.
|
package/dist/node/index.cjs
CHANGED
|
@@ -1,158 +1 @@
|
|
|
1
|
-
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/index.ts
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
createAgentMcpServer: () => createAgentMcpServer,
|
|
24
|
-
createMcpTransport: () => createMcpTransport
|
|
25
|
-
});
|
|
26
|
-
module.exports = __toCommonJS(index_exports);
|
|
27
|
-
|
|
28
|
-
// src/mcp-server.ts
|
|
29
|
-
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
30
|
-
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
31
|
-
function createAgentMcpServer(options) {
|
|
32
|
-
const { name, version, session, exposeCommands = true } = options;
|
|
33
|
-
const server = new import_server.Server({ name, version }, { capabilities: { tools: {} } });
|
|
34
|
-
const tools = [
|
|
35
|
-
{
|
|
36
|
-
name: "submit",
|
|
37
|
-
description: "Submit a prompt to the AI agent and wait for the response",
|
|
38
|
-
inputSchema: {
|
|
39
|
-
type: "object",
|
|
40
|
-
properties: {
|
|
41
|
-
prompt: { type: "string", description: "The prompt to send to the agent" }
|
|
42
|
-
},
|
|
43
|
-
required: ["prompt"]
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
];
|
|
47
|
-
if (exposeCommands) {
|
|
48
|
-
for (const cmd of session.listCommands()) {
|
|
49
|
-
tools.push({
|
|
50
|
-
name: `command_${cmd.name}`,
|
|
51
|
-
description: cmd.description,
|
|
52
|
-
inputSchema: {
|
|
53
|
-
type: "object",
|
|
54
|
-
properties: {
|
|
55
|
-
args: { type: "string", description: "Command arguments" }
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
server.setRequestHandler(import_types.ListToolsRequestSchema, async () => ({
|
|
62
|
-
tools
|
|
63
|
-
}));
|
|
64
|
-
server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
|
|
65
|
-
const { name: toolName, arguments: toolArgs } = request.params;
|
|
66
|
-
if (toolName === "submit") {
|
|
67
|
-
const prompt = toolArgs?.prompt;
|
|
68
|
-
if (!prompt) {
|
|
69
|
-
return {
|
|
70
|
-
content: [{ type: "text", text: "Error: prompt is required" }],
|
|
71
|
-
isError: true
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
const result = await waitForCompletion(session, prompt);
|
|
75
|
-
return {
|
|
76
|
-
content: [{ type: "text", text: result.response }]
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
if (toolName.startsWith("command_")) {
|
|
80
|
-
const cmdName = toolName.slice("command_".length);
|
|
81
|
-
const args = toolArgs?.args ?? "";
|
|
82
|
-
const result = await session.executeCommand(cmdName, args);
|
|
83
|
-
return {
|
|
84
|
-
content: [
|
|
85
|
-
{
|
|
86
|
-
type: "text",
|
|
87
|
-
text: result?.message ?? `Unknown command: ${cmdName}`
|
|
88
|
-
}
|
|
89
|
-
],
|
|
90
|
-
isError: !result
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
return {
|
|
94
|
-
content: [{ type: "text", text: `Unknown tool: ${toolName}` }],
|
|
95
|
-
isError: true
|
|
96
|
-
};
|
|
97
|
-
});
|
|
98
|
-
return server;
|
|
99
|
-
}
|
|
100
|
-
function waitForCompletion(session, prompt) {
|
|
101
|
-
return new Promise((resolve, reject) => {
|
|
102
|
-
const onComplete = (result) => {
|
|
103
|
-
cleanup();
|
|
104
|
-
resolve(result);
|
|
105
|
-
};
|
|
106
|
-
const onInterrupted = (result) => {
|
|
107
|
-
cleanup();
|
|
108
|
-
resolve(result);
|
|
109
|
-
};
|
|
110
|
-
const onError = (error) => {
|
|
111
|
-
cleanup();
|
|
112
|
-
reject(error);
|
|
113
|
-
};
|
|
114
|
-
const cleanup = () => {
|
|
115
|
-
session.off("complete", onComplete);
|
|
116
|
-
session.off("interrupted", onInterrupted);
|
|
117
|
-
session.off("error", onError);
|
|
118
|
-
};
|
|
119
|
-
session.on("complete", onComplete);
|
|
120
|
-
session.on("interrupted", onInterrupted);
|
|
121
|
-
session.on("error", onError);
|
|
122
|
-
session.submit(prompt).catch((err) => {
|
|
123
|
-
cleanup();
|
|
124
|
-
reject(err instanceof Error ? err : new Error(String(err)));
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// src/mcp-transport.ts
|
|
130
|
-
function createMcpTransport(options) {
|
|
131
|
-
let session = null;
|
|
132
|
-
let server = null;
|
|
133
|
-
return {
|
|
134
|
-
name: "mcp",
|
|
135
|
-
attach(s) {
|
|
136
|
-
session = s;
|
|
137
|
-
},
|
|
138
|
-
async start() {
|
|
139
|
-
if (!session) throw new Error("No session attached. Call attach() first.");
|
|
140
|
-
server = createAgentMcpServer({ ...options, session });
|
|
141
|
-
},
|
|
142
|
-
async stop() {
|
|
143
|
-
if (server) {
|
|
144
|
-
await server.close();
|
|
145
|
-
server = null;
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
|
-
getServer() {
|
|
149
|
-
if (!server) throw new Error("Transport not started. Call start() first.");
|
|
150
|
-
return server;
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
155
|
-
0 && (module.exports = {
|
|
156
|
-
createAgentMcpServer,
|
|
157
|
-
createMcpTransport
|
|
158
|
-
});
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@modelcontextprotocol/sdk/server/index.js"),t=require("@modelcontextprotocol/sdk/types.js");function n(n){let{name:i,version:a,session:o,exposeCommands:s=!0}=n,c=new e.Server({name:i,version:a},{capabilities:{tools:{}}}),l=[{name:`submit`,description:`Submit a prompt to the AI agent and wait for the response`,inputSchema:{type:`object`,properties:{prompt:{type:`string`,description:`The prompt to send to the agent`}},required:[`prompt`]}}];if(s)for(let e of o.listCommands())l.push({name:`command_${e.name}`,description:e.description,inputSchema:{type:`object`,properties:{args:{type:`string`,description:`Command arguments`}}}});return c.setRequestHandler(t.ListToolsRequestSchema,async()=>({tools:l})),c.setRequestHandler(t.CallToolRequestSchema,async e=>{let{name:t,arguments:n}=e.params;if(t===`submit`){let e=n?.prompt;return e?{content:[{type:`text`,text:(await r(o,e)).response}]}:{content:[{type:`text`,text:`Error: prompt is required`}],isError:!0}}if(t.startsWith(`command_`)){let e=t.slice(8),r=n?.args??``,i=await o.executeCommand(e,r);return{content:[{type:`text`,text:i?.message??`Unknown command: ${e}`}],isError:!i}}return{content:[{type:`text`,text:`Unknown tool: ${t}`}],isError:!0}}),c}function r(e,t){return new Promise((n,r)=>{let i=e=>{s(),n(e)},a=e=>{s(),n(e)},o=e=>{s(),r(e)},s=()=>{e.off(`complete`,i),e.off(`interrupted`,a),e.off(`error`,o)};e.on(`complete`,i),e.on(`interrupted`,a),e.on(`error`,o),e.submit(t).catch(e=>{s(),r(e instanceof Error?e:Error(String(e)))})})}function i(e){let t=null,r=null;return{name:`mcp`,attach(e){t=e},async start(){if(!t)throw Error(`No session attached. Call attach() first.`);r=n({...e,session:t})},async stop(){r&&=(await r.close(),null)},getServer(){if(!r)throw Error(`Transport not started. Call start() first.`);return r}}}exports.createAgentMcpServer=n,exports.createMcpTransport=i;
|
package/dist/node/index.d.ts
CHANGED
|
@@ -1,26 +1,19 @@
|
|
|
1
|
-
import { Server } from
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* MCP transport adapter — exposes InteractiveSession as an MCP server.
|
|
6
|
-
*
|
|
7
|
-
* Uses the low-level MCP Server class to avoid TypeScript depth issues
|
|
8
|
-
* with McpServer.registerTool() generics. Registers tools/list and
|
|
9
|
-
* tools/call handlers directly.
|
|
10
|
-
*/
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { IInteractiveSession, ITransportAdapter } from "@robota-sdk/agent-interface-transport";
|
|
11
3
|
|
|
4
|
+
//#region src/mcp-server.d.ts
|
|
12
5
|
interface IAgentMcpOptions {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
6
|
+
/** Name for the MCP server. */
|
|
7
|
+
name: string;
|
|
8
|
+
/** Version string. */
|
|
9
|
+
version: string;
|
|
10
|
+
/** IInteractiveSession to expose. */
|
|
11
|
+
session: IInteractiveSession;
|
|
12
|
+
/** If true, register each system command as a separate MCP tool. Default: true. */
|
|
13
|
+
exposeCommands?: boolean;
|
|
21
14
|
}
|
|
22
15
|
/**
|
|
23
|
-
* Create an MCP server that exposes
|
|
16
|
+
* Create an MCP server that exposes IInteractiveSession over Model Context Protocol.
|
|
24
17
|
*
|
|
25
18
|
* Usage:
|
|
26
19
|
* ```typescript
|
|
@@ -35,24 +28,19 @@ interface IAgentMcpOptions {
|
|
|
35
28
|
* ```
|
|
36
29
|
*/
|
|
37
30
|
declare function createAgentMcpServer(options: IAgentMcpOptions): Server;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
* ITransportAdapter implementation for MCP transport.
|
|
41
|
-
*
|
|
42
|
-
* Wraps createAgentMcpServer into the unified ITransportAdapter interface
|
|
43
|
-
* while exposing the underlying MCP Server via getServer().
|
|
44
|
-
*/
|
|
45
|
-
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/mcp-transport.d.ts
|
|
46
33
|
interface IMcpTransportOptions {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
34
|
+
/** Name for the MCP server. */
|
|
35
|
+
name: string;
|
|
36
|
+
/** Version string. */
|
|
37
|
+
version: string;
|
|
38
|
+
/** If true, register each system command as a separate MCP tool. Default: true. */
|
|
39
|
+
exposeCommands?: boolean;
|
|
53
40
|
}
|
|
54
|
-
declare function createMcpTransport(options: IMcpTransportOptions): ITransportAdapter & {
|
|
55
|
-
|
|
41
|
+
declare function createMcpTransport(options: IMcpTransportOptions): ITransportAdapter<IInteractiveSession> & {
|
|
42
|
+
getServer(): Server;
|
|
56
43
|
};
|
|
57
|
-
|
|
44
|
+
//#endregion
|
|
58
45
|
export { type IAgentMcpOptions, type IMcpTransportOptions, createAgentMcpServer, createMcpTransport };
|
|
46
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/mcp-server.ts","../../src/mcp-transport.ts"],"mappings":";;;;UAaiB,gBAAA;EAMf;EAJA,IAAA;EAMA;EAJA,OAAA;EAIc;EAFd,OAAA,EAAS,mBAAmB;EAoBM;EAlBlC,cAAA;AAAA;;;;;AAkBqE;;;;AC3BvE;;;;;;;iBD2BgB,oBAAA,CAAqB,OAAA,EAAS,gBAAA,GAAmB,MAAM;;;UC3BtD,oBAAA;EDKf;ECHA,IAAA;EDKS;ECHT,OAAA;EDKc;ECHd,cAAA;AAAA;AAAA,iBAGc,kBAAA,CACd,OAAA,EAAS,oBAAA,GACR,iBAAA,CAAkB,mBAAA;EAAyB,SAAA,IAAa,MAAA;AAAA"}
|
package/dist/node/index.js
CHANGED
|
@@ -1,130 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
-
function createAgentMcpServer(options) {
|
|
5
|
-
const { name, version, session, exposeCommands = true } = options;
|
|
6
|
-
const server = new Server({ name, version }, { capabilities: { tools: {} } });
|
|
7
|
-
const tools = [
|
|
8
|
-
{
|
|
9
|
-
name: "submit",
|
|
10
|
-
description: "Submit a prompt to the AI agent and wait for the response",
|
|
11
|
-
inputSchema: {
|
|
12
|
-
type: "object",
|
|
13
|
-
properties: {
|
|
14
|
-
prompt: { type: "string", description: "The prompt to send to the agent" }
|
|
15
|
-
},
|
|
16
|
-
required: ["prompt"]
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
];
|
|
20
|
-
if (exposeCommands) {
|
|
21
|
-
for (const cmd of session.listCommands()) {
|
|
22
|
-
tools.push({
|
|
23
|
-
name: `command_${cmd.name}`,
|
|
24
|
-
description: cmd.description,
|
|
25
|
-
inputSchema: {
|
|
26
|
-
type: "object",
|
|
27
|
-
properties: {
|
|
28
|
-
args: { type: "string", description: "Command arguments" }
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
35
|
-
tools
|
|
36
|
-
}));
|
|
37
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
38
|
-
const { name: toolName, arguments: toolArgs } = request.params;
|
|
39
|
-
if (toolName === "submit") {
|
|
40
|
-
const prompt = toolArgs?.prompt;
|
|
41
|
-
if (!prompt) {
|
|
42
|
-
return {
|
|
43
|
-
content: [{ type: "text", text: "Error: prompt is required" }],
|
|
44
|
-
isError: true
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
const result = await waitForCompletion(session, prompt);
|
|
48
|
-
return {
|
|
49
|
-
content: [{ type: "text", text: result.response }]
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
if (toolName.startsWith("command_")) {
|
|
53
|
-
const cmdName = toolName.slice("command_".length);
|
|
54
|
-
const args = toolArgs?.args ?? "";
|
|
55
|
-
const result = await session.executeCommand(cmdName, args);
|
|
56
|
-
return {
|
|
57
|
-
content: [
|
|
58
|
-
{
|
|
59
|
-
type: "text",
|
|
60
|
-
text: result?.message ?? `Unknown command: ${cmdName}`
|
|
61
|
-
}
|
|
62
|
-
],
|
|
63
|
-
isError: !result
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
return {
|
|
67
|
-
content: [{ type: "text", text: `Unknown tool: ${toolName}` }],
|
|
68
|
-
isError: true
|
|
69
|
-
};
|
|
70
|
-
});
|
|
71
|
-
return server;
|
|
72
|
-
}
|
|
73
|
-
function waitForCompletion(session, prompt) {
|
|
74
|
-
return new Promise((resolve, reject) => {
|
|
75
|
-
const onComplete = (result) => {
|
|
76
|
-
cleanup();
|
|
77
|
-
resolve(result);
|
|
78
|
-
};
|
|
79
|
-
const onInterrupted = (result) => {
|
|
80
|
-
cleanup();
|
|
81
|
-
resolve(result);
|
|
82
|
-
};
|
|
83
|
-
const onError = (error) => {
|
|
84
|
-
cleanup();
|
|
85
|
-
reject(error);
|
|
86
|
-
};
|
|
87
|
-
const cleanup = () => {
|
|
88
|
-
session.off("complete", onComplete);
|
|
89
|
-
session.off("interrupted", onInterrupted);
|
|
90
|
-
session.off("error", onError);
|
|
91
|
-
};
|
|
92
|
-
session.on("complete", onComplete);
|
|
93
|
-
session.on("interrupted", onInterrupted);
|
|
94
|
-
session.on("error", onError);
|
|
95
|
-
session.submit(prompt).catch((err) => {
|
|
96
|
-
cleanup();
|
|
97
|
-
reject(err instanceof Error ? err : new Error(String(err)));
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// src/mcp-transport.ts
|
|
103
|
-
function createMcpTransport(options) {
|
|
104
|
-
let session = null;
|
|
105
|
-
let server = null;
|
|
106
|
-
return {
|
|
107
|
-
name: "mcp",
|
|
108
|
-
attach(s) {
|
|
109
|
-
session = s;
|
|
110
|
-
},
|
|
111
|
-
async start() {
|
|
112
|
-
if (!session) throw new Error("No session attached. Call attach() first.");
|
|
113
|
-
server = createAgentMcpServer({ ...options, session });
|
|
114
|
-
},
|
|
115
|
-
async stop() {
|
|
116
|
-
if (server) {
|
|
117
|
-
await server.close();
|
|
118
|
-
server = null;
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
getServer() {
|
|
122
|
-
if (!server) throw new Error("Transport not started. Call start() first.");
|
|
123
|
-
return server;
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
export {
|
|
128
|
-
createAgentMcpServer,
|
|
129
|
-
createMcpTransport
|
|
130
|
-
};
|
|
1
|
+
import{Server as e}from"@modelcontextprotocol/sdk/server/index.js";import{CallToolRequestSchema as t,ListToolsRequestSchema as n}from"@modelcontextprotocol/sdk/types.js";function r(r){let{name:a,version:o,session:s,exposeCommands:c=!0}=r,l=new e({name:a,version:o},{capabilities:{tools:{}}}),u=[{name:`submit`,description:`Submit a prompt to the AI agent and wait for the response`,inputSchema:{type:`object`,properties:{prompt:{type:`string`,description:`The prompt to send to the agent`}},required:[`prompt`]}}];if(c)for(let e of s.listCommands())u.push({name:`command_${e.name}`,description:e.description,inputSchema:{type:`object`,properties:{args:{type:`string`,description:`Command arguments`}}}});return l.setRequestHandler(n,async()=>({tools:u})),l.setRequestHandler(t,async e=>{let{name:t,arguments:n}=e.params;if(t===`submit`){let e=n?.prompt;return e?{content:[{type:`text`,text:(await i(s,e)).response}]}:{content:[{type:`text`,text:`Error: prompt is required`}],isError:!0}}if(t.startsWith(`command_`)){let e=t.slice(8),r=n?.args??``,i=await s.executeCommand(e,r);return{content:[{type:`text`,text:i?.message??`Unknown command: ${e}`}],isError:!i}}return{content:[{type:`text`,text:`Unknown tool: ${t}`}],isError:!0}}),l}function i(e,t){return new Promise((n,r)=>{let i=e=>{s(),n(e)},a=e=>{s(),n(e)},o=e=>{s(),r(e)},s=()=>{e.off(`complete`,i),e.off(`interrupted`,a),e.off(`error`,o)};e.on(`complete`,i),e.on(`interrupted`,a),e.on(`error`,o),e.submit(t).catch(e=>{s(),r(e instanceof Error?e:Error(String(e)))})})}function a(e){let t=null,n=null;return{name:`mcp`,attach(e){t=e},async start(){if(!t)throw Error(`No session attached. Call attach() first.`);n=r({...e,session:t})},async stop(){n&&=(await n.close(),null)},getServer(){if(!n)throw Error(`Transport not started. Call start() first.`);return n}}}export{r as createAgentMcpServer,a as createMcpTransport};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/mcp-server.ts","../../src/mcp-transport.ts"],"sourcesContent":["/**\n * MCP transport adapter — exposes IInteractiveSession as an MCP server.\n *\n * Uses the low-level MCP Server class to avoid TypeScript depth issues\n * with McpServer.registerTool() generics. Registers tools/list and\n * tools/call handlers directly.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';\n\nimport type { IExecutionResult, IInteractiveSession } from '@robota-sdk/agent-interface-transport';\n\nexport interface IAgentMcpOptions {\n /** Name for the MCP server. */\n name: string;\n /** Version string. */\n version: string;\n /** IInteractiveSession to expose. */\n session: IInteractiveSession;\n /** If true, register each system command as a separate MCP tool. Default: true. */\n exposeCommands?: boolean;\n}\n\n/**\n * Create an MCP server that exposes IInteractiveSession over Model Context Protocol.\n *\n * Usage:\n * ```typescript\n * const server = createAgentMcpServer({\n * name: 'robota-agent',\n * version: '1.0.0',\n * session: interactiveSession,\n * });\n *\n * import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\n * await server.connect(new StdioServerTransport());\n * ```\n */\nexport function createAgentMcpServer(options: IAgentMcpOptions): Server {\n const { name, version, session, exposeCommands = true } = options;\n\n const server = new Server({ name, version }, { capabilities: { tools: {} } });\n\n // Build tool definitions\n const tools: Array<{\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n }> = [\n {\n name: 'submit',\n description: 'Submit a prompt to the AI agent and wait for the response',\n inputSchema: {\n type: 'object',\n properties: {\n prompt: { type: 'string', description: 'The prompt to send to the agent' },\n },\n required: ['prompt'],\n },\n },\n ];\n\n if (exposeCommands) {\n for (const cmd of session.listCommands()) {\n tools.push({\n name: `command_${cmd.name}`,\n description: cmd.description,\n inputSchema: {\n type: 'object',\n properties: {\n args: { type: 'string', description: 'Command arguments' },\n },\n },\n });\n }\n }\n\n // tools/list handler\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools,\n }));\n\n // tools/call handler\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name: toolName, arguments: toolArgs } = request.params;\n\n if (toolName === 'submit') {\n const prompt = (toolArgs as Record<string, string>)?.prompt;\n if (!prompt) {\n return {\n content: [{ type: 'text', text: 'Error: prompt is required' }],\n isError: true,\n };\n }\n const result = await waitForCompletion(session, prompt);\n return {\n content: [{ type: 'text', text: result.response }],\n };\n }\n\n // System commands: command_<name>\n if (toolName.startsWith('command_')) {\n const cmdName = toolName.slice('command_'.length);\n const args = (toolArgs as Record<string, string>)?.args ?? '';\n const result = await session.executeCommand(cmdName, args);\n return {\n content: [\n {\n type: 'text',\n text: result?.message ?? `Unknown command: ${cmdName}`,\n },\n ],\n isError: !result,\n };\n }\n\n return {\n content: [{ type: 'text', text: `Unknown tool: ${toolName}` }],\n isError: true,\n };\n });\n\n return server;\n}\n\n/**\n * Submit a prompt and wait for the complete/interrupted/error event.\n */\nfunction waitForCompletion(\n session: IInteractiveSession,\n prompt: string,\n): Promise<IExecutionResult> {\n return new Promise((resolve, reject) => {\n const onComplete = (result: IExecutionResult): void => {\n cleanup();\n resolve(result);\n };\n const onInterrupted = (result: IExecutionResult): void => {\n cleanup();\n resolve(result);\n };\n const onError = (error: Error): void => {\n cleanup();\n reject(error);\n };\n\n const cleanup = (): void => {\n session.off('complete', onComplete);\n session.off('interrupted', onInterrupted);\n session.off('error', onError);\n };\n\n session.on('complete', onComplete);\n session.on('interrupted', onInterrupted);\n session.on('error', onError);\n\n session.submit(prompt).catch((err) => {\n cleanup();\n reject(err instanceof Error ? err : new Error(String(err)));\n });\n });\n}\n","/**\n * ITransportAdapter implementation for MCP transport.\n *\n * Wraps createAgentMcpServer into the unified ITransportAdapter interface\n * while exposing the underlying MCP Server via getServer().\n */\n\nimport { createAgentMcpServer } from './mcp-server.js';\n\nimport type { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport type { IInteractiveSession, ITransportAdapter } from '@robota-sdk/agent-interface-transport';\n\nexport interface IMcpTransportOptions {\n /** Name for the MCP server. */\n name: string;\n /** Version string. */\n version: string;\n /** If true, register each system command as a separate MCP tool. Default: true. */\n exposeCommands?: boolean;\n}\n\nexport function createMcpTransport(\n options: IMcpTransportOptions,\n): ITransportAdapter<IInteractiveSession> & { getServer(): Server } {\n let session: IInteractiveSession | null = null;\n let server: Server | null = null;\n\n return {\n name: 'mcp',\n attach(s: IInteractiveSession) {\n session = s;\n },\n async start() {\n if (!session) throw new Error('No session attached. Call attach() first.');\n server = createAgentMcpServer({ ...options, session });\n },\n async stop() {\n if (server) {\n await server.close();\n server = null;\n }\n },\n getServer() {\n if (!server) throw new Error('Transport not started. Call start() first.');\n return server;\n },\n };\n}\n"],"mappings":"0KAuCA,SAAgB,EAAqB,EAAmC,CACtE,GAAM,CAAE,OAAM,UAAS,UAAS,iBAAiB,IAAS,EAEpD,EAAS,IAAI,EAAO,CAAE,OAAM,SAAQ,EAAG,CAAE,aAAc,CAAE,MAAO,CAAC,CAAE,CAAE,CAAC,EAGtE,EAID,CACH,CACE,KAAM,SACN,YAAa,4DACb,YAAa,CACX,KAAM,SACN,WAAY,CACV,OAAQ,CAAE,KAAM,SAAU,YAAa,iCAAkC,CAC3E,EACA,SAAU,CAAC,QAAQ,CACrB,CACF,CACF,EAEA,GAAI,EACF,IAAK,IAAM,KAAO,EAAQ,aAAa,EACrC,EAAM,KAAK,CACT,KAAM,WAAW,EAAI,OACrB,YAAa,EAAI,YACjB,YAAa,CACX,KAAM,SACN,WAAY,CACV,KAAM,CAAE,KAAM,SAAU,YAAa,mBAAoB,CAC3D,CACF,CACF,CAAC,EAiDL,OA5CA,EAAO,kBAAkB,EAAwB,UAAa,CAC5D,OACF,EAAE,EAGF,EAAO,kBAAkB,EAAuB,KAAO,IAAY,CACjE,GAAM,CAAE,KAAM,EAAU,UAAW,GAAa,EAAQ,OAExD,GAAI,IAAa,SAAU,CACzB,IAAM,EAAU,GAAqC,OAQrD,OAPK,EAOE,CACL,QAAS,CAAC,CAAE,KAAM,OAAQ,MAAM,MAFb,EAAkB,EAAS,CAAM,EAAA,CAEb,QAAS,CAAC,CACnD,EARS,CACL,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,2BAA4B,CAAC,EAC7D,QAAS,EACX,CAMJ,CAGA,GAAI,EAAS,WAAW,UAAU,EAAG,CACnC,IAAM,EAAU,EAAS,MAAM,CAAiB,EAC1C,EAAQ,GAAqC,MAAQ,GACrD,EAAS,MAAM,EAAQ,eAAe,EAAS,CAAI,EACzD,MAAO,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,GAAQ,SAAW,oBAAoB,GAC/C,CACF,EACA,QAAS,CAAC,CACZ,CACF,CAEA,MAAO,CACL,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,iBAAiB,GAAW,CAAC,EAC7D,QAAS,EACX,CACF,CAAC,EAEM,CACT,CAKA,SAAS,EACP,EACA,EAC2B,CAC3B,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,IAAM,EAAc,GAAmC,CACrD,EAAQ,EACR,EAAQ,CAAM,CAChB,EACM,EAAiB,GAAmC,CACxD,EAAQ,EACR,EAAQ,CAAM,CAChB,EACM,EAAW,GAAuB,CACtC,EAAQ,EACR,EAAO,CAAK,CACd,EAEM,MAAsB,CAC1B,EAAQ,IAAI,WAAY,CAAU,EAClC,EAAQ,IAAI,cAAe,CAAa,EACxC,EAAQ,IAAI,QAAS,CAAO,CAC9B,EAEA,EAAQ,GAAG,WAAY,CAAU,EACjC,EAAQ,GAAG,cAAe,CAAa,EACvC,EAAQ,GAAG,QAAS,CAAO,EAE3B,EAAQ,OAAO,CAAM,CAAC,CAAC,MAAO,GAAQ,CACpC,EAAQ,EACR,EAAO,aAAe,MAAQ,EAAU,MAAM,OAAO,CAAG,CAAC,CAAC,CAC5D,CAAC,CACH,CAAC,CACH,CC7IA,SAAgB,EACd,EACkE,CAClE,IAAI,EAAsC,KACtC,EAAwB,KAE5B,MAAO,CACL,KAAM,MACN,OAAO,EAAwB,CAC7B,EAAU,CACZ,EACA,MAAM,OAAQ,CACZ,GAAI,CAAC,EAAS,MAAU,MAAM,2CAA2C,EACzE,EAAS,EAAqB,CAAE,GAAG,EAAS,SAAQ,CAAC,CACvD,EACA,MAAM,MAAO,CACX,AAEE,KADA,MAAM,EAAO,MAAM,EACV,KAEb,EACA,WAAY,CACV,GAAI,CAAC,EAAQ,MAAU,MAAM,4CAA4C,EACzE,OAAO,CACT,CACF,CACF"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robota-sdk/agent-transport-mcp",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.0.0-beta.76",
|
|
4
|
+
"description": "Model Context Protocol (MCP) server transport for the Robota SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/node/index.js",
|
|
7
7
|
"types": "dist/node/index.d.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./dist/node/index.d.ts",
|
|
11
|
+
"source": "./src/index.ts",
|
|
11
12
|
"node": {
|
|
12
13
|
"import": "./dist/node/index.js",
|
|
13
14
|
"require": "./dist/node/index.cjs"
|
|
@@ -26,32 +27,34 @@
|
|
|
26
27
|
"bugs": {
|
|
27
28
|
"url": "https://github.com/woojubb/robota/issues"
|
|
28
29
|
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=22.0.0"
|
|
32
|
+
},
|
|
29
33
|
"files": [
|
|
30
|
-
"dist"
|
|
34
|
+
"dist",
|
|
35
|
+
"src"
|
|
31
36
|
],
|
|
32
37
|
"dependencies": {
|
|
33
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
34
|
-
"
|
|
35
|
-
"@robota-sdk/agent-sdk": "3.0.0-beta.63"
|
|
38
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
39
|
+
"@robota-sdk/agent-interface-transport": "3.0.0-beta.76"
|
|
36
40
|
},
|
|
37
41
|
"devDependencies": {
|
|
38
|
-
"rimraf": "^5.0.
|
|
39
|
-
"
|
|
40
|
-
"typescript": "^5.
|
|
41
|
-
"vitest": "^
|
|
42
|
+
"rimraf": "^5.0.10",
|
|
43
|
+
"tsdown": "^0.22.2",
|
|
44
|
+
"typescript": "^5.9.3",
|
|
45
|
+
"vitest": "^3.2.6"
|
|
42
46
|
},
|
|
43
47
|
"license": "MIT",
|
|
44
48
|
"publishConfig": {
|
|
45
49
|
"access": "public"
|
|
46
50
|
},
|
|
47
51
|
"scripts": {
|
|
48
|
-
"build": "
|
|
49
|
-
"build:js": "
|
|
50
|
-
"build:types": "
|
|
52
|
+
"build": "tsdown",
|
|
53
|
+
"build:js": "tsdown --no-dts",
|
|
54
|
+
"build:types": "tsdown --dts",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
51
56
|
"test": "vitest run --passWithNoTests",
|
|
52
57
|
"test:coverage": "vitest run --coverage --passWithNoTests",
|
|
53
|
-
"typecheck": "tsc --noEmit",
|
|
54
|
-
"lint": "eslint src/ --ext .ts",
|
|
55
58
|
"clean": "rimraf dist"
|
|
56
59
|
}
|
|
57
60
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
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/index.ts
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
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
|
+
}
|
package/dist/node/index.d.cts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
-
import { InteractiveSession, ITransportAdapter } from '@robota-sdk/agent-sdk';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* MCP transport adapter — exposes InteractiveSession as an MCP server.
|
|
6
|
-
*
|
|
7
|
-
* Uses the low-level MCP Server class to avoid TypeScript depth issues
|
|
8
|
-
* with McpServer.registerTool() generics. Registers tools/list and
|
|
9
|
-
* tools/call handlers directly.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
interface IAgentMcpOptions {
|
|
13
|
-
/** Name for the MCP server. */
|
|
14
|
-
name: string;
|
|
15
|
-
/** Version string. */
|
|
16
|
-
version: string;
|
|
17
|
-
/** InteractiveSession to expose. */
|
|
18
|
-
session: InteractiveSession;
|
|
19
|
-
/** If true, register each system command as a separate MCP tool. Default: true. */
|
|
20
|
-
exposeCommands?: boolean;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Create an MCP server that exposes InteractiveSession over Model Context Protocol.
|
|
24
|
-
*
|
|
25
|
-
* Usage:
|
|
26
|
-
* ```typescript
|
|
27
|
-
* const server = createAgentMcpServer({
|
|
28
|
-
* name: 'robota-agent',
|
|
29
|
-
* version: '1.0.0',
|
|
30
|
-
* session: interactiveSession,
|
|
31
|
-
* });
|
|
32
|
-
*
|
|
33
|
-
* import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
34
|
-
* await server.connect(new StdioServerTransport());
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
declare function createAgentMcpServer(options: IAgentMcpOptions): Server;
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* ITransportAdapter implementation for MCP transport.
|
|
41
|
-
*
|
|
42
|
-
* Wraps createAgentMcpServer into the unified ITransportAdapter interface
|
|
43
|
-
* while exposing the underlying MCP Server via getServer().
|
|
44
|
-
*/
|
|
45
|
-
|
|
46
|
-
interface IMcpTransportOptions {
|
|
47
|
-
/** Name for the MCP server. */
|
|
48
|
-
name: string;
|
|
49
|
-
/** Version string. */
|
|
50
|
-
version: string;
|
|
51
|
-
/** If true, register each system command as a separate MCP tool. Default: true. */
|
|
52
|
-
exposeCommands?: boolean;
|
|
53
|
-
}
|
|
54
|
-
declare function createMcpTransport(options: IMcpTransportOptions): ITransportAdapter & {
|
|
55
|
-
getServer(): Server;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export { type IAgentMcpOptions, type IMcpTransportOptions, createAgentMcpServer, createMcpTransport };
|