mcp-gpt-image 0.2.0__tar.gz
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.
- mcp_gpt_image-0.2.0/.env.example +14 -0
- mcp_gpt_image-0.2.0/.github/workflows/ci.yml +33 -0
- mcp_gpt_image-0.2.0/.github/workflows/release.yml +68 -0
- mcp_gpt_image-0.2.0/.gitignore +13 -0
- mcp_gpt_image-0.2.0/CHANGELOG.md +114 -0
- mcp_gpt_image-0.2.0/CLAUDE.md +92 -0
- mcp_gpt_image-0.2.0/LICENSE +110 -0
- mcp_gpt_image-0.2.0/Makefile +29 -0
- mcp_gpt_image-0.2.0/PKG-INFO +235 -0
- mcp_gpt_image-0.2.0/README.md +201 -0
- mcp_gpt_image-0.2.0/docs/README.md +40 -0
- mcp_gpt_image-0.2.0/docs/configuration.md +140 -0
- mcp_gpt_image-0.2.0/docs/getting-started.md +187 -0
- mcp_gpt_image-0.2.0/docs/refinement.md +202 -0
- mcp_gpt_image-0.2.0/docs/tools.md +175 -0
- mcp_gpt_image-0.2.0/docs/troubleshooting.md +303 -0
- mcp_gpt_image-0.2.0/pyproject.toml +92 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/__init__.py +1 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/__main__.py +24 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/adapters/__init__.py +1 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/adapters/_errors.py +53 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/adapters/images_client.py +132 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/adapters/responses_client.py +123 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/config.py +52 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/domain/__init__.py +1 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/domain/errors.py +31 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/domain/image_store.py +89 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/domain/results.py +47 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/domain/types.py +43 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/server.py +56 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/tools/__init__.py +34 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/tools/_base.py +63 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/tools/_validation.py +123 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/tools/edit.py +84 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/tools/generate.py +80 -0
- mcp_gpt_image-0.2.0/src/gpt_image_mcp/tools/refine.py +81 -0
- mcp_gpt_image-0.2.0/tests/conftest.py +198 -0
- mcp_gpt_image-0.2.0/tests/test_edit.py +171 -0
- mcp_gpt_image-0.2.0/tests/test_generate.py +201 -0
- mcp_gpt_image-0.2.0/tests/test_image_store.py +69 -0
- mcp_gpt_image-0.2.0/tests/test_refine.py +183 -0
- mcp_gpt_image-0.2.0/tests/test_server.py +55 -0
- mcp_gpt_image-0.2.0/uv.lock +1849 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Required. Standard OpenAI variable, reused as-is.
|
|
2
|
+
OPENAI_API_KEY=sk-...
|
|
3
|
+
|
|
4
|
+
# Where generated images are written. Created on first use.
|
|
5
|
+
GPT_IMAGE_OUTPUT_DIR=./generated-images
|
|
6
|
+
|
|
7
|
+
# Image model. Set to gpt-image-2-2026-04-21 to pin a snapshot and freeze behaviour.
|
|
8
|
+
GPT_IMAGE_MODEL=gpt-image-2
|
|
9
|
+
|
|
10
|
+
# Seconds before an image call is abandoned. Large sizes and complex prompts are slow.
|
|
11
|
+
GPT_IMAGE_TIMEOUT=300
|
|
12
|
+
|
|
13
|
+
# Same, for refine_image, which runs a reasoning model in front of the drawing.
|
|
14
|
+
GPT_IMAGE_REFINE_TIMEOUT=300
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["**"]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
check:
|
|
13
|
+
name: Lint and test (Python ${{ matrix.python-version }})
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
strategy:
|
|
16
|
+
fail-fast: false
|
|
17
|
+
matrix:
|
|
18
|
+
python-version: ["3.12", "3.13"]
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- uses: astral-sh/setup-uv@v5
|
|
23
|
+
with:
|
|
24
|
+
enable-cache: true
|
|
25
|
+
|
|
26
|
+
- name: Install
|
|
27
|
+
run: uv sync --all-extras --python ${{ matrix.python-version }}
|
|
28
|
+
|
|
29
|
+
- name: Lint
|
|
30
|
+
run: make lint
|
|
31
|
+
|
|
32
|
+
- name: Test
|
|
33
|
+
run: make test
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Publishing runs on a published GitHub release, never on a tag push, so a tag
|
|
2
|
+
# pushed by mistake cannot ship a version to PyPI.
|
|
3
|
+
#
|
|
4
|
+
# Credentials: none. PyPI is configured with a trusted publisher pointing at this
|
|
5
|
+
# repository, this workflow file and the "pypi" environment below, and the upload
|
|
6
|
+
# is authorised by the OIDC token minted by `id-token: write`.
|
|
7
|
+
name: Release
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
release:
|
|
11
|
+
types: [published]
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
name: Build distributions
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- uses: astral-sh/setup-uv@v5
|
|
24
|
+
with:
|
|
25
|
+
enable-cache: true
|
|
26
|
+
|
|
27
|
+
- name: Install
|
|
28
|
+
run: uv sync --all-extras
|
|
29
|
+
|
|
30
|
+
- name: Check the tag matches the packaged version
|
|
31
|
+
run: |
|
|
32
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
33
|
+
packaged="$(uv run python -c 'import tomllib,pathlib; print(tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"])')"
|
|
34
|
+
if [ "$tag" != "$packaged" ]; then
|
|
35
|
+
echo "Release tag $GITHUB_REF_NAME does not match version $packaged in pyproject.toml"
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
- name: Lint
|
|
40
|
+
run: make lint
|
|
41
|
+
|
|
42
|
+
- name: Test
|
|
43
|
+
run: make test
|
|
44
|
+
|
|
45
|
+
- name: Build
|
|
46
|
+
run: uv build
|
|
47
|
+
|
|
48
|
+
- uses: actions/upload-artifact@v4
|
|
49
|
+
with:
|
|
50
|
+
name: dist
|
|
51
|
+
path: dist/
|
|
52
|
+
|
|
53
|
+
publish:
|
|
54
|
+
name: Publish to PyPI
|
|
55
|
+
needs: build
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
environment:
|
|
58
|
+
name: pypi
|
|
59
|
+
url: https://pypi.org/p/mcp-gpt-image
|
|
60
|
+
permissions:
|
|
61
|
+
id-token: write
|
|
62
|
+
steps:
|
|
63
|
+
- uses: actions/download-artifact@v4
|
|
64
|
+
with:
|
|
65
|
+
name: dist
|
|
66
|
+
path: dist/
|
|
67
|
+
|
|
68
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project follows
|
|
5
|
+
[semantic versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## 0.2.0 - 2026-08-14
|
|
8
|
+
|
|
9
|
+
A breaking release, top to bottom. The package has a new name, a new import path, one supported
|
|
10
|
+
model and a third tool. Nothing from 0.1.x is carried forward: there are no compatibility
|
|
11
|
+
shims, no deprecated aliases and no dual code paths, because the model this server was written
|
|
12
|
+
against is being retired and a transitional layer would only have made the migration slower to
|
|
13
|
+
find.
|
|
14
|
+
|
|
15
|
+
Migrating is a matter of changing the command your MCP client runs, renaming five environment
|
|
16
|
+
variables, and deleting the `model` and `input_fidelity` arguments from any tool call you have
|
|
17
|
+
scripted.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **`refine_image`, multi-turn refinement.** The reason this release exists. It runs through the
|
|
22
|
+
OpenAI Responses API with the built-in image generation tool, driven by the mainline model
|
|
23
|
+
`gpt-5.6`, and returns a `session_id` alongside the saved image. Passing that id on the next
|
|
24
|
+
call continues the same conversation, so a correction is one sentence rather than a rewritten
|
|
25
|
+
prompt plus a re-uploaded image. Sessions last about 30 days. It costs more per image than
|
|
26
|
+
`generate_image`, because a reasoning model is doing the steering, and it pays for itself from
|
|
27
|
+
the second turn on.
|
|
28
|
+
- **Local validation of the refinement session id.** An id that cannot be an OpenAI response id
|
|
29
|
+
is rejected here with a message telling you to omit it, rather than spending a round trip on an
|
|
30
|
+
id an agent invented.
|
|
31
|
+
- **Custom sizes, validated before the request leaves.** Any `WIDTHxHEIGHT` with both dimensions
|
|
32
|
+
a multiple of 16, an aspect ratio between 1:3 and 3:1 and a maximum of 3840x2160 is accepted,
|
|
33
|
+
and the four rules are checked locally so a malformed size costs a validation error instead of
|
|
34
|
+
a billed refusal.
|
|
35
|
+
- **`output_format` on every result.** Callers previously had to infer the format from the file
|
|
36
|
+
extension, which was the wrong thing to trust. See the format detection change below.
|
|
37
|
+
- **A documentation set** under [docs/](docs/): getting started, a full tool reference, the
|
|
38
|
+
refinement workflow, configuration, and troubleshooting organised by symptom.
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- **Package renamed from `openai-imagegen-mcp` to `mcp-gpt-image`.** The old name described a
|
|
43
|
+
family of models that no longer exists, and it put the vendor first in a name that people type.
|
|
44
|
+
The console script is now `mcp-gpt-image`, so client configs need their `args` updated.
|
|
45
|
+
- **Import path renamed from `openai_imagegen_mcp` to `gpt_image_mcp`.** Only relevant if you
|
|
46
|
+
imported the server to embed it; the MCP surface is unaffected.
|
|
47
|
+
- **Environment variables renamed from `OPENAI_IMAGEGEN_*` to `GPT_IMAGE_*`.**
|
|
48
|
+
`OPENAI_IMAGEGEN_OUTPUT_DIR` becomes `GPT_IMAGE_OUTPUT_DIR`,
|
|
49
|
+
`OPENAI_IMAGEGEN_DEFAULT_MODEL` becomes `GPT_IMAGE_MODEL`, and `OPENAI_IMAGEGEN_TIMEOUT`
|
|
50
|
+
becomes `GPT_IMAGE_TIMEOUT`, joined by the new `GPT_IMAGE_REFINE_TIMEOUT`. The prefix now
|
|
51
|
+
matches the package rather than the vendor. `OPENAI_API_KEY` deliberately keeps its standard
|
|
52
|
+
name so an existing OpenAI setup works untouched.
|
|
53
|
+
- **Only gpt-image-2 is supported.** `dall-e-2` and `dall-e-3` were shut down on 2026-05-12, and
|
|
54
|
+
on 2026-06-02 OpenAI announced that `gpt-image-1.5`, `gpt-image-1-mini` and
|
|
55
|
+
`chatgpt-image-latest` retire on 2026-12-01, all migrating to gpt-image-2. Supporting a family
|
|
56
|
+
of models with one live member would have been a menu of expiry dates. `GPT_IMAGE_MODEL` still
|
|
57
|
+
lets you choose between the floating `gpt-image-2` and the pinned snapshot
|
|
58
|
+
`gpt-image-2-2026-04-21`, which is the only choice left that changes anything.
|
|
59
|
+
- **Errors surface as MCP tool errors instead of an `{"error": ...}` payload.** The old shape
|
|
60
|
+
returned a successful tool result whose content happened to describe a failure, and agents read
|
|
61
|
+
that as an image that exists. A tool error carrying the API's own message is something a caller
|
|
62
|
+
can actually react to, and the message is passed through untouched so it can.
|
|
63
|
+
- **The output format is detected from the returned bytes, not from the request.** The API
|
|
64
|
+
sometimes answers a webp request with PNG data, and a file named `.webp` holding PNG bytes
|
|
65
|
+
breaks whatever opens it next, far away from the cause. Files are now named after their real
|
|
66
|
+
content and the result reports what was written, which means `output_format` in a result can
|
|
67
|
+
differ from the `output_format` you asked for. That is the deliberate behaviour.
|
|
68
|
+
- **Default timeouts raised to 300 seconds.** gpt-image-2 plans before it draws, and complex
|
|
69
|
+
prompts can take up to two minutes, with large sizes at the top of that range. The old
|
|
70
|
+
180-second budget cancelled calls that would have succeeded.
|
|
71
|
+
- **Python floor raised to 3.12**, and dependencies moved to `fastmcp>=3.4.7,<4` and
|
|
72
|
+
`openai>=3.0,<4`. The FastMCP floor is where Starlette is pinned above CVE-2026-48710, and an
|
|
73
|
+
OpenAI SDK older than 3.0 predates gpt-image-2 entirely.
|
|
74
|
+
|
|
75
|
+
### Removed
|
|
76
|
+
|
|
77
|
+
- **The `model` parameter on the tools.** With one supported model, a per-call override could
|
|
78
|
+
only ever be a way to get an error from OpenAI. The model is a server setting now, which is
|
|
79
|
+
also the right place for a decision about reproducibility.
|
|
80
|
+
- **`input_fidelity` on `edit_image`.** gpt-image-2 always processes input images at high
|
|
81
|
+
fidelity, so the parameter has nothing left to select. Faces, logos and lettering survive an
|
|
82
|
+
edit without a setting to remember, which is a straight improvement: forgetting to raise it was
|
|
83
|
+
the usual reason a logo came back smudged.
|
|
84
|
+
- **`background="transparent"`.** gpt-image-2 does not support transparent backgrounds and the
|
|
85
|
+
API refuses the value, so it is absent from the schema and a client asking for it gets an
|
|
86
|
+
immediate schema error rather than a billed refusal. This is a real regression against the
|
|
87
|
+
previous generation: pipelines that relied on a cut-out subject now need a mask or an external
|
|
88
|
+
background remover.
|
|
89
|
+
- **HTTP transport and the cloud entrypoint.** The streamable-http mode, the `PORT` variable and
|
|
90
|
+
the root-level `fastmcp_server.py` are gone. Every real use of this server was a local MCP
|
|
91
|
+
client spawning it on stdio, and the HTTP path carried a deployment story, an authentication
|
|
92
|
+
question and a surface area that nothing was using.
|
|
93
|
+
- **The Docker image and its Dockerfile.** They existed to serve the HTTP mode, and they went
|
|
94
|
+
with it. A stdio server is started by its client and has nothing to containerise.
|
|
95
|
+
- **The `response_format` parameter.** It belonged to the DALL-E models and returns a 400 on
|
|
96
|
+
gpt-image-2, which returns base64 through `output_format` directly.
|
|
97
|
+
|
|
98
|
+
### Fixed
|
|
99
|
+
|
|
100
|
+
- **Concurrent calls sharing a prompt no longer overwrite each other.** Filenames carry a UTC
|
|
101
|
+
timestamp with microsecond precision, so two tools invoked at the same moment with the same
|
|
102
|
+
prompt write two files rather than one.
|
|
103
|
+
- **A malformed API payload no longer writes a broken file.** Bytes that are not a PNG, a JPEG or
|
|
104
|
+
a WEBP are refused with a typed error and nothing is written, instead of leaving a file on disk
|
|
105
|
+
that fails to open later.
|
|
106
|
+
- **File descriptors no longer stay open across an await in `edit_image`.** Input images are read
|
|
107
|
+
into memory and sent as multipart tuples, so a slow API call cannot hold handles open for its
|
|
108
|
+
duration.
|
|
109
|
+
|
|
110
|
+
## 0.1.0
|
|
111
|
+
|
|
112
|
+
Initial release, as `openai-imagegen-mcp`. Two tools, `generate_image` and `edit_image`, on
|
|
113
|
+
`gpt-image-1.5`, over stdio or streamable-http. Never published to PyPI, so 0.2.0 is the first
|
|
114
|
+
version anyone can install.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# CLAUDE.md, mcp-gpt-image
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
MCP server exposing OpenAI's `gpt-image-2` as three tools: `generate_image`,
|
|
6
|
+
`edit_image` and `refine_image`. The point of the server is chaining: Claude
|
|
7
|
+
generates a picture, feeds the returned path back in, and keeps correcting it.
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
Three layers, dependencies pointing inward only.
|
|
12
|
+
|
|
13
|
+
- **Domain** (`domain/`): `types.py` holds every Literal alias and constant that
|
|
14
|
+
mirrors an API constraint; `results.py` the data carried between layers;
|
|
15
|
+
`image_store.py` the only module that writes files; `errors.py` the exception
|
|
16
|
+
hierarchy. Imports neither `openai` nor `fastmcp`.
|
|
17
|
+
- **Adapters** (`adapters/`): `images_client.py` for `/v1/images`,
|
|
18
|
+
`responses_client.py` for the Responses API image tool, `_errors.py` for the
|
|
19
|
+
single translation from SDK exceptions to domain errors. The only modules that
|
|
20
|
+
call the OpenAI SDK. `server.py` names `AsyncOpenAI` too, but for a type
|
|
21
|
+
annotation on the injection seam, never to make a call.
|
|
22
|
+
- **Tools** (`tools/`): one file per MCP tool, plus `_base.py` (dependency bundle
|
|
23
|
+
and the error decorator) and `_validation.py`.
|
|
24
|
+
- **Composition root**: `server.py`. Nothing else builds a client or a store.
|
|
25
|
+
|
|
26
|
+
## Key conventions
|
|
27
|
+
|
|
28
|
+
- **Tool discovery is automatic.** `tools/__init__.py` imports every module in the
|
|
29
|
+
package that does not start with `_` and calls its `register(mcp, deps)`. A tool
|
|
30
|
+
module without that function raises at startup. Adding a tool means adding a
|
|
31
|
+
file; helpers must be underscore-prefixed or they will be treated as tools.
|
|
32
|
+
- **The schema is the validation.** `Literal` aliases and `Field(ge=, le=)`
|
|
33
|
+
become enums and bounds in the MCP tool schema, so the client is refused before
|
|
34
|
+
a call is billed. `_validation.py` only holds what JSON Schema cannot express:
|
|
35
|
+
parsing a free-form size, and touching the filesystem.
|
|
36
|
+
- **File paths as the interface.** Tools return paths, never bytes. An image never
|
|
37
|
+
enters the conversation, so passing it between tools costs nothing.
|
|
38
|
+
- **The extension follows the bytes.** `ImageStore.sniff_format` reads the magic
|
|
39
|
+
bytes and names the file after what actually arrived. The API sometimes answers a
|
|
40
|
+
webp request with PNG, and a `.webp` file holding PNG bytes breaks the next tool
|
|
41
|
+
that opens it.
|
|
42
|
+
- **Errors surface as `ToolError`.** The `reports_errors` decorator in
|
|
43
|
+
`tools/_base.py` converts domain and validation errors. FastMCP passes the
|
|
44
|
+
message through untouched, which is what lets a calling agent fix its own call.
|
|
45
|
+
Anything unlisted crashes on purpose.
|
|
46
|
+
- **Async all the way.** `AsyncOpenAI`, awaited directly. There is no
|
|
47
|
+
`asyncio.to_thread` wrapper any more.
|
|
48
|
+
|
|
49
|
+
## What gpt-image-2 refuses
|
|
50
|
+
|
|
51
|
+
Learned the hard way, do not reintroduce:
|
|
52
|
+
|
|
53
|
+
- `background="transparent"` returns an API error. The `Background` alias has two
|
|
54
|
+
values for that reason.
|
|
55
|
+
- `input_fidelity` is rejected: the model always works at high fidelity.
|
|
56
|
+
- `response_format` and `style` belong to DALL-E, which was shut down in May 2026.
|
|
57
|
+
- `moderation` exists on generations but not on edits.
|
|
58
|
+
- Inside the Responses `image_generation` tool object, only `quality` is confirmed
|
|
59
|
+
as a configuration key. Framing and format live on the other two tools.
|
|
60
|
+
|
|
61
|
+
The model id is the single point of change: `MODEL` in `domain/types.py`, or the
|
|
62
|
+
`GPT_IMAGE_MODEL` variable to pin the `gpt-image-2-2026-04-21` snapshot.
|
|
63
|
+
`ORCHESTRATOR_MODEL` (`gpt-5.6`) is the mainline model driving `refine_image`; an
|
|
64
|
+
image model cannot be the primary model of a Responses call.
|
|
65
|
+
|
|
66
|
+
## Commands
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
make install # uv sync --all-extras
|
|
70
|
+
make check # lint + test, what CI runs
|
|
71
|
+
make lint # ruff check, ruff format --check, mypy
|
|
72
|
+
make test # pytest, fully mocked, never hits the network
|
|
73
|
+
make run # start the server on stdio
|
|
74
|
+
make build # uv build
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Testing
|
|
78
|
+
|
|
79
|
+
Every test drives the real MCP surface: `fastmcp.Client(server)` calling tools by
|
|
80
|
+
name. The single seam is `create_server(settings, sdk=...)`, which injects a mock
|
|
81
|
+
in place of `AsyncOpenAI`; there is no `patch()` on an import path. Tests assert on
|
|
82
|
+
what was sent to the SDK as much as on what came back, because half the contract is
|
|
83
|
+
about parameters this server must never send.
|
|
84
|
+
|
|
85
|
+
Building SDK exceptions in tests needs a duck-typed response object, not a real
|
|
86
|
+
one: `openai` 3.x runs on HTTPX2 and constructing a genuine response is a trap.
|
|
87
|
+
|
|
88
|
+
## Publishing
|
|
89
|
+
|
|
90
|
+
Version lives in `pyproject.toml` only. A published GitHub release triggers
|
|
91
|
+
`release.yml`, which checks the tag against that version, runs the gates, builds,
|
|
92
|
+
then uploads through PyPI trusted publishing. No token exists anywhere.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Functional Source License, Version 1.1, MIT Future License
|
|
2
|
+
|
|
3
|
+
## Abbreviation
|
|
4
|
+
|
|
5
|
+
FSL-1.1-MIT
|
|
6
|
+
|
|
7
|
+
## Notice
|
|
8
|
+
|
|
9
|
+
Copyright 2026 Fabien Vauchelles
|
|
10
|
+
|
|
11
|
+
## Terms and Conditions
|
|
12
|
+
|
|
13
|
+
### Licensor ("We")
|
|
14
|
+
|
|
15
|
+
The party offering the Software under these Terms and Conditions.
|
|
16
|
+
|
|
17
|
+
### The Software
|
|
18
|
+
|
|
19
|
+
The "Software" is each version of the software that we make available under
|
|
20
|
+
these Terms and Conditions, as indicated by our inclusion of these Terms and
|
|
21
|
+
Conditions with the Software.
|
|
22
|
+
|
|
23
|
+
### License Grant
|
|
24
|
+
|
|
25
|
+
Subject to your compliance with this License Grant and the Patents,
|
|
26
|
+
Redistribution and Trademark clauses below, we hereby grant you the right to
|
|
27
|
+
use, copy, modify, create derivative works, publicly perform, publicly display
|
|
28
|
+
and redistribute the Software for any Permitted Purpose identified below.
|
|
29
|
+
|
|
30
|
+
### Permitted Purpose
|
|
31
|
+
|
|
32
|
+
A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
|
|
33
|
+
means making the Software available to others in a commercial product or
|
|
34
|
+
service that:
|
|
35
|
+
|
|
36
|
+
1. substitutes for the Software;
|
|
37
|
+
|
|
38
|
+
2. substitutes for any other product or service we offer using the Software
|
|
39
|
+
that exists as of the date we make the Software available; or
|
|
40
|
+
|
|
41
|
+
3. offers the same or substantially similar functionality as the Software.
|
|
42
|
+
|
|
43
|
+
Permitted Purposes specifically include using the Software:
|
|
44
|
+
|
|
45
|
+
1. for your internal use and access;
|
|
46
|
+
|
|
47
|
+
2. for non-commercial education;
|
|
48
|
+
|
|
49
|
+
3. for non-commercial research; and
|
|
50
|
+
|
|
51
|
+
4. in connection with professional services that you provide to a licensee
|
|
52
|
+
using the Software in accordance with these Terms and Conditions.
|
|
53
|
+
|
|
54
|
+
### Patents
|
|
55
|
+
|
|
56
|
+
To the extent your use for a Permitted Purpose would necessarily infringe our
|
|
57
|
+
patents, the license grant above includes a license under our patents. If you
|
|
58
|
+
make a claim against any party that the Software infringes or contributes to
|
|
59
|
+
the infringement of any patent, then your patent license to the Software ends
|
|
60
|
+
immediately.
|
|
61
|
+
|
|
62
|
+
### Redistribution
|
|
63
|
+
|
|
64
|
+
The Terms and Conditions apply to all copies, modifications and derivatives of
|
|
65
|
+
the Software.
|
|
66
|
+
|
|
67
|
+
If you redistribute any copies, modifications or derivatives of the Software,
|
|
68
|
+
you must include a copy of or a link to these Terms and Conditions and not
|
|
69
|
+
remove any copyright notices provided in or with the Software.
|
|
70
|
+
|
|
71
|
+
### Disclaimer
|
|
72
|
+
|
|
73
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
|
|
74
|
+
IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
|
|
75
|
+
PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
|
|
76
|
+
|
|
77
|
+
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
|
|
78
|
+
SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
|
|
79
|
+
EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
|
|
80
|
+
|
|
81
|
+
### Trademarks
|
|
82
|
+
|
|
83
|
+
Except for displaying the License Details and identifying us as the origin of
|
|
84
|
+
the Software, you have no right under these Terms and Conditions to use our
|
|
85
|
+
trademarks, trade names, service marks or product names.
|
|
86
|
+
|
|
87
|
+
## Grant of Future License
|
|
88
|
+
|
|
89
|
+
We hereby irrevocably grant you an additional license to use the Software under
|
|
90
|
+
the MIT license that is effective on the second anniversary of the date we make
|
|
91
|
+
the Software available. On or after that date, you may use the Software under
|
|
92
|
+
the MIT license, in which case the following will apply:
|
|
93
|
+
|
|
94
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
95
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
96
|
+
the Software without restriction, including without limitation the rights to
|
|
97
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
98
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
99
|
+
so, subject to the following conditions:
|
|
100
|
+
|
|
101
|
+
The above copyright notice and this permission notice shall be included in all
|
|
102
|
+
copies or substantial portions of the Software.
|
|
103
|
+
|
|
104
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
105
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
106
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
107
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
108
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
109
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
110
|
+
SOFTWARE.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
.PHONY: install build check lint format test run clean
|
|
2
|
+
|
|
3
|
+
install:
|
|
4
|
+
uv sync --all-extras
|
|
5
|
+
|
|
6
|
+
build:
|
|
7
|
+
uv build
|
|
8
|
+
|
|
9
|
+
# What CI runs, and what to run before pushing.
|
|
10
|
+
check: lint test
|
|
11
|
+
|
|
12
|
+
lint:
|
|
13
|
+
uv run ruff check src tests
|
|
14
|
+
uv run ruff format --check src tests
|
|
15
|
+
uv run mypy
|
|
16
|
+
|
|
17
|
+
format:
|
|
18
|
+
uv run ruff format src tests
|
|
19
|
+
uv run ruff check --fix src tests
|
|
20
|
+
|
|
21
|
+
test:
|
|
22
|
+
uv run pytest
|
|
23
|
+
|
|
24
|
+
run:
|
|
25
|
+
uv run mcp-gpt-image
|
|
26
|
+
|
|
27
|
+
clean:
|
|
28
|
+
rm -rf dist .pytest_cache .ruff_cache .mypy_cache
|
|
29
|
+
find src tests -name __pycache__ -type d -exec rm -rf {} +
|