Second step of milestone 461. An MCP client (Claude Code, Claude Desktop via mcp-remote, Open WebUI) connects to /mcp with a personal API token. - mcp>=2.2 (SDK v2 MCPServer). Mounted by wrapping app.asgi_app, so /mcp never enters Quart's pipeline; stateless streamable HTTP so a redeploy never strands a client on a session id the new process didn't issue. - Auth: Authorization: Bearer stw_… resolves to a Caller (user + effective role) in a ContextVar; 401 with WWW-Authenticate otherwise. The web pipeline's database gate runs first, so a DB restart answers 503 instead of failing inside a tool. - Registry, default-deny: register_tool(fn, role=, read_only=) is the only door; role and read_only are required. Tools above the caller's role, or from a disabled plugin, are neither listed nor callable. Undeclared arguments are refused (not silently dropped); a tool's ValueError reaches the agent as its reason; a DB failure mid-tool says so. - Plugins contribute tools via an optional register_mcp_tools(register) export, all-or-nothing, replaced on hot-reload. Documented in docs/plugins/writing-a-plugin.md. - First tool: whoami. - Tests drive app.asgi_app directly (Quart's test client never reaches /mcp); the integration test mints a real token against Postgres and runs initialize → tools/list → whoami → revoke → 401. Task #4966 "MCP-2 — /mcp transport". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
55 lines
1.2 KiB
TOML
55 lines
1.2 KiB
TOML
[build-system]
|
|
requires = ["hatchling"]
|
|
build-backend = "hatchling.build"
|
|
|
|
[project]
|
|
name = "steward"
|
|
version = "0.1.0"
|
|
requires-python = ">=3.11"
|
|
dependencies = [
|
|
"quart>=0.19",
|
|
"sqlalchemy[asyncio]>=2.0",
|
|
"asyncpg>=0.29",
|
|
"alembic>=1.13",
|
|
"bcrypt>=4.1",
|
|
"python-dotenv>=1.0",
|
|
"pyyaml>=6.0",
|
|
"packaging>=24.0",
|
|
"click>=8.0",
|
|
"httpx>=0.27",
|
|
"cryptography>=42",
|
|
# Floor, not a cap: MCPServer (mcp.server.mcpserver) and the mcp.types
|
|
# mirror that steward/mcp/ is written against first ship in SDK 2.2.
|
|
"mcp>=2.2",
|
|
]
|
|
|
|
[project.optional-dependencies]
|
|
ldap = [
|
|
"ldap3>=2.9",
|
|
]
|
|
snmp = [
|
|
"pysnmp-lextudio>=6.2",
|
|
]
|
|
# Batteries-included Ansible (bundles the common community collections) for the
|
|
# playbook runner. Installed into the image via Dockerfile `pip install .[ansible]`.
|
|
ansible = [
|
|
"ansible>=10,<13",
|
|
]
|
|
dev = [
|
|
"pytest>=8.0",
|
|
"pytest-asyncio>=0.23",
|
|
]
|
|
|
|
[project.scripts]
|
|
steward = "steward.cli:main"
|
|
|
|
[tool.pytest.ini_options]
|
|
asyncio_mode = "auto"
|
|
testpaths = ["tests"]
|
|
markers = [
|
|
"integration: requires a live Postgres; runs only in the integration CI lane",
|
|
]
|
|
|
|
[tool.hatch.build.targets.wheel]
|
|
packages = ["steward"]
|