Metadata-Version: 2.4
Name: flask-apcore
Version: 0.3.1
Summary: Flask Extension for apcore AI-Perceivable Core integration, exposes Flask routes as MCP tools with auto-discovery, schema inference, YAML binding, and built-in observability. Supports MCP server (stdio/HTTP) for LLM tool integration.
Project-URL: Homepage, https://github.com/aiperceivable/
Project-URL: Repository, https://github.com/aiperceivable/flask-apcore
Project-URL: Documentation, https://github.com/aiperceivable/flask-apcore#readme
Project-URL: Issues, https://github.com/aiperceivable/flash-apcore/issues
Author-email: aiperceivable <tercel.yi@gmail.com>
License-Expression: Apache-2.0
Keywords: ai,apcore,auto-discovery,binding,cli,extension,flask,integration,llm,mcp,module,observability,pydantic,schema,tools,yaml
Requires-Python: >=3.11
Requires-Dist: apcore-toolkit>=0.2.0
Requires-Dist: apcore>=0.13.0
Requires-Dist: flask>=3.0
Requires-Dist: pydantic>=2.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: all
Requires-Dist: apcore-mcp>=0.10.0; extra == 'all'
Requires-Dist: flask-restx>=1.0; extra == 'all'
Requires-Dist: flask-smorest>=0.42; extra == 'all'
Provides-Extra: dev
Requires-Dist: apdev[dev]>=0.2.0; extra == 'dev'
Requires-Dist: coverage>=7.0; extra == 'dev'
Requires-Dist: mypy>=1.0; extra == 'dev'
Requires-Dist: pre-commit>=3.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest-flask>=1.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.1; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: apcore-mcp>=0.10.0; extra == 'mcp'
Provides-Extra: restx
Requires-Dist: flask-restx>=1.0; extra == 'restx'
Provides-Extra: smorest
Requires-Dist: flask-smorest>=0.42; extra == 'smorest'
Description-Content-Type: text/markdown

# flask-apcore

Flask Extension for [apcore](https://github.com/aiperceivable/apcore-python) (AI-Perceivable Core) integration. Expose your Flask routes as MCP tools with auto-discovery, Pydantic schema inference, and built-in observability.

## Features

- **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
- **`@module` decorator** -- define standalone AI-callable modules with full schema enforcement
- **YAML binding** -- zero-code module definitions via external `.binding.yaml` files
- **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 serve` for 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** -- `Apcore` class provides property-based access to all components

## Requirements

- Python >= 3.11
- Flask >= 3.0
- apcore >= 0.13.0
- apcore-toolkit >= 0.2.0

## Installation

```bash
# 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]
```

## Quick Start

### 1. Add Apcore to your Flask app

```python
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**:

```python
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 app
```

### 2. Scan routes and start MCP server

```bash
export FLASK_APP=app.py

# Scan Flask routes -> register as apcore modules
flask apcore scan

# Start MCP server (stdio, for Claude Desktop / Cursor)
flask apcore serve
```

That's it. Your Flask routes are now MCP tools.

### 3. Unified entry point

The `Apcore` instance provides property-based access to all components:

```python
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")
```

### 4. Connect an MCP client

For **Claude Desktop**, add to your config:

```json
{
  "mcpServers": {
    "my-flask-app": {
      "command": "flask",
      "args": ["apcore", "serve"],
      "env": { "FLASK_APP": "app.py" }
    }
  }
}
```

For **HTTP transport** (remote access):

```bash
flask apcore serve --http --host 0.0.0.0 --port 9100
```

## Integration Paths

flask-apcore supports three ways to define AI-perceivable modules:

### Route Scanning (zero-intrusion)

Scan existing Flask routes without modifying any code:

```bash
# 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 --verify
```

### `@module` Decorator (precise control)

Define standalone modules with full schema enforcement:

```python
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)
```

### YAML Binding (zero-code)

Define modules externally in `.binding.yaml` files:

```yaml
# 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.

## CLI Commands

### `flask apcore scan`

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
```

### `flask apcore serve`

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)
```

## Configuration

All settings use the `APCORE_` prefix in `app.config`:

```python
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
)
```

## Observability

Enable tracing, metrics, and structured logging:

```python
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 |

## System Modules

Enable built-in system modules for introspection and control:

```python
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 status
- `system.manifest.full` / `system.manifest.module` — Module introspection
- `system.usage.summary` / `system.usage.module` — Usage statistics
- `system.control.toggle_feature` / `system.control.update_config` / `system.control.reload_module` — Runtime control

## MCP Tool Explorer

The MCP Tool Explorer is a browser UI provided by [apcore-mcp](https://github.com/aiperceivable/apcore-mcp) for inspecting registered modules and executing them interactively.

> **Security:** Without JWT authentication, Explorer endpoints are unauthenticated. Either enable `--jwt-secret` or only expose in development/staging environments.

```bash
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.

## JWT Authentication

Protect MCP endpoints with JWT Bearer tokens (requires `apcore-mcp>=0.10.0`, HTTP transports only):

```bash
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-execute
```

When 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-auth` to allow unauthenticated access
- Use `--exempt-paths` to bypass auth for specific paths

## Approval System

Require approval for module execution (apcore-mcp 0.8.0+):

```bash
# Interactive approval via MCP elicitation
flask apcore serve --http --approval elicit

# Auto-approve all (testing)
flask apcore serve --http --approval auto-approve
```

## Docker Demo

A 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.

```bash
cd examples/demo
docker compose up --build
```

See [examples/demo/README.md](examples/demo/README.md) for full details.

## Public API

```python
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,
)
```

## Development

```bash
git clone https://github.com/aiperceivable/flask-apcore.git
cd flask-apcore
pip install -e ".[dev,mcp]"
pytest
```

## License

Apache-2.0
