chore: release v0.5.0-alpha.1 - #558
Merged
Merged
Conversation
Danielmoraisg
approved these changes
Jul 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cuts the staging alpha
v0.5.0-alpha.1, so the hosted MCP server's deployment wrapper can pin an exact version and exercise this release in staging before it ships. Tagging publishes a GitHub Release with wheels and a pre-release PyPI upload.What this PR contains
0.4.0-beta.2->0.5.0-alpha.1across every workspace package's__init__.py, the rootpyproject.toml,.claude-plugin/plugin.json, anduv.lock.CHANGELOG.md: not stamped.## [Unreleased]keeps accumulating across alphas and is what the Release workflow uses as this tag's release body, so the eventualbetapromotion still stamps the whole set into one section.Released content (already in dev)
Added
dev, beta and stable releases frommain.scripts/release.py alpha-pr <bump>branches offorigin/dev, bumps the lockstep version, and opens a release PR intodev; once merged,publishfromdevtags the commit and pushes the tag, whose wheels reach PyPI as a pre-release so the hosted MCP server's deployment wrapper can pin an exact version and exercise a release in staging before it ships.release-pr betathen promotes the alpha to the first beta of the sameX.Y.Z(0.5.0-alpha.3->0.5.0-beta.1) through thedev->mainrelease PR, andpublishfrommainships it.alpha-prisrelease-prbased ondev, so both lines take the same reviewed path — a repository ruleset requires a pull request onmainanddevalike, and no flow pushes a release branch: the bump always arrives via a merged PR andpublishpushes only the tag, asserting the branch matches its remote so an unmerged release PR is reported as such. An alpha deliberately does not stampCHANGELOG.md:## [Unreleased]keeps accumulating acrossalpha.1..alpha.Nand is what the Release workflow uses as an alpha's release body, so the beta promotion still stamps the whole set into one section. Which branch a release ships from is derived from the version's own pre-release track rather than the operator's checkout, and enforced before the bump, again before the tag, and in the Release workflow itself, which asserts the tagged commit is an ancestor oforigin/devfor an alpha ororigin/mainotherwise before any wheel is built.Changed (breaking, internal API)
mcpSDK 2.x (mcp[cli]==2.0.*), superseding both the>=1.29.0,<2floor bump and the<2cap entry listed below in this same cycle, whose closing note that the upper bound stays no longer describes the shipped pin. 2.0 removes themcp.server.fastmcpmodule and renamesFastMCPtoMCPServer, so the pin could not move without the port. Nothing changes for a caller using the server over stdio or HTTP: the 187-tool surface, the--toolsetsselections, the remote-safe floor, the tool-error envelope, and the structured log shapes are all unchanged, verified by the full suite plus a live smoke against the API. What changes is the code that embeds this package.request_handlers[CallToolRequest]slot; 2.0 removes that dict and shipsMCPServer(middleware=[...])instead.install_tool_call_middlewarebecomesbuild_tool_call_middleware, which returns oneServerMiddlewarefor the composition root to pass to the constructor.extra_tool_middlewaresonbuild_pipefy_mcp_serveris unchanged, so an embedder registering middleware needs no edit; an embedder that calledinstall_tool_call_middlewaredirectly does. Three shape changes for a middleware author.ToolCallContext.reqis gone (readtool_nameandarguments, now raw pre-validation values off the params). The seam's return type is the SDK'sHandlerResult(BaseModel | dict[str, Any] | None) rather than atypes.ServerResult, and a middleware that short-circuits returns the serialized wire dictshort_circuit_errorbuilds, not a model. AndToolCallContextcarries a requiredprotocol_version:short_circuit_errorshapes its result against it, so any code constructing a context by hand (a middleware test fixture, a synthetic call) has to supply one.serverInfo.versionnow reports this package's version (pipefy_mcp.__version__) instead of the SDK's. FastMCP neither accepted nor forwarded a version, so the advertised string trackedmcpitself;MCPServertakes one.host,transport_security, andjson_responseleave the server constructor for the per-transport call, sowire_hosted_observabilitytakes them.portis unaffected: it only ever reached the uvicorn bind. An embedder that callsstreamable_http_app()itself must passjson_responsethere, or its POST replies become SSE-framed instead of plain JSON.core/fastmcp_tool_lifecycle.pyis deleted in favour of the publicMCPServer.remove_tool.transport_security_forpairs the resource-server URL parse with the allowlist build in one place.context_from_requestis renamedcontext_from_server_request, and its input changes from atypes.CallToolRequestmodel to aServerRequestContext. An embedder importing the old name frompipefy_mcp.core.tool_middlewaregets anImportError; the arguments and request id are read off the context (ctx.params,ctx.request) rather than offreq.params.McpRuntime.transport_securityis removed. The SDK takes the DNS-rebinding allowlist per transport, onstreamable_http_app(), so it no longer travels on the runtime; reading the attribute raisesAttributeError. Resolve it withtransport_security_for(settings)frompipefy_mcp.core.transport_securityand pass the result to the serving call, asrun_serverdoes.call_nextreturns inside a tool-call middleware is now polymorphic. The SDK's own handler returns the serialized wire dict, keyedisErrorin camelCase, and so doesshort_circuit_error; aCallToolResultmodel, with a snake_caseis_error, only arrives from an inner middleware that built its result by hand. Read the flag throughresult_is_error(result), the supported accessor. A 1.x-style attribute read silently returns the default on the dict, so every real tool failure reads as a success. That exact read caused a regression during this port.short_circuit_errornow shapes one. The SDK shapes a handler's result for the wire per negotiated revision inside the middleware chain, so a middleware that returns without awaitingcall_nextis never shaped. Returned as aCallToolResult, its 2.0resultTypedefault ("complete", a 2026-07-28 field) reached the wire on a connection that negotiated 2025-11-25, invisibly, because a client's surface validation ignores unknown fields.short_circuit_errornow takes theToolCallContextas its first argument and returns the wire dict, shaped by the SDK's ownserialize_server_resultagainstctx.protocol_version. A middleware author updates the call (short_circuit_error(ctx, "quota exceeded", code="RATE_LIMITED")); nothing changes for a client.ToolCallContextgainsprotocol_version.tools/callthat names no tool is logged undertool: "<unnamed>"rather than an empty string, so it does not share a dashboard bucket with the real tool names. The call still reaches the middleware chain, andctx.tool_nameis still"", the raw view of what the client sent.wire_hosted_observabilityreturns owns the lifespan that enterssession_manager.run(). Serving it directly (whatrun_serverdoes, and what a uvicorn deployment does) needs nothing extra. An embedder thatMounts it under a host Starlette or FastAPI app must enterapp.session_manager.run()from the host's own lifespan, because Starlette does not run a mounted app's lifespan; skipping it leaves the session manager unstarted and every request fails.413above it) carries forward from the 1.29.0 floor bump documented below, unchanged: the wiring passes nomax_request_body_size, so the SDK default stands. Attachments are unaffected (no tool takes bytes or base64); a long free-text argument is the reachable case. A hosted deployment that needs a larger body raises it on thestreamable_http_app()call.<3. Two of the surfaces this package builds on sit outside what SemVer covers: theMCPServer(middleware=[...])list, which upstream marks provisional, and theTool.runpatch behind the argument-validation envelope. A minor release can move either with no signal, and no CI job resolves pastuv.lock, so nothing would catch it before a release. Patch releases are accepted; widening to a new minor is a deliberate change that re-tests both surfaces.httpx2rather thanhttpx(this package still declareshttpxitself, and nothing hands an httpx object into the SDK), and addsmcp-types,opentelemetry-api, andtruststorewhile droppinghttpx-sse.truststorevalidates TLS against the OS trust store, so a minimal container with no system CA store needsSSL_CERT_FILE. Two transitive majors arrive together:starlette0.46.1 to 1.3.1 andsse-starlette2.2.1 to 3.4.6.starletteis now a declared dependency ofpipefy-mcp-server(starlette>=1.3.1,<2) rather than an undeclared transitive, because four modules import it directly (theStarletteappwire_hosted_observabilityreturns, and theRequestannotation onMcpRuntime.session_for_requestand the two identity resolvers), so an SDK release that dropped or vendored it would have broken those imports with no bound to catch it.uvicornis now declared for the same reason (uvicorn>=0.34.0,<1):run_serverimports it and builds theConfig/Serverpair on the--transport httppath, while the SDK requires it only under asys_platform != 'emscripten'marker, so the transitive guarantee was conditional.ctx.debugcall in the tool modules emitsMCPDeprecationWarning— around a thousand warning records in a suite run, against a previously clean baseline. Migrating those calls is a behavior change deserving its own review, so the suite carries one scopedfilterwarningsignore instead, matched on the message rather than the class so the SDK's other deprecation subjects still surface. The ignore is bounded by construction: when the SDK removes the capability the calls break outright and the suite fails regardless.2025-11-25on theinitializehandshake, but a client that opens with the envelopedserver/discoverreaches2026-07-28, which a defaultmcp2.xClient(mode="auto") does. Adopting that revision as the served default is still separate work; what changed here is that the tools now behave correctly on both.Changed (installer)
install.shresolves the newest GitHub Release whose tag is not an alpha, so a staging alpha published fromdevnever becomes what a defaultcurl … | shinstall hands out; the public install path stays on the beta line. Alphas are also flagged as GitHub pre-releases, keeping the "Latest" badge on the newest beta. Filtering is by tag shape rather than the API'sprereleaseflag, because the whole pre-1.0 line is betas that must stay installable.install.sh --version vX.Y.Z-alpha.Nstill installs an alpha on purpose.After merge
Run
uv run python scripts/release.py publishfromdevto tag and publish — the one irreversible, human-gated step.