Migration
Migration to v1 (2026-04-30)
Section titled “Migration to v1 (2026-04-30)”Migration Guide: mcp-core 1.12.0 — Multi-Mode (stdio + HTTP) Architecture
Section titled “Migration Guide: mcp-core 1.12.0 — Multi-Mode (stdio + HTTP) Architecture”Audience: users of n24q02m-mcp-core / @n24q02m/mcp-core and any of the
8 downstream MCP plugins (wet, mnemo, better-code-review-graph, imagine,
better-telegram, better-notion, better-email, better-godot).
Effective: mcp-core 1.12.0 onward. Smart_stdio bridge for stdio path is soft-deprecated in this release and scheduled for hard removal in 2.0.0.
User action required
Section titled “User action required”After upgrading mcp-core to 1.12.0, run the cleanup CLI once to terminate any leftover bridge daemons and remove their lock + cache state:
mcp-clean-state --kill-daemonsThis:
- Iterates every
*.lockfile under~/.config/mcp/locks/(or%LOCALAPPDATA%\mcp\locks\on Windows). - Parses the daemon PID from line 0 of each lock.
- If the PID is alive, terminates the process (cross-platform: SIGKILL
on POSIX,
TerminateProcesson Windows). - Removes the
*.lockfile plus matching*.tools.jsoncompanion in the cache directory and any*.tools-list-changedsentinel.
Output:
Killed N daemons; cleaned M lock files.If you skip this step, leftover bridge daemons continue to listen on their random ports (harmless but wasteful) until the next reboot or 24h TTL sweep. The next stdio plugin invocation does not consult these old daemons — it spawns a new direct FastMCP stdio process instead — so there is no correctness risk, only resource leak.
Plugin developer guidance
Section titled “Plugin developer guidance”If you import mcp_core (Python) or @n24q02m/mcp-core (TypeScript) inside
your own plugin code, the API surface changes are:
Python — API kept
Section titled “Python — API kept”| Symbol | Status | Notes |
|---|---|---|
mcp_core.transport.run_http_daemon | NEW (1.12.0) | Renamed from run_local_server; alias preserved. |
mcp_core.transport.run_local_server | KEPT (alias) | Calls run_http_daemon. Still works in 1.x. |
mcp_core.config.* | KEPT | Config storage primitives (load_config, save_config, config.enc). |
mcp_core.crypto.* | KEPT | ECDH P-256 + AES-256-GCM helpers. |
mcp_core.relay.register_relay_form_tool | NEW (1.12.0) | Transient HTTP relay form for stdio-mode credential setup. |
mcp_core.lifecycle.* | KEPT | Lock file primitives still used by HTTP daemon mode. |
mcp_core.install.* | KEPT | Plugin installer helpers. |
Python — API deprecated (removed in 2.0.0)
Section titled “Python — API deprecated (removed in 2.0.0)”| Symbol | Replacement |
|---|---|
mcp_core.transport.run_smart_stdio_proxy | mcp.run(transport="stdio") directly inside the plugin’s main(). Emits DeprecationWarning from 1.12.0. |
mcp_core.transport.persist_capabilities_cache | No replacement — the <lock>.tools.json cache is no longer written for stdio path. HTTP daemon mode never used it. |
Sentinel polling helpers (watch_tools_list_changed) | No replacement — sentinel auto-respawn was a stdio-bridge implementation detail. |
TypeScript — API kept
Section titled “TypeScript — API kept”| Symbol | Status | Notes |
|---|---|---|
runHttpDaemon | NEW (1.12.0) | Renamed from runLocalServer; alias preserved. |
runLocalServer | KEPT (alias) | Calls runHttpDaemon. |
loadConfig / saveConfig | KEPT | Config storage. |
EcdhAesCrypto | KEPT | Crypto parity with Python. |
TypeScript — API deprecated (removed in 2.0.0)
Section titled “TypeScript — API deprecated (removed in 2.0.0)”| Symbol | Replacement |
|---|---|
runSmartStdioProxy | new StdioServerTransport() from @modelcontextprotocol/sdk/server/stdio.js, then await server.connect(transport). Emits deprecation warning. |
Migration template — Python main()
Section titled “Migration template — Python main()”Before (mcp-core ≤1.11):
import osimport sys
from mcp_core.transport import run_smart_stdio_proxy
def main() -> None: if "--stdio" in sys.argv or os.environ.get("MCP_TRANSPORT") == "stdio": daemon_cmd = [sys.executable, "-m", "wet_mcp"] sys.exit(run_smart_stdio_proxy("wet-mcp", daemon_cmd)) # ... HTTP path unchangedAfter (mcp-core ≥1.12):
import osimport sys
from wet_mcp.server import mcp # Your FastMCP instance
def main() -> None: if "--stdio" in sys.argv or os.environ.get("MCP_TRANSPORT") == "stdio": # Direct FastMCP stdio — no bridge, no daemon, no lock file. # Universal MCP client compatibility. mcp.run(transport="stdio") return # ... HTTP path unchangedMigration template — TypeScript main.ts
Section titled “Migration template — TypeScript main.ts”Before:
import { runSmartStdioProxy } from '@n24q02m/mcp-core'
if (process.env.MCP_TRANSPORT === 'stdio') { const exit = await runSmartStdioProxy('better-notion-mcp', [process.argv[0], ...process.argv.slice(1)]) process.exit(exit)}After:
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
if (process.env.MCP_TRANSPORT === 'stdio') { const transport = new StdioServerTransport() await server.connect(transport) return}Relay form for stdio-mode credential setup
Section titled “Relay form for stdio-mode credential setup”If your plugin requires user credentials (e.g. an API key or OAuth flow) and you are migrating to stdio mode, register the transient relay form helper so users can configure credentials via a local browser tab:
from mcp_core.relay import register_relay_form_toolfrom wet_mcp.config import save_credentials # Your callback
register_relay_form_tool( mcp, server_name="wet-mcp", on_save=save_credentials, # Called with (server_name, creds_dict))The helper registers a tool named config__open_relay. When the user
invokes that tool, the plugin spawns an in-process HTTP server on a
random local port, opens the user’s browser to a credential form,
and self-shutdowns after submission or 10 minutes idle.
Troubleshooting
Section titled “Troubleshooting””ToolSearch finds nothing” after upgrade
Section titled “”ToolSearch finds nothing” after upgrade”Confirm:
mcp-clean-state --kill-daemonswas run (kills leftover bridge daemons).- Restart your MCP client (Claude Code, Cursor, etc.) so it re-spawns the plugin process and re-discovers tools.
- The plugin’s manifest does not point at an old
n24q02m/<plugin>:latestDocker image — pull:stdioor:httpexplicitly.
”DeprecationWarning: run_smart_stdio_proxy”
Section titled “”DeprecationWarning: run_smart_stdio_proxy””Expected if you have a third-party plugin still using the old bridge import. The plugin still works in 1.x; track its upgrade and pin to a 1.x mcp-core release if you cannot wait.
Bridge daemon won’t terminate
Section titled “Bridge daemon won’t terminate”mcp-clean-state --kill-daemons uses SIGKILL / TerminateProcess. If a
process survives, it likely was not the lock owner — manually kill via
your task manager and re-run the cleanup.
Migration from mcp-relay-core
Section titled “Migration from mcp-relay-core”Migration from mcp-relay-core to mcp-core
Section titled “Migration from mcp-relay-core to mcp-core”mcp-core is a functional superset of the now-archived
mcp-relay-core. The Python and
TypeScript public layouts are 1:1, so migrating a downstream server is a pure
import + dependency rename. Every symbol that lived in mcp_relay_core.* (or
@n24q02m/mcp-relay-core/*) lives at the same path under mcp_core.* (or
@n24q02m/mcp-core/*).
Why migrate
Section titled “Why migrate”mcp-relay-coreis archived as of 2026-04-11. No new releases.mcp-coreadds the unified Streamable HTTP 2025-11-25 transport, OAuth 2.1 Bearer middleware, lifecycle locks, install/agents config writer, and the shared embedding-daemon + stdio-proxy packages.- All existing crypto / storage / OAuth / relay / schema modules are preserved byte-for-byte (with the package name substitution), so behavior is identical.
Python (packages/core-py)
Section titled “Python (packages/core-py)”Import rewrites
Section titled “Import rewrites”| Old (mcp-relay-core) | New (mcp-core) |
|---|---|
from mcp_relay_core import X | from mcp_core import X |
from mcp_relay_core.crypto.aes | from mcp_core.crypto.aes |
from mcp_relay_core.crypto.ecdh | from mcp_core.crypto.ecdh |
from mcp_relay_core.crypto.kdf | from mcp_core.crypto.kdf |
from mcp_relay_core.storage.config_file | from mcp_core.storage.config_file |
from mcp_relay_core.storage.encryption | from mcp_core.storage.encryption |
from mcp_relay_core.storage.machine_id | from mcp_core.storage.machine_id |
from mcp_relay_core.storage.mode | from mcp_core.storage.mode |
from mcp_relay_core.storage.resolver | from mcp_core.storage.resolver |
from mcp_relay_core.storage.session_lock | from mcp_core.storage.session_lock |
from mcp_relay_core.oauth import ... | from mcp_core.oauth import ... |
from mcp_relay_core.relay.client | from mcp_core.relay.client |
from mcp_relay_core.relay.browser | from mcp_core.relay.browser |
from mcp_relay_core.relay.wordlist | from mcp_core.relay.wordlist |
from mcp_relay_core.schema.types | from mcp_core.schema.types |
In most server codebases this is a single sed command:
grep -rl "mcp_relay_core" src tests | xargs sed -i 's/mcp_relay_core/mcp_core/g'Dependency rename
Section titled “Dependency rename”pyproject.toml:
[project]dependencies = [ "mcp-relay-core>=1.4.0", "n24q02m-mcp-core>=1.0.0",]Note the PyPI package name is n24q02m-mcp-core (not mcp-core), but the
import path stays mcp_core.
After the rename, run uv lock --upgrade-package n24q02m-mcp-core (or your
project’s lockfile equivalent) and commit.
API changes you might notice
Section titled “API changes you might notice”The Python port is byte-faithful with two intentional bug fixes that
surfaced under mcp-core’s stricter ty type-check:
mcp_core.relay.client.create_session(...)now accepts an optional keyword-onlyoauth_state: dict[str, str] | None = Noneparameter. When non-None it is forwarded to the relay-server asoauthStatein the POST body. Existing callers that did not passoauth_stateare unaffected.mcp_core.relay.client.RelaySession.public_keyis nowEllipticCurvePublicKey | Noneinstead ofEllipticCurvePublicKey. This restores the documented “decrypt-only reconstructed session” use case inOAuthProvider.exchange_code(). No callers in mcp-relay-core read this field, so existing code is unaffected.
These two fixes lived as latent bugs in mcp-relay-core/oauth/provider.py —
that file referenced API signatures that did not exist in
mcp-relay-core/relay/client.py. The OAuth Provider was apparently never
exercised end-to-end on the Python side.
TypeScript (packages/core-ts)
Section titled “TypeScript (packages/core-ts)”Import rewrites
Section titled “Import rewrites”Old (@n24q02m/mcp-relay-core) | New (@n24q02m/mcp-core) |
|---|---|
from '@n24q02m/mcp-relay-core' | from '@n24q02m/mcp-core' |
from '@n24q02m/mcp-relay-core/crypto' | from '@n24q02m/mcp-core/crypto' |
from '@n24q02m/mcp-relay-core/storage' | from '@n24q02m/mcp-core/storage' |
from '@n24q02m/mcp-relay-core/relay' | from '@n24q02m/mcp-core/relay' |
from '@n24q02m/mcp-relay-core/schema' | from '@n24q02m/mcp-core/schema' |
from '@n24q02m/mcp-relay-core/oauth' | from '@n24q02m/mcp-core/oauth' |
Single sed command:
grep -rl "@n24q02m/mcp-relay-core" src tests | xargs sed -i 's|@n24q02m/mcp-relay-core|@n24q02m/mcp-core|g'Dependency rename
Section titled “Dependency rename”package.json:
"dependencies": { "@n24q02m/mcp-relay-core": "^1.1.0" "@n24q02m/mcp-core": "^1.0.0"}Run bun install (or pnpm install / npm install) and commit the lockfile.
New features available (opt-in)
Section titled “New features available (opt-in)”Once migrated, you can adopt these without further breaking changes:
mcp_core.transport.streamable_http.StreamableHTTPServerand@n24q02m/mcp-core/transport’sStreamableHTTPServer— thin wrappers around the FastMCP /@modelcontextprotocol/sdktransports that integrate the lifecycle lock and OAuth middleware out of the box.mcp_core.transport.oauth_middleware.OAuthMiddleware(Python) /OAuthMiddleware(TypeScript) — RFC 6750 + RFC 9728 compliant Bearer token validation that returns 401 withWWW-Authenticate: Bearer resource_metadata="...".mcp_core.lifecycle.lock.LifecycleLock— cross-platform file lock that prevents two server instances from binding the same(name, port)pair. Stores PID + port as readable metadata while the lock is held.mcp_core.install.agents.AgentInstaller— write or remove an MCP server entry in agent config files (Claude Code, Cursor, Codex, Windsurf, OpenCode).mcp-embedding-daemonPyPI package — shared FastAPI server scaffold for the upcoming ONNX/GGUF embedding backend. Exposes/healthand scaffolded/embed+/rerank(501 with roadmap link).mcp-stdio-proxyPyPI package —mcp-stdio-proxyCLI that forwards stdio MCP frames to an HTTP MCP server, for agents without native HTTP.
Verification checklist
Section titled “Verification checklist”After applying the rename in your downstream repo:
-
grep -rn "mcp_relay_core\|mcp-relay-core\|@n24q02m/mcp-relay-core" src testsreturns nothing -
pyproject.tomlno longer referencesmcp-relay-core -
package.jsonno longer references@n24q02m/mcp-relay-core - Lockfile regenerated and committed
- Test suite passes
- Lint + type check pass
If anything breaks, please open an issue at https://github.com/n24q02m/mcp-core/issues with the error message and the rename diff.