wraptc 1.0.3 → 1.0.4
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/bin/wraptc +4 -4
- package/package.json +2 -2
- package/src/cli/__tests__/cli.test.ts +337 -0
- package/src/cli/index.ts +149 -0
- package/src/core/__tests__/fixtures/configs/project-config.json +14 -0
- package/src/core/__tests__/fixtures/configs/system-config.json +14 -0
- package/src/core/__tests__/fixtures/configs/user-config.json +15 -0
- package/src/core/__tests__/integration/integration.test.ts +241 -0
- package/src/core/__tests__/integration/mock-coder-adapter.test.ts +243 -0
- package/src/core/__tests__/test-utils.ts +136 -0
- package/src/core/__tests__/unit/adapters/runner.test.ts +302 -0
- package/src/core/__tests__/unit/basic-test.test.ts +44 -0
- package/src/core/__tests__/unit/basic.test.ts +12 -0
- package/src/core/__tests__/unit/config.test.ts +244 -0
- package/src/core/__tests__/unit/error-patterns.test.ts +181 -0
- package/src/core/__tests__/unit/memory-monitor.test.ts +354 -0
- package/src/core/__tests__/unit/plugin/registry.test.ts +356 -0
- package/src/core/__tests__/unit/providers/codex.test.ts +173 -0
- package/src/core/__tests__/unit/providers/configurable.test.ts +429 -0
- package/src/core/__tests__/unit/providers/gemini.test.ts +251 -0
- package/src/core/__tests__/unit/providers/opencode.test.ts +258 -0
- package/src/core/__tests__/unit/providers/qwen-code.test.ts +195 -0
- package/src/core/__tests__/unit/providers/simple-codex.test.ts +18 -0
- package/src/core/__tests__/unit/router.test.ts +967 -0
- package/src/core/__tests__/unit/state.test.ts +1079 -0
- package/src/core/__tests__/unit/unified/capabilities.test.ts +186 -0
- package/src/core/__tests__/unit/wrap-terminalcoder.test.ts +32 -0
- package/src/core/adapters/builtin/codex.ts +35 -0
- package/src/core/adapters/builtin/gemini.ts +34 -0
- package/src/core/adapters/builtin/index.ts +31 -0
- package/src/core/adapters/builtin/mock-coder.ts +148 -0
- package/src/core/adapters/builtin/qwen.ts +34 -0
- package/src/core/adapters/define.ts +48 -0
- package/src/core/adapters/index.ts +43 -0
- package/src/core/adapters/loader.ts +143 -0
- package/src/core/adapters/provider-bridge.ts +190 -0
- package/src/core/adapters/runner.ts +437 -0
- package/src/core/adapters/types.ts +172 -0
- package/src/core/config.ts +290 -0
- package/src/core/define-provider.ts +212 -0
- package/src/core/error-patterns.ts +147 -0
- package/src/core/index.ts +130 -0
- package/src/core/memory-monitor.ts +171 -0
- package/src/core/plugin/builtin.ts +87 -0
- package/src/core/plugin/index.ts +34 -0
- package/src/core/plugin/registry.ts +350 -0
- package/src/core/plugin/types.ts +209 -0
- package/src/core/provider-factory.ts +397 -0
- package/src/core/provider-loader.ts +171 -0
- package/src/core/providers/codex.ts +56 -0
- package/src/core/providers/configurable.ts +637 -0
- package/src/core/providers/custom.ts +261 -0
- package/src/core/providers/gemini.ts +41 -0
- package/src/core/providers/index.ts +383 -0
- package/src/core/providers/opencode.ts +168 -0
- package/src/core/providers/qwen-code.ts +41 -0
- package/src/core/router.ts +370 -0
- package/src/core/state.ts +258 -0
- package/src/core/types.ts +206 -0
- package/src/core/unified/capabilities.ts +184 -0
- package/src/core/unified/errors.ts +141 -0
- package/src/core/unified/index.ts +29 -0
- package/src/core/unified/output.ts +189 -0
- package/src/core/wrap-terminalcoder.ts +245 -0
- package/src/mcp/__tests__/server.test.ts +295 -0
- package/src/mcp/server.ts +284 -0
- package/src/test-fixtures/mock-coder.sh +194 -0
- package/dist/cli/index.js +0 -16501
- package/dist/core/index.js +0 -7531
- package/dist/mcp/server.js +0 -14568
- package/dist/wraptc-1.0.3.tgz +0 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, spyOn, test } from "bun:test";
|
|
2
|
+
import type { CodingRequest } from "../../core/index.js";
|
|
3
|
+
import { WrapTerminalCoderMCPServer } from "../server.js";
|
|
4
|
+
|
|
5
|
+
// Mock WrapTerminalCoder
|
|
6
|
+
const mockRoute = mock(async (request: CodingRequest) => ({
|
|
7
|
+
provider: "gemini",
|
|
8
|
+
text: "Generated response",
|
|
9
|
+
usage: { inputTokens: 10, outputTokens: 20 },
|
|
10
|
+
meta: { elapsedMs: 1500 },
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
const mockGetProviderInfo = mock(async () => [
|
|
14
|
+
{
|
|
15
|
+
id: "gemini",
|
|
16
|
+
displayName: "Gemini CLI",
|
|
17
|
+
requestsToday: 5,
|
|
18
|
+
outOfCreditsUntil: null,
|
|
19
|
+
available: true,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "codex",
|
|
23
|
+
displayName: "Codex CLI",
|
|
24
|
+
requestsToday: 2,
|
|
25
|
+
outOfCreditsUntil: new Date(Date.now() + 3600000), // 1 hour from now
|
|
26
|
+
available: true,
|
|
27
|
+
},
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
const mockWrapTerminalCoder = {
|
|
31
|
+
route: mockRoute,
|
|
32
|
+
getProviderInfo: mockGetProviderInfo,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
mock.module("../../core/index.js", () => ({
|
|
36
|
+
WrapTerminalCoder: {
|
|
37
|
+
create: mock(async () => mockWrapTerminalCoder),
|
|
38
|
+
},
|
|
39
|
+
CodingRequestSchema: {
|
|
40
|
+
safeParse: mock((data: any) => ({
|
|
41
|
+
success: true,
|
|
42
|
+
data,
|
|
43
|
+
})),
|
|
44
|
+
},
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
// Mock MCP SDK
|
|
48
|
+
const mockSetRequestHandler = mock();
|
|
49
|
+
const mockConnect = mock(async () => {});
|
|
50
|
+
|
|
51
|
+
const mockServer = {
|
|
52
|
+
setRequestHandler: mockSetRequestHandler,
|
|
53
|
+
connect: mockConnect,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
mock.module("@modelcontextprotocol/sdk/server/index.js", () => ({
|
|
57
|
+
Server: mock(() => mockServer),
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
mock.module("@modelcontextprotocol/sdk/server/stdio.js", () => ({
|
|
61
|
+
StdioServerTransport: mock(() => ({})),
|
|
62
|
+
}));
|
|
63
|
+
|
|
64
|
+
mock.module("@modelcontextprotocol/sdk/types.js", () => ({
|
|
65
|
+
CallToolRequestSchema: "call-tool-schema",
|
|
66
|
+
ListToolsRequestSchema: "list-tools-schema",
|
|
67
|
+
}));
|
|
68
|
+
|
|
69
|
+
describe("WrapTerminalCoderMCPServer", () => {
|
|
70
|
+
let server: WrapTerminalCoderMCPServer;
|
|
71
|
+
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
// Reset mocks
|
|
74
|
+
mockSetRequestHandler.mockClear();
|
|
75
|
+
mockConnect.mockClear();
|
|
76
|
+
mockRoute.mockClear();
|
|
77
|
+
mockGetProviderInfo.mockClear();
|
|
78
|
+
|
|
79
|
+
server = new WrapTerminalCoderMCPServer();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("constructor", () => {
|
|
83
|
+
test("should create server with request handlers", () => {
|
|
84
|
+
expect(mockSetRequestHandler).toHaveBeenCalledTimes(2);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe("tool handlers", () => {
|
|
89
|
+
describe("ListTools", () => {
|
|
90
|
+
test("should return list of available tools", async () => {
|
|
91
|
+
const listToolsHandler = mockSetRequestHandler.mock.calls.find(
|
|
92
|
+
(call: any[]) => call[0] === "list-tools-schema",
|
|
93
|
+
)?.[1];
|
|
94
|
+
|
|
95
|
+
expect(listToolsHandler).toBeDefined();
|
|
96
|
+
const result = await listToolsHandler?.();
|
|
97
|
+
|
|
98
|
+
expect(result.tools).toHaveLength(3);
|
|
99
|
+
expect(result.tools.map((t: any) => t.name)).toEqual([
|
|
100
|
+
"run_coding_task",
|
|
101
|
+
"get_providers",
|
|
102
|
+
"dry_run",
|
|
103
|
+
]);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("should include tool schemas with correct properties", async () => {
|
|
107
|
+
const listToolsHandler = mockSetRequestHandler.mock.calls.find(
|
|
108
|
+
(call: any[]) => call[0] === "list-tools-schema",
|
|
109
|
+
)?.[1];
|
|
110
|
+
|
|
111
|
+
const result = await listToolsHandler?.();
|
|
112
|
+
|
|
113
|
+
const runCodingTask = result.tools.find((t: any) => t.name === "run_coding_task");
|
|
114
|
+
expect(runCodingTask.inputSchema.properties.prompt.type).toBe("string");
|
|
115
|
+
expect(runCodingTask.inputSchema.properties.mode.enum).toEqual([
|
|
116
|
+
"generate",
|
|
117
|
+
"edit",
|
|
118
|
+
"explain",
|
|
119
|
+
"test",
|
|
120
|
+
]);
|
|
121
|
+
expect(runCodingTask.inputSchema.required).toEqual(["prompt"]);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("CallTool - run_coding_task", () => {
|
|
126
|
+
let callToolHandler: Function;
|
|
127
|
+
|
|
128
|
+
beforeEach(() => {
|
|
129
|
+
const handler = mockSetRequestHandler.mock.calls.find(
|
|
130
|
+
(call: any[]) => call[0] === "call-tool-schema",
|
|
131
|
+
)?.[1];
|
|
132
|
+
callToolHandler = handler!;
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("should execute coding task successfully", async () => {
|
|
136
|
+
const request = {
|
|
137
|
+
params: {
|
|
138
|
+
name: "run_coding_task",
|
|
139
|
+
arguments: {
|
|
140
|
+
prompt: "Write a function to calculate fibonacci",
|
|
141
|
+
mode: "generate",
|
|
142
|
+
language: "typescript",
|
|
143
|
+
files: ["utils.ts"],
|
|
144
|
+
provider: "gemini",
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const result = await callToolHandler(request);
|
|
150
|
+
|
|
151
|
+
expect(result.content[0].type).toBe("text");
|
|
152
|
+
const parsed = JSON.parse(result.content[0].text);
|
|
153
|
+
expect(parsed).toEqual({
|
|
154
|
+
provider: "gemini",
|
|
155
|
+
text: "Generated response",
|
|
156
|
+
usage: { inputTokens: 10, outputTokens: 20 },
|
|
157
|
+
meta: { elapsedMs: 1500 },
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("should handle errors gracefully", async () => {
|
|
162
|
+
// Mock route to throw
|
|
163
|
+
mockRoute.mockImplementationOnce(async () => {
|
|
164
|
+
throw new Error("Provider failed");
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const request = {
|
|
168
|
+
params: {
|
|
169
|
+
name: "run_coding_task",
|
|
170
|
+
arguments: {
|
|
171
|
+
prompt: "Test prompt",
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const result = await callToolHandler(request);
|
|
177
|
+
|
|
178
|
+
expect(result.isError).toBe(true);
|
|
179
|
+
expect(result.content[0].text).toContain("Error:");
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe("CallTool - get_providers", () => {
|
|
184
|
+
let callToolHandler: Function;
|
|
185
|
+
|
|
186
|
+
beforeEach(() => {
|
|
187
|
+
const handler = mockSetRequestHandler.mock.calls.find(
|
|
188
|
+
(call: any[]) => call[0] === "call-tool-schema",
|
|
189
|
+
)?.[1];
|
|
190
|
+
callToolHandler = handler!;
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("should return provider information", async () => {
|
|
194
|
+
const request = {
|
|
195
|
+
params: {
|
|
196
|
+
name: "get_providers",
|
|
197
|
+
arguments: {},
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const result = await callToolHandler(request);
|
|
202
|
+
|
|
203
|
+
expect(result.content[0].type).toBe("text");
|
|
204
|
+
const parsed = JSON.parse(result.content[0].text);
|
|
205
|
+
expect(parsed).toHaveLength(2);
|
|
206
|
+
expect(parsed[0].id).toBe("gemini");
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe("CallTool - dry_run", () => {
|
|
211
|
+
let callToolHandler: Function;
|
|
212
|
+
|
|
213
|
+
beforeEach(() => {
|
|
214
|
+
const handler = mockSetRequestHandler.mock.calls.find(
|
|
215
|
+
(call: any[]) => call[0] === "call-tool-schema",
|
|
216
|
+
)?.[1];
|
|
217
|
+
callToolHandler = handler!;
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("should return available providers for request", async () => {
|
|
221
|
+
const request = {
|
|
222
|
+
params: {
|
|
223
|
+
name: "dry_run",
|
|
224
|
+
arguments: {
|
|
225
|
+
prompt: "Test prompt",
|
|
226
|
+
mode: "generate",
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const result = await callToolHandler(request);
|
|
232
|
+
|
|
233
|
+
expect(result.content[0].type).toBe("text");
|
|
234
|
+
const parsed = JSON.parse(result.content[0].text);
|
|
235
|
+
|
|
236
|
+
expect(parsed.request.prompt).toBe("Test prompt");
|
|
237
|
+
expect(parsed.request.mode).toBe("generate");
|
|
238
|
+
expect(parsed.availableProviders).toBeDefined();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("should filter out providers that are out of credits", async () => {
|
|
242
|
+
const request = {
|
|
243
|
+
params: {
|
|
244
|
+
name: "dry_run",
|
|
245
|
+
arguments: {
|
|
246
|
+
prompt: "Test prompt",
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const result = await callToolHandler(request);
|
|
252
|
+
|
|
253
|
+
const parsed = JSON.parse(result.content[0].text);
|
|
254
|
+
// Should only include gemini since codex is out of credits
|
|
255
|
+
expect(parsed.availableProviders).toHaveLength(1);
|
|
256
|
+
expect(parsed.availableProviders[0].id).toBe("gemini");
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
describe("CallTool - unknown tool", () => {
|
|
261
|
+
let callToolHandler: Function;
|
|
262
|
+
|
|
263
|
+
beforeEach(() => {
|
|
264
|
+
const handler = mockSetRequestHandler.mock.calls.find(
|
|
265
|
+
(call: any[]) => call[0] === "call-tool-schema",
|
|
266
|
+
)?.[1];
|
|
267
|
+
callToolHandler = handler!;
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test("should return error for unknown tool", async () => {
|
|
271
|
+
const request = {
|
|
272
|
+
params: {
|
|
273
|
+
name: "unknown_tool",
|
|
274
|
+
arguments: {},
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const result = await callToolHandler(request);
|
|
279
|
+
|
|
280
|
+
expect(result.isError).toBe(true);
|
|
281
|
+
expect(result.content[0].text).toBe("Unknown tool: unknown_tool");
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe("run", () => {
|
|
287
|
+
test("should connect server to transport", async () => {
|
|
288
|
+
spyOn(console, "error").mockImplementation(() => {});
|
|
289
|
+
|
|
290
|
+
await server.run();
|
|
291
|
+
|
|
292
|
+
expect(mockConnect).toHaveBeenCalledTimes(1);
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
});
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { CodingRequestSchema, WrapTerminalCoder } from "../core/index.js";
|
|
8
|
+
import type { CodingRequest } from "../core/index.js";
|
|
9
|
+
|
|
10
|
+
// Tool schemas
|
|
11
|
+
const RunCodingTaskSchema = z.object({
|
|
12
|
+
prompt: z.string().min(1),
|
|
13
|
+
mode: z.enum(["generate", "edit", "explain", "test"]).default("generate"),
|
|
14
|
+
language: z.string().optional(),
|
|
15
|
+
files: z.array(z.string()).optional(),
|
|
16
|
+
provider: z.string().optional(),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const GetProvidersSchema = z.object({});
|
|
20
|
+
|
|
21
|
+
const DryRunSchema = z.object({
|
|
22
|
+
prompt: z.string().min(1),
|
|
23
|
+
mode: z.enum(["generate", "edit", "explain", "test"]).default("generate"),
|
|
24
|
+
provider: z.string().optional(),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
class WrapTerminalCoderMCPServer {
|
|
28
|
+
private server: Server;
|
|
29
|
+
private wtc?: WrapTerminalCoder;
|
|
30
|
+
|
|
31
|
+
constructor() {
|
|
32
|
+
this.server = new Server(
|
|
33
|
+
{
|
|
34
|
+
name: "wrap-terminalcoder-mcp",
|
|
35
|
+
version: "0.1.0",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
capabilities: {
|
|
39
|
+
tools: {},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
this.setupToolHandlers();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private async getWTC(): Promise<WrapTerminalCoder> {
|
|
48
|
+
if (!this.wtc) {
|
|
49
|
+
this.wtc = await WrapTerminalCoder.create();
|
|
50
|
+
}
|
|
51
|
+
return this.wtc;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private setupToolHandlers(): void {
|
|
55
|
+
// List available tools
|
|
56
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
57
|
+
return {
|
|
58
|
+
tools: [
|
|
59
|
+
{
|
|
60
|
+
name: "run_coding_task",
|
|
61
|
+
description: "Execute a coding task using the best available provider",
|
|
62
|
+
inputSchema: {
|
|
63
|
+
type: "object",
|
|
64
|
+
properties: {
|
|
65
|
+
prompt: {
|
|
66
|
+
type: "string",
|
|
67
|
+
description: "The coding task prompt",
|
|
68
|
+
},
|
|
69
|
+
mode: {
|
|
70
|
+
type: "string",
|
|
71
|
+
enum: ["generate", "edit", "explain", "test"],
|
|
72
|
+
description: "The type of coding task",
|
|
73
|
+
default: "generate",
|
|
74
|
+
},
|
|
75
|
+
language: {
|
|
76
|
+
type: "string",
|
|
77
|
+
description: "Language hint for the request",
|
|
78
|
+
optional: true,
|
|
79
|
+
},
|
|
80
|
+
files: {
|
|
81
|
+
type: "array",
|
|
82
|
+
items: { type: "string" },
|
|
83
|
+
description: "File paths for context",
|
|
84
|
+
optional: true,
|
|
85
|
+
},
|
|
86
|
+
provider: {
|
|
87
|
+
type: "string",
|
|
88
|
+
description: "Specific provider to use",
|
|
89
|
+
optional: true,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
required: ["prompt"],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "get_providers",
|
|
97
|
+
description: "List all available providers and their current status",
|
|
98
|
+
inputSchema: {
|
|
99
|
+
type: "object",
|
|
100
|
+
properties: {},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: "dry_run",
|
|
105
|
+
description: "Show which provider would be used for a request without executing it",
|
|
106
|
+
inputSchema: {
|
|
107
|
+
type: "object",
|
|
108
|
+
properties: {
|
|
109
|
+
prompt: {
|
|
110
|
+
type: "string",
|
|
111
|
+
description: "The coding task prompt",
|
|
112
|
+
},
|
|
113
|
+
mode: {
|
|
114
|
+
type: "string",
|
|
115
|
+
enum: ["generate", "edit", "explain", "test"],
|
|
116
|
+
description: "The type of coding task",
|
|
117
|
+
default: "generate",
|
|
118
|
+
},
|
|
119
|
+
provider: {
|
|
120
|
+
type: "string",
|
|
121
|
+
description: "Specific provider to use",
|
|
122
|
+
optional: true,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
required: ["prompt"],
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Handle tool calls
|
|
133
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
134
|
+
try {
|
|
135
|
+
const { name, arguments: args } = request.params;
|
|
136
|
+
|
|
137
|
+
switch (name) {
|
|
138
|
+
case "run_coding_task": {
|
|
139
|
+
const validated = RunCodingTaskSchema.parse(args);
|
|
140
|
+
|
|
141
|
+
const wtc = await this.getWTC();
|
|
142
|
+
|
|
143
|
+
const codingRequest: CodingRequest = {
|
|
144
|
+
prompt: validated.prompt,
|
|
145
|
+
mode: validated.mode,
|
|
146
|
+
language: validated.language,
|
|
147
|
+
fileContext: validated.files,
|
|
148
|
+
provider: validated.provider,
|
|
149
|
+
stream: false,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// Validate with Zod
|
|
153
|
+
const validation = CodingRequestSchema.safeParse(codingRequest);
|
|
154
|
+
if (!validation.success) {
|
|
155
|
+
return {
|
|
156
|
+
content: [
|
|
157
|
+
{
|
|
158
|
+
type: "text" as const,
|
|
159
|
+
text: `Invalid request: ${validation.error.message}`,
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
isError: true,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const response = await wtc.route(codingRequest);
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
content: [
|
|
170
|
+
{
|
|
171
|
+
type: "text" as const,
|
|
172
|
+
text: JSON.stringify(
|
|
173
|
+
{
|
|
174
|
+
provider: response.provider,
|
|
175
|
+
text: response.text,
|
|
176
|
+
usage: response.usage,
|
|
177
|
+
meta: response.meta,
|
|
178
|
+
},
|
|
179
|
+
null,
|
|
180
|
+
2,
|
|
181
|
+
),
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
case "get_providers": {
|
|
188
|
+
const wtc = await this.getWTC();
|
|
189
|
+
const providers = await wtc.getProviderInfo();
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
content: [
|
|
193
|
+
{
|
|
194
|
+
type: "text" as const,
|
|
195
|
+
text: JSON.stringify(providers, null, 2),
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
case "dry_run": {
|
|
202
|
+
const validated = DryRunSchema.parse(args);
|
|
203
|
+
|
|
204
|
+
const wtc = await this.getWTC();
|
|
205
|
+
|
|
206
|
+
const codingRequest: CodingRequest = {
|
|
207
|
+
prompt: validated.prompt,
|
|
208
|
+
mode: validated.mode,
|
|
209
|
+
provider: validated.provider,
|
|
210
|
+
stream: false,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// We can't directly access router's private methods, so we'll check provider availability
|
|
214
|
+
const providers = await wtc.getProviderInfo();
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
content: [
|
|
218
|
+
{
|
|
219
|
+
type: "text" as const,
|
|
220
|
+
text: JSON.stringify(
|
|
221
|
+
{
|
|
222
|
+
request: codingRequest,
|
|
223
|
+
availableProviders: providers.filter(
|
|
224
|
+
(p: { outOfCreditsUntil?: string | number | null }) =>
|
|
225
|
+
p.outOfCreditsUntil ? new Date(p.outOfCreditsUntil) <= new Date() : true,
|
|
226
|
+
),
|
|
227
|
+
},
|
|
228
|
+
null,
|
|
229
|
+
2,
|
|
230
|
+
),
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
default:
|
|
237
|
+
return {
|
|
238
|
+
content: [
|
|
239
|
+
{
|
|
240
|
+
type: "text" as const,
|
|
241
|
+
text: `Unknown tool: ${name}`,
|
|
242
|
+
},
|
|
243
|
+
],
|
|
244
|
+
isError: true,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
} catch (error) {
|
|
248
|
+
const err = error as Error;
|
|
249
|
+
return {
|
|
250
|
+
content: [
|
|
251
|
+
{
|
|
252
|
+
type: "text" as const,
|
|
253
|
+
text: `Error: ${err.message}`,
|
|
254
|
+
},
|
|
255
|
+
],
|
|
256
|
+
isError: true,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async run(): Promise<void> {
|
|
263
|
+
const transport = new StdioServerTransport();
|
|
264
|
+
await this.server.connect(transport);
|
|
265
|
+
console.error("Wrap TerminalCoder MCP server running on stdio");
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Exportable function to run MCP server
|
|
270
|
+
export async function runMCPServer(): Promise<void> {
|
|
271
|
+
const server = new WrapTerminalCoderMCPServer();
|
|
272
|
+
await server.run();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Run server if executed directly
|
|
276
|
+
if (import.meta.main) {
|
|
277
|
+
const server = new WrapTerminalCoderMCPServer();
|
|
278
|
+
server.run().catch((error) => {
|
|
279
|
+
console.error("Fatal error:", error);
|
|
280
|
+
process.exit(1);
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export { WrapTerminalCoderMCPServer };
|