Flask Extension for apcore (AI-Perceivable Core) integration. Expose your Flask routes as MCP tools with auto-discovery, Pydantic schema inference, and built-in observability.
- Route scanning -- auto-discover Flask routes and convert them to apcore modules
- Annotation inference --
GET-> readonly+cacheable,DELETE-> destructive,PUT-> idempotent - Pydantic schema inference -- input/output schemas extracted from type hints automatically
- Docstring enrichment -- parameter descriptions from docstrings injected into JSON Schema
@moduledecorator -- define standalone AI-callable modules with full schema enforcement- YAML binding -- zero-code module definitions via external
.binding.yamlfiles - Python output -- generate
@module-decorated Python files from scanned routes - MCP server -- stdio and streamable-http transports via
flask apcore serve - Observability -- distributed tracing, metrics, structured logging, error history, usage tracking
- System modules -- built-in health, manifest, usage, and control modules (apcore 0.11.0+)
- Input validation -- validate tool inputs against Pydantic schemas before execution
- CLI-first workflow --
flask apcore scan+flask apcore servefor zero-intrusion integration - MCP Tool Explorer -- browser UI for inspecting modules via
flask apcore serve --explorer - JWT authentication -- protect MCP endpoints with Bearer tokens via
--jwt-secret - Approval system -- require approval for destructive operations via
--approval - AI enhancement -- enrich module metadata using local SLMs via
--ai-enhance - Unified entry point --
Apcoreclass provides property-based access to all components
- Python >= 3.11
- Flask >= 3.0
- apcore >= 0.13.0
- apcore-toolkit >= 0.2.0
# Core
pip install flask-apcore
# With MCP server support (required for `flask apcore serve`)
pip install flask-apcore[mcp]
# All optional extras
pip install flask-apcore[mcp,smorest,restx]from flask import Flask
from flask_apcore import Apcore
app = Flask(__name__)
apcore = Apcore(app)
@app.route("/greet/<name>", methods=["GET"])
def greet(name: str) -> dict:
"""Greet a user by name."""
return {"message": f"Hello, {name}!"}Or use the factory pattern:
from flask import Flask
from flask_apcore import Apcore
apcore = Apcore()
def create_app():
app = Flask(__name__)
# ... register routes / blueprints ...
apcore.init_app(app)
return appexport FLASK_APP=app.py
# Scan Flask routes -> register as apcore modules
flask apcore scan
# Start MCP server (stdio, for Claude Desktop / Cursor)
flask apcore serveThat's it. Your Flask routes are now MCP tools.
The Apcore instance provides property-based access to all components:
apcore = Apcore(app)
with app.app_context():
# Properties
apcore.registry # Registry
apcore.executor # Executor (lazy-created)
apcore.settings # ApcoreSettings
apcore.metrics # MetricsCollector | None
apcore.events # EventEmitter | None
apcore.error_history # ErrorHistory | None
apcore.usage # UsageCollector | None
# Convenience methods
apcore.call("task_stats.v1")
apcore.validate("task_stats.v1", {"key": "value"})
apcore.list_modules(tags=["api"])
apcore.describe("task_stats.v1")For Claude Desktop, add to your config:
{
"mcpServers": {
"my-flask-app": {
"command": "flask",
"args": ["apcore", "serve"],
"env": { "FLASK_APP": "app.py" }
}
}
}For HTTP transport (remote access):
flask apcore serve --http --host 0.0.0.0 --port 9100flask-apcore supports three ways to define AI-perceivable modules:
Scan existing Flask routes without modifying any code:
# Direct registration (in-memory)
flask apcore scan
# Generate YAML binding files (persistent)
flask apcore scan --output yaml --dir ./apcore_modules
# Generate Python module files
flask apcore scan --output python --dir ./apcore_modules
# Preview without side effects
flask apcore scan --dry-run
# Filter routes by regex
flask apcore scan --include "user.*" --exclude ".*delete"
# AI-enhanced module descriptions (requires APCORE_AI_ENABLED)
flask apcore scan --ai-enhance
# Verify written output
flask apcore scan --output yaml --verifyDefine standalone modules with full schema enforcement:
from flask_apcore import Apcore, module
from pydantic import BaseModel
class SummaryResult(BaseModel):
total: int
active: int
@module(id="user_stats.v1", tags=["analytics"])
def user_stats() -> SummaryResult:
"""Return user statistics."""
return SummaryResult(total=100, active=42)
app = Flask(__name__)
app.config["APCORE_MODULE_PACKAGES"] = ["myapp.modules"]
Apcore(app)Define modules externally in .binding.yaml files:
# apcore_modules/greet.binding.yaml
bindings:
- module_id: greet.get
target: app.greet
description: "Greet a user by name"Set APCORE_AUTO_DISCOVER=True (default) to load bindings on startup.
Scan Flask routes and generate apcore module definitions.
Options:
-s, --source [auto|native|smorest|restx] Scanner source (default: auto)
-o, --output [yaml|python] Output format; omit for direct registration
-d, --dir PATH Output directory (default: APCORE_MODULE_DIR)
--dry-run Preview without writing or registering
--include REGEX Only include matching module IDs
--exclude REGEX Exclude matching module IDs
--ai-enhance AI-enhance module metadata (requires APCORE_AI_ENABLED)
--verify Verify written output
Start an MCP server exposing registered modules as tools.
Options:
--stdio Use stdio transport (default)
--http Use streamable-http transport
--host TEXT HTTP host (default: 127.0.0.1)
-p, --port INT HTTP port (default: 9100)
--name TEXT MCP server name (default: apcore-mcp)
--validate-inputs Validate tool inputs against schemas
--log-level [DEBUG|INFO|WARNING|ERROR]
Module Filtering:
--tags TEXT Comma-separated tags to filter modules
--prefix TEXT Module ID prefix filter
Explorer:
--explorer Enable the MCP Tool Explorer UI
--explorer-prefix TEXT URL prefix for explorer (default: /explorer)
--allow-execute Allow Try-it execution in the explorer
--explorer-title TEXT Page title for Explorer UI
--explorer-project-name Project name shown in Explorer footer
--explorer-project-url Project URL shown in Explorer footer
Authentication:
--jwt-secret TEXT JWT secret key for MCP auth (HTTP only)
--jwt-algorithm ALGO JWT signing algorithm (default: HS256)
--jwt-audience TEXT Expected JWT audience claim
--jwt-issuer TEXT Expected JWT issuer claim
--require-auth/--no-require-auth Require auth for all requests (default: True)
--exempt-paths TEXT Comma-separated paths exempt from auth
Execution:
--approval [off|elicit|auto-approve|always-deny] Approval mode (default: off)
--output-formatter TEXT Output formatter dotted path (e.g., apcore_toolkit.to_markdown)
All settings use the APCORE_ prefix in app.config:
app.config.update(
# Core
APCORE_AUTO_DISCOVER=True, # Auto-load bindings and @module packages
APCORE_MODULE_DIR="apcore_modules/",# Directory for binding files
APCORE_BINDING_PATTERN="*.binding.yaml", # Glob pattern for binding files
APCORE_MODULE_PACKAGES=[], # Python packages to scan for @module functions
APCORE_SCANNER_SOURCE="auto", # Scanner: auto, native, smorest, restx
# System Modules (apcore 0.11.0+)
APCORE_SYS_MODULES_ENABLED=False, # Enable system modules (health, manifest, usage, control)
APCORE_SYS_MODULES_EVENTS_ENABLED=False, # Enable event system for platform notifications
# Middleware & Execution
APCORE_MIDDLEWARES=[], # Middleware dotted paths (e.g. ["myapp.mw.AuthMW"])
APCORE_ACL_PATH=None, # ACL file path (e.g. "acl.yaml")
APCORE_CONTEXT_FACTORY=None, # Custom ContextFactory dotted path
APCORE_EXECUTOR_CONFIG=None, # Executor config dict (passed to apcore.Config)
APCORE_EXTENSIONS=[], # Extension plugin dotted paths
# MCP Server
APCORE_SERVE_TRANSPORT="stdio", # Transport: stdio, streamable-http, sse
APCORE_SERVE_HOST="127.0.0.1", # HTTP host
APCORE_SERVE_PORT=9100, # HTTP port
APCORE_SERVER_NAME="apcore-mcp", # MCP server name
APCORE_SERVER_VERSION=None, # MCP server version string
APCORE_SERVE_VALIDATE_INPUTS=False, # Validate inputs against schemas
APCORE_SERVE_LOG_LEVEL=None, # Log level: DEBUG, INFO, WARNING, ERROR
APCORE_SERVE_TAGS=None, # Filter modules by tags (list of strings)
APCORE_SERVE_PREFIX=None, # Filter modules by ID prefix
# MCP Authentication
APCORE_SERVE_JWT_SECRET=None, # JWT secret key (enables auth when set)
APCORE_SERVE_JWT_ALGORITHM="HS256", # Signing algorithm
APCORE_SERVE_JWT_AUDIENCE=None, # Expected audience claim
APCORE_SERVE_JWT_ISSUER=None, # Expected issuer claim
APCORE_SERVE_REQUIRE_AUTH=True, # Require auth for all requests
APCORE_SERVE_EXEMPT_PATHS=None, # Paths exempt from auth (list of strings)
# MCP Approval (apcore-mcp 0.8.0+)
APCORE_SERVE_APPROVAL="off", # Approval mode: off, elicit, auto-approve, always-deny
APCORE_SERVE_OUTPUT_FORMATTER=None, # Output formatter dotted path
# MCP Tool Explorer
APCORE_SERVE_EXPLORER=False, # Enable Tool Explorer UI in MCP server
APCORE_SERVE_EXPLORER_PREFIX="/explorer",# URL prefix for explorer
APCORE_SERVE_ALLOW_EXECUTE=False, # Allow Try-it execution in explorer
APCORE_SERVE_EXPLORER_TITLE="MCP Tool Explorer", # Explorer page title
APCORE_SERVE_EXPLORER_PROJECT_NAME=None, # Project name in explorer footer
APCORE_SERVE_EXPLORER_PROJECT_URL=None, # Project URL in explorer footer
# Observability
APCORE_TRACING_ENABLED=False, # Enable distributed tracing
APCORE_TRACING_EXPORTER="stdout", # Exporter: stdout, memory, otlp
APCORE_TRACING_OTLP_ENDPOINT=None, # OTLP collector URL (e.g. "http://localhost:4317")
APCORE_TRACING_SERVICE_NAME="flask-apcore", # Service name for traces
APCORE_METRICS_ENABLED=False, # Enable metrics collection
APCORE_METRICS_BUCKETS=None, # Custom histogram buckets (list of floats)
APCORE_LOGGING_ENABLED=False, # Enable structured logging
APCORE_LOGGING_FORMAT="json", # Format: json, text
APCORE_LOGGING_LEVEL="INFO", # Level: trace, debug, info, warn, error, fatal
# Scan Options
APCORE_SCAN_AI_ENHANCE=False, # AI-enhance scanned modules (requires APCORE_AI_ENABLED)
APCORE_SCAN_VERIFY=False, # Verify written output by default
)Enable tracing, metrics, and structured logging:
app.config.update(
APCORE_TRACING_ENABLED=True,
APCORE_TRACING_EXPORTER="otlp",
APCORE_TRACING_OTLP_ENDPOINT="http://localhost:4317",
APCORE_METRICS_ENABLED=True,
APCORE_LOGGING_ENABLED=True,
APCORE_LOGGING_FORMAT="json",
)When observability is enabled, the following middleware is automatically wired into the Executor:
| Middleware | Trigger | Purpose |
|---|---|---|
| TracingMiddleware | TRACING_ENABLED |
Distributed tracing spans |
| MetricsMiddleware | METRICS_ENABLED |
Latency histograms, call counts |
| ObsLoggingMiddleware | LOGGING_ENABLED |
Structured log entries |
| ErrorHistoryMiddleware | Any of above | Ring buffer of recent errors |
| UsageMiddleware | METRICS_ENABLED |
Per-module usage stats and trends |
| PlatformNotifyMiddleware | SYS_MODULES_EVENTS_ENABLED |
Threshold-based event emission |
Enable built-in system modules for introspection and control:
app.config.update(
APCORE_SYS_MODULES_ENABLED=True,
APCORE_SYS_MODULES_EVENTS_ENABLED=True, # Optional: enable event system
)This registers:
system.health.summary/system.health.module— Health statussystem.manifest.full/system.manifest.module— Module introspectionsystem.usage.summary/system.usage.module— Usage statisticssystem.control.toggle_feature/system.control.update_config/system.control.reload_module— Runtime control
The MCP Tool Explorer is a browser UI provided by apcore-mcp for inspecting registered modules and executing them interactively.
Security: Without JWT authentication, Explorer endpoints are unauthenticated. Either enable
--jwt-secretor only expose in development/staging environments.
flask apcore serve --http --explorer --allow-execute \
--explorer-title "My API Tools" \
--explorer-project-name "My Project"Browse to http://127.0.0.1:9100/explorer/ to view the interactive explorer with Try-it execution.
Protect MCP endpoints with JWT Bearer tokens (requires apcore-mcp>=0.10.0, HTTP transports only):
flask apcore serve --http \
--jwt-secret "change-me-in-production" \
--jwt-algorithm HS256 \
--jwt-audience my-api \
--jwt-issuer https://auth.example.com \
--explorer --allow-executeWhen JWT is enabled:
- All MCP endpoints require a valid
Authorization: Bearer <token>header - The Explorer UI shows a token input field for authentication
- Use
--no-require-authto allow unauthenticated access - Use
--exempt-pathsto bypass auth for specific paths
Require approval for module execution (apcore-mcp 0.8.0+):
# Interactive approval via MCP elicitation
flask apcore serve --http --approval elicit
# Auto-approve all (testing)
flask apcore serve --http --approval auto-approveA complete runnable demo is included in examples/demo/. It demonstrates the full pipeline: Flask CRUD routes with Pydantic schemas, route scanning, annotation inference, @module registration, MCP server, and observability.
cd examples/demo
docker compose up --buildSee examples/demo/README.md for full details.
from flask_apcore import (
Apcore, # Flask Extension (unified entry point)
module, # @module decorator
Registry, # Module registry
Executor, # Module executor with middleware pipeline
Context, # Request context
Identity, # User identity
ACL, # Access control list
Config, # Executor configuration
Middleware, # Middleware base class
ModuleAnnotations, # Behavioral hints (readonly, destructive, etc.)
ModuleDescriptor, # Module metadata
FunctionModule, # Module wrapper for functions
PreflightResult, # Preflight validation result
# Approval system
ApprovalHandler,
AutoApproveHandler,
AlwaysDenyHandler,
# Events
EventEmitter,
EventSubscriber,
ApCoreEvent,
# Cancellation
CancelToken,
# Errors
ModuleError,
ModuleNotFoundError,
ACLDeniedError,
SchemaValidationError,
InvalidInputError,
# System modules
register_sys_modules,
)git clone https://github.com/aiperceivable/flask-apcore.git
cd flask-apcore
pip install -e ".[dev,mcp]"
pytestApache-2.0