gpteam 0.1.22 → 0.1.24

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 CHANGED
@@ -16,14 +16,14 @@ Client config writing follows the same safety pattern as cc-switch: keep Codex t
16
16
 
17
17
  The Image MCP config uses cc-switch-style per-client env blocks. Codex writes `[mcp_servers.gpteam_image.env]`, OpenCode writes `mcp.gpteam_image.environment`, and Claude Code writes `mcpServers.gpteam_image.env` in `~/.claude.json`. The MCP receives `GPTEAM_API_KEY` and `GPTEAM_BASE_URL` from that MCP config, so it does not depend on Codex `auth.json` or inherited `OPENAI_API_KEY`.
18
18
 
19
- The Image MCP exposes both a synchronous compatibility tool and a local async job flow:
19
+ The Image MCP exposes an async-first local job flow plus a legacy compatibility alias:
20
20
 
21
21
  - `create_image_job`: recommended for normal use. It starts a local background image job and returns `job_id` quickly, which avoids losing the whole generation when a VPN, proxy, or client has a 60-second idle timeout.
22
22
  - `get_image_job_status`: checks whether the local job is queued, running, succeeded, failed, canceled, or expired.
23
23
  - `cancel_image_job`: cancels a queued/running local job.
24
24
  - `download_image_result`: returns the completed file metadata and image content. Use `metadata_only`, `include_image`, and `include_revised_prompt` to control large result payloads.
25
25
  - `get_capabilities`: returns supported sizes, formats, quality values, async support, cancellation semantics, queue limits, and image-to-image support.
26
- - `generate_image`: waits for completion and writes the image file immediately. This is kept for synchronous compatibility.
26
+ - `generate_image`: legacy compatibility alias. It now creates the same async job and returns `job_id` immediately instead of blocking until the image is complete.
27
27
 
28
28
  Image MCP results are returned as stable JSON text and MCP `structuredContent`. Successful results include final file path, model, action, size, format, quality, byte size, SHA-256, MIME type, image dimensions, duration, retry count, `job_id`, `trace_id`, and optional `idempotency_key`. Error results use stable `error.code`, `error.message`, `error.retryable`, `error.stage`, `error.upstream_status`, and `error.trace_id` fields while keeping compatibility fields such as `category` and `http_status`.
29
29
 
package/lib/config.js CHANGED
@@ -13,8 +13,7 @@ const IMAGE_MCP_ENABLED_TOOLS = [
13
13
  'get_image_job_status',
14
14
  'download_image_result',
15
15
  'cancel_image_job',
16
- 'get_capabilities',
17
- 'generate_image'
16
+ 'get_capabilities'
18
17
  ];
19
18
 
20
19
  export const CLIENTS = [
@@ -63,7 +62,7 @@ export function writeCodexConfig(settings) {
63
62
  `command = ${tomlString(mcpCommand.command)}`,
64
63
  `args = [${mcpCommand.args.map((arg) => tomlString(arg)).join(', ')}]`,
65
64
  'startup_timeout_sec = 20',
66
- 'tool_timeout_sec = 300',
65
+ 'tool_timeout_sec = 900',
67
66
  `enabled_tools = [${IMAGE_MCP_ENABLED_TOOLS.map((name) => tomlString(name)).join(', ')}]`,
68
67
  'default_tools_approval_mode = "prompt"',
69
68
  '',
package/lib/help.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export const PACKAGE_NAME = 'gpteam';
2
- export const PACKAGE_VERSION = '0.1.22';
2
+ export const PACKAGE_VERSION = '0.1.24';
3
3
 
4
4
  export function getHelpText() {
5
5
  return [
@@ -14,9 +14,32 @@ export const DEFAULT_BASE_URL = 'https://api.gpteamservices.com/v1';
14
14
  export const DEFAULT_IMAGE_MODEL = 'gpt-image-2';
15
15
  export { DEFAULT_IMAGE_FORMAT };
16
16
 
17
+ export const IMAGE_FORMATS = ['png', 'jpeg', 'webp'];
18
+ export const IMAGE_QUALITY_LEVELS = ['low', 'medium', 'high', 'auto'];
19
+ export const IMAGE_BACKGROUND_OPTIONS = ['auto', 'opaque'];
20
+ export const IMAGE_MODERATION_OPTIONS = ['auto', 'low'];
21
+ export const POPULAR_IMAGE_SIZES = [
22
+ { label: '1K 方图', size: '1024x1024', aspect_ratio: '1:1' },
23
+ { label: '1K 横图', size: '1536x1024', aspect_ratio: '3:2' },
24
+ { label: '1K 竖图', size: '1024x1536', aspect_ratio: '2:3' },
25
+ { label: '2K 方图', size: '2048x2048', aspect_ratio: '1:1' },
26
+ { label: '2K 宽屏', size: '2048x1152', aspect_ratio: '16:9' },
27
+ { label: '2K 竖幅', size: '1152x2048', aspect_ratio: '9:16' },
28
+ { label: '4K 横图', size: '3840x2160', aspect_ratio: '16:9' },
29
+ { label: '4K 竖图', size: '2160x3840', aspect_ratio: '9:16' },
30
+ { label: '自动', size: 'auto', aspect_ratio: 'auto' }
31
+ ];
32
+ export const IMAGE_SIZE_CONSTRAINTS = {
33
+ max_edge_px: 3840,
34
+ edge_multiple_px: 16,
35
+ max_long_to_short_ratio: 3,
36
+ min_total_pixels: 655360,
37
+ max_total_pixels: 8294400
38
+ };
39
+
17
40
  const defaultMaxAttempts = 3;
18
41
  const defaultRetryDelayMs = 800;
19
- const defaultRequestTimeoutMs = 5 * 60 * 1000;
42
+ const defaultRequestTimeoutMs = 15 * 60 * 1000;
20
43
  const defaultMaxConcurrentJobs = 2;
21
44
  const defaultMaxQueuedJobs = 20;
22
45
  const defaultJobTTLMS = 30 * 60 * 1000;
@@ -32,7 +55,7 @@ export function buildImageGenerationPayload(input = {}, options = {}) {
32
55
  stream: true,
33
56
  size: String(input.size || '1024x1024'),
34
57
  quality: String(input.quality || 'high'),
35
- output_format: normalizeImageFormat(input.format || input.output_format || DEFAULT_IMAGE_FORMAT)
58
+ output_format: resolveImageOutputFormat(input)
36
59
  };
37
60
  const imageOptions = { ...options, home: options.home };
38
61
  const images = normalizeInputImages(collectInputImageValues(input), imageOptions);
@@ -41,6 +64,7 @@ export function buildImageGenerationPayload(input = {}, options = {}) {
41
64
  if (mask) payload.mask = { image_url: mask };
42
65
  copyOptionalImageToolOption(payload, input, 'background');
43
66
  copyOptionalImageToolOption(payload, input, 'moderation');
67
+ copyOptionalImageToolOption(payload, input, 'output_compression');
44
68
  return payload;
45
69
  }
46
70
 
@@ -88,15 +112,7 @@ export function normalizeBaseUrl(value) {
88
112
 
89
113
  export async function generateImage(input = {}, options = {}) {
90
114
  const startedAt = now(options);
91
- const prompt = String(input.prompt || '').trim();
92
- if (!prompt) {
93
- throw new ImageMCPError('prompt 不能为空', {
94
- code: 'prompt_required',
95
- category: 'parameter',
96
- stage: 'validate',
97
- retryable: false
98
- });
99
- }
115
+ validateImageInput(input, { requirePrompt: true });
100
116
  const credentials = loadGPTeamCredentials(options);
101
117
  const payload = buildImageGenerationPayload(input, options);
102
118
  const fetchImpl = options.fetch || globalThis.fetch;
@@ -147,6 +163,11 @@ export function createImageJobStore(options = {}) {
147
163
  }
148
164
 
149
165
  export function createImageJob(input = {}, options = {}) {
166
+ try {
167
+ validateImageInput(input, { requirePrompt: true });
168
+ } catch (error) {
169
+ return resultFromError(error);
170
+ }
150
171
  const store = options.store || defaultJobStore;
151
172
  configureJobStore(store, options);
152
173
  cleanupImageJobs(store);
@@ -280,6 +301,7 @@ export function getCapabilities(options = {}) {
280
301
  ok: true,
281
302
  model: DEFAULT_IMAGE_MODEL,
282
303
  default_model: DEFAULT_IMAGE_MODEL,
304
+ preferred_tool: 'create_image_job',
283
305
  supports_async: true,
284
306
  supports_cancel: true,
285
307
  cancel_semantics: 'best_effort',
@@ -288,9 +310,18 @@ export function getCapabilities(options = {}) {
288
310
  supports_mask: true,
289
311
  image_input_fields: ['images', 'image', 'image_path', 'image_paths', 'input_image', 'input_images'],
290
312
  mask_input_fields: ['mask', 'mask_path'],
291
- sizes: ['1024x1024', '1536x1024', '1024x1536', 'auto'],
292
- formats: ['png', 'jpeg', 'webp'],
293
- quality: ['low', 'medium', 'high', 'auto'],
313
+ sizes: POPULAR_IMAGE_SIZES.map((item) => item.size),
314
+ popular_sizes: POPULAR_IMAGE_SIZES,
315
+ size_presets: ['1K', '2K', '4K', 'auto'],
316
+ size_constraints: IMAGE_SIZE_CONSTRAINTS,
317
+ aspect_ratios: ['1:1', '3:2', '2:3', '16:9', '9:16', 'custom'],
318
+ supports_custom_size: true,
319
+ formats: IMAGE_FORMATS,
320
+ output_formats: IMAGE_FORMATS,
321
+ quality: IMAGE_QUALITY_LEVELS,
322
+ background: IMAGE_BACKGROUND_OPTIONS,
323
+ moderation: IMAGE_MODERATION_OPTIONS,
324
+ supports_output_compression: true,
294
325
  max_prompt_length: 32000,
295
326
  statuses: ['queued', 'running', 'succeeded', 'failed', 'canceled', 'expired'],
296
327
  default_output_format: DEFAULT_IMAGE_FORMAT,
@@ -301,6 +332,22 @@ export function getCapabilities(options = {}) {
301
332
  };
302
333
  }
303
334
 
335
+ export function validateImageInput(input = {}, options = {}) {
336
+ if (options.requirePrompt && !String(input.prompt || '').trim()) {
337
+ throw imageParamError('prompt_required', 'prompt 不能为空', 'prompt', input.prompt, {
338
+ hint: '请先让用户提供图片提示词。'
339
+ });
340
+ }
341
+ validateImageSize(input.size);
342
+ validateEnumImageParam('quality', input.quality, IMAGE_QUALITY_LEVELS);
343
+ validateImageFormatParam(input);
344
+ validateEnumImageParam('background', input.background, IMAGE_BACKGROUND_OPTIONS, {
345
+ hint: 'gpt-image-2 不支持 transparent,请使用 auto 或 opaque。'
346
+ });
347
+ validateEnumImageParam('moderation', input.moderation, IMAGE_MODERATION_OPTIONS);
348
+ validateOutputCompression(input.output_compression);
349
+ }
350
+
304
351
  async function fetchImageWithRetry(fetchImpl, credentials, payload, options) {
305
352
  const maxAttempts = resolveBoundedInt(1, options.maxAttempts, options.env && options.env.GPTEAM_IMAGE_MAX_ATTEMPTS, defaultMaxAttempts);
306
353
  const retryDelayMs = resolveBoundedInt(0, options.retryDelayMs, options.env && options.env.GPTEAM_IMAGE_RETRY_DELAY_MS, defaultRetryDelayMs);
@@ -722,6 +769,107 @@ function resolveRevisedPromptFlag(input) {
722
769
  return true;
723
770
  }
724
771
 
772
+ function validateImageSize(value) {
773
+ if (value === undefined || value === null || value === '') return;
774
+ const text = String(value).trim().toLowerCase();
775
+ if (text === 'auto') return;
776
+ const match = text.match(/^(\d{2,5})x(\d{2,5})$/);
777
+ if (!match) {
778
+ throw imageParamError('invalid_size', 'size 必须是 auto 或类似 1024x1024 的宽高格式。', 'size', value, {
779
+ supported_values: POPULAR_IMAGE_SIZES.map((item) => item.size),
780
+ hint: '常用:1024x1024、1536x1024、1024x1536、2048x2048、2048x1152、1152x2048、3840x2160、2160x3840。'
781
+ });
782
+ }
783
+ const width = Number(match[1]);
784
+ const height = Number(match[2]);
785
+ const longSide = Math.max(width, height);
786
+ const shortSide = Math.min(width, height);
787
+ const pixels = width * height;
788
+ const valid = width % IMAGE_SIZE_CONSTRAINTS.edge_multiple_px === 0 &&
789
+ height % IMAGE_SIZE_CONSTRAINTS.edge_multiple_px === 0 &&
790
+ longSide <= IMAGE_SIZE_CONSTRAINTS.max_edge_px &&
791
+ longSide / shortSide <= IMAGE_SIZE_CONSTRAINTS.max_long_to_short_ratio &&
792
+ pixels >= IMAGE_SIZE_CONSTRAINTS.min_total_pixels &&
793
+ pixels <= IMAGE_SIZE_CONSTRAINTS.max_total_pixels;
794
+ if (!valid) {
795
+ throw imageParamError('invalid_size', 'size 超出 gpt-image-2 支持范围。', 'size', value, {
796
+ constraints: IMAGE_SIZE_CONSTRAINTS,
797
+ supported_values: POPULAR_IMAGE_SIZES.map((item) => item.size),
798
+ hint: '宽高需为 16 的倍数,长边不超过 3840,长短边比例不超过 3:1。'
799
+ });
800
+ }
801
+ }
802
+
803
+ function validateEnumImageParam(field, value, supportedValues, extra = {}) {
804
+ if (value === undefined || value === null || value === '') return;
805
+ const normalized = String(value).trim().toLowerCase();
806
+ if (supportedValues.includes(normalized)) return;
807
+ throw imageParamError(`invalid_${field}`, `${field} 参数不支持。`, field, value, {
808
+ supported_values: supportedValues,
809
+ ...extra
810
+ });
811
+ }
812
+
813
+ function validateImageFormatParam(input) {
814
+ const formatValue = input.format;
815
+ const outputFormatValue = input.output_format;
816
+ const format = normalizeDeclaredImageFormat(formatValue);
817
+ const outputFormat = normalizeDeclaredImageFormat(outputFormatValue);
818
+ if (formatValue !== undefined && formatValue !== null && formatValue !== '' && !IMAGE_FORMATS.includes(format)) {
819
+ throw invalidImageFormatError('format', formatValue);
820
+ }
821
+ if (outputFormatValue !== undefined && outputFormatValue !== null && outputFormatValue !== '' && !IMAGE_FORMATS.includes(outputFormat)) {
822
+ throw invalidImageFormatError('output_format', outputFormatValue);
823
+ }
824
+ if (format && outputFormat && format !== outputFormat) {
825
+ throw imageParamError('invalid_format_conflict', 'format 和 output_format 不一致。', 'output_format', outputFormatValue, {
826
+ format,
827
+ output_format: outputFormat,
828
+ hint: '两个字段同时传入时必须表示同一种格式,例如 jpg 和 jpeg 可以同时使用。'
829
+ });
830
+ }
831
+ }
832
+
833
+ function validateOutputCompression(value) {
834
+ if (value === undefined || value === null || value === '') return;
835
+ const amount = Number(value);
836
+ if (Number.isInteger(amount) && amount >= 0 && amount <= 100) return;
837
+ throw imageParamError('invalid_output_compression', 'output_compression 必须是 0 到 100 的整数。', 'output_compression', value, {
838
+ supported_values: ['0-100']
839
+ });
840
+ }
841
+
842
+ function resolveImageOutputFormat(input = {}) {
843
+ return normalizeImageFormat(input.output_format || input.format || DEFAULT_IMAGE_FORMAT);
844
+ }
845
+
846
+ function normalizeDeclaredImageFormat(value) {
847
+ if (value === undefined || value === null || value === '') return '';
848
+ const normalized = String(value).trim().toLowerCase();
849
+ return normalized === 'jpg' ? 'jpeg' : normalized;
850
+ }
851
+
852
+ function invalidImageFormatError(field, received) {
853
+ return imageParamError('invalid_format', 'format/output_format 参数不支持。', field, received, {
854
+ supported_values: IMAGE_FORMATS,
855
+ hint: '支持 png、jpeg、webp,jpg 会按 jpeg 处理。'
856
+ });
857
+ }
858
+
859
+ function imageParamError(code, message, field, received, details = {}) {
860
+ return new ImageMCPError(message, {
861
+ code,
862
+ category: 'parameter',
863
+ stage: 'validate',
864
+ retryable: false,
865
+ details: {
866
+ field,
867
+ received,
868
+ ...details
869
+ }
870
+ });
871
+ }
872
+
725
873
  function resolveCodexHome(env, home) {
726
874
  return expandHome(firstNonEmpty(env.GPTEAM_CODEX_HOME, env.CODEX_HOME, path.join(home, '.codex')), home);
727
875
  }
@@ -6,87 +6,119 @@ import {
6
6
  cancelImageJob,
7
7
  createImageJob,
8
8
  downloadImageResult,
9
- generateImage,
10
9
  getCapabilities,
10
+ IMAGE_BACKGROUND_OPTIONS,
11
+ IMAGE_FORMATS,
12
+ IMAGE_MODERATION_OPTIONS,
13
+ IMAGE_QUALITY_LEVELS,
14
+ POPULAR_IMAGE_SIZES,
11
15
  getImageJobStatus,
12
16
  resultFromError,
13
17
  structuredToolResult,
14
18
  toolResultContent
15
19
  } from './image.js';
16
20
 
21
+ const imageToolPromptingInstruction = '调用前请先确认用户真实需求:尺寸或比例、质量、输出格式、保存目录或文件名。缺少这些关键信息时应先追问,不要直接默认生成。常规生图请优先用 create_image_job,避免长任务占住 MCP 连接。';
22
+
17
23
  const imageInputProperties = {
18
24
  prompt: {
19
25
  type: 'string',
20
- description: 'Image prompt.'
26
+ description: '图片提示词。需要尽量具体,若用户只给模糊需求,应先追问风格、主体、场景和用途。'
21
27
  },
22
28
  size: {
23
29
  type: 'string',
24
- description: 'Image size, for example 1024x1024.',
25
- default: '1024x1024'
30
+ description: `图片尺寸。支持 auto、常用尺寸 ${POPULAR_IMAGE_SIZES.map((item) => `${item.label} ${item.size}`).join('、')},也支持满足约束的自定义宽高,例如 9:16 可用 1152x2048 或 2160x3840。`
26
31
  },
27
32
  quality: {
28
33
  type: 'string',
29
- description: 'Image quality.',
30
- default: 'high'
34
+ description: '图片质量。生图前应让用户确认速度优先还是质量优先。',
35
+ enum: IMAGE_QUALITY_LEVELS
31
36
  },
32
37
  format: {
33
38
  type: 'string',
34
- description: 'Output image format.',
35
- enum: ['png', 'jpeg', 'webp'],
36
- default: 'png'
39
+ description: '输出图片格式。',
40
+ enum: IMAGE_FORMATS
41
+ },
42
+ output_format: {
43
+ type: 'string',
44
+ description: '输出图片格式别名,和 format 等价。',
45
+ enum: IMAGE_FORMATS
37
46
  },
38
47
  output_path: {
39
48
  type: 'string',
40
- description: 'Optional output file path or directory.'
49
+ description: '保存路径,可以是目录或完整文件路径。缺少时建议先询问用户想保存到哪里。'
41
50
  },
42
51
  overwrite: {
43
52
  type: 'boolean',
44
- description: 'Overwrite output_path when it already exists. Defaults to false.',
53
+ description: '文件已存在时是否覆盖。默认 false,会自动生成 -v2、-v3 等新文件名。',
45
54
  default: false
46
55
  },
47
56
  idempotency_key: {
48
57
  type: 'string',
49
- description: 'Optional idempotency key. Reusing it returns the same local job in this MCP process.'
58
+ description: '幂等键。客户端超时后用同一个 key 重试,会复用同一进程内的本地任务,避免重复生成。'
59
+ },
60
+ image: {
61
+ type: 'string',
62
+ description: '单张输入图片,支持 data URL、HTTPS URL 或本地文件路径,用于图生图或编辑。'
50
63
  },
51
64
  images: {
52
65
  type: 'array',
53
66
  items: { type: 'string' },
54
- description: 'Optional input images for image-to-image or edit. Values may be data URLs, HTTPS URLs, or local file paths.'
67
+ description: '多张输入图片,支持 data URL、HTTPS URL 或本地文件路径,用于图生图或编辑。'
55
68
  },
56
69
  image_path: {
57
70
  type: 'string',
58
- description: 'Optional single local image path alias for image-to-image or edit.'
71
+ description: '单张本地输入图片路径别名,用于图生图或编辑。'
59
72
  },
60
73
  image_paths: {
61
74
  type: 'array',
62
75
  items: { type: 'string' },
63
- description: 'Optional local image path aliases for image-to-image or edit.'
76
+ description: '多张本地输入图片路径别名,用于图生图或编辑。'
64
77
  },
65
78
  input_image: {
66
79
  type: 'string',
67
- description: 'Optional single input image alias. Accepts a data URL, HTTPS URL, or local file path.'
80
+ description: '单张输入图片别名,支持 data URLHTTPS URL 或本地文件路径。'
68
81
  },
69
82
  input_images: {
70
83
  type: 'array',
71
84
  items: { type: 'string' },
72
- description: 'Optional input image aliases. Accepts data URLs, HTTPS URLs, or local file paths.'
85
+ description: '多张输入图片别名,支持 data URL、HTTPS URL 或本地文件路径。'
73
86
  },
74
87
  mask: {
75
88
  type: 'string',
76
- description: 'Optional mask image for image edit. Value may be a data URL, HTTPS URL, or local file path.'
89
+ description: '编辑遮罩图片,支持 data URLHTTPS URL 或本地文件路径。'
77
90
  },
78
91
  mask_path: {
79
92
  type: 'string',
80
- description: 'Optional local mask image path alias for image edit.'
93
+ description: '本地遮罩图片路径别名。'
81
94
  },
82
95
  input_fidelity: {
83
96
  type: 'string',
84
- description: 'Accepted for compatibility. The GPTeam Image 2 bridge currently ignores this option because the upstream Codex image tool rejects it on edits.',
97
+ description: '兼容字段。当前 GPTeam Image 2 桥接会忽略该字段,因为上游 Codex 图片工具会拒绝 edits 中的该参数。',
85
98
  enum: ['low', 'high']
86
99
  },
100
+ background: {
101
+ type: 'string',
102
+ description: '背景策略。gpt-image-2 当前支持 auto 或 opaque。',
103
+ enum: IMAGE_BACKGROUND_OPTIONS
104
+ },
105
+ moderation: {
106
+ type: 'string',
107
+ description: '内容安全策略。通常保持 auto,需要更低过滤强度时可用 low。',
108
+ enum: IMAGE_MODERATION_OPTIONS
109
+ },
110
+ output_compression: {
111
+ type: 'integer',
112
+ description: '输出压缩比例,0 到 100 的整数。'
113
+ },
114
+ include_revised_prompt: {
115
+ type: 'boolean',
116
+ description: '是否返回上游修订后的提示词。',
117
+ default: true
118
+ },
87
119
  return_revised_prompt: {
88
120
  type: 'boolean',
89
- description: 'Return upstream revised prompt when available.',
121
+ description: '是否返回上游修订后的提示词,兼容旧字段。',
90
122
  default: true
91
123
  }
92
124
  };
@@ -94,7 +126,7 @@ const imageInputProperties = {
94
126
  const tools = [
95
127
  {
96
128
  name: 'create_image_job',
97
- description: 'Recommended for normal use. Create a local background GPTeam Image 2 job and return immediately with a job_id.',
129
+ description: `推荐常规使用。创建本地后台 GPTeam Image 2 任务并立即返回 job_id。${imageToolPromptingInstruction}`,
98
130
  inputSchema: {
99
131
  type: 'object',
100
132
  properties: imageInputProperties,
@@ -104,22 +136,22 @@ const tools = [
104
136
  },
105
137
  {
106
138
  name: 'get_image_job_status',
107
- description: 'Get the status of a local GPTeam Image 2 job.',
139
+ description: '查询本地 GPTeam Image 2 图片任务状态。',
108
140
  inputSchema: jobIDSchema()
109
141
  },
110
142
  {
111
143
  name: 'cancel_image_job',
112
- description: 'Cancel a local GPTeam Image 2 job when it is still queued or running.',
144
+ description: '取消仍在 queued running 的本地 GPTeam Image 2 图片任务。取消是 best-effort,上游已开始生成时不保证同步取消。',
113
145
  inputSchema: jobIDSchema()
114
146
  },
115
147
  {
116
148
  name: 'download_image_result',
117
- description: 'Return the saved local file metadata and image content for a completed image job.',
149
+ description: '下载已完成图片任务的本地文件元数据和图片内容,可选择只返回 metadata',
118
150
  inputSchema: downloadSchema()
119
151
  },
120
152
  {
121
153
  name: 'get_capabilities',
122
- description: 'Return GPTeam Image MCP capabilities, supported sizes, formats, quality levels, async support, and queue limits.',
154
+ description: '返回 GPTeam Image MCP 能力,包括支持尺寸、格式、质量、异步任务、取消语义、队列上限和参数约束。',
123
155
  inputSchema: {
124
156
  type: 'object',
125
157
  properties: {},
@@ -128,7 +160,7 @@ const tools = [
128
160
  },
129
161
  {
130
162
  name: 'generate_image',
131
- description: 'Compatibility tool. Generate an image through GPTeam Image 2, wait for completion, and save it locally.',
163
+ description: `兼容旧调用的异步别名。为避免长图或高质量任务导致 MCP 连接断开,它不会等待图片完成,而是创建任务并立即返回 job_id;之后必须用 get_image_job_status download_image_result 获取结果。${imageToolPromptingInstruction}`,
132
164
  inputSchema: {
133
165
  type: 'object',
134
166
  properties: imageInputProperties,
@@ -192,7 +224,7 @@ export async function callImageTool(toolName, args = {}, deps = {}) {
192
224
  case 'get_capabilities':
193
225
  return getCapabilities(deps);
194
226
  case 'generate_image':
195
- return await generateImage(args || {}, deps);
227
+ return createImageJob(args || {}, deps);
196
228
  default:
197
229
  throw new McpError(ErrorCode.InvalidParams, `未知工具:${toolName}`);
198
230
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gpteam",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "description": "GPTeam API interactive client configurator and ingress benchmark CLI.",
5
5
  "type": "module",
6
6
  "bin": {