gpteam 0.1.14 → 0.1.15
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 -1
- package/bin/gpteam-image-mcp.js +7 -0
- package/lib/cli.js +3 -0
- package/lib/config.js +40 -1
- package/lib/help.js +2 -2
- package/lib/image-mcp/image.js +186 -0
- package/lib/image-mcp/server.js +74 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -12,7 +12,9 @@ When a selected client is missing, the interactive CLI shows the exact install c
|
|
|
12
12
|
|
|
13
13
|
Recommendation order is deterministic: success rate first, then an experience score. The score weighs first SSE event time, total completion time, p90 completion tail latency, and health-check time, so a node with one very slow probe is not recommended just because its median result looks good. The result table also marks the fastest full completion separately from the balanced recommendation, because deep or long-output model runs may care more about full completion time than first-token responsiveness. Model prompts use "configurable context" wording because the value is written to the client-side Codex `model_context_window`; it must not be confused with a public marketing total-window label.
|
|
14
14
|
|
|
15
|
-
Client config writing follows the same safety pattern as cc-switch: keep Codex top-level fields separate from provider tables, preserve unrelated sections such as MCP servers, merge OpenCode/OpenClaw providers additively, and stop before writing when an existing JSON/JSON5 config cannot be parsed. GPTeam keeps
|
|
15
|
+
Client config writing follows the same safety pattern as cc-switch: keep Codex top-level fields separate from provider tables, preserve unrelated sections such as MCP servers, merge OpenCode/OpenClaw providers additively, and stop before writing when an existing JSON/JSON5 config cannot be parsed. GPTeam keeps three proxy-specific extensions on top of that baseline: Codex explicitly disables WebSocket prewarm, Codex writes the `gpteam_image` MCP server so Image 2 can be called from chat, and Claude Code writes the reasoning-effort header through `ANTHROPIC_CUSTOM_HEADERS`.
|
|
16
|
+
|
|
17
|
+
For Codex, the Image MCP config uses the cc-switch-style MCP env block. On macOS/Linux it writes `command = "npx"` with `args = ["-y", "-p", "gpteam", "gpteam-image-mcp"]`; on Windows it writes a `cmd /c npx -y -p gpteam gpteam-image-mcp` wrapper. The MCP receives `GPTEAM_API_KEY` and `GPTEAM_BASE_URL` from `[mcp_servers.gpteam_image.env]`, so it does not depend on Codex `auth.json`.
|
|
16
18
|
|
|
17
19
|
Claude Code is written to `~/.claude/settings.json` under the `env` section, using the GPTeam `/anthropic` base URL. OpenClaw writes `models.providers.gpteam` and also selects `gpteam/<model>` under `agents.defaults.model`, so the chosen model is active without an extra manual step.
|
|
18
20
|
|
package/lib/cli.js
CHANGED
|
@@ -71,6 +71,9 @@ export async function runCli(argv = []) {
|
|
|
71
71
|
for (const filePath of written) console.log(`- ${filePath}`);
|
|
72
72
|
console.log(`入口:${formatNodeLabel(selectedNode)}`);
|
|
73
73
|
console.log(`地址:${selectedNode.baseUrl}`);
|
|
74
|
+
if (client.id === 'codex') {
|
|
75
|
+
printHint(theme, '已写入 GPTeam Image MCP。Codex 对话里需要生图时可调用 generate_image,MCP 使用专用环境变量读取 API key。');
|
|
76
|
+
}
|
|
74
77
|
} finally {
|
|
75
78
|
rl.close();
|
|
76
79
|
}
|
package/lib/config.js
CHANGED
|
@@ -5,6 +5,9 @@ import JSON5 from 'json5';
|
|
|
5
5
|
import { endpointRoot } from './nodes.js';
|
|
6
6
|
|
|
7
7
|
const PROVIDER_ID = 'gpteam';
|
|
8
|
+
const IMAGE_MCP_ID = 'gpteam_image';
|
|
9
|
+
const IMAGE_MCP_PACKAGE = 'gpteam';
|
|
10
|
+
const IMAGE_MCP_BIN = 'gpteam-image-mcp';
|
|
8
11
|
|
|
9
12
|
export const CLIENTS = [
|
|
10
13
|
{ id: 'codex', label: 'Codex' },
|
|
@@ -46,10 +49,26 @@ export function writeCodexConfig(settings) {
|
|
|
46
49
|
'requires_openai_auth = true',
|
|
47
50
|
'supports_websockets = false'
|
|
48
51
|
];
|
|
52
|
+
const mcpCommand = codexImageMCPCommand();
|
|
53
|
+
const managedImageMCP = [
|
|
54
|
+
`[mcp_servers.${IMAGE_MCP_ID}]`,
|
|
55
|
+
`command = ${tomlString(mcpCommand.command)}`,
|
|
56
|
+
`args = [${mcpCommand.args.map((arg) => tomlString(arg)).join(', ')}]`,
|
|
57
|
+
'startup_timeout_sec = 20',
|
|
58
|
+
'tool_timeout_sec = 300',
|
|
59
|
+
'enabled_tools = ["generate_image"]',
|
|
60
|
+
'default_tools_approval_mode = "prompt"',
|
|
61
|
+
'',
|
|
62
|
+
`[mcp_servers.${IMAGE_MCP_ID}.env]`,
|
|
63
|
+
`GPTEAM_API_KEY = ${tomlString(settings.apiKey)}`,
|
|
64
|
+
`GPTEAM_BASE_URL = ${tomlString(settings.node.baseUrl)}`,
|
|
65
|
+
`GPTEAM_CODEX_HOME = ${tomlString(dir)}`
|
|
66
|
+
];
|
|
49
67
|
const next = joinTomlSections([
|
|
50
68
|
managedRoot.join('\n'),
|
|
51
69
|
rootLines.join('\n'),
|
|
52
70
|
rest.join('\n'),
|
|
71
|
+
managedImageMCP.join('\n'),
|
|
53
72
|
managedProvider.join('\n')
|
|
54
73
|
]);
|
|
55
74
|
assertNoDuplicateTomlKeys(next);
|
|
@@ -179,18 +198,34 @@ function claudeBaseUrl(baseUrl) {
|
|
|
179
198
|
return `${root}/anthropic`;
|
|
180
199
|
}
|
|
181
200
|
|
|
201
|
+
function codexImageMCPCommand() {
|
|
202
|
+
if (process.platform === 'win32') {
|
|
203
|
+
return {
|
|
204
|
+
command: 'cmd',
|
|
205
|
+
args: ['/c', 'npx', '-y', '-p', IMAGE_MCP_PACKAGE, IMAGE_MCP_BIN]
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
command: 'npx',
|
|
210
|
+
args: ['-y', '-p', IMAGE_MCP_PACKAGE, IMAGE_MCP_BIN]
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
182
214
|
function stripCodexManagedConfig(raw) {
|
|
183
215
|
const rootLines = [];
|
|
184
216
|
const rest = [];
|
|
185
217
|
let inManagedProvider = false;
|
|
218
|
+
let inManagedImageMCP = false;
|
|
186
219
|
|
|
187
220
|
for (const line of String(raw || '').split(/\r?\n/)) {
|
|
188
221
|
if (isTableHeader(line)) {
|
|
189
222
|
inManagedProvider = isGpteamProviderHeader(line);
|
|
190
|
-
|
|
223
|
+
inManagedImageMCP = isGpteamImageMCPHeader(line);
|
|
224
|
+
if (!inManagedProvider && !inManagedImageMCP) rest.push(line);
|
|
191
225
|
continue;
|
|
192
226
|
}
|
|
193
227
|
if (isManagedRootLine(line)) continue;
|
|
228
|
+
if (inManagedImageMCP) continue;
|
|
194
229
|
if (inManagedProvider) {
|
|
195
230
|
if (isManagedProviderLine(line)) continue;
|
|
196
231
|
if (isSalvageableRootLine(line)) rootLines.push(line);
|
|
@@ -245,6 +280,10 @@ function isGpteamProviderHeader(line) {
|
|
|
245
280
|
return /^\s*\[model_providers\.gpteam\]\s*$/.test(line);
|
|
246
281
|
}
|
|
247
282
|
|
|
283
|
+
function isGpteamImageMCPHeader(line) {
|
|
284
|
+
return /^\s*\[mcp_servers\.gpteam_image(?:\.[^\]]+)?\]\s*$/.test(line);
|
|
285
|
+
}
|
|
286
|
+
|
|
248
287
|
function isManagedRootLine(line) {
|
|
249
288
|
return /^\s*(model|model_provider|model_context_window|model_reasoning_effort|disable_response_storage)\s*=/.test(line);
|
|
250
289
|
}
|
package/lib/help.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const PACKAGE_NAME = 'gpteam';
|
|
2
|
-
export const PACKAGE_VERSION = '0.1.
|
|
2
|
+
export const PACKAGE_VERSION = '0.1.15';
|
|
3
3
|
|
|
4
4
|
export function getHelpText() {
|
|
5
5
|
return [
|
|
@@ -22,6 +22,6 @@ export function getHelpText() {
|
|
|
22
22
|
' --help 显示帮助',
|
|
23
23
|
' --version 显示版本',
|
|
24
24
|
'',
|
|
25
|
-
'说明:输入 key 后会先请求 /v1/models 校验,通过后才继续。选好客户端后会检测本机命令是否可用,缺失时提示安装;Windows 暂不支持自动配置 OpenClaw。测速会请求 GET /api/health 和流式 POST /v1/responses。入口之间并行测速,写新配置前会先备份旧配置。'
|
|
25
|
+
'说明:输入 key 后会先请求 /v1/models 校验,通过后才继续。选好客户端后会检测本机命令是否可用,缺失时提示安装;Windows 暂不支持自动配置 OpenClaw。测速会请求 GET /api/health 和流式 POST /v1/responses。入口之间并行测速,写新配置前会先备份旧配置。Codex 配置会同时写入 GPTeam Image MCP,生图工具从 MCP env 读取 key。'
|
|
26
26
|
].join('\n');
|
|
27
27
|
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
export const DEFAULT_BASE_URL = 'https://api.gpteamservices.com/v1';
|
|
6
|
+
export const DEFAULT_IMAGE_MODEL = 'gpt-image-2';
|
|
7
|
+
export const DEFAULT_IMAGE_FORMAT = 'png';
|
|
8
|
+
|
|
9
|
+
export function buildImageGenerationPayload(input = {}) {
|
|
10
|
+
return {
|
|
11
|
+
model: String(input.model || DEFAULT_IMAGE_MODEL),
|
|
12
|
+
prompt: String(input.prompt || '').trim(),
|
|
13
|
+
response_format: 'b64_json',
|
|
14
|
+
size: String(input.size || '1024x1024'),
|
|
15
|
+
quality: String(input.quality || 'high'),
|
|
16
|
+
output_format: normalizeImageFormat(input.format || input.output_format || DEFAULT_IMAGE_FORMAT)
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function loadGPTeamCredentials(options = {}) {
|
|
21
|
+
const env = options.env || process.env;
|
|
22
|
+
const codexHome = resolveCodexHome(env, options.home || os.homedir());
|
|
23
|
+
const readFile = options.readFile || ((filePath) => fs.readFileSync(filePath, 'utf8'));
|
|
24
|
+
const configText = safeRead(path.join(codexHome, 'config.toml'), readFile);
|
|
25
|
+
const configuredBaseUrl = parseGPTeamBaseUrl(configText);
|
|
26
|
+
const apiKey = firstNonEmpty(env.GPTEAM_API_KEY);
|
|
27
|
+
if (!apiKey) {
|
|
28
|
+
throw new Error('没有找到 GPTeam API key。请在 MCP 配置 env 中设置 GPTEAM_API_KEY,或先运行 npx gpteam 完成本地配置。');
|
|
29
|
+
}
|
|
30
|
+
const baseUrl = normalizeBaseUrl(firstNonEmpty(env.GPTEAM_BASE_URL, configuredBaseUrl, DEFAULT_BASE_URL));
|
|
31
|
+
return { apiKey, baseUrl, codexHome };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function parseGPTeamBaseUrl(configText) {
|
|
35
|
+
let inGPTeamProvider = false;
|
|
36
|
+
for (const rawLine of String(configText || '').split(/\r?\n/)) {
|
|
37
|
+
const line = rawLine.trim();
|
|
38
|
+
const table = line.match(/^\[([^\]]+)\]$/);
|
|
39
|
+
if (table) {
|
|
40
|
+
inGPTeamProvider = table[1] === 'model_providers.gpteam';
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (!inGPTeamProvider) continue;
|
|
44
|
+
const match = line.match(/^base_url\s*=\s*"((?:\\"|[^"])*)"/);
|
|
45
|
+
if (match) return unescapeTomlString(match[1]);
|
|
46
|
+
}
|
|
47
|
+
return '';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function normalizeBaseUrl(value) {
|
|
51
|
+
const trimmed = String(value || '').trim().replace(/\/+$/, '');
|
|
52
|
+
if (!trimmed) return DEFAULT_BASE_URL;
|
|
53
|
+
if (/\/v1$/i.test(trimmed)) return trimmed;
|
|
54
|
+
return `${trimmed}/v1`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function generateImage(input = {}, options = {}) {
|
|
58
|
+
const prompt = String(input.prompt || '').trim();
|
|
59
|
+
if (!prompt) throw new Error('prompt 不能为空');
|
|
60
|
+
const credentials = loadGPTeamCredentials(options);
|
|
61
|
+
const payload = buildImageGenerationPayload(input);
|
|
62
|
+
const fetchImpl = options.fetch || globalThis.fetch;
|
|
63
|
+
if (typeof fetchImpl !== 'function') {
|
|
64
|
+
throw new Error('当前 Node.js 运行时不支持 fetch,请升级到 Node.js 18.18 或更高版本。');
|
|
65
|
+
}
|
|
66
|
+
const response = await fetchImpl(`${credentials.baseUrl}/images/generations`, {
|
|
67
|
+
method: 'POST',
|
|
68
|
+
headers: {
|
|
69
|
+
Authorization: `Bearer ${credentials.apiKey}`,
|
|
70
|
+
'Content-Type': 'application/json'
|
|
71
|
+
},
|
|
72
|
+
body: JSON.stringify(payload)
|
|
73
|
+
});
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
const detail = typeof response.text === 'function' ? await response.text() : '';
|
|
76
|
+
throw new Error(`GPTeam /v1/images/generations 返回 HTTP ${response.status}${detail ? `:${redactSecret(detail, credentials.apiKey)}` : ''}`);
|
|
77
|
+
}
|
|
78
|
+
const data = await response.json();
|
|
79
|
+
const first = Array.isArray(data && data.data) ? data.data[0] : null;
|
|
80
|
+
const b64 = first && typeof first.b64_json === 'string' ? first.b64_json : '';
|
|
81
|
+
if (!b64) {
|
|
82
|
+
throw new Error('GPTeam 图片接口没有返回 b64_json 图片数据。');
|
|
83
|
+
}
|
|
84
|
+
const format = normalizeImageFormat(payload.output_format);
|
|
85
|
+
const outputPath = resolveOutputPath(input.output_path, format, options);
|
|
86
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
87
|
+
fs.writeFileSync(outputPath, Buffer.from(b64, 'base64'));
|
|
88
|
+
return {
|
|
89
|
+
path: outputPath,
|
|
90
|
+
mimeType: mimeTypeForFormat(format),
|
|
91
|
+
b64,
|
|
92
|
+
model: payload.model,
|
|
93
|
+
size: payload.size,
|
|
94
|
+
quality: payload.quality,
|
|
95
|
+
format,
|
|
96
|
+
revisedPrompt: first && typeof first.revised_prompt === 'string' ? first.revised_prompt : ''
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function toolResultContent(result) {
|
|
101
|
+
const text = [
|
|
102
|
+
'GPTeam Image 2 生成完成。',
|
|
103
|
+
`文件:${result.path}`,
|
|
104
|
+
`模型:${result.model}`,
|
|
105
|
+
`规格:${result.size} / ${result.quality} / ${result.format}`
|
|
106
|
+
];
|
|
107
|
+
if (result.revisedPrompt) text.push(`修订提示词:${result.revisedPrompt}`);
|
|
108
|
+
return [
|
|
109
|
+
{ type: 'text', text: text.join('\n') },
|
|
110
|
+
{ type: 'image', data: result.b64, mimeType: result.mimeType }
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function resolveCodexHome(env, home) {
|
|
115
|
+
return expandHome(firstNonEmpty(env.GPTEAM_CODEX_HOME, env.CODEX_HOME, path.join(home, '.codex')), home);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function safeRead(filePath, readFile) {
|
|
119
|
+
try {
|
|
120
|
+
return readFile(filePath);
|
|
121
|
+
} catch {
|
|
122
|
+
return '';
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function resolveOutputPath(rawOutputPath, format, options) {
|
|
127
|
+
const env = options.env || process.env;
|
|
128
|
+
const home = options.home || os.homedir();
|
|
129
|
+
const name = `gpteam-image-${new Date().toISOString().replace(/[-:]/g, '').replace(/\..+/, '').replace('T', '-')}.${format}`;
|
|
130
|
+
if (rawOutputPath) {
|
|
131
|
+
const expanded = expandHome(String(rawOutputPath), home);
|
|
132
|
+
if (isDirectoryLike(expanded)) return path.join(expanded, name);
|
|
133
|
+
return expanded;
|
|
134
|
+
}
|
|
135
|
+
const outputDir = expandHome(firstNonEmpty(env.GPTEAM_IMAGE_OUTPUT_DIR, defaultImageOutputDir(home)), home);
|
|
136
|
+
return path.join(outputDir, name);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function defaultImageOutputDir(home) {
|
|
140
|
+
const desktop = path.join(home, 'Desktop');
|
|
141
|
+
return fs.existsSync(desktop) ? desktop : process.cwd();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function isDirectoryLike(value) {
|
|
145
|
+
return /[\\/]$/.test(value) || (fs.existsSync(value) && fs.statSync(value).isDirectory());
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function normalizeImageFormat(value) {
|
|
149
|
+
const normalized = String(value || DEFAULT_IMAGE_FORMAT).trim().toLowerCase();
|
|
150
|
+
if (normalized === 'jpg') return 'jpeg';
|
|
151
|
+
if (['png', 'jpeg', 'webp'].includes(normalized)) return normalized;
|
|
152
|
+
return DEFAULT_IMAGE_FORMAT;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function mimeTypeForFormat(format) {
|
|
156
|
+
if (format === 'jpeg') return 'image/jpeg';
|
|
157
|
+
if (format === 'webp') return 'image/webp';
|
|
158
|
+
return 'image/png';
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function firstNonEmpty(...values) {
|
|
162
|
+
for (const value of values) {
|
|
163
|
+
const text = String(value || '').trim();
|
|
164
|
+
if (text) return text;
|
|
165
|
+
}
|
|
166
|
+
return '';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function expandHome(value, home) {
|
|
170
|
+
const text = String(value || '');
|
|
171
|
+
if (text === '~') return home;
|
|
172
|
+
if (text.startsWith(`~${path.sep}`)) return path.join(home, text.slice(2));
|
|
173
|
+
if (text.startsWith('~/')) return path.join(home, text.slice(2));
|
|
174
|
+
return text;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function unescapeTomlString(value) {
|
|
178
|
+
return String(value || '').replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function redactSecret(text, secret) {
|
|
182
|
+
let result = String(text || '');
|
|
183
|
+
if (secret) result = result.split(secret).join('[redacted]');
|
|
184
|
+
result = result.replace(/sk-[A-Za-z0-9_-]{6,}/g, 'sk-[redacted]');
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
+
import { generateImage, toolResultContent } from './image.js';
|
|
5
|
+
|
|
6
|
+
const GENERATE_IMAGE_TOOL = {
|
|
7
|
+
name: 'generate_image',
|
|
8
|
+
description: 'Generate an image through GPTeam Image 2 and save it to a local file.',
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
prompt: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
description: 'Image prompt.'
|
|
15
|
+
},
|
|
16
|
+
size: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Image size, for example 1024x1024.',
|
|
19
|
+
default: '1024x1024'
|
|
20
|
+
},
|
|
21
|
+
quality: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
description: 'Image quality.',
|
|
24
|
+
default: 'high'
|
|
25
|
+
},
|
|
26
|
+
format: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'Output image format.',
|
|
29
|
+
enum: ['png', 'jpeg', 'webp'],
|
|
30
|
+
default: 'png'
|
|
31
|
+
},
|
|
32
|
+
output_path: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
description: 'Optional output file path or directory.'
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
required: ['prompt'],
|
|
38
|
+
additionalProperties: false
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export function createServer(deps = {}) {
|
|
43
|
+
const server = new Server({
|
|
44
|
+
name: 'gpteam-image-mcp',
|
|
45
|
+
version: '0.1.0'
|
|
46
|
+
}, {
|
|
47
|
+
capabilities: {
|
|
48
|
+
tools: {}
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
53
|
+
tools: [GENERATE_IMAGE_TOOL]
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
57
|
+
const toolName = request.params && request.params.name;
|
|
58
|
+
if (toolName !== GENERATE_IMAGE_TOOL.name) {
|
|
59
|
+
throw new Error(`未知工具:${toolName}`);
|
|
60
|
+
}
|
|
61
|
+
const result = await generateImage(request.params.arguments || {}, deps);
|
|
62
|
+
return {
|
|
63
|
+
content: toolResultContent(result)
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return server;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function runServer() {
|
|
71
|
+
const server = createServer();
|
|
72
|
+
const transport = new StdioServerTransport();
|
|
73
|
+
await server.connect(transport);
|
|
74
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gpteam",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "GPTeam API interactive client configurator and ingress benchmark CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"gpteam": "bin/gpteam-api-config.js",
|
|
8
|
-
"gpteam-api-config": "bin/gpteam-api-config.js"
|
|
8
|
+
"gpteam-api-config": "bin/gpteam-api-config.js",
|
|
9
|
+
"gpteam-image-mcp": "bin/gpteam-image-mcp.js"
|
|
9
10
|
},
|
|
10
11
|
"files": [
|
|
11
12
|
"bin",
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
"test": "node --test test/*.test.mjs"
|
|
16
17
|
},
|
|
17
18
|
"dependencies": {
|
|
19
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
18
20
|
"json5": "^2.2.3"
|
|
19
21
|
},
|
|
20
22
|
"engines": {
|