A Python Model Context Protocol server built on FastMCP that exposes BytePlus multimodal generation through a typed, safe tool surface.
The server provides a conditional MCP tool surface across three BytePlus products plus artifact access and an optional media upload helper:
| Product | Tools | Description |
|---|---|---|
| Seed Audio | seed_audio_generate, seed_audio_generate_variations |
Full-scene audio generation through Seed Speech |
| Seedream | seedream_generate_image, seedream_edit_image, seedream_generate_image_variations |
Image generation and editing through ModelArk |
| Seedance | seedance_create_task, seedance_create_task_variations, seedance_get_task, seedance_list_tasks, seedance_cancel_or_delete_task |
Async video generation and task management through ModelArk |
| Artifacts | seed_media_get_artifact |
Retrieve persisted media inline by artifact ID |
| TOS (optional) | media_upload |
Upload Base64 or local-file media to BytePlus TOS, return a presigned HTTPS URL for use as a reference |
Key features:
- Durable artifacts — all generated media is persisted locally so MCP resources remain usable after provider URLs expire (2h audio, 24h image/video)
- Parallel variations — generate N independent variations in a single
call with
asyncio.gather, partial failures captured per variation - Per-variation seeds — Seedream supports reproducible generation with
base_seed + indexdeterministic seeds - Typed inputs — Pydantic models validate all inputs before spending quota; unsupported combinations are rejected at the MCP layer
- Model capability registry — logical model families map to operator-configured model IDs; validates resolutions, formats, and batch support per model
- Security — DNS-pinned SSRF-safe downloads, tenant/principal ownership, scoped JWT auth for network HTTP, Host/Origin protection, and body limits
- Runtime controls — shared provider/principal concurrency, daily budget reservations, safe retries, task ownership, readiness, metrics, and tracing
- 459 offline tests — unit, contract, integration, HTTP security, E2E, and MCP conformance with 88.08% branch coverage
All input media is validated through an SSRF-safe URL policy and size-checked Base64 before reaching the provider. The table below shows what each tool accepts as reference input:
| Tool | Modality | URL | Base64 | Speaker ID | Roles | Max | Constraints |
|---|---|---|---|---|---|---|---|
seedream_generate_image |
Image | ✓ | ✓ | — | reference | 10 PRO / 14 LITE·4X | — |
seedance_create_task |
Image | ✓ | ✓ | — | first_frame · last_frame · reference_image |
9 | — |
seedance_create_task |
Video | ✓ | — | — | reference_video |
3 | URL only |
seedance_create_task |
Audio | ✓ | ✓ | — | reference_audio |
3 | Not sole input |
seed_audio_generate |
Audio | ✓ | ✓ | ✓ | — | 3 | Exclusive w/ image |
seed_audio_generate |
Image | ✓ | ✓ | — | — | 1 | Exclusive w/ audio |
The
_variationssiblings (seedream_generate_image_variations,seedance_create_task_variations,seed_audio_generate_variations) accept the same input modalities as their base tools.
Note
Video references must be pre-hosted. seedance_create_task accepts
video references as a public HTTPS URL only — there is no inline Base64
option. Use the media_upload tool to upload Base64 or a local file path
(stdio only) to BytePlus TOS and receive a presigned HTTPS GET URL you can
pass directly to seedance_create_task. Alternatively, host the video on
your own accessible HTTPS endpoint (S3, TOS, etc.). The URL must resolve to
a public IP (private/loopback/link-local addresses are rejected by the SSRF
policy). media_upload requires TOS_ACCESS_KEY / TOS_SECRET_KEY /
TOS_BUCKET env vars; see Configuration.
The server uses two provider gateways behind one normalized domain layer, a lifespan-owned runtime for concurrency/budget/ownership, and a durable artifact store. See docs/architecture.md for the full overview.
Components
flowchart TB
subgraph Client["MCP clients"]
CD[Claude Desktop]
CX[Codex CLI]
OC[OpenCode]
TR[TRAE IDE]
end
subgraph Server["ModelArk MCP Server (FastMCP)"]
Tools["Tools<br/>Seedream · Seedance · Seed Audio · Artifacts · TOS upload"]
Domain["Domain layer<br/>models · capability registry · errors"]
Runtime["Runtime services<br/>concurrency · budget · ownership · retry"]
Store["Artifact store<br/>filesystem + .meta.json"]
Sec["Security<br/>JWT · SSRF-safe downloads · body limits"]
Obs["Observability<br/>structured logs · metrics · traces"]
end
subgraph Providers["BytePlus"]
MA["ModelArk gateway<br/>Bearer auth"]
SS["Seed Speech gateway<br/>X-Api-Key"]
end
Client <-->|"stdio / HTTP"| Tools
Tools --> Domain
Tools --> Runtime
Tools --> Store
Domain --> Providers
Sec -. guards .-> Tools
Obs -. observes .-> Tools
Layered view
flowchart LR
L1["Transport<br/>stdio · Streamable HTTP"] --> L2["Server<br/>FastMCP tools · resources · routes"]
L2 --> L3["Domain<br/>typed models · capability registry"]
L3 --> L4["Provider gateways<br/>ModelArk · Seed Speech"]
L4 --> L5["BytePlus<br/>Seedream · Seedance · Seed Audio"]
L2 -.-> X["Cross-cutting<br/>security · runtime · observability · artifacts"]
Request sequence (a billable generate call)
sequenceDiagram
participant C as MCP Client
participant T as Tool handler
participant R as Runtime services
participant P as Provider gateway
participant B as BytePlus
participant A as Artifact store
C->>T: call tool (Pydantic inputs)
T->>T: validate inputs + capability registry
T->>R: reserve budget + acquire provider/principal semaphores
R-->>T: budget reservation
T->>P: call_with_retry(provider request)
P->>B: HTTPS (Bearer / X-Api-Key)
B-->>P: media URL (2h audio / 24h image·video)
P-->>T: provider result
T->>A: persist artifact (durable copy)
A-->>T: seed-media://artifacts/{id}
T->>R: commit budget
T-->>C: ArtifactRef (stable resource URI)
# Clone the repository
git clone <repo-url> modelark-mcp
cd modelark-mcp
# Install dependencies
uv sync
# Configure credentials
cp .env.example .env
# Edit .env and fill in your BytePlus API keys
# Run the server
make start
# Or run the verification script to test your credentials
uv run python scripts/verify_phase0.pyEdit .env with your BytePlus credentials:
BYTEPLUS_MODELARK_API_KEY=your_modelark_key
BYTEPLUS_SEED_AUDIO_API_KEY=your_seed_audio_key # pragma: allowlist secret
SEEDREAM_DEFAULT_MODEL=dola-seedream-5-0-pro-260628
SEEDANCE_DEFAULT_MODEL=dreamina-seedance-2-0-260128If a credential is absent, the server skips registering that product's
tools. seed_media_get_artifact is always available, provider tools appear only
when their credentials are configured, and media_upload appears only when TOS
credentials are configured.
See Configuration for the full environment variable reference.
The server runs as a stdio process. Configure it in your MCP client:
{
"mcpServers": {
"modelark-seed": {
"command": "uv",
"args": ["--directory", "/path/to/modelark-mcp", "run", "python", "-m", "modelark_mcp"],
"env": {
"BYTEPLUS_MODELARK_API_KEY": "your_modelark_key",
"BYTEPLUS_SEED_AUDIO_API_KEY": "your_seed_audio_key"
}
}
}
}Codex reads MCP servers from ~/.codex/config.toml (global) or
.codex/config.toml (project-scoped, trusted projects). The format is TOML
with one [mcp_servers.<name>] table per server:
[mcp_servers.modelark-seed]
command = "uv"
args = ["--directory", "/path/to/modelark-mcp", "run", "python", "-m", "modelark_mcp"]
[mcp_servers.modelark-seed.env]
BYTEPLUS_MODELARK_API_KEY = "your_modelark_key"
BYTEPLUS_SEED_AUDIO_API_KEY = "your_seed_audio_key"OpenCode reads MCP servers from opencode.json / opencode.jsonc at the
project root, or ~/.config/opencode/opencode.json globally. Use the
top-level mcp key with type: "local" and command as an array:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"modelark-seed": {
"type": "local",
"command": ["uv", "--directory", "/path/to/modelark-mcp", "run", "python", "-m", "modelark_mcp"],
"enabled": true,
"environment": {
"BYTEPLUS_MODELARK_API_KEY": "your_modelark_key",
"BYTEPLUS_SEED_AUDIO_API_KEY": "your_seed_audio_key"
}
}
}
}TRAE uses the standard mcpServers JSON shape, added either via
Settings → MCP → Add → Manual in the IDE, or declared in a project-level
.trae/mcp.json (enable project-level MCP in Settings → MCP). The
${workspaceFolder} variable resolves to the project root:
{
"mcpServers": {
"modelark-seed": {
"command": "uv",
"args": ["--directory", "${workspaceFolder}", "run", "python", "-m", "modelark_mcp"],
"env": {
"BYTEPLUS_MODELARK_API_KEY": "your_modelark_key",
"BYTEPLUS_SEED_AUDIO_API_KEY": "your_seed_audio_key"
}
}
}
}The same
mcpServersJSON can be pasted directly into TRAE's Manual configuration window if you already run this server in another IDE.
TRAE Work is the standalone AI-native workspace
(web, desktop, mobile) with Work and Code dual modes. It reads the same
mcpServers JSON shape as TRAE IDE and resolves ${workspaceFolder} to the
project root. On the desktop app you also pick a runtime environment —
本地 (local) or 云端 (cloud) — when adding the server.
Add the server via Avatar → Settings → MCP → Create → Manual configuration, then paste:
{
"mcpServers": {
"modelark-seed": {
"command": "uv",
"args": ["--directory", "${workspaceFolder}", "run", "python", "-m", "modelark_mcp"],
"env": {
"BYTEPLUS_MODELARK_API_KEY": "your_modelark_key",
"BYTEPLUS_SEED_AUDIO_API_KEY": "your_seed_audio_key"
}
}
}
}Runtime environment. Pick 本地 to run the server on your machine (the repo must be cloned locally, as above). Pick 云端 to run it in a TRAE-managed cloud sandbox; in that case sync the repository to the cloud environment first (for example via the GitHub integration) so
${workspaceFolder}resolves to a path that actually contains the project. See the official TRAE Work MCP guide.
See the Integration Guide for configuration snippets for Cursor IDE, VS Code MCP extension, and the MCP Inspector.
make help # Show all targets
make install # Install dependencies from uv.lock
make start # Run the server over stdio
make start-http # Run the server over Streamable HTTP (localhost:3000)
make dev # Run in dev mode with auto-reload
make test # Run the test suite
make lint # Lint with ruff
make typecheck # Type-check with mypy
make inspect # Launch FastMCP inspector
make check-env # Validate environment configuration| Document | Description |
|---|---|
| Getting Started | Installation, configuration, and first run |
| Architecture | System structure, two-gateway domain layer, request flow |
| Configuration | Full environment variable reference |
| Integration Guide | MCP client setup (Claude, Cursor, VS Code, Inspector) |
| API Reference | Complete tool schemas, inputs, outputs, and examples |
| Use Cases | Common scenarios with example tool calls |
| Tools | Tool reference with input/output tables |
| Security | Consolidated security model (auth, SSRF, body limits) |
| Runtime | Concurrency, budget, ownership, retry |
| Observability | Structured logging, metrics, tracing |
| Models | Model capability registry and validation |
| Artifacts | Durable artifact lifecycle and store |
| Transports | stdio vs HTTP deployment |
| Deployment | Container, Kubernetes, and remote HTTP deployment |
| Troubleshooting | Common errors and fixes |
modelark-mcp/
├── src/modelark_mcp/
│ ├── server.py # Deterministic FastMCP factory and registration
│ ├── __main__.py # Entry point (truststore injection)
│ ├── runtime.py # Lifespan-owned stores, limits, budgets, ownership
│ ├── config/ # Pydantic Settings, model capability registry
│ ├── domain/ # ArtifactRef, errors, media, models
│ ├── providers/ # ModelArk + Seed Speech gateways and adapters
│ ├── tools/ # 9 tool handlers + parallel helpers + cost estimation
│ ├── artifacts/ # Tenant-aware filesystem artifact store
│ ├── observability/ # Structured logging and Prometheus metrics
│ └── security/ # JWT, URL/media policy, safe downloader, HTTP limits
├── tests/
│ ├── unit/ # Model validators, URL policy, media policy, helpers
│ ├── contract/ # Provider gateway + adapter contract tests
│ ├── integration/ # Tool handler, HTTP security, MCP conformance
│ └── e2e/ # In-process FastMCP client tests
├── docs/ # User and contributor documentation
├── plans/ # Implementation plans
├── scripts/ # Verification and smoke test scripts
├── fastmcp.json # Declarative server configuration
├── Makefile # Task runner
└── pyproject.toml # Project metadata and dependencies
- Python 3.12+ on uv
- FastMCP v3 — MCP server framework
- httpx — async HTTP client
- Pydantic v2 — typed models and validation
- pydantic-settings — environment configuration
- Starlette — HTTP middleware, body limits, and responses
- prometheus-client —
/metricsand Prometheus exposition - cachetools — TTL caching for provider responses and principal limits
- truststore — macOS system certificate injection
- respx — HTTP mocking for contract tests
See the project configuration for license details.
All three provider surfaces and both transports are implemented. The local release gate passes 459 offline tests at 88.08% branch coverage, Ruff formatting and lint, strict mypy, Bandit/secret scans, and package build. Dependency audit and container health are enforced by CI. Remote HTTP requires JWT configuration and is intentionally fail-closed.