@cpujia/codex-mcp-server 1.0.1 → 2.0.0
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/dist/codex-client.d.ts +15 -4
- package/dist/codex-client.js +18 -90
- package/dist/index.js +278 -131
- package/dist/tools.d.ts +23 -56
- package/dist/tools.js +72 -54
- package/package.json +1 -1
package/dist/codex-client.d.ts
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
|
+
type ReasoningEffort = "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
|
1
2
|
interface CodexClientConfig {
|
|
2
3
|
baseURL: string;
|
|
3
4
|
apiKey: string;
|
|
4
5
|
model: string;
|
|
5
6
|
timeout: number;
|
|
6
7
|
maxTokens: number;
|
|
7
|
-
reasoningEffort?:
|
|
8
|
+
reasoningEffort?: ReasoningEffort;
|
|
9
|
+
}
|
|
10
|
+
interface JsonSchemaFormat {
|
|
11
|
+
name: string;
|
|
12
|
+
schema: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
interface CodexRequestOptions {
|
|
15
|
+
instructions: string;
|
|
16
|
+
input: string;
|
|
17
|
+
jsonSchema?: JsonSchemaFormat;
|
|
18
|
+
reasoningEffort?: ReasoningEffort;
|
|
19
|
+
maxTokens?: number;
|
|
8
20
|
}
|
|
9
21
|
export declare class CodexClient {
|
|
10
22
|
private config;
|
|
11
23
|
constructor(config: CodexClientConfig);
|
|
12
|
-
|
|
13
|
-
generateStream(input: string, onChunk: (text: string) => void): Promise<void>;
|
|
24
|
+
request(options: CodexRequestOptions): Promise<string>;
|
|
14
25
|
}
|
|
15
|
-
export {};
|
|
26
|
+
export type { CodexClientConfig, CodexRequestOptions, JsonSchemaFormat, ReasoningEffort, };
|
package/dist/codex-client.js
CHANGED
|
@@ -3,23 +3,30 @@ export class CodexClient {
|
|
|
3
3
|
constructor(config) {
|
|
4
4
|
this.config = config;
|
|
5
5
|
}
|
|
6
|
-
async
|
|
6
|
+
async request(options) {
|
|
7
7
|
const controller = new AbortController();
|
|
8
8
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
9
9
|
try {
|
|
10
|
+
const effort = options.reasoningEffort ?? this.config.reasoningEffort;
|
|
11
|
+
const maxTokens = options.maxTokens ?? this.config.maxTokens;
|
|
10
12
|
const requestBody = {
|
|
11
13
|
model: this.config.model,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
},
|
|
17
|
-
],
|
|
18
|
-
max_output_tokens: this.config.maxTokens,
|
|
14
|
+
instructions: options.instructions,
|
|
15
|
+
input: [{ role: "user", content: options.input }],
|
|
16
|
+
max_output_tokens: maxTokens,
|
|
17
|
+
store: false,
|
|
19
18
|
};
|
|
20
|
-
if (
|
|
21
|
-
requestBody.reasoning = {
|
|
22
|
-
|
|
19
|
+
if (effort) {
|
|
20
|
+
requestBody.reasoning = { effort };
|
|
21
|
+
}
|
|
22
|
+
if (options.jsonSchema) {
|
|
23
|
+
requestBody.text = {
|
|
24
|
+
format: {
|
|
25
|
+
type: "json_schema",
|
|
26
|
+
name: options.jsonSchema.name,
|
|
27
|
+
strict: true,
|
|
28
|
+
schema: options.jsonSchema.schema,
|
|
29
|
+
},
|
|
23
30
|
};
|
|
24
31
|
}
|
|
25
32
|
const response = await fetch(`${this.config.baseURL}/responses`, {
|
|
@@ -59,83 +66,4 @@ export class CodexClient {
|
|
|
59
66
|
throw new Error(`Unknown error: ${String(error)}`);
|
|
60
67
|
}
|
|
61
68
|
}
|
|
62
|
-
async generateStream(input, onChunk) {
|
|
63
|
-
const controller = new AbortController();
|
|
64
|
-
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
65
|
-
try {
|
|
66
|
-
const requestBody = {
|
|
67
|
-
model: this.config.model,
|
|
68
|
-
input: [
|
|
69
|
-
{
|
|
70
|
-
role: "user",
|
|
71
|
-
content: input,
|
|
72
|
-
},
|
|
73
|
-
],
|
|
74
|
-
max_output_tokens: this.config.maxTokens,
|
|
75
|
-
stream: true,
|
|
76
|
-
};
|
|
77
|
-
if (this.config.reasoningEffort) {
|
|
78
|
-
requestBody.reasoning = {
|
|
79
|
-
effort: this.config.reasoningEffort,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
const response = await fetch(`${this.config.baseURL}/responses`, {
|
|
83
|
-
method: "POST",
|
|
84
|
-
headers: {
|
|
85
|
-
"Content-Type": "application/json",
|
|
86
|
-
Authorization: `Bearer ${this.config.apiKey}`,
|
|
87
|
-
},
|
|
88
|
-
body: JSON.stringify(requestBody),
|
|
89
|
-
signal: controller.signal,
|
|
90
|
-
});
|
|
91
|
-
clearTimeout(timeoutId);
|
|
92
|
-
if (!response.ok) {
|
|
93
|
-
const errorText = await response.text();
|
|
94
|
-
throw new Error(`Codex API error (${response.status}): ${errorText}`);
|
|
95
|
-
}
|
|
96
|
-
if (!response.body) {
|
|
97
|
-
throw new Error("No response body");
|
|
98
|
-
}
|
|
99
|
-
const reader = response.body.getReader();
|
|
100
|
-
const decoder = new TextDecoder();
|
|
101
|
-
let buffer = "";
|
|
102
|
-
while (true) {
|
|
103
|
-
const { done, value } = await reader.read();
|
|
104
|
-
if (done)
|
|
105
|
-
break;
|
|
106
|
-
buffer += decoder.decode(value, { stream: true });
|
|
107
|
-
const lines = buffer.split("\n");
|
|
108
|
-
buffer = lines.pop() || "";
|
|
109
|
-
for (const line of lines) {
|
|
110
|
-
if (!line.trim() || line.startsWith(":"))
|
|
111
|
-
continue;
|
|
112
|
-
if (line.startsWith("data: ")) {
|
|
113
|
-
const data = line.slice(6);
|
|
114
|
-
if (data === "[DONE]") {
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
try {
|
|
118
|
-
const parsed = JSON.parse(data);
|
|
119
|
-
if (parsed.type === "response.output_text.delta") {
|
|
120
|
-
onChunk(parsed.delta || "");
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
catch (e) {
|
|
124
|
-
console.error("Failed to parse SSE data:", e);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
catch (error) {
|
|
131
|
-
clearTimeout(timeoutId);
|
|
132
|
-
if (error instanceof Error) {
|
|
133
|
-
if (error.name === "AbortError") {
|
|
134
|
-
throw new Error(`Request timeout after ${this.config.timeout}ms`);
|
|
135
|
-
}
|
|
136
|
-
throw error;
|
|
137
|
-
}
|
|
138
|
-
throw new Error(`Unknown error: ${String(error)}`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
69
|
}
|
package/dist/index.js
CHANGED
|
@@ -4,10 +4,12 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import { CodexClient } from "./codex-client.js";
|
|
6
6
|
import { tools } from "./tools.js";
|
|
7
|
+
// --- 配置 ---
|
|
7
8
|
const CODEX_API_BASE_URL = process.env.CODEX_API_BASE_URL;
|
|
8
9
|
const CODEX_API_KEY = process.env.CODEX_API_KEY;
|
|
9
10
|
const CODEX_MODEL = process.env.CODEX_MODEL || "gpt-5.3-codex";
|
|
10
|
-
const CODEX_REASONING_EFFORT = process.env.CODEX_REASONING_EFFORT
|
|
11
|
+
const CODEX_REASONING_EFFORT = (process.env.CODEX_REASONING_EFFORT ||
|
|
12
|
+
"high");
|
|
11
13
|
if (!CODEX_API_BASE_URL || !CODEX_API_KEY) {
|
|
12
14
|
console.error("Error: CODEX_API_BASE_URL and CODEX_API_KEY must be set");
|
|
13
15
|
process.exit(1);
|
|
@@ -16,18 +18,201 @@ const codexClient = new CodexClient({
|
|
|
16
18
|
baseURL: CODEX_API_BASE_URL,
|
|
17
19
|
apiKey: CODEX_API_KEY,
|
|
18
20
|
model: CODEX_MODEL,
|
|
19
|
-
timeout: parseInt(process.env.CODEX_TIMEOUT || "
|
|
20
|
-
maxTokens: parseInt(process.env.CODEX_MAX_TOKENS || "
|
|
21
|
+
timeout: parseInt(process.env.CODEX_TIMEOUT || "120000"),
|
|
22
|
+
maxTokens: parseInt(process.env.CODEX_MAX_TOKENS || "16384"),
|
|
21
23
|
reasoningEffort: CODEX_REASONING_EFFORT,
|
|
22
24
|
});
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
// --- JSON Schemas(强制结构化输出) ---
|
|
26
|
+
const GENERATE_SCHEMA = {
|
|
27
|
+
name: "generate_result",
|
|
28
|
+
schema: {
|
|
29
|
+
type: "object",
|
|
30
|
+
properties: {
|
|
31
|
+
code: { type: "string", description: "Generated code" },
|
|
32
|
+
language: { type: "string", description: "Programming language" },
|
|
33
|
+
explanation: {
|
|
34
|
+
type: "string",
|
|
35
|
+
description: "Brief explanation of approach and key decisions",
|
|
36
|
+
},
|
|
37
|
+
dependencies: {
|
|
38
|
+
type: "array",
|
|
39
|
+
items: { type: "string" },
|
|
40
|
+
description: "Required packages or imports",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
required: ["code", "language", "explanation", "dependencies"],
|
|
44
|
+
additionalProperties: false,
|
|
29
45
|
},
|
|
30
|
-
}
|
|
46
|
+
};
|
|
47
|
+
const REVIEW_SCHEMA = {
|
|
48
|
+
name: "review_result",
|
|
49
|
+
schema: {
|
|
50
|
+
type: "object",
|
|
51
|
+
properties: {
|
|
52
|
+
findings: {
|
|
53
|
+
type: "array",
|
|
54
|
+
items: {
|
|
55
|
+
type: "object",
|
|
56
|
+
properties: {
|
|
57
|
+
severity: {
|
|
58
|
+
type: "string",
|
|
59
|
+
enum: ["critical", "high", "medium", "low", "info"],
|
|
60
|
+
},
|
|
61
|
+
category: {
|
|
62
|
+
type: "string",
|
|
63
|
+
enum: [
|
|
64
|
+
"security",
|
|
65
|
+
"performance",
|
|
66
|
+
"best-practice",
|
|
67
|
+
"bug",
|
|
68
|
+
"maintainability",
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
location: {
|
|
72
|
+
type: "string",
|
|
73
|
+
description: "Line number or code section",
|
|
74
|
+
},
|
|
75
|
+
issue: {
|
|
76
|
+
type: "string",
|
|
77
|
+
description: "Description of the problem",
|
|
78
|
+
},
|
|
79
|
+
suggestion: {
|
|
80
|
+
type: "string",
|
|
81
|
+
description: "Concrete fix or improvement",
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
required: ["severity", "category", "location", "issue", "suggestion"],
|
|
85
|
+
additionalProperties: false,
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
summary: { type: "string", description: "Overall assessment" },
|
|
89
|
+
score: { type: "number", description: "Quality score 0-100" },
|
|
90
|
+
},
|
|
91
|
+
required: ["findings", "summary", "score"],
|
|
92
|
+
additionalProperties: false,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
const TEST_SCHEMA = {
|
|
96
|
+
name: "test_result",
|
|
97
|
+
schema: {
|
|
98
|
+
type: "object",
|
|
99
|
+
properties: {
|
|
100
|
+
test_code: {
|
|
101
|
+
type: "string",
|
|
102
|
+
description: "Complete test file content",
|
|
103
|
+
},
|
|
104
|
+
language: { type: "string", description: "Programming language" },
|
|
105
|
+
framework: { type: "string", description: "Test framework used" },
|
|
106
|
+
test_cases: {
|
|
107
|
+
type: "array",
|
|
108
|
+
items: {
|
|
109
|
+
type: "object",
|
|
110
|
+
properties: {
|
|
111
|
+
name: { type: "string", description: "Test name" },
|
|
112
|
+
description: {
|
|
113
|
+
type: "string",
|
|
114
|
+
description: "What this test verifies",
|
|
115
|
+
},
|
|
116
|
+
type: {
|
|
117
|
+
type: "string",
|
|
118
|
+
enum: ["unit", "integration", "edge-case", "error-handling"],
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
required: ["name", "description", "type"],
|
|
122
|
+
additionalProperties: false,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
coverage_notes: {
|
|
126
|
+
type: "string",
|
|
127
|
+
description: "What is covered and gaps remaining",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
required: [
|
|
131
|
+
"test_code",
|
|
132
|
+
"language",
|
|
133
|
+
"framework",
|
|
134
|
+
"test_cases",
|
|
135
|
+
"coverage_notes",
|
|
136
|
+
],
|
|
137
|
+
additionalProperties: false,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
// --- Developer 指令(per-tool) ---
|
|
141
|
+
const INSTRUCTIONS = {
|
|
142
|
+
generate: [
|
|
143
|
+
"You are a senior software engineer. Generate production-quality code.",
|
|
144
|
+
"",
|
|
145
|
+
"Rules:",
|
|
146
|
+
"- Write complete, runnable code — no placeholders, no TODOs",
|
|
147
|
+
"- Include necessary imports and type definitions",
|
|
148
|
+
"- Follow language-specific conventions and idioms",
|
|
149
|
+
"- If project context is provided, follow its conventions strictly",
|
|
150
|
+
"- Optimize for readability first, performance second",
|
|
151
|
+
].join("\n"),
|
|
152
|
+
review: [
|
|
153
|
+
"You are a security-focused code reviewer. Provide independent, critical analysis.",
|
|
154
|
+
"",
|
|
155
|
+
"Rules:",
|
|
156
|
+
"- Reference exact locations in the code (line numbers or function names)",
|
|
157
|
+
"- Prioritize: critical > high > medium > low > info",
|
|
158
|
+
"- For each finding, provide a concrete, actionable fix",
|
|
159
|
+
"- Score fairly: 90+ excellent, 70-89 good, 50-69 needs work, <50 concerning",
|
|
160
|
+
"- Be honest — don't inflate scores. Empty findings array is fine for clean code",
|
|
161
|
+
].join("\n"),
|
|
162
|
+
test: [
|
|
163
|
+
"You are a testing expert. Write comprehensive test suites that catch real bugs.",
|
|
164
|
+
"",
|
|
165
|
+
"Rules:",
|
|
166
|
+
"- Generate a complete, runnable test file",
|
|
167
|
+
"- Cover: happy path, edge cases, error handling, boundary conditions",
|
|
168
|
+
"- Use the specified framework's idioms and best practices",
|
|
169
|
+
"- Include descriptive test names explaining expected behavior",
|
|
170
|
+
"- Mock external dependencies appropriately",
|
|
171
|
+
"- Think about what would break in production and test that",
|
|
172
|
+
].join("\n"),
|
|
173
|
+
};
|
|
174
|
+
// --- 输入构建 ---
|
|
175
|
+
function buildInput(sections) {
|
|
176
|
+
const parts = [];
|
|
177
|
+
for (const [label, content] of Object.entries(sections)) {
|
|
178
|
+
if (content?.trim()) {
|
|
179
|
+
parts.push(`## ${label}\n${content.trim()}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return parts.join("\n\n");
|
|
183
|
+
}
|
|
184
|
+
// --- 安全 JSON 解析(带 fallback) ---
|
|
185
|
+
function safeJsonParse(raw) {
|
|
186
|
+
try {
|
|
187
|
+
return JSON.parse(raw);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// API 可能不支持 text.format,返回了非 JSON
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function formatGenerateOutput(parsed) {
|
|
195
|
+
let text = `\`\`\`${parsed.language}\n${parsed.code}\n\`\`\``;
|
|
196
|
+
text += `\n\n**Explanation:** ${parsed.explanation}`;
|
|
197
|
+
if (parsed.dependencies.length > 0) {
|
|
198
|
+
text += `\n\n**Dependencies:** ${parsed.dependencies.join(", ")}`;
|
|
199
|
+
}
|
|
200
|
+
return text;
|
|
201
|
+
}
|
|
202
|
+
function formatReviewOutput(parsed) {
|
|
203
|
+
const findings = parsed.findings
|
|
204
|
+
.map((f, i) => `${i + 1}. **[${f.severity.toUpperCase()}]** ${f.category} — \`${f.location}\`\n Issue: ${f.issue}\n Fix: ${f.suggestion}`)
|
|
205
|
+
.join("\n\n");
|
|
206
|
+
return `**Score: ${parsed.score}/100**\n\n${parsed.summary}\n\n---\n\n${findings || "No issues found."}`;
|
|
207
|
+
}
|
|
208
|
+
function formatTestOutput(parsed) {
|
|
209
|
+
const cases = parsed.test_cases
|
|
210
|
+
.map((tc) => `- [${tc.type}] **${tc.name}**: ${tc.description}`)
|
|
211
|
+
.join("\n");
|
|
212
|
+
return `\`\`\`${parsed.language}\n${parsed.test_code}\n\`\`\`\n\n**Framework:** ${parsed.framework}\n**Test Cases (${parsed.test_cases.length}):**\n${cases}\n\n**Coverage:** ${parsed.coverage_notes}`;
|
|
213
|
+
}
|
|
214
|
+
// --- MCP Server ---
|
|
215
|
+
const server = new Server({ name: "codex-mcp-server", version: "2.0.0" }, { capabilities: { tools: {} } });
|
|
31
216
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
32
217
|
return { tools };
|
|
33
218
|
});
|
|
@@ -36,69 +221,88 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
36
221
|
try {
|
|
37
222
|
switch (name) {
|
|
38
223
|
case "codex_generate": {
|
|
39
|
-
const { prompt, language,
|
|
40
|
-
const input =
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
224
|
+
const { prompt, language, project_context, files, conversation_context, } = args;
|
|
225
|
+
const input = buildInput({
|
|
226
|
+
Task: prompt,
|
|
227
|
+
Language: language,
|
|
228
|
+
"Project Context": project_context,
|
|
229
|
+
"Reference Files": files,
|
|
230
|
+
"Conversation Context": conversation_context,
|
|
231
|
+
});
|
|
232
|
+
const raw = await codexClient.request({
|
|
233
|
+
instructions: INSTRUCTIONS.generate,
|
|
234
|
+
input,
|
|
235
|
+
jsonSchema: GENERATE_SCHEMA,
|
|
236
|
+
});
|
|
237
|
+
const parsed = safeJsonParse(raw);
|
|
238
|
+
if (parsed && typeof parsed.code === "string") {
|
|
239
|
+
return {
|
|
240
|
+
content: [
|
|
241
|
+
{
|
|
242
|
+
type: "text",
|
|
243
|
+
text: formatGenerateOutput(parsed),
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
// Fallback:API 不支持结构化输出时直接返回原文
|
|
249
|
+
return { content: [{ type: "text", text: raw }] };
|
|
63
250
|
}
|
|
64
|
-
case "
|
|
65
|
-
const { code, language, focus } = args;
|
|
66
|
-
const input =
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
251
|
+
case "codex_review": {
|
|
252
|
+
const { code, language, focus, project_context, conversation_context } = args;
|
|
253
|
+
const input = buildInput({
|
|
254
|
+
"Code to Review": `\`\`\`${language ?? ""}\n${code}\n\`\`\``,
|
|
255
|
+
Language: language,
|
|
256
|
+
"Focus Area": focus ?? "all",
|
|
257
|
+
"Project Context": project_context,
|
|
258
|
+
"Review Context": conversation_context,
|
|
259
|
+
});
|
|
260
|
+
const raw = await codexClient.request({
|
|
261
|
+
instructions: INSTRUCTIONS.review,
|
|
262
|
+
input,
|
|
263
|
+
jsonSchema: REVIEW_SCHEMA,
|
|
264
|
+
reasoningEffort: "high",
|
|
265
|
+
});
|
|
266
|
+
const parsed = safeJsonParse(raw);
|
|
267
|
+
if (parsed && Array.isArray(parsed.findings)) {
|
|
268
|
+
return {
|
|
269
|
+
content: [
|
|
270
|
+
{
|
|
271
|
+
type: "text",
|
|
272
|
+
text: formatReviewOutput(parsed),
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
return { content: [{ type: "text", text: raw }] };
|
|
76
278
|
}
|
|
77
|
-
case "
|
|
78
|
-
const { code,
|
|
79
|
-
const input =
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
279
|
+
case "codex_test": {
|
|
280
|
+
const { code, language, framework, project_context, conversation_context, } = args;
|
|
281
|
+
const input = buildInput({
|
|
282
|
+
"Code to Test": `\`\`\`${language ?? ""}\n${code}\n\`\`\``,
|
|
283
|
+
Language: language,
|
|
284
|
+
"Test Framework": framework,
|
|
285
|
+
"Project Context": project_context,
|
|
286
|
+
Context: conversation_context,
|
|
287
|
+
});
|
|
288
|
+
const raw = await codexClient.request({
|
|
289
|
+
instructions: INSTRUCTIONS.test,
|
|
290
|
+
input,
|
|
291
|
+
jsonSchema: TEST_SCHEMA,
|
|
292
|
+
reasoningEffort: "high",
|
|
293
|
+
});
|
|
294
|
+
const parsed = safeJsonParse(raw);
|
|
295
|
+
if (parsed && typeof parsed.test_code === "string") {
|
|
296
|
+
return {
|
|
297
|
+
content: [
|
|
298
|
+
{
|
|
299
|
+
type: "text",
|
|
300
|
+
text: formatTestOutput(parsed),
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
return { content: [{ type: "text", text: raw }] };
|
|
102
306
|
}
|
|
103
307
|
default:
|
|
104
308
|
throw new Error(`Unknown tool: ${name}`);
|
|
@@ -107,73 +311,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
107
311
|
catch (error) {
|
|
108
312
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
109
313
|
return {
|
|
110
|
-
content: [
|
|
111
|
-
{
|
|
112
|
-
type: "text",
|
|
113
|
-
text: `Error: ${errorMessage}`,
|
|
114
|
-
},
|
|
115
|
-
],
|
|
314
|
+
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
116
315
|
isError: true,
|
|
117
316
|
};
|
|
118
317
|
}
|
|
119
318
|
});
|
|
120
|
-
|
|
121
|
-
let input = "";
|
|
122
|
-
if (language) {
|
|
123
|
-
input += `Language: ${language}\n\n`;
|
|
124
|
-
}
|
|
125
|
-
if (context) {
|
|
126
|
-
input += `Context:\n${context}\n\n`;
|
|
127
|
-
}
|
|
128
|
-
input += `Task: ${prompt}\n\nGenerate the code:`;
|
|
129
|
-
return input;
|
|
130
|
-
}
|
|
131
|
-
function buildEditInput(code, instruction, language) {
|
|
132
|
-
let input = "";
|
|
133
|
-
if (language) {
|
|
134
|
-
input += `Language: ${language}\n\n`;
|
|
135
|
-
}
|
|
136
|
-
input += `Original code:\n\`\`\`\n${code}\n\`\`\`\n\n`;
|
|
137
|
-
input += `Instruction: ${instruction}\n\n`;
|
|
138
|
-
input += `Modified code:`;
|
|
139
|
-
return input;
|
|
140
|
-
}
|
|
141
|
-
function buildExplainInput(code, language, focus) {
|
|
142
|
-
let input = "";
|
|
143
|
-
if (language) {
|
|
144
|
-
input += `Language: ${language}\n\n`;
|
|
145
|
-
}
|
|
146
|
-
input += `Code:\n\`\`\`\n${code}\n\`\`\`\n\n`;
|
|
147
|
-
if (focus) {
|
|
148
|
-
input += `Focus on: ${focus}\n\n`;
|
|
149
|
-
}
|
|
150
|
-
input += `Explain this code:`;
|
|
151
|
-
return input;
|
|
152
|
-
}
|
|
153
|
-
function buildFixInput(code, error, language) {
|
|
154
|
-
let input = "";
|
|
155
|
-
if (language) {
|
|
156
|
-
input += `Language: ${language}\n\n`;
|
|
157
|
-
}
|
|
158
|
-
input += `Code with error:\n\`\`\`\n${code}\n\`\`\`\n\n`;
|
|
159
|
-
input += `Error message:\n${error}\n\n`;
|
|
160
|
-
input += `Fixed code:`;
|
|
161
|
-
return input;
|
|
162
|
-
}
|
|
163
|
-
function buildRefactorInput(code, goal, language) {
|
|
164
|
-
let input = "";
|
|
165
|
-
if (language) {
|
|
166
|
-
input += `Language: ${language}\n\n`;
|
|
167
|
-
}
|
|
168
|
-
input += `Original code:\n\`\`\`\n${code}\n\`\`\`\n\n`;
|
|
169
|
-
input += `Refactoring goal: ${goal}\n\n`;
|
|
170
|
-
input += `Refactored code:`;
|
|
171
|
-
return input;
|
|
172
|
-
}
|
|
319
|
+
// --- 启动 ---
|
|
173
320
|
async function main() {
|
|
174
321
|
const transport = new StdioServerTransport();
|
|
175
322
|
await server.connect(transport);
|
|
176
|
-
console.error("Codex MCP Server running on stdio");
|
|
323
|
+
console.error("Codex MCP Server v2.0.0 running on stdio");
|
|
177
324
|
}
|
|
178
325
|
main().catch((error) => {
|
|
179
326
|
console.error("Fatal error:", error);
|
package/dist/tools.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export declare const tools: ({
|
|
|
2
2
|
name: string;
|
|
3
3
|
description: string;
|
|
4
4
|
inputSchema: {
|
|
5
|
-
type:
|
|
5
|
+
type: "object";
|
|
6
6
|
properties: {
|
|
7
7
|
prompt: {
|
|
8
8
|
type: string;
|
|
@@ -12,41 +12,21 @@ export declare const tools: ({
|
|
|
12
12
|
type: string;
|
|
13
13
|
description: string;
|
|
14
14
|
};
|
|
15
|
-
|
|
15
|
+
project_context: {
|
|
16
16
|
type: string;
|
|
17
17
|
description: string;
|
|
18
18
|
};
|
|
19
|
-
|
|
20
|
-
instruction?: undefined;
|
|
21
|
-
focus?: undefined;
|
|
22
|
-
error?: undefined;
|
|
23
|
-
goal?: undefined;
|
|
24
|
-
};
|
|
25
|
-
required: string[];
|
|
26
|
-
};
|
|
27
|
-
} | {
|
|
28
|
-
name: string;
|
|
29
|
-
description: string;
|
|
30
|
-
inputSchema: {
|
|
31
|
-
type: string;
|
|
32
|
-
properties: {
|
|
33
|
-
code: {
|
|
19
|
+
files: {
|
|
34
20
|
type: string;
|
|
35
21
|
description: string;
|
|
36
22
|
};
|
|
37
|
-
|
|
38
|
-
type: string;
|
|
39
|
-
description: string;
|
|
40
|
-
};
|
|
41
|
-
language: {
|
|
23
|
+
conversation_context: {
|
|
42
24
|
type: string;
|
|
43
25
|
description: string;
|
|
44
26
|
};
|
|
45
|
-
|
|
46
|
-
context?: undefined;
|
|
27
|
+
code?: undefined;
|
|
47
28
|
focus?: undefined;
|
|
48
|
-
|
|
49
|
-
goal?: undefined;
|
|
29
|
+
framework?: undefined;
|
|
50
30
|
};
|
|
51
31
|
required: string[];
|
|
52
32
|
};
|
|
@@ -54,7 +34,7 @@ export declare const tools: ({
|
|
|
54
34
|
name: string;
|
|
55
35
|
description: string;
|
|
56
36
|
inputSchema: {
|
|
57
|
-
type:
|
|
37
|
+
type: "object";
|
|
58
38
|
properties: {
|
|
59
39
|
code: {
|
|
60
40
|
type: string;
|
|
@@ -65,14 +45,21 @@ export declare const tools: ({
|
|
|
65
45
|
description: string;
|
|
66
46
|
};
|
|
67
47
|
focus: {
|
|
48
|
+
type: string;
|
|
49
|
+
enum: string[];
|
|
50
|
+
description: string;
|
|
51
|
+
};
|
|
52
|
+
project_context: {
|
|
53
|
+
type: string;
|
|
54
|
+
description: string;
|
|
55
|
+
};
|
|
56
|
+
conversation_context: {
|
|
68
57
|
type: string;
|
|
69
58
|
description: string;
|
|
70
59
|
};
|
|
71
60
|
prompt?: undefined;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
error?: undefined;
|
|
75
|
-
goal?: undefined;
|
|
61
|
+
files?: undefined;
|
|
62
|
+
framework?: undefined;
|
|
76
63
|
};
|
|
77
64
|
required: string[];
|
|
78
65
|
};
|
|
@@ -80,51 +67,31 @@ export declare const tools: ({
|
|
|
80
67
|
name: string;
|
|
81
68
|
description: string;
|
|
82
69
|
inputSchema: {
|
|
83
|
-
type:
|
|
70
|
+
type: "object";
|
|
84
71
|
properties: {
|
|
85
72
|
code: {
|
|
86
73
|
type: string;
|
|
87
74
|
description: string;
|
|
88
75
|
};
|
|
89
|
-
error: {
|
|
90
|
-
type: string;
|
|
91
|
-
description: string;
|
|
92
|
-
};
|
|
93
76
|
language: {
|
|
94
77
|
type: string;
|
|
95
78
|
description: string;
|
|
96
79
|
};
|
|
97
|
-
|
|
98
|
-
context?: undefined;
|
|
99
|
-
instruction?: undefined;
|
|
100
|
-
focus?: undefined;
|
|
101
|
-
goal?: undefined;
|
|
102
|
-
};
|
|
103
|
-
required: string[];
|
|
104
|
-
};
|
|
105
|
-
} | {
|
|
106
|
-
name: string;
|
|
107
|
-
description: string;
|
|
108
|
-
inputSchema: {
|
|
109
|
-
type: string;
|
|
110
|
-
properties: {
|
|
111
|
-
code: {
|
|
80
|
+
framework: {
|
|
112
81
|
type: string;
|
|
113
82
|
description: string;
|
|
114
83
|
};
|
|
115
|
-
|
|
84
|
+
project_context: {
|
|
116
85
|
type: string;
|
|
117
86
|
description: string;
|
|
118
87
|
};
|
|
119
|
-
|
|
88
|
+
conversation_context: {
|
|
120
89
|
type: string;
|
|
121
90
|
description: string;
|
|
122
91
|
};
|
|
123
92
|
prompt?: undefined;
|
|
124
|
-
|
|
125
|
-
instruction?: undefined;
|
|
93
|
+
files?: undefined;
|
|
126
94
|
focus?: undefined;
|
|
127
|
-
error?: undefined;
|
|
128
95
|
};
|
|
129
96
|
required: string[];
|
|
130
97
|
};
|
package/dist/tools.js
CHANGED
|
@@ -1,112 +1,130 @@
|
|
|
1
1
|
export const tools = [
|
|
2
2
|
{
|
|
3
3
|
name: "codex_generate",
|
|
4
|
-
description:
|
|
4
|
+
description: [
|
|
5
|
+
"Generate code using Codex deep reasoning. Use for complex tasks where a second expert perspective adds value.",
|
|
6
|
+
"",
|
|
7
|
+
"Returns structured JSON: { code, language, explanation, dependencies }",
|
|
8
|
+
"",
|
|
9
|
+
"When to use:",
|
|
10
|
+
"- Complex algorithms, data structures, state machines",
|
|
11
|
+
"- Performance-critical code requiring deep optimization",
|
|
12
|
+
"- System design implementations (interpreters, compilers, protocols)",
|
|
13
|
+
"- When you want a different model's approach to a hard problem",
|
|
14
|
+
"",
|
|
15
|
+
"Do NOT use for simple code generation — handle that natively.",
|
|
16
|
+
].join("\n"),
|
|
5
17
|
inputSchema: {
|
|
6
18
|
type: "object",
|
|
7
19
|
properties: {
|
|
8
20
|
prompt: {
|
|
9
21
|
type: "string",
|
|
10
|
-
description: "
|
|
22
|
+
description: "What to generate. Be specific: requirements, constraints, expected behavior.",
|
|
11
23
|
},
|
|
12
24
|
language: {
|
|
13
25
|
type: "string",
|
|
14
|
-
description: "
|
|
26
|
+
description: "Target language (e.g., typescript, python, rust, go).",
|
|
15
27
|
},
|
|
16
|
-
|
|
28
|
+
project_context: {
|
|
17
29
|
type: "string",
|
|
18
|
-
description: "
|
|
30
|
+
description: "Project info: tech stack, conventions, architecture. Pass CLAUDE.md or relevant standards.",
|
|
19
31
|
},
|
|
20
|
-
|
|
21
|
-
required: ["prompt"],
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
name: "codex_edit",
|
|
26
|
-
description: "Edit existing code based on instructions. Use this for modifying, updating, or improving existing code.",
|
|
27
|
-
inputSchema: {
|
|
28
|
-
type: "object",
|
|
29
|
-
properties: {
|
|
30
|
-
code: {
|
|
31
|
-
type: "string",
|
|
32
|
-
description: "The existing code to edit",
|
|
33
|
-
},
|
|
34
|
-
instruction: {
|
|
32
|
+
files: {
|
|
35
33
|
type: "string",
|
|
36
|
-
description: "
|
|
34
|
+
description: "Related source files. Include types, interfaces, imports the generated code should reference.",
|
|
37
35
|
},
|
|
38
|
-
|
|
36
|
+
conversation_context: {
|
|
39
37
|
type: "string",
|
|
40
|
-
description: "
|
|
38
|
+
description: "Recent conversation summary: what was discussed, decided, or attempted.",
|
|
41
39
|
},
|
|
42
40
|
},
|
|
43
|
-
required: ["
|
|
41
|
+
required: ["prompt"],
|
|
44
42
|
},
|
|
45
43
|
},
|
|
46
44
|
{
|
|
47
|
-
name: "
|
|
48
|
-
description:
|
|
45
|
+
name: "codex_review",
|
|
46
|
+
description: [
|
|
47
|
+
"Independent code review from a second AI. Analyzes security, performance, and best practices that the primary AI might miss.",
|
|
48
|
+
"",
|
|
49
|
+
"Returns structured JSON: { findings: [{ severity, category, location, issue, suggestion }], summary, score }",
|
|
50
|
+
"",
|
|
51
|
+
"When to use:",
|
|
52
|
+
"- Before committing security-sensitive code (auth, crypto, input validation)",
|
|
53
|
+
"- Performance-critical paths that need independent analysis",
|
|
54
|
+
"- When you want a second opinion on code quality",
|
|
55
|
+
"- PR review where an independent perspective matters",
|
|
56
|
+
"",
|
|
57
|
+
"Severity levels: critical, high, medium, low, info",
|
|
58
|
+
"Score range: 0-100 (90+ excellent, 70-89 good, 50-69 needs work, <50 concerning)",
|
|
59
|
+
].join("\n"),
|
|
49
60
|
inputSchema: {
|
|
50
61
|
type: "object",
|
|
51
62
|
properties: {
|
|
52
63
|
code: {
|
|
53
64
|
type: "string",
|
|
54
|
-
description: "
|
|
65
|
+
description: "Code to review.",
|
|
55
66
|
},
|
|
56
67
|
language: {
|
|
57
68
|
type: "string",
|
|
58
|
-
description: "Programming language of the code",
|
|
69
|
+
description: "Programming language of the code.",
|
|
59
70
|
},
|
|
60
71
|
focus: {
|
|
61
72
|
type: "string",
|
|
62
|
-
|
|
73
|
+
enum: ["security", "performance", "best-practices", "all"],
|
|
74
|
+
description: "Review focus area. Default: all.",
|
|
75
|
+
},
|
|
76
|
+
project_context: {
|
|
77
|
+
type: "string",
|
|
78
|
+
description: "Project conventions, lint rules, architectural guidelines.",
|
|
79
|
+
},
|
|
80
|
+
conversation_context: {
|
|
81
|
+
type: "string",
|
|
82
|
+
description: "Why this review is needed: what changed, what concerns exist.",
|
|
63
83
|
},
|
|
64
84
|
},
|
|
65
85
|
required: ["code"],
|
|
66
86
|
},
|
|
67
87
|
},
|
|
68
88
|
{
|
|
69
|
-
name: "
|
|
70
|
-
description:
|
|
89
|
+
name: "codex_test",
|
|
90
|
+
description: [
|
|
91
|
+
"Generate comprehensive test suites with deep reasoning for edge case coverage.",
|
|
92
|
+
"",
|
|
93
|
+
"Returns structured JSON: { test_code, language, framework, test_cases: [{ name, description, type }], coverage_notes }",
|
|
94
|
+
"",
|
|
95
|
+
"When to use:",
|
|
96
|
+
"- Complex business logic needing thorough edge case coverage",
|
|
97
|
+
"- Functions with many branches, error paths, or boundary conditions",
|
|
98
|
+
"- When test quality directly impacts reliability",
|
|
99
|
+
"- Integration tests requiring careful mock setup",
|
|
100
|
+
"",
|
|
101
|
+
"Test case types: unit, integration, edge-case, error-handling",
|
|
102
|
+
].join("\n"),
|
|
71
103
|
inputSchema: {
|
|
72
104
|
type: "object",
|
|
73
105
|
properties: {
|
|
74
106
|
code: {
|
|
75
107
|
type: "string",
|
|
76
|
-
description: "
|
|
77
|
-
},
|
|
78
|
-
error: {
|
|
79
|
-
type: "string",
|
|
80
|
-
description: "The error message or description of the bug",
|
|
108
|
+
description: "Code to generate tests for.",
|
|
81
109
|
},
|
|
82
110
|
language: {
|
|
83
111
|
type: "string",
|
|
84
|
-
description: "Programming language
|
|
112
|
+
description: "Programming language (e.g., typescript, python).",
|
|
85
113
|
},
|
|
86
|
-
|
|
87
|
-
required: ["code", "error"],
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
name: "codex_refactor",
|
|
92
|
-
description: "Refactor code to improve quality, readability, or performance. Use this for code cleanup and optimization.",
|
|
93
|
-
inputSchema: {
|
|
94
|
-
type: "object",
|
|
95
|
-
properties: {
|
|
96
|
-
code: {
|
|
114
|
+
framework: {
|
|
97
115
|
type: "string",
|
|
98
|
-
description: "
|
|
116
|
+
description: "Test framework (e.g., jest, vitest, pytest, go test).",
|
|
99
117
|
},
|
|
100
|
-
|
|
118
|
+
project_context: {
|
|
101
119
|
type: "string",
|
|
102
|
-
description: "
|
|
120
|
+
description: "Project test conventions: existing patterns, mock strategies, test directory structure.",
|
|
103
121
|
},
|
|
104
|
-
|
|
122
|
+
conversation_context: {
|
|
105
123
|
type: "string",
|
|
106
|
-
description: "
|
|
124
|
+
description: "Context about the code: known issues, requirements, what needs coverage.",
|
|
107
125
|
},
|
|
108
126
|
},
|
|
109
|
-
required: ["code"
|
|
127
|
+
required: ["code"],
|
|
110
128
|
},
|
|
111
129
|
},
|
|
112
130
|
];
|