foxxe-mcp 0.3.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.
@@ -0,0 +1,7 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .venv/
5
+ build/
6
+ dist/
7
+ .pytest_cache/
@@ -0,0 +1,106 @@
1
+ # Changelog
2
+
3
+ Semver, honestly (PRD §7): transport or auth behaviour changes are **major**,
4
+ new helpers **minor**, SDK patch bumps that change no foxxe-mcp behaviour
5
+ **patch**. Every release records the exact SDK bound it pins.
6
+
7
+ ## v0.3.0 — 2026-09-04
8
+
9
+ **First PyPI release.** `pip install foxxe-mcp==0.3.0`.
10
+
11
+ **SDK bound** — unchanged: `mcp>=1.29.1,<2`, `fastmcp>=2.14,<3` (extra).
12
+
13
+ **Changed**
14
+
15
+ - Distribution moves from a git dependency pinned by tag to PyPI, amending
16
+ PRD §7. Found by the first real deploy: the repo is private, so a Docker
17
+ build cannot clone it (`git clone ... exit code: 128`) — and no fleet
18
+ Dockerfile has GitHub credentials. The alternatives were threading a PAT
19
+ through eleven Dockerfiles or making the repo public, which would publish
20
+ `fleet.toml` and the internal audit with it. PyPI keeps the repo private and
21
+ publishes only the package. NG3 rules out a *private* index; this is not one.
22
+ - The sdist is an explicit allowlist (`src/`, README, CHANGELOG, LICENSE,
23
+ pyproject). `fleet.toml`, `docs/` and `scripts/` carry internal topology and
24
+ are deliberately not published.
25
+
26
+ ## v0.2.1 — 2026-09-04
27
+
28
+ **SDK bound** — unchanged.
29
+
30
+ **Fixed**
31
+
32
+ - `setup_logging()` now takes sole ownership of the root handlers. The SDK
33
+ calls its own `configure_logging()` from the server constructor, so a plain
34
+ handler is already installed by the time `serve()` runs; adding a second one
35
+ made ainm emit every line twice — once bare, once JSON — doubling log volume
36
+ and leaving the JSON stream unparseable. Caught by booting the converted
37
+ pilot, not by the unit tests.
38
+
39
+ ## v0.2.0 — 2026-09-04
40
+
41
+ Found by the ainm pilot, which is what a pilot is for.
42
+
43
+ **SDK bound** — unchanged: `mcp>=1.29.1,<2`, `fastmcp>=2.14,<3` (extra).
44
+
45
+ **Added**
46
+
47
+ - `serve(mcp, app=...)` runs an app already built by `build_app()` instead of
48
+ building a second one. ainm's tests reach for a module-level `server.app`, so
49
+ the object has to exist at import time *and* be the one `__main__` runs;
50
+ without this, `python server.py` left an inert second session manager behind.
51
+ Passing `app=` together with `build_app` keyword arguments is a `TypeError`
52
+ rather than a silent ignore.
53
+
54
+ ## v0.1.0 — 2026-09-03
55
+
56
+ First release. Extracted from the ainm bootstrap, which had the cleanest
57
+ hand-rolled `server.py` in the fleet (PRD §8.1).
58
+
59
+ **SDK bound**
60
+
61
+ | name | bound | resolved in CI |
62
+ |---|---|---|
63
+ | `mcp` | `>=1.29.1,<2` | 1.29.1 |
64
+ | `fastmcp` (extra) | `>=2.14,<3` | not installed by default |
65
+
66
+ PRD OQ1 answered: **hold at `<2` for the first release.** The fleet is entirely
67
+ pre-2.0, and converting the plumbing and the tool-definition API surface in one
68
+ step would double the blast radius of the ainm pilot. The `MCPServer` *name* is
69
+ exported now, so the v1.0 pin flip is a change to this library and to no server.
70
+ Verified against `mcp==2.1.1` by `scripts/verify_sdk2.py`.
71
+
72
+ The floor is 1.29.1 — the newest 1.x — deliberately above every floor in the
73
+ audit (`1.6.0`, `1.25`, `1.26.0`, `1.28.1`). Four floors across six apps was the
74
+ drift; one floor ends it.
75
+
76
+ **Added**
77
+
78
+ - `MCPServer` / `FastMCP` / `Context` / `Image` — unwrapped SDK re-exports under
79
+ a name stable across the 1.x→2.x rename (`_sdk`).
80
+ - `serve()` and `build_app()` — transport selection, Fly bind conventions,
81
+ allowed-host handling, lifespan wrapping, graceful shutdown.
82
+ - `/health` (dependency-free) and `/version` → `{server, foxxe_mcp, sdk}`.
83
+ - `check_sdk()` — boot-time enforcement of the bound on **both** names, which is
84
+ the only place the transitive `fastmcp` → `mcp>=2` case (PRD finding B) can be
85
+ caught.
86
+ - `BearerAuthMiddleware` — pure-ASGI, probes exempt.
87
+ - `ToolError`, `error_envelope()`, `error_json()`, `failing_to_json()`.
88
+ - `setup_logging()` — JSON lines, level and format from env.
89
+ - `taisce_fetch()` — service-token key reveal, never logs the value.
90
+ - `sentinel_push()` — opt-in, fire-and-forget, cannot raise into the caller.
91
+ - `foxxe-herd` — fleet drift report and herd redeploy (PRD §8.5, §11).
92
+
93
+ **Open questions answered**
94
+
95
+ - **OQ1** — hold at `<2`; export the 2.x name. See above.
96
+ - **OQ2** — bearer only. The SDK already implements the OAuth resource-server
97
+ model and three servers pass `auth_server_provider=` / `auth=AuthSettings(...)`
98
+ to the constructor; `serve()` carries those through untouched. Reimplementing
99
+ OAuth here would be NG2 in a different costume and would leave two
100
+ implementations to reconcile instead of one.
101
+ - **OQ3** — `sentinel_push()` is **opt-in**, off unless `SENTINEL_URL` is set. A
102
+ shared library that starts emitting to a central collector the moment it is
103
+ installed is a surprise, and a fleet migration is the worst moment for one.
104
+
105
+ Still open: **OQ4** (`git_create_repo` in git-mcp) and **OQ5** (mnemos' duplicate
106
+ root `requirements.txt` declaration) are fleet questions, not library ones.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Todd McCaffrey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,137 @@
1
+ Metadata-Version: 2.5
2
+ Name: foxxe-mcp
3
+ Version: 0.3.0
4
+ Summary: Shared MCP plumbing for the FoxxeLabs server fleet: one SDK pin, one bootstrap, thin re-export.
5
+ Project-URL: Homepage, https://github.com/todd427/foxxe-mcp
6
+ Author: Todd McCaffrey
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.10
10
+ Requires-Dist: httpx>=0.27.0
11
+ Requires-Dist: mcp<2,>=1.29.1
12
+ Requires-Dist: pydantic>=2.0
13
+ Requires-Dist: starlette>=0.36.0
14
+ Requires-Dist: uvicorn>=0.27.0
15
+ Provides-Extra: dev
16
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
17
+ Requires-Dist: pytest>=8.0; extra == 'dev'
18
+ Provides-Extra: fastmcp
19
+ Requires-Dist: fastmcp<3,>=2.14; extra == 'fastmcp'
20
+ Description-Content-Type: text/markdown
21
+
22
+ # foxxe-mcp
23
+
24
+ Shared MCP plumbing for the FoxxeLabs server fleet. One SDK pin, one bootstrap, thin re-export of the SDK server class — so a spec revision is one commit here, not eleven latent crash-loops.
25
+
26
+ See [docs/PRD-foxxe-mcp.md](docs/PRD-foxxe-mcp.md) for why. See [docs/migration.md](docs/migration.md) for how to convert a server.
27
+
28
+ **Status:** v0.1.0 — library built, fleet unmigrated.
29
+
30
+ ## The shape
31
+
32
+ ```python
33
+ from foxxe_mcp import MCPServer, serve
34
+
35
+ mcp = MCPServer("tomhas")
36
+
37
+ @mcp.tool()
38
+ def status() -> str:
39
+ """Report the current run state."""
40
+ ...
41
+
42
+ if __name__ == "__main__":
43
+ serve(mcp)
44
+ ```
45
+
46
+ `MCPServer` is the SDK's own class, re-exported unwrapped. The tool decorator is
47
+ the SDK's, reached through the instance — foxxe-mcp never wraps the registration
48
+ API (PRD NG2), so every SDK feature is available the day it ships.
49
+
50
+ `serve()` owns what eleven servers currently hand-roll: transport, bind address,
51
+ allowed host, `/health`, `/version`, logging, graceful shutdown.
52
+
53
+ ## Why the name is `MCPServer` on an SDK that calls it `FastMCP`
54
+
55
+ v0.1 pins `mcp>=1.29.1,<2`, where the class is `FastMCP`. The export is named
56
+ `MCPServer` anyway — that is the mcp 2.x name, and exporting it now means the
57
+ eventual pin flip changes this library and no server code at all.
58
+
59
+ That is a claim, so it is tested: `scripts/verify_sdk2.py` runs the same library
60
+ against `mcp==2.1.1` and checks that `MCPServer` resolves to the renamed class,
61
+ that a tool still registers, and that `/health`, `/version` and `/mcp` all still
62
+ answer. Both majors, one codebase.
63
+
64
+ ```
65
+ $ python scripts/verify_sdk2.py
66
+ foxxe-mcp 0.1.0 against mcp 2.1.1
67
+ PASS SDK major detected as 2 — mcp.server.mcpserver
68
+ PASS MCPServer resolves to the renamed class
69
+ ...
70
+ All checks passed — the v1.0 pin flip is a one-line change.
71
+ ```
72
+
73
+ ## The bound is enforced twice
74
+
75
+ Once at install time, in `pyproject.toml`, and again at boot, in `check_sdk()`.
76
+ The second one is not redundant. PRD finding B: rialu declares no `mcp` at all,
77
+ only an unbounded `fastmcp`, and `fastmcp` 3.x pulls `mcp>=2` transitively — a
78
+ declaration that binds only at install time cannot catch what a resolver did to
79
+ a *transitive* dependency. `serve()` calls `check_sdk()` before the first tool
80
+ call, so a wrong resolution fails at boot, next to its cause.
81
+
82
+ `mcp` out of range is fatal. `fastmcp` out of range warns, and is fatal under
83
+ `FOXXE_MCP_STRICT_SDK=1`.
84
+
85
+ ## Install
86
+
87
+ ```
88
+ foxxe-mcp==0.3.0
89
+ ```
90
+
91
+ Add the `fastmcp` extra only if the server actually uses the third-party
92
+ `fastmcp` package: `foxxe-mcp[fastmcp]==0.3.0`. Never declare `mcp` or
93
+ `fastmcp` directly in a server again — that is the whole point.
94
+
95
+ Published to PyPI rather than installed as a git dependency, which is a
96
+ deliberate amendment to PRD §7. The git-by-tag plan died on contact with the
97
+ first real deploy: this repo is private, so `pip install git+https://...`
98
+ inside a Docker build has no credentials and fails with exit 128. The
99
+ alternatives were a build-time PAT threaded through eleven Dockerfiles, or
100
+ making the repo public — which would publish `fleet.toml` and the internal
101
+ audit along with it. PyPI keeps the repo private, publishes only the package,
102
+ and makes every Dockerfile a plain `pip install` again. NG3 rules out a
103
+ *private* index; this is the public one.
104
+
105
+ ## Fleet operations
106
+
107
+ ```
108
+ foxxe-herd status # poll every server's /version — who is on what
109
+ foxxe-herd redeploy # dry run
110
+ foxxe-herd redeploy --yes # fly deploy every migrated server
111
+ ```
112
+
113
+ `fleet.toml` maps server name to Fly app name (verified against `flyer
114
+ apps_list`, not guessed — the `-foxxelabs` suffix is inconsistent) and records
115
+ which servers are migrated and which are still unbounded.
116
+
117
+ ## Environment
118
+
119
+ | Variable | Default | Meaning |
120
+ |---|---|---|
121
+ | `PORT` | `8080` | Port to bind |
122
+ | `BIND_HOST` | `0.0.0.0` | Bind address (Fly requires all interfaces) |
123
+ | `ALLOWED_HOST` | `$FLY_APP_NAME.fly.dev` | Public hostname for DNS-rebinding protection — **not** the bind address |
124
+ | `MCP_TRANSPORT` | `http` | `http` or `stdio` |
125
+ | `LOG_LEVEL` | `INFO` | Root log level |
126
+ | `LOG_FORMAT` | `json` | `text` for human-readable |
127
+ | `MCP_BEARER_TOKENS` | unset | Comma-separated; presence enables bearer auth |
128
+ | `FOXXE_MCP_STRICT_SDK` | unset | `1` makes an out-of-range `fastmcp` fatal |
129
+ | `TAISCE_SERVICE_TOKEN` | unset | Service token for `taisce_fetch()` |
130
+ | `SENTINEL_URL` | unset | Presence enables `sentinel_push()` |
131
+
132
+ ## Development
133
+
134
+ ```
135
+ python -m venv .venv && .venv/bin/pip install -e '.[dev]'
136
+ .venv/bin/python -m pytest
137
+ ```
@@ -0,0 +1,116 @@
1
+ # foxxe-mcp
2
+
3
+ Shared MCP plumbing for the FoxxeLabs server fleet. One SDK pin, one bootstrap, thin re-export of the SDK server class — so a spec revision is one commit here, not eleven latent crash-loops.
4
+
5
+ See [docs/PRD-foxxe-mcp.md](docs/PRD-foxxe-mcp.md) for why. See [docs/migration.md](docs/migration.md) for how to convert a server.
6
+
7
+ **Status:** v0.1.0 — library built, fleet unmigrated.
8
+
9
+ ## The shape
10
+
11
+ ```python
12
+ from foxxe_mcp import MCPServer, serve
13
+
14
+ mcp = MCPServer("tomhas")
15
+
16
+ @mcp.tool()
17
+ def status() -> str:
18
+ """Report the current run state."""
19
+ ...
20
+
21
+ if __name__ == "__main__":
22
+ serve(mcp)
23
+ ```
24
+
25
+ `MCPServer` is the SDK's own class, re-exported unwrapped. The tool decorator is
26
+ the SDK's, reached through the instance — foxxe-mcp never wraps the registration
27
+ API (PRD NG2), so every SDK feature is available the day it ships.
28
+
29
+ `serve()` owns what eleven servers currently hand-roll: transport, bind address,
30
+ allowed host, `/health`, `/version`, logging, graceful shutdown.
31
+
32
+ ## Why the name is `MCPServer` on an SDK that calls it `FastMCP`
33
+
34
+ v0.1 pins `mcp>=1.29.1,<2`, where the class is `FastMCP`. The export is named
35
+ `MCPServer` anyway — that is the mcp 2.x name, and exporting it now means the
36
+ eventual pin flip changes this library and no server code at all.
37
+
38
+ That is a claim, so it is tested: `scripts/verify_sdk2.py` runs the same library
39
+ against `mcp==2.1.1` and checks that `MCPServer` resolves to the renamed class,
40
+ that a tool still registers, and that `/health`, `/version` and `/mcp` all still
41
+ answer. Both majors, one codebase.
42
+
43
+ ```
44
+ $ python scripts/verify_sdk2.py
45
+ foxxe-mcp 0.1.0 against mcp 2.1.1
46
+ PASS SDK major detected as 2 — mcp.server.mcpserver
47
+ PASS MCPServer resolves to the renamed class
48
+ ...
49
+ All checks passed — the v1.0 pin flip is a one-line change.
50
+ ```
51
+
52
+ ## The bound is enforced twice
53
+
54
+ Once at install time, in `pyproject.toml`, and again at boot, in `check_sdk()`.
55
+ The second one is not redundant. PRD finding B: rialu declares no `mcp` at all,
56
+ only an unbounded `fastmcp`, and `fastmcp` 3.x pulls `mcp>=2` transitively — a
57
+ declaration that binds only at install time cannot catch what a resolver did to
58
+ a *transitive* dependency. `serve()` calls `check_sdk()` before the first tool
59
+ call, so a wrong resolution fails at boot, next to its cause.
60
+
61
+ `mcp` out of range is fatal. `fastmcp` out of range warns, and is fatal under
62
+ `FOXXE_MCP_STRICT_SDK=1`.
63
+
64
+ ## Install
65
+
66
+ ```
67
+ foxxe-mcp==0.3.0
68
+ ```
69
+
70
+ Add the `fastmcp` extra only if the server actually uses the third-party
71
+ `fastmcp` package: `foxxe-mcp[fastmcp]==0.3.0`. Never declare `mcp` or
72
+ `fastmcp` directly in a server again — that is the whole point.
73
+
74
+ Published to PyPI rather than installed as a git dependency, which is a
75
+ deliberate amendment to PRD §7. The git-by-tag plan died on contact with the
76
+ first real deploy: this repo is private, so `pip install git+https://...`
77
+ inside a Docker build has no credentials and fails with exit 128. The
78
+ alternatives were a build-time PAT threaded through eleven Dockerfiles, or
79
+ making the repo public — which would publish `fleet.toml` and the internal
80
+ audit along with it. PyPI keeps the repo private, publishes only the package,
81
+ and makes every Dockerfile a plain `pip install` again. NG3 rules out a
82
+ *private* index; this is the public one.
83
+
84
+ ## Fleet operations
85
+
86
+ ```
87
+ foxxe-herd status # poll every server's /version — who is on what
88
+ foxxe-herd redeploy # dry run
89
+ foxxe-herd redeploy --yes # fly deploy every migrated server
90
+ ```
91
+
92
+ `fleet.toml` maps server name to Fly app name (verified against `flyer
93
+ apps_list`, not guessed — the `-foxxelabs` suffix is inconsistent) and records
94
+ which servers are migrated and which are still unbounded.
95
+
96
+ ## Environment
97
+
98
+ | Variable | Default | Meaning |
99
+ |---|---|---|
100
+ | `PORT` | `8080` | Port to bind |
101
+ | `BIND_HOST` | `0.0.0.0` | Bind address (Fly requires all interfaces) |
102
+ | `ALLOWED_HOST` | `$FLY_APP_NAME.fly.dev` | Public hostname for DNS-rebinding protection — **not** the bind address |
103
+ | `MCP_TRANSPORT` | `http` | `http` or `stdio` |
104
+ | `LOG_LEVEL` | `INFO` | Root log level |
105
+ | `LOG_FORMAT` | `json` | `text` for human-readable |
106
+ | `MCP_BEARER_TOKENS` | unset | Comma-separated; presence enables bearer auth |
107
+ | `FOXXE_MCP_STRICT_SDK` | unset | `1` makes an out-of-range `fastmcp` fatal |
108
+ | `TAISCE_SERVICE_TOKEN` | unset | Service token for `taisce_fetch()` |
109
+ | `SENTINEL_URL` | unset | Presence enables `sentinel_push()` |
110
+
111
+ ## Development
112
+
113
+ ```
114
+ python -m venv .venv && .venv/bin/pip install -e '.[dev]'
115
+ .venv/bin/python -m pytest
116
+ ```
@@ -0,0 +1,58 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "foxxe-mcp"
7
+ version = "0.3.0"
8
+ description = "Shared MCP plumbing for the FoxxeLabs server fleet: one SDK pin, one bootstrap, thin re-export."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Todd McCaffrey" }]
13
+
14
+ # ── The fleet's single SDK pin (PRD G1) ──────────────────────────────────────
15
+ # This is the ONLY place in the fleet that may depend on `mcp` or `fastmcp`.
16
+ # v0.1 holds at the 1.x API surface (PRD OQ1): the fleet is entirely pre-2.0,
17
+ # and converting plumbing and the tool-definition API in one step would double
18
+ # the blast radius of the ainm pilot. `foxxe_mcp.MCPServer` already exports the
19
+ # 2.x *name*, so the v1.0 pin flip is a lib bump, not a fleet-wide edit.
20
+ dependencies = [
21
+ "mcp>=1.29.1,<2",
22
+ "starlette>=0.36.0",
23
+ "uvicorn>=0.27.0",
24
+ "httpx>=0.27.0",
25
+ "pydantic>=2.0",
26
+ ]
27
+
28
+ [project.optional-dependencies]
29
+ # PRD finding B: bounding one name is insufficient — rialu declares no `mcp` at
30
+ # all, only unbounded `fastmcp`, and fastmcp 3.x pulls mcp>=2 transitively.
31
+ # A server that needs the third-party fastmcp takes it from HERE, bounded, and
32
+ # `foxxe_mcp.check_sdk()` enforces the bound at runtime whether or not the
33
+ # resolver honoured it.
34
+ fastmcp = ["fastmcp>=2.14,<3"]
35
+ dev = [
36
+ "pytest>=8.0",
37
+ "pytest-asyncio>=0.23",
38
+ ]
39
+
40
+ [project.urls]
41
+ Homepage = "https://github.com/todd427/foxxe-mcp"
42
+
43
+ [project.scripts]
44
+ foxxe-herd = "foxxe_mcp.herd:main"
45
+
46
+ [tool.hatch.build.targets.wheel]
47
+ packages = ["src/foxxe_mcp"]
48
+
49
+ # The repo is private and the package is public, so the sdist is an explicit
50
+ # allowlist rather than "everything not gitignored". fleet.toml, docs/ and
51
+ # scripts/ carry internal topology — Fly app names, which servers are broken,
52
+ # the audit — and must not be published.
53
+ [tool.hatch.build.targets.sdist]
54
+ include = ["src/foxxe_mcp", "README.md", "CHANGELOG.md", "LICENSE", "pyproject.toml"]
55
+
56
+ [tool.pytest.ini_options]
57
+ asyncio_mode = "auto"
58
+ testpaths = ["tests"]
@@ -0,0 +1,86 @@
1
+ """foxxe-mcp — shared MCP plumbing for the FoxxeLabs fleet.
2
+
3
+ A choke point, not a framework (PRD §5). A server reads as if it were written
4
+ directly against the SDK, with two differences: the import comes from
5
+ ``foxxe_mcp``, and ``serve()`` replaces the hand-rolled transport wiring::
6
+
7
+ from foxxe_mcp import MCPServer, serve
8
+
9
+ mcp = MCPServer("tomhas")
10
+
11
+ @mcp.tool()
12
+ def status() -> str:
13
+ ...
14
+
15
+ if __name__ == "__main__":
16
+ serve(mcp)
17
+
18
+ ``MCPServer`` is the SDK's own class, re-exported unwrapped. The tool
19
+ decorator is the SDK's, reached through the instance — foxxe-mcp never wraps
20
+ the registration API (NG2), so every SDK feature is available the day it ships,
21
+ with no wrapper lag.
22
+
23
+ The name ``MCPServer`` is the mcp 2.x name, exported regardless of which SDK
24
+ major is installed. On the current 1.x pin it is ``FastMCP`` underneath. That
25
+ is the whole trick: when the fleet moves to 2.x, the pin changes here and
26
+ server code does not change at all.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ from ._sdk import (
32
+ FASTMCP_VERSION,
33
+ SDK_MAJOR,
34
+ SDK_MODULE,
35
+ SDK_VERSION,
36
+ Context,
37
+ FastMCP,
38
+ Image,
39
+ MCPServer,
40
+ SdkReport,
41
+ check_sdk,
42
+ )
43
+ from .auth import BearerAuthMiddleware, tokens_from_env
44
+ from .errors import ToolError, error_envelope, error_json, failing_to_json
45
+ from .health import health_route, version_payload, version_route
46
+ from .logging import setup_logging
47
+ from .secrets import TaisceError, taisce_fetch
48
+ from .serve import build_app, resolve_allowed_host, resolve_bind, serve
49
+ from .telemetry import sentinel_enabled, sentinel_push
50
+ from .version import __version__
51
+
52
+ __all__ = [
53
+ # ── SDK re-exports (unwrapped) ──────────────────────────────────────────
54
+ "MCPServer",
55
+ "FastMCP",
56
+ "Context",
57
+ "Image",
58
+ # ── Bootstrap (PRD G2) ──────────────────────────────────────────────────
59
+ "serve",
60
+ "build_app",
61
+ "resolve_bind",
62
+ "resolve_allowed_host",
63
+ # ── Shared plumbing (PRD G3) ────────────────────────────────────────────
64
+ "BearerAuthMiddleware",
65
+ "tokens_from_env",
66
+ "ToolError",
67
+ "error_envelope",
68
+ "error_json",
69
+ "failing_to_json",
70
+ "setup_logging",
71
+ "taisce_fetch",
72
+ "TaisceError",
73
+ "sentinel_push",
74
+ "sentinel_enabled",
75
+ "health_route",
76
+ "version_route",
77
+ "version_payload",
78
+ # ── Drift visibility (PRD G4) ───────────────────────────────────────────
79
+ "__version__",
80
+ "SDK_VERSION",
81
+ "SDK_MAJOR",
82
+ "SDK_MODULE",
83
+ "FASTMCP_VERSION",
84
+ "SdkReport",
85
+ "check_sdk",
86
+ ]