feat(fable-mcp): build wheel in Docker image, serve download, add admin log tool, and Settings install UI
- Dockerfile: build fable-mcp wheel into /app/dist/ during image build - routes/fable_mcp_dist.py: GET /api/fable-mcp/info + /download endpoints - app.py: register fable_mcp_dist_bp - fable_mcp/tools/admin.py: get_app_logs() hitting /api/admin/logs - fable_mcp/server.py: fable_get_app_logs MCP tool - SettingsView: "Fable MCP" section in API Keys tab with download button and install instructions - client.ts: getFableMcpInfo() helper - ci.yml: add fable-mcp/** to trigger paths Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@ from fabledassistant.routes.milestones import milestones_bp
|
||||
from fabledassistant.routes.task_logs import task_logs_bp
|
||||
from fabledassistant.routes.projects import projects_bp
|
||||
from fabledassistant.routes.push import push_bp
|
||||
from fabledassistant.routes.fable_mcp_dist import fable_mcp_dist_bp
|
||||
from fabledassistant.routes.quick_capture import quick_capture_bp
|
||||
from fabledassistant.routes.settings import settings_bp
|
||||
from fabledassistant.routes.tasks import tasks_bp
|
||||
@@ -69,6 +70,7 @@ def create_app() -> Quart:
|
||||
app.register_blueprint(images_bp)
|
||||
app.register_blueprint(milestones_bp)
|
||||
app.register_blueprint(notes_bp)
|
||||
app.register_blueprint(fable_mcp_dist_bp)
|
||||
app.register_blueprint(projects_bp)
|
||||
app.register_blueprint(push_bp)
|
||||
app.register_blueprint(quick_capture_bp)
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
"""Serve the fable-mcp distribution wheel built into the Docker image."""
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from quart import Blueprint, jsonify, send_file
|
||||
|
||||
from fabledassistant.auth import login_required
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
fable_mcp_dist_bp = Blueprint("fable_mcp_dist", __name__, url_prefix="/api/fable-mcp")
|
||||
|
||||
# Wheel is built into the image at this path (see Dockerfile)
|
||||
_DIST_DIR = Path(os.environ.get("FABLE_MCP_DIST_DIR", "/app/dist"))
|
||||
|
||||
|
||||
def _find_wheel() -> Path | None:
|
||||
"""Return the newest fable_mcp wheel in the dist dir, or None."""
|
||||
wheels = sorted(_DIST_DIR.glob("fable_mcp-*.whl"), reverse=True)
|
||||
return wheels[0] if wheels else None
|
||||
|
||||
|
||||
@fable_mcp_dist_bp.route("/info", methods=["GET"])
|
||||
@login_required
|
||||
async def fable_mcp_info():
|
||||
"""Return availability and filename of the bundled fable-mcp wheel."""
|
||||
wheel = _find_wheel()
|
||||
return jsonify({
|
||||
"available": wheel is not None,
|
||||
"filename": wheel.name if wheel else None,
|
||||
})
|
||||
|
||||
|
||||
@fable_mcp_dist_bp.route("/download", methods=["GET"])
|
||||
@login_required
|
||||
async def download_fable_mcp():
|
||||
"""Serve the fable-mcp wheel file as a download."""
|
||||
wheel = _find_wheel()
|
||||
if wheel is None:
|
||||
return jsonify({"error": "Package not built into this image"}), 404
|
||||
return await send_file(wheel, as_attachment=True)
|
||||
Reference in New Issue
Block a user