Skip to main content
Glama
aiperceivable

FastAPI Apcore MCP Server

fastapi-apcore

FastAPI integration for apcore (AI-Perceivable Core). Expose your FastAPI routes as MCP tools with auto-discovery, Pydantic schema extraction, and built-in observability.

Features

  • Route scanning -- auto-discover FastAPI routes and convert them to apcore modules

  • Annotation inference -- GET -> readonly+cacheable, DELETE -> destructive, PUT -> idempotent

  • Pydantic schema extraction -- input/output schemas extracted from Pydantic models and OpenAPI spec

  • Two scanner backends -- OpenAPI-based (accurate) and native route inspection (fast)

  • Display overlay -- binding_path= applies a sparse binding.yaml display overlay (§5.13) to control CLI/MCP/A2A surface aliases, descriptions, and guidance without touching code

  • Simplified module IDs -- simplify_ids=True extracts clean function names from FastAPI operationIds (deprecated — use binding_path with display.cli.alias instead)

  • @module decorator -- define standalone AI-callable modules with full schema enforcement

  • YAML binding -- zero-code module definitions via external .binding.yaml files

  • MCP server -- stdio, streamable-http, and SSE transports via fastapi-apcore serve

  • Observability -- distributed tracing, metrics, structured logging, error history, usage tracking

  • Input validation -- validate tool inputs against Pydantic schemas before execution

  • CLI-first workflow -- fastapi-apcore scan + fastapi-apcore serve for zero-intrusion integration

  • MCP Tool Explorer -- browser UI for inspecting modules via --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

  • Async tasks -- background task submission, status tracking, and cancellation

  • Unified entry point -- FastAPIApcore class provides property-based access to all components

  • CLI generation -- create_cli() turns FastAPI routes into a Click CLI that proxies to the running API

  • HTTP proxy modules -- HTTPProxyRegistryWriter registers scanned routes as HTTP-forwarding modules for CLI and remote execution

Related MCP server: FastAPI-MCP

Requirements

  • Python >= 3.11

  • FastAPI >= 0.100

  • apcore >= 0.25.0

  • apcore-toolkit >= 0.10.0

  • apcore-mcp >= 0.17.1 (optional, [mcp] extra)

  • apcore-cli >= 0.10.3 (optional, [cli] extra)

Installation

# Core
pip install fastapi-apcore

# With MCP server support (required for serve/export)
pip install fastapi-apcore[mcp]

# With CLI
pip install fastapi-apcore[cli]

# Everything
pip install fastapi-apcore[all]

Quick Start

1. Add FastAPIApcore to your app

from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi_apcore import FastAPIApcore

apcore = FastAPIApcore()

@asynccontextmanager
async def lifespan(app: FastAPI):
    apcore.init_app(app)  # Auto-scan routes, discover modules, start MCP server
    yield

app = FastAPI(lifespan=lifespan)

@app.get("/greet/{name}")
def greet(name: str) -> dict:
    """Greet a user by name."""
    return {"message": f"Hello, {name}!"}

Or use the factory pattern:

from fastapi import FastAPI
from fastapi_apcore import FastAPIApcore

apcore = FastAPIApcore()

def create_app() -> FastAPI:
    app = FastAPI()
    # ... add routers ...
    apcore.init_app(app)
    return app

2. Scan routes and start MCP server

# Scan FastAPI routes -> register as apcore modules -> start MCP server
fastapi-apcore serve myapp.main:app --transport streamable-http --port 9090 --explorer

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

3. Connect an MCP client

For Claude Desktop, add to your config:

{
  "mcpServers": {
    "my-fastapi-app": {
      "command": "fastapi-apcore",
      "args": ["serve", "myapp.main:app"]
    }
  }
}

For HTTP transport (remote access):

fastapi-apcore serve myapp.main:app --transport streamable-http --host 0.0.0.0 --port 9090

Integration Paths

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

Route Scanning (zero-intrusion)

Scan existing FastAPI routes without modifying any code:

# Direct registration (in-memory)
fastapi-apcore scan myapp.main:app

# Generate YAML binding files (persistent)
fastapi-apcore scan myapp.main:app --output yaml --dir ./apcore_modules

# Preview without side effects
fastapi-apcore scan myapp.main:app --dry-run

# Filter routes by regex
fastapi-apcore scan myapp.main:app --include "users\." --exclude "\.delete$"

@module Decorator

Define standalone modules with explicit schemas:

from fastapi_apcore import FastAPIApcore

apcore = FastAPIApcore()

@apcore.module(id="math.add", tags=["math"], description="Add two numbers")
def add(a: int, b: int) -> int:
    return a + b

YAML Binding (zero-code)

Define modules via external .binding.yaml files in APCORE_MODULE_DIR:

bindings:
  - module_id: users.greet
    target: myapp.views:greet
    description: "Greet a user by name"
    tags: [users]
    input_schema:
      properties:
        name: { type: string }
      required: [name]
    output_schema:
      properties:
        message: { type: string }

Unified Entry Point

The FastAPIApcore instance provides property-based access to all components:

apcore = FastAPIApcore()

# Properties (lazy-loaded singletons)
apcore.registry           # apcore Registry
apcore.executor           # apcore Executor (with extensions)
apcore.settings           # ApcoreSettings
apcore.metrics_collector   # MetricsCollector | None
apcore.context_factory    # FastAPIContextFactory
apcore.task_manager       # AsyncTaskManager
apcore.extension_manager  # ExtensionManager

# Module execution
result = apcore.call("math.add", {"a": 1, "b": 2})
result = await apcore.call_async("math.add", {"a": 1, "b": 2}, request=request)

# Streaming
async for chunk in apcore.stream("ai.chat", {"prompt": "hello"}, request=request):
    ...

# With timeout/cancellation
result = apcore.cancellable_call("slow.task", timeout=30.0)

# Module introspection
apcore.list_modules(tags=["math"])
apcore.describe("math.add")

# Background tasks
task_id = await apcore.submit_task("batch.process", {"ids": [1, 2, 3]})
status = apcore.get_task_status(task_id)
await apcore.cancel_task(task_id)

# MCP serving
apcore.serve(transport="streamable-http", port=9090, explorer=True)
tools = apcore.to_openai_tools(strict=True)

# Standalone MCP server (fresh registry, scan + serve in one call)
apcore.create_mcp_server(app, transport="streamable-http", port=9090)

# CLI generation (routes become Click commands that proxy to the running API)
cli = apcore.create_cli(app, prog_name="myapp-cli", base_url="http://localhost:8000")
cli(standalone_mode=True)

# MCP helpers (inside module execution)
await FastAPIApcore.report_progress(context, progress=50, total=100)
response = await FastAPIApcore.elicit(context, "Please confirm")

# Singleton access
apcore = FastAPIApcore.get_instance()

init_app() Reference

init_app() performs a complete initialization sequence:

apcore.init_app(
    app,                    # FastAPI application instance
    scan=True,              # Auto-scan routes (default: True)
    scan_source="openapi",  # Scanner backend: "openapi" or "native"
    simplify_ids=False,     # Use simplified module IDs (function names only)
    include=None,           # Regex: only register matching module IDs
    exclude=None,           # Regex: skip matching module IDs
)

What init_app() does (in order):

  1. Auto-discover modules from YAML bindings and APCORE_MODULE_PACKAGES

  2. Scan FastAPI routes and register them as apcore modules

  3. Enable hot-reload if APCORE_HOT_RELOAD=true

  4. Start embedded MCP server if APCORE_EMBEDDED_SERVER is configured

create_mcp_server() Reference

Create a standalone MCP server with a fresh registry (independent of the singleton):

# Full scan mode -- scan all routes and serve as MCP tools
apcore.create_mcp_server(
    app,
    transport="streamable-http",
    port=9090,
    binding_path="bindings/",    # apply display overlay (§5.13) — sets MCP tool names/descriptions
    commands_dir="commands/",    # convention-scanned plain functions (§5.14)
    explorer=True,
)

# Custom modules mode -- discover from a directory only
apcore.create_mcp_server(
    extensions_dir="./mcp/modules",
    scan=False,
    transport="streamable-http",
    port=9090,
)

binding_path accepts a single .binding.yaml file or a directory of *.binding.yaml files. When set, DisplayResolver resolves metadata["display"]["mcp"] on all scanned modules before they are registered, so MCP tool names and descriptions reflect the overlay.

commands_dir points to a directory of plain Python function files. When set, ConventionScanner from apcore-toolkit scans for public functions and registers them as additional modules alongside the scanned API routes (§5.14).

create_cli() Reference

Generate a Click CLI group that proxies to your running FastAPI server. Commands are auto-grouped by namespace prefix using GroupedModuleGroup (e.g., myapp-cli product list instead of myapp-cli product.list):

from fastapi_apcore import FastAPIApcore
from myapp.main import app

apcore = FastAPIApcore()
cli = apcore.create_cli(
    app,
    prog_name="myapp-cli",
    base_url="http://localhost:8000",
    binding_path="bindings/",    # apply display overlay (§5.13) — sets CLI command names/descriptions
    commands_dir="commands/",    # convention-scanned plain functions (§5.14)
    max_content_width=160,       # wider help output for long command names
)

if __name__ == "__main__":
    cli(standalone_mode=True)

Each scanned route becomes a CLI command named after display.cli.alias from the binding overlay (or the module_id if no overlay is set). The commands forward HTTP requests to the running API using HTTPProxyRegistryWriter. Built-in subcommands include list, describe, completion, and man.

commands_dir points to a directory of plain Python function files. When set, ConventionScanner from apcore-toolkit scans for public functions and registers them as additional modules alongside the scanned API routes (§5.14).

Display Overlay (binding_path)

Control how each route appears to CLI users and MCP clients without changing your FastAPI code:

# bindings/myapp.binding.yaml
bindings:
  - module_id: product.get_product_product__product_id_.get
    display:
      alias: product-get          # base alias for all surfaces
      description: Get a product by its ID
      cli:
        alias: get-product        # CLI command name: myapp-cli get-product
      mcp:
        alias: get_product        # MCP tool name (must match [a-zA-Z_][a-zA-Z0-9_-]*)
        guidance: |
          Always pass product_id as a UUID string, not an integer.

Resolution chain per field: surface-specific override > display default > scanner value.

Note: simplify_ids=True is deprecated. Use binding_path with display.cli.alias / display.mcp.alias in binding.yaml instead.

Configuration

All settings are read from environment variables with the APCORE_ prefix:

Core Settings

Variable

Default

Description

APCORE_MODULE_DIR

apcore_modules/

Directory for YAML binding files

APCORE_AUTO_DISCOVER

true

Auto-discover modules on startup

APCORE_BINDING_PATTERN

*.binding.yaml

Glob pattern for binding files

APCORE_MODULE_PACKAGES

Comma-separated packages to scan for @module

APCORE_VALIDATE_INPUTS

false

Validate inputs before module execution

MCP Server Settings

Variable

Default

Description

APCORE_SERVE_TRANSPORT

stdio

MCP transport: stdio, streamable-http, sse

APCORE_SERVE_HOST

127.0.0.1

Host for HTTP transport

APCORE_SERVE_PORT

9090

Port for HTTP transport

APCORE_SERVER_NAME

apcore-mcp

MCP server name

APCORE_SERVER_VERSION

MCP server version string

APCORE_EXPLORER_ENABLED

false

Enable MCP Tool Explorer UI

APCORE_EXPLORER_PREFIX

/explorer

URL prefix for Explorer

APCORE_EXPLORER_ALLOW_EXECUTE

false

Allow execution from Explorer

Authentication & Security

Variable

Default

Description

APCORE_JWT_SECRET

JWT secret for MCP authentication

APCORE_JWT_ALGORITHM

HS256

JWT signing algorithm

APCORE_JWT_AUDIENCE

Expected JWT audience claim

APCORE_JWT_ISSUER

Expected JWT issuer claim

APCORE_ACL_PATH

Path to ACL YAML rules file

APCORE_MIDDLEWARES

Comma-separated middleware dotted paths

Access control (ACL)

Set APCORE_ACL_PATH to a YAML rules file and apcore enforces role-based allow/deny on every module call. Roles come from the request's authenticated user: FastAPIContextFactory reads request.state.user (roles / groups / scopes) into the apcore Identity that ACL conditions match against. A denied call raises ACLDeniedError, which your route maps to HTTP 403.

# acl.yaml
default_effect: deny
rules:
  - description: Admins may call any module
    callers: ["*"]
    targets: ["*"]
    effect: allow
    conditions: { roles: ["admin"] }

See examples/acl_demo/ for a runnable FastAPI app plus integration tests (tests/test_acl_demo.py).

Observability

Variable

Default

Description

APCORE_TRACING

Enable tracing: true or JSON config

APCORE_METRICS

Enable metrics: true or JSON config

APCORE_OBSERVABILITY_LOGGING

Enable structured logging: true or JSON config

Task Management

Variable

Default

Description

APCORE_TASK_MAX_CONCURRENT

10

Max concurrent background tasks

APCORE_TASK_MAX_TASKS

1000

Max total tasks in queue

APCORE_TASK_CLEANUP_AGE

3600

Max age (seconds) for completed tasks

APCORE_CANCEL_DEFAULT_TIMEOUT

Default cancellation timeout (seconds)

Advanced

Variable

Default

Description

APCORE_EXECUTOR_CONFIG

JSON string for Executor configuration

APCORE_CONTEXT_FACTORY

Dotted path to custom ContextFactory class

APCORE_HOT_RELOAD

false

Watch module files for changes

APCORE_HOT_RELOAD_PATHS

Comma-separated paths to watch

APCORE_EMBEDDED_SERVER

Auto-start MCP server on init: true or JSON config

APCORE_OUTPUT_FORMATTER

Dotted path to output formatter function

APCORE_AI_ENHANCE

false

Enable AI metadata enhancement

See fastapi_apcore.ApcoreSettings for the full list of 40 settings.

CLI Reference

scan -- Scan FastAPI routes

fastapi-apcore scan myapp.main:app [OPTIONS]

Option

Description

--source, -s

Scanner backend: openapi (default) or native

--output, -o

Output format: yaml. Omit for direct registration

--dir, -d

Output directory (default: APCORE_MODULE_DIR)

--dry-run

Preview without writing files or registering

--include

Regex: only include matching module IDs

--exclude

Regex: exclude matching module IDs

--ai-enhance

AI-enhance module metadata

--verify

Verify written output

serve -- Start MCP server

fastapi-apcore serve myapp.main:app [OPTIONS]

Option

Description

--transport, -t

Transport: stdio (default), streamable-http, sse

--host

Host for HTTP transport (default: 127.0.0.1)

--port, -p

Port for HTTP transport (default: 9090)

--name

MCP server name

--explorer

Enable MCP Tool Explorer UI

--jwt-secret

JWT secret key for authentication

--approval

Approval mode: off, elicit, auto-approve, always-deny

--tags

Comma-separated module tag filter

--prefix

Module ID prefix filter

--validate-inputs

Validate inputs before execution

--output-formatter

Dotted path to output formatter function

--log-level

Log level: DEBUG, INFO, WARNING, ERROR

export -- Export as OpenAI tools

fastapi-apcore export [OPTIONS]

Option

Description

--format, -f

Export format (default: openai-tools)

--strict

Add strict: true for Structured Outputs

--embed-annotations

Include annotation metadata in descriptions

--tags

Comma-separated module tag filter

--prefix

Module ID prefix filter

tasks -- Manage async tasks

fastapi-apcore tasks list [--status STATUS]
fastapi-apcore tasks cancel TASK_ID
fastapi-apcore tasks cleanup [--max-age SECONDS]

Scanning

Two scanner backends are available:

Backend

Method

Best for

OpenAPI (default)

Uses FastAPI's auto-generated OpenAPI spec

Accuracy, handles all FastAPI features

Native

Directly inspects app.routes

Speed, no OpenAPI generation overhead

from fastapi_apcore import get_scanner

# OpenAPI scanner (default) -- full operationId-based IDs
scanner = get_scanner("openapi")
modules = scanner.scan(app)

# OpenAPI scanner with simplified IDs (recommended for CLI)
scanner = get_scanner("openapi", simplify_ids=True)
modules = scanner.scan(app)
# product.get_product_product__product_id_.get → product.get_product.get

# Native scanner
scanner = get_scanner("native")
modules = scanner.scan(app, include=r"users\.", exclude=r"\.delete$")

The simplify_ids option extracts the original Python function name from FastAPI's auto-generated operationId and uses only the first path segment as prefix, producing much shorter and more readable module IDs:

Default:       credit_purchase.purchase.status.get_purchase_status_by_payment_intent.get  (73 chars)
simplify_ids:  credit_purchase.get_purchase_status_by_payment_intent.get                  (57 chars)

It defaults to False for backward compatibility.

Users only need to interact with two things:

  • FastAPIApcore -- the unified entry point (import from fastapi_apcore)

  • CLI -- fastapi-apcore scan/serve/export/tasks

Everything in engine/ is internal wiring that FastAPIApcore manages automatically.

Integration with apcore Ecosystem

fastapi-apcore bridges FastAPI to the apcore protocol:

FastAPI App -> FastAPIApcore -> apcore (Registry/Executor) -> apcore-mcp (MCP Server)

All apcore types are re-exported for convenience:

from fastapi_apcore import (
    Registry, Executor, Context, Identity, Config,
    ACL, Middleware, ModuleAnnotations, FunctionModule,
    CancelToken, PreflightResult, ModuleError,
    ExecutionCancelledError, ModuleDisabledError,
    ApprovalHandler, AutoApproveHandler, AlwaysDenyHandler,
    EventEmitter, EventSubscriber, ApCoreEvent,
    HTTPProxyRegistryWriter,  # lazy import, requires apcore-toolkit
    module,  # @module decorator
)

License

Apache-2.0

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Exposes FastAPI endpoints as Model Context Protocol (MCP) tools while preserving existing authentication, schemas, and documentation. It enables seamless integration of FastAPI services into MCP ecosystems using a native ASGI transport layer.
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Exposes FastAPI API-key management and request-making routes as MCP tools, enabling users to manage and interact with their APIs via Claude.
    MIT