feat(keycardai-mcp)!: port to mcp 2.0 (ECO-195) - #218
Open
Larry-Osakwe wants to merge 5 commits into
Open
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
All alerts resolved. Learn more about Socket for GitHub. This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored. |
Larry-Osakwe
marked this pull request as ready for review
July 28, 2026 22:23
This comment was marked as outdated.
This comment was marked as outdated.
2 similar comments
Contributor
📦 Release PreviewThis analysis shows the expected release impact: 📈 Expected Version Changes📋 Package Details[
{
"package_name": "keycardai-mcp",
"package_dir": "packages/mcp",
"has_changes": true,
"current_version": "0.26.0",
"next_version": "0.27.0",
"increment": "MINOR"
}
]📝 Changelog PreviewThis comment was automatically generated by the release preview workflow. |
Contributor
📦 Release PreviewThis analysis shows the expected release impact: 📈 Expected Version Changes📋 Package Details[
{
"package_name": "keycardai-mcp",
"package_dir": "packages/mcp",
"has_changes": true,
"current_version": "0.26.0",
"next_version": "0.27.0",
"increment": "MINOR"
}
]📝 Changelog PreviewThis comment was automatically generated by the release preview workflow. |
Larry-Osakwe
added a commit
that referenced
this pull request
Jul 29, 2026
mcp 2.0.0 published 2026-07-28. The floor was unbounded, so fresh installs began resolving onto the new major immediately, against code written for 1.x. Capping rather than porting because the wider ecosystem has not crossed over. Two of the four agent frameworks this package ships integrations for still pin mcp below 2.0 outright: openai-agents 0.19.0 mcp<2,>=1.19.0 crewai 1.15.8 mcp~=1.28.1 fastmcp 3.x pins it too. A hard mcp>=2.0 floor here would make keycardai-mcp uninstallable next to any of them. The 2.0 port itself is written and green in #218; it lands once those pins move. Co-authored-by: GitHub Action <action@github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
mcp 2.0 removed the bundled FastMCP 1.x (mcp.server.fastmcp). Its
successor in-package is mcp.server.mcpserver.MCPServer, which exposes
the same two APIs AuthProvider.app() uses (session_manager and
streamable_http_app), so the retarget is direct.
Import moves:
mcp.server.fastmcp.{Context,FastMCP} -> mcp.server.mcpserver.{Context,MCPServer}
mcp.shared.context.RequestContext -> mcp.server.context.ServerRequestContext
streamablehttp_client -> streamable_http_client
mcp 2.0 also renamed model fields to snake_case. Constructors still
accept the camelCase aliases, so only attribute reads broke:
result.nextCursor and mcp_tool.inputSchema. The agent integrations
guarded theirs with hasattr(), which under 2.0 returns False and
silently yields an empty tool schema rather than raising, so those are
corrected too.
Breaking: AuthProvider.app() is typed MCPServer instead of FastMCP.
Consumers needing mcp 1.x pin the prior keycardai-mcp minor.
keycardai-fastmcp is untouched and unaffected. It only imports
credential types, ClientFactory and exceptions from this package, and
it stays on fastmcp 3.x / mcp 1.x until fastmcp 4.0 is stable.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
packages/mcp now requires mcp>=2.0. fastmcp 3.x, openai-agents and crewai all still pin mcp<2.0, and a uv workspace resolves to a single lock, so they cannot co-resolve. Workspace: packages/fastmcp and packages/mcp-fastmcp move to workspace.exclude and carry their own resolution. They pick up keycardai-mcp from the index (0.26.0, mcp 1.x) rather than the local path, which is the honest arrangement while the two sit on different mcp majors. packages/mcp test extra: drops fastmcp, keycardai-mcp-fastmcp, openai-agents and crewai. The suites that need them are guarded by pytest.importorskip and report SKIPPED, so the gap shows in test output instead of disappearing. test_agent_integrations.py gained a guard; it was importing agents at module scope and failing collection. Release tooling: scripts/changelog.py enumerated packages from workspace.members, so both excluded packages would have silently dropped out of version bumps and changelogs. Added tool.keycardai.release standalone-members, which it now unions in. Verified all 7 packages are still discovered. packages/fastmcp gains real CI coverage for the first time (ECO-172): it is added to the justfile test and coverage targets, and its test extra was missing pytest-cov and requests, which it had been getting incidentally from the shared workspace environment. Coverage gates all pass: oauth 84.38%, starlette 79.57%, mcp 62.60%, fastmcp 85.01%, mcp-fastmcp 100%, a2a 45 tests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Socket blocked the previous commit, correctly. Removing openai-agents and crewai was not enough: pydantic-ai also pins mcp<2.0, transitively via fastmcp-slim[client]<4. Left in place, the resolver backtracked pydantic-ai 2.x -> 1.44.0 to satisfy mcp>=2.0, and 1.44.0 pulled fastmcp 2.14.1, which carries the known fastmcp advisory (Socket vulnerability score 25). Removed from packages/mcp: - test extra: pydantic-ai (alongside openai-agents and crewai) - optional-dependencies: the crewai and pydantic-ai convenience extras The extras go because pip install keycardai-mcp[crewai] cannot resolve against mcp>=2.0 regardless. The integration modules still ship; bring your own framework install on a pre-2.0 keycardai-mcp. Root lock no longer contains fastmcp, pydantic-ai, crewai or openai-agents at any version, and resolves mcp 2.0.0. Coverage note, so the number is not misread: packages/mcp reports 75.10% here against 62.60% before. That is not an improvement. The four *_agents.py integration modules are no longer imported, so coverage.py drops them from the report and the denominator shrinks from ~2500 to 2036 statements. The same code is untested; less of it is now measured. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Release blocker. release.yml ran `uv build --package <name>`, which resolves against workspace.members -- exactly what the split removed the two fastmcp packages from. Verified: `uv build --package keycardai-fastmcp` errors with "not found in workspace", same for keycardai-mcp-fastmcp. changelog.py being correct made this worse, not better: tags get cut and changelogs written, then publish dies. Now builds by path using the package-dir output detect-package already emits. Verified for both excluded packages and two workspace members. Resolution trap. packages/fastmcp declared keycardai-mcp>=0.15.0 unbounded alongside fastmcp>=3.1.0 (mcp<2.0). Since keycardai-mcp 0.27+ needs mcp>=2.0, the pair is unsatisfiable, so pip silently backtracked keycardai-mcp to 0.26.0 and froze there. Capped <0.27 so the conflict is explicit. mcp>=2.0.0 was unbounded, reproducing this exact incident on mcp 3.0. Capped <3.0. Sibling isolation. The standalone locks took every keycardai-* package from the index, so a breaking change in packages/oauth no longer failed packages/fastmcp. keycardai-oauth is now path-linked in both, and keycardai-fastmcp is path-linked into the bridge (same mcp<2.0 major). Only keycardai-mcp stays on the index, where the major genuinely differs. keycardai-starlette also stays: it is transitive via keycardai-mcp and tool.uv.sources only redirects direct dependencies. The input_schema fix was unverified. The existing pydantic test sets inputSchema on a MagicMock, where hasattr is unconditionally True, so it cannot tell the two spellings apart. Added test_tool_schema_reads.py, which drives the real _convert_mcp_tool_to_langchain with a real mcp.types.Tool and asserts the generated args_schema carries the field. Confirmed it fails when the camelCase read is restored. tests/conftest.py imported dotenv unguarded. It is a crewai-only shim and crewai is no longer installable here, so the single-package flow errored at collection. Guarded. Coverage gate stays 60, but the denominator it measures grew to 2679 statements against ~2500 before, because the new test imports the integration modules and pulls all four back into measurement. Removing the frameworks alone had shrunk it to 2036 and inflated the figure to ~75% on less code. The reviewer's suggested 72 was correct for that 2036 state and no longer applies. Headroom is thin at 60.88%. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rebased onto main now that #219 shipped the mcp<2.0 cap as keycardai-mcp 0.27.0. That version number invalidates the cap this branch carried. packages/fastmcp pinned "keycardai-mcp>=0.15.0,<0.27" on the assumption that 0.27 would be the mcp 2.0 port. It is not: 0.27.0 is the cap release, and it is the only published version that constrains mcp correctly. keycardai-mcp 0.26.0 mcp>=1.13.1 unbounded -> resolves mcp 2.0 keycardai-mcp 0.27.0 mcp>=1.28.1,<2.0 safe So "<0.27" excluded the one good version and forced the broken one. Now ">=0.27,<0.28": the floor guarantees the mcp cap is present, the ceiling excludes the 2.0 line this branch starts. Both standalone locks verified resolving keycardai-mcp 0.27.0 with mcp 1.29.0. Also resolved the pyproject conflict against main's cap by keeping this branch's mcp>=2.0.0,<3.0, and regenerated all three lockfiles from the merged manifests rather than hand-merging them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Larry-Osakwe
force-pushed
the
larry/mcp-2.0
branch
from
July 29, 2026 00:39
43254d5 to
04d6cdf
Compare
This comment was marked as spam.
This comment was marked as spam.
Contributor
|
Similarly to the TS changes – should we cut a |
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.
Closes ECO-195. Ports
keycardai-mcptomcp2.0.0 (published 2026-07-28 13:45 UTC) and reshapes the repo so both mcp majors can coexist.All checks green, Socket included.
Why urgent
keycardai-mcpdeclaredmcp>=1.28.1with no upper bound, so every fresh install since 13:45 UTC resolves onto a new major against code written for 1.x.The port
mcp2.0 deletedmcp.server.fastmcp— the FastMCP 1.x bundled inside the official package. (Distinct from the standalonefastmcp3.x/4.x thatkeycardai-fastmcpwraps.) Successormcp.server.mcpserver.MCPServerexposes both APIsAuthProvider.app()uses —session_managerandstreamable_http_app— so the retarget is direct.mcp.server.fastmcp.{Context, FastMCP}mcp.server.mcpserver.{Context, MCPServer}mcp.shared.context.RequestContextmcp.server.context.ServerRequestContextstreamablehttp_clientstreamable_http_clientEverything else survives:
ClientSession,Tool,PKCEParameters,AuthSettings, all ofmcp.types.The subtle bug worth reviewing
2.0 renamed model fields to snake_case, but constructors still accept the camelCase aliases — so only attribute reads break, and unevenly:
result.nextCursorraises loudly. Easy.mcp_tool.inputSchema if hasattr(mcp_tool, "inputSchema") else {}. Under 2.0 thathasattrreturnsFalse, so it silently yields an empty tool schema rather than raising. Verified directly. Fixed toinput_schema.Breaking changes
AuthProvider.app()is typedMCPServer, notFastMCP.keycardai-mcpno longer installs alongsidecrewai,openai-agentsorpydantic-ai. All three pinmcp<2.0(pydantic-ai transitively viafastmcp-slim[client]<4). Those users pin the prior minor.crewaiandpydantic-aiconvenience extras are removed.pip install keycardai-mcp[crewai]could not resolve against mcp 2.0 anyway. The integration modules still ship — bring your own framework install on a pre-2.0 release. Restored when the frameworks move (ECO-198).Workspace split
A uv workspace resolves to one lock, so
packages/fastmcpandpackages/mcp-fastmcp(fastmcp 3.x → mcp<2.0) move toworkspace.excludewith their own lockfiles. They resolvekeycardai-mcpfrom the index (0.26.0, mcp 1.x) rather than the local path — honest, while the two sit on different majors.Three things this nearly broke silently
Socket caught a real regression, and it was right to block. Removing openai-agents and crewai was not enough — pydantic-ai also pins mcp<2.0, so the resolver backtracked it 2.x → 1.44.0, which dragged in
fastmcp 2.14.1and its known advisory (Socket vulnerability score 25). The root lock now contains none of those four packages at any version.Release tooling.
scripts/changelog.pyenumerated packages fromworkspace.members, so both excluded packages would have dropped out of version bumps and changelogs with no error. Addedtool.keycardai.release.standalone-members, unioned in. Verified all 7 packages still discovered.packages/fastmcp's test extra was incomplete — missingpytest-covandrequests, which it had been getting incidentally from the shared workspace env. Only surfaced once it resolved alone. It is now in the justfile test and coverage targets, so it has real CI coverage for the first time (part of ECO-172).Coverage, stated honestly
packages/mcp's 75.10% is not an improvement over the previous 62.60%. The four*_agents.pyintegration modules are no longer imported, so coverage.py drops them from the report and the denominator shrinks from ~2500 to 2036 statements. The same code is untested; less of it is measured. Worth a decision on whether the 60% gate should rise to compensate.Skips report visibly rather than vanishing:
Supersedes #219, which capped
mcp<2.0as the conservative alternative.🤖 Generated with Claude Code