A schema-enforced module framework where every interface is inherently perceivable by AI.
A schema-driven module development framework that makes every interface naturally perceivable and understandable by AI.
apcore is a protocol specification. Language implementations are maintained in separate repositories — see Implementations.
The growing number of fragmented MCP implementations across the ecosystem proves the demand is real. apcore is the only solution that provides a complete SDK with a unified standard — enforced schema, behavioral annotations, access control, audit trails, and cross-language consistency. It doesn't replace any project's AI capabilities; it brings them all under one standard.
- What is apcore?
- Why AI-Perceivable?
- Core Principles
- Architecture Overview
- Quick Start
- Module Development
- Schema System
- Context Object
- ACL Access Control
- Middleware
- Configuration
- Observability
- Error Handling
- Cross-Language Support
- Relationship with Other Tools
- Implementations
- Ecosystem
- Documentation Index
- Contributing
- License
apcore is a universal module development framework that makes every module naturally perceivable and understandable by AI through enforced Schema definitions.
┌─────────────────────────────────────────────────────────────┐
│ apcore — AI-Perceivable Core │
│ │
│ Universal framework + Enforced AI-Perceivable support │
│ - Directory as ID (zero-config module discovery) │
│ - Schema-driven (input/output mandatory) │
│ - ACL / Observability / Middleware │
└─────────────────────────────────────────────────────────────┘
↓ Modules callable by
┌──────────┬──────────┬──────────┬──────────┐
│ │ │ │ │
Legacy Code AI/LLM HTTP API CLI Tool MCP Server
(import) (understands) (REST) (terminal) (Claude)
Not just an AI framework, but a universal framework that is naturally AI-Perceivable.
Today, many projects build their own MCP servers independently — Stripe has one, TipTap has one, NestJS has one. Each uses different interfaces, different standards, and none provides a programmable SDK. The result is a fragmented ecosystem where developers must learn a new approach for every integration.
apcore takes a different path: SDK-first, standard-unified.
| Fragmented MCP Solutions | apcore | |
|---|---|---|
| Programmable SDK | No — only MCP servers | Yes — apcore-python, apcore-typescript |
| Unified Standard | No — each project rolls its own | Yes — same schema, annotations, ACL across all integrations |
| Behavioral Annotations | None or minimal | readonly, destructive, requires_approval, idempotent, open_world |
| Access Control | None | Pattern-based ACL with role support |
| Audit Trail | None | Built-in tracing, metrics, structured logging |
| Cross-Language | Per-language silos | Python and TypeScript with identical behavior |
Traditional module development faces a fundamental contradiction:
Traditional modules: Code can call, but AI cannot understand
AI-Perceivable modules: Code can call, AI can also perceive and understand
AI has become an important caller in software systems, but most modules lack AI-understandable metadata. apcore fundamentally solves this by enforcing input_schema / output_schema / description.
apcore solves how to build modules (development framework), not how to call tools (communication protocol). Once modules are built, they can be called by code / AI / HTTP / CLI / MCP or any other means.
| Scenario | Without apcore | With apcore |
|---|---|---|
| LLM calling your business functions | Manually write tool descriptions, map parameters | Schema auto-provided, LLM understands directly |
| New team members onboarding | Read source code, guess parameters | Clear from Schema + annotations |
| Cross-team module reuse | Outdated docs, unclear interfaces | Schema is doc, enforced validation |
| Security audit | Manually trace call relationships | ACL + call chain auto-tracked |
| Expose as MCP Server | Rewrite interface definitions | Adapter reads Schema directly |
Reality: AI has become a key caller in software systems
Decision: Enforce input_schema / output_schema / description
Result: Modules understandable by both humans and AI, no extra cost
| Principle | Description |
|---|---|
| Schema-Driven | All modules enforce input_schema / output_schema / description |
| Directory as ID | Directory path auto-maps to module ID, zero config |
| AI-Perceivable | Schema enables AI/LLM perception and understanding—a design requirement, not optional |
| Universal Framework | Modules callable by code/AI/HTTP/CLI or any other means |
| Progressive Integration | Existing code gains AI-Perceivable capability via decorators, function calls, or YAML binding |
| Cross-Language Spec | Language-agnostic protocol specification, any language can implement conformant SDK |
| Traditional Frameworks | apcore | |
|---|---|---|
| Schema | Optional | Enforced |
| AI-Perceivable | Not guaranteed | Guaranteed |
| Module Discovery | Manual registration | Auto-discovery from directory |
| Input Validation | Implement yourself | Framework automatic |
| Behavior Annotations | None | readonly / destructive / requires_approval etc. |
| Call Tracing | Implement yourself | trace_id auto-propagated |
apcore's architecture consists of two orthogonal dimensions: Framework Technical Architecture (vertical) and Business Layering Recommendations (horizontal).
The technical layers of the framework itself, defining the complete flow from module registration to execution:
┌─────────────────────────────────────────────────┐
│ Application Layer │
│ HTTP API / CLI / MCP Server / Custom Interface│
└─────────────────────┬───────────────────────────┘
↓ calls
┌─────────────────────────────────────────────────┐
│ Execution Layer │
│ ACL check → Input validation → Middleware chain│
│ → Execute → Output validation │
└──────────┬──────────────────────────────────────┘
↓ lookup module
┌─────────────────────────────────────────────────┐
│ Registry Layer │
│ Scan & discover → ID mapping → Interface │
│ validation → Module storage │
└──────────┬──────────────────────────────────────┘
↓ read
┌─────────────────────────────────────────────────┐
│ Module Layer │
│ User-written business modules (conforming to │
│ Module interface specification) │
└─────────────────────────────────────────────────┘
Under the extensions/ directory, modules should be organized by responsibility (enforced by ACL):
extensions/
├── api/ # API Layer: Handle external requests
│ └── ACL: Can only call orchestrator.*
│
├── orchestrator/ # Orchestration Layer: Compose business flows
│ └── ACL: Can only call executor.* and common.*
│
├── executor/ # Execution Layer: Concrete business operations
│ └── ACL: Can call common.*, can connect to external systems
│
└── common/ # Common Layer: Shared utilities and helpers
└── ACL: Read-only operations, called by all layers
Key Points:
- Framework technical architecture (Application → Execution → Registry → Module) is apcore's implementation mechanism
- Business layering (api → orchestrator → executor → common) is a best practice recommendation, enforced through ACL configuration
- The two are orthogonal: any business layer module (api/orchestrator/executor/common) goes through the same framework layer processing
A module call goes through the following steps:
executor.call("executor.email.send_email", inputs, context)
│
├─ 1. Context processing: Create/update call context (trace_id, caller_id, call_chain)
├─ 2. Lookup module: Find target module from Registry
├─ 3. ACL check: Verify caller has permission to call target module
├─ 4. Input validation: Validate input parameters against input_schema
├─ 5. Middleware before: Execute middleware before() hooks in sequence
├─ 6. Module execution: Call module.execute(inputs, context)
├─ 7. Output validation: Validate output result against output_schema
├─ 8. Middleware after: Execute middleware after() hooks in reverse order
│
└─ Return result
Module IDs are automatically generated from relative paths under the module root directory (default root is extensions/, multiple roots can be configured):
File path: Canonical ID:
extensions/api/handler/user.py → api.handler.user
extensions/executor/email/send_email.py → executor.email.send_email
extensions/common/util/validator.py → common.util.validator
Rules:
1. Remove module root prefix (default `extensions/`)
2. Remove file extension
3. Replace `/` with `.`
4. Must match: ^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$
5. Maximum length: 128 characters
**Multiple Roots and Namespaces**
- If multiple module root directories are configured, each root directory automatically uses the directory name as a **namespace**, ensuring Module ID uniqueness within the same Registry.
- For example: `extensions_roots: ["./extensions", "./plugins"]` → `extensions.executor.email.send_email`, `plugins.my_tool`.
- Automatic namespacing can be overridden with explicit configuration (e.g., `{root: "./extensions", namespace: "core"}` → `core.executor.email.send_email`).
- Single root mode has no namespace by default (backward compatible); in multi-root mode, at most one root can set `namespace: ""` to omit the prefix.
**Cross-Project Conflicts**
- Same IDs from different projects **will not conflict**, unless they are merged into the same Registry / call domain (e.g., unified gateway or shared executor).
Detailed documentation: Registry API | Executor API | Context Object
pip install apcorefrom apcore import module, Registry, Executor
# 1. Define module with @module decorator (Schema auto-inferred from type annotations)
@module(id="executor.greet", tags=["greeting"])
def greet(name: str) -> dict:
"""Generate greeting message"""
return {"message": f"Hello, {name}!"}
# 2. Register & call
registry = Registry()
registry.register("executor.greet", greet)
executor = Executor(registry=registry)
result = executor.call("executor.greet", {"name": "World"})
print(result) # {"message": "Hello, World!"}my-project/
├── apcore.yaml # Framework configuration
├── extensions/ # Module directory (directory path = module ID)
│ ├── api/ # API layer
│ ├── orchestrator/ # Orchestration layer
│ └── executor/ # Execution layer
├── schemas/ # Schema definitions (YAML, shared across languages)
└── acl/ # Permission configuration
Detailed definitions: Module Interface | Creating Modules Guide
apcore provides four ways to define modules, suitable for different scenarios:
The most complete approach, supporting all features:
# extensions/executor/email/send_email.py
# Module ID auto-generated: executor.email.send_email
from apcore import Module, ModuleAnnotations, Context
from pydantic import BaseModel, Field
class SendEmailInput(BaseModel):
"""LLM understands what parameters are needed through this Schema"""
to: str = Field(..., description="Recipient email address")
subject: str = Field(..., description="Email subject")
body: str = Field(..., description="Email body")
class SendEmailOutput(BaseModel):
"""LLM understands what is returned through this Schema"""
success: bool
message_id: str = None
class SendEmailModule(Module):
"""Send email module
Detailed documentation:
- Supports text and HTML format emails
- Uses SMTP protocol to connect to external mail server
- Configuration items: smtp_host, smtp_port, smtp_user, smtp_pass
Usage example:
Input: {"to": "user@example.com", "subject": "Hello", "body": "World"}
Output: {"success": true, "message_id": "msg_123"}
Notes:
- SMTP server information must be configured in the configuration file
- Gmail limits 500 emails/day, other providers may have different limits
- EmailSendError exception will be raised on send failure
"""
# Core layer (must be defined)
input_schema = SendEmailInput
output_schema = SendEmailOutput
description = "Send email to specified recipient. Uses SMTP protocol, non-idempotent operation, requires mail server configuration."
# Optional: Detailed documentation (for complex modules)
documentation = """
# Features
Send emails via SMTP protocol, supporting plain text and HTML formats.
## Configuration Requirements
- SMTP server information must be configured in apcore.yaml
- Valid SMTP authentication credentials required
## Use Cases
- Send notification emails, verification codes, reports
## Limitations
- Gmail: 500 emails/day
- Attachment size: ≤25MB
"""
# Annotation layer (optional, type-safe)
annotations = ModuleAnnotations(
readonly=False, # Has side effects
destructive=False, # Won't delete/overwrite data
idempotent=False, # Repeated calls will send repeatedly
requires_approval=True, # Requires user confirmation
open_world=True, # Connects to external system (SMTP)
)
tags = ["email", "notification"]
def execute(self, inputs: dict, context: Context) -> dict:
validated = SendEmailInput(**inputs)
# ... send email logic ...
return SendEmailOutput(success=True, message_id="msg_123").model_dump()This module automatically has:
- LLM-understandable Schema and behavior annotations
- Auto-generated ID (
executor.email.send_email) - Input/output validation
- Call chain tracing, observability
Suitable for scenarios where source code can be modified, one-line integration:
# Before: Plain function
def send_email(to: str, subject: str, body: str) -> dict:
"""Send email"""
return {"success": True, "message_id": "msg_123"}
# After: Add one line decorator, automatically becomes an apcore module
from apcore import module
@module(id="email.send", tags=["email"])
def send_email(to: str, subject: str, body: str) -> dict:
"""Send email"""
return {"success": True, "message_id": "msg_123"}
# Schema automatically inferred from type annotationsSuitable for scenarios where you don't want to modify source code, completely non-invasive to existing code:
from apcore import module
# Existing business code, no modification needed
class EmailService:
def send(self, to: str, subject: str, body: str) -> dict:
"""Send email"""
return {"success": True}
service = EmailService()
module(service.send, id="email.send") # Register as apcore moduleSuitable for scenarios where source code cannot be modified (third-party libraries, legacy systems, etc.), pure YAML configuration:
# bindings/email.binding.yaml
bindings:
- module_id: "email.send"
target: "myapp.services.email:send_email" # Callable object path
description: "Send email"
auto_schema: true # Auto-generate Schema from type annotations
annotations:
open_world: true
requires_approval: true
tags: ["email"]| Approach | Code Invasiveness | Use Case | Schema Definition |
|---|---|---|---|
| Class-based | High (write new class) | New module development | Manual definition (most complete) |
@module Decorator |
Low (add one line) | Modifiable code | Inferred from type annotations |
module() Function Call |
Very low (don't modify original function) | Existing classes/methods | Inferred from type annotations |
| External Binding | Zero | Cannot modify source code scenarios | Auto-inferred or manually specified |
Detailed definitions: Schema Definition Guide | ModuleAnnotations API
Each module's metadata is divided into three layers, progressing from required to optional:
┌──────────────────────────────────────────────────┐
│ Core Layer (REQUIRED) │
│ input_schema / output_schema / description │
│ → AI understands "what this module does" │
│ │
│ + documentation (OPTIONAL, detailed docs) │
│ → AI understands "detailed use cases and │
│ constraints" │
├──────────────────────────────────────────────────┤
│ Annotation Layer (OPTIONAL, type-safe) │
│ annotations / examples / tags / version │
│ → AI understands "how to use correctly" │
├──────────────────────────────────────────────────┤
│ Extension Layer (OPTIONAL, free dictionary) │
│ metadata: dict[str, Any] │
│ → Custom requirements (framework doesn't │
│ validate) │
└──────────────────────────────────────────────────┘
Borrowing from Claude Skill's Progressive Disclosure design, apcore uses two fields to organize module documentation:
| Field | Required | Length Limit | Markdown | Purpose |
|---|---|---|---|---|
description |
Required | ≤200 characters | No | Brief module function description for AI quick matching and understanding |
documentation |
Optional | ≤5000 characters | Yes | Detailed documentation including use cases, constraints, configuration requirements |
- Module discovery phase: AI reads all modules'
description, quickly determines candidate modules - Call decision phase: AI loads
documentationon-demand, learns detailed usage and constraints
Complete format rules and correspondence with Claude Skill / OpenAPI: see Protocol Specification §4.8. Code examples: see Class-based Modules above.
Schema is based on JSON Schema Draft 2020-12, supports YAML format definition (shared across languages). Schema files are placed in the schemas/ directory, with paths corresponding to module IDs.
Complete Schema format and YAML examples: see Schema Definition Guide | Protocol Specification §4.
Annotations describe module behavior characteristics, helping AI make safer call decisions:
| Annotation | Type | Description | AI Behavior Impact |
|---|---|---|---|
readonly |
bool | No side effects, read-only operation | AI can safely call autonomously |
destructive |
bool | May delete or overwrite data | AI should request user confirmation before calling |
idempotent |
bool | Repeated calls have same result | AI can safely retry |
requires_approval |
bool | Requires explicit user consent | AI must wait for user approval |
open_world |
bool | Connects to external systems | AI should inform user of external interaction |
# Read-only query - AI can call autonomously
annotations = ModuleAnnotations(readonly=True)
# Delete operation - AI needs to request confirmation
annotations = ModuleAnnotations(destructive=True, requires_approval=True)
# External API call - AI needs to inform user
annotations = ModuleAnnotations(open_world=True, idempotent=True)Fields with x- prefix in Schema are LLM-specific extensions, don't affect standard JSON Schema validation:
| Field | Description | Example |
|---|---|---|
x-llm-description |
Extended description for LLM (more detailed than description) | "User's login password, at least 8 characters" |
x-examples |
Example values to help LLM understand format | ["user@example.com"] |
x-sensitive |
Mark sensitive fields (password, API Key, etc.) | true |
x-constraints |
Business constraints described in natural language | "Must be a registered user" |
x-deprecated |
Deprecation information | {"since": "2.0", "use": "new_field"} |
Complete usage and examples: see Schema Definition Guide | Protocol Specification §4.3.
In the extension layer (metadata dictionary), you can provide optional AI intent hints to help agents understand when and how to use the module. These are conventions, not enforced by the framework.
| Key | Purpose |
|---|---|
x-when-to-use |
Positive guidance: scenarios where this module is the right choice |
x-when-not-to-use |
Negative guidance: scenarios where a different module should be used |
x-common-mistakes |
Known pitfalls that AI agents frequently encounter |
x-workflow-hints |
Suggested pre/post steps or related modules in a typical workflow |
Detailed usage: see Protocol Specification §4.6.
Context is the execution context that runs through the entire call chain, carrying tracing, permissions, and shared data:
class Context:
trace_id: str # Call trace ID (UUID v4; W3C trace-id compatible in distributed scenarios)
caller_id: str | None # Caller module ID (None for top-level calls)
call_chain: list[str] # Call chain (accumulated in call order)
executor: Executor # Executor reference (entry point for inter-module calls)
identity: Identity # Caller identity
data: dict # Shared data (reference-shared within call chain)# Top-level call
context = Context(trace_id="abc-123", identity=Identity(id="user_1", roles=["admin"]))
# Module A is called
# trace_id: "abc-123" ← Stays the same
# caller_id: None ← No caller at top level
# call_chain: ["module_a"]
# Module A internally calls Module B
result = context.executor.call("module_b", inputs, context)
# trace_id: "abc-123" ← Stays the same
# caller_id: "module_a" ← Caller is module_a
# call_chain: ["module_a", "module_b"]
# Module B internally calls Module C
# trace_id: "abc-123" ← Same trace_id for entire chain
# caller_id: "module_b"
# call_chain: ["module_a", "module_b", "module_c"]Key Feature: context.data is reference-shared throughout the entire call chain, allowing modules to pass intermediate results and implement pipeline-style data flow.
Detailed definitions: ACL Configuration Guide | Protocol Specification §6
ACL (Access Control List) controls which modules can call which modules, default deny:
# acl/global_acl.yaml
rules:
# API layer can only call orchestration layer
- callers: ["api.*"]
targets: ["orchestrator.*"]
effect: allow
# Orchestration layer can call execution layer
- callers: ["orchestrator.*"]
targets: ["executor.*"]
effect: allow
# Forbid cross-layer calls (API directly calling execution layer)
- callers: ["api.*"]
targets: ["executor.*"]
effect: deny
# System internal modules unrestricted
- callers: ["@system"]
targets: ["*"]
effect: allow
default_effect: deny # Default deny when no rules match
audit:
enabled: true
log_level: info
include_denied: true| Identifier | Description |
|---|---|
@external |
Top-level external call (HTTP request, CLI command, etc.) |
@system |
Framework internal call |
* |
Wildcard, matches all |
rules:
- callers: ["api.*"]
targets: ["executor.payment.*"]
effect: allow
conditions:
identity_types: ["user"] # Only user identity
roles: ["admin", "finance"] # Only admin or finance roles
max_call_depth: 5 # Maximum call depthDetailed definitions: Middleware Guide
Middleware uses the Onion Model, allowing custom logic to be inserted before and after module execution:
Request → [MW1.before → [MW2.before → [MW3.before →
[Module.execute()]
← MW3.after] ← MW2.after] ← MW1.after] ← Response
class LoggingMiddleware(Middleware):
def before(self, module_id: str, inputs: dict, context: Context) -> dict:
log.info(f"Calling {module_id} with trace_id={context.trace_id}")
return inputs # Can modify inputs
def after(self, module_id: str, inputs: dict, output: dict, context: Context) -> dict:
log.info(f"Result from {module_id}: success")
return output # Can modify output
def on_error(self, module_id: str, inputs: dict, error: Exception, context: Context):
log.error(f"Error in {module_id}: {error}")
# Optional: Convert exception, trigger alerts, etc.Typical middleware scenarios: logging, performance monitoring, caching, rate limiting, retry, auditing.
The framework is centrally configured through apcore.yaml:
# apcore.yaml
version: "1.0.0"
project:
name: "my-ai-project"
version: "0.1.0"
# Module discovery (single root mode, backward compatible)
extensions:
root: "./extensions" # Module root directory
auto_discover: true # Auto-scan and discover
lazy_load: true # Lazy load (load module only on first call)
max_depth: 8 # Maximum directory depth
# Or: Multi-root mode (namespace isolation)
# extensions:
# roots:
# - root: "./extensions"
# namespace: "core" # Explicit namespace → core.executor.email.send_email
# - "./plugins" # Auto namespace → plugins.my_tool
# Schema loading
schema:
root: "./schemas"
strategy: "yaml_first" # yaml_first | native_first | yaml_only
validation:
strict: true # Strict validation mode
coerce_types: true # Automatic type coercion
# Access control
acl:
root: "./acl"
default_effect: "deny" # Default deny
audit:
enabled: true
# Logging
logging:
level: "info" # trace | debug | info | warn | error | fatal
format: "json" # json | text
# Observability
observability:
tracing:
enabled: true
sampling_rate: 1.0 # 1.0 = full collection, 0.1 = 10% sampling
exporter: "stdout" # stdout | otlp | jaeger
metrics:
enabled: true
exporter: "prometheus"
# Middleware
middleware:
- class: "myapp.middleware.LoggingMiddleware"
priority: 100
- class: "myapp.middleware.CachingMiddleware"
priority: 50
config:
ttl: 300apcore has built-in three pillars of observability, compatible with OpenTelemetry:
trace_idis automatically generated and propagated through the call chain- Span naming convention:
apcore.{component}.{operation} - Supports export to stdout / OTLP / Jaeger
- Structured logging, automatically includes
trace_id - Fields marked with
x-sensitiveare automatically redacted (e.g., passwords show as***REDACTED***) - Executor automatically provides
context.redacted_inputs, middleware and logs should use redacted data
| Metric Name | Type | Description |
|---|---|---|
apcore_module_calls_total |
Counter | Total module calls |
apcore_module_duration_seconds |
Histogram | Module execution duration distribution |
apcore_module_errors_total |
Counter | Total module errors |
apcore defines a unified error format and standard error codes:
{
"code": "SCHEMA_VALIDATION_ERROR",
"message": "Input validation failed",
"details": {"field": "email", "reason": "invalid format"},
"cause": null,
"trace_id": "abc-123",
"timestamp": "2026-01-01T00:00:00Z"
}| Category | Error Code | Description | Retryable |
|---|---|---|---|
| Module | MODULE_NOT_FOUND |
Module does not exist | No |
| Module | MODULE_EXECUTE_ERROR |
Execution exception | Depends |
| Module | MODULE_TIMEOUT |
Execution timeout | Yes |
| Schema | SCHEMA_VALIDATION_ERROR |
Input/output validation failed | No |
| Schema | SCHEMA_NOT_FOUND |
Schema file does not exist | No |
| ACL | ACL_DENIED |
Permission denied | No |
| Binding | BINDING_INVALID_TARGET |
Invalid binding target path | No |
| Binding | BINDING_CALLABLE_NOT_FOUND |
Bound callable object not found | No |
| Approval | APPROVAL_DENIED |
Approval explicitly denied | No |
| Approval | APPROVAL_TIMEOUT |
Approval request timed out | Yes |
| Approval | APPROVAL_PENDING |
Approval still pending | Yes |
| General | GENERAL_INTERNAL_ERROR |
Internal error | Yes |
apcore is a language-agnostic framework specification. Canonical IDs are automatically adapted to local naming conventions in different languages:
Canonical ID (universal): executor.email.send_email
Local representation:
Python: executor/email/send_email.py class SendEmailModule
Rust: executor/email/send_email.rs struct SendEmailModule
Go: executor/email/send_email.go type SendEmailModule
Java: executor/email/SendEmail.java class SendEmailModule
TypeScript: executor/email/sendEmail.ts class SendEmailModule
- Automatic language detection (based on file extension)
- Case conversion (PascalCase ↔ snake_case ↔ camelCase)
- Path separator normalization (
/vs::vs.) - Supports manual override (
id_mapconfiguration)
Any language SDK implementation can choose different conformance levels:
| Level | Scope | Includes |
|---|---|---|
| Level 0 (Core) | Minimally viable | ID mapping, Schema loading, Registry, Executor |
| Level 1 (Standard) | Production ready | + ACL, middleware, error handling, observability |
| Level 2 (Full) | Complete implementation | + Extension point framework, async task management, W3C Trace Context, Prometheus metrics, version negotiation, schema migration, module isolation, multi-version coexistence |
Reference Implementation: apcore-python
| apcore | MCP | |
|---|---|---|
| Positioning | Development framework | Communication protocol |
| Solves | How to build modules | How to call tools |
| Focus | Code organization, Schema, ACL, observability | Transport format, RPC |
| Relationship | apcore modules can be exposed as MCP Server | MCP is one exposure method |
| apcore | LangChain etc. | |
|---|---|---|
| Positioning | Module development framework | LLM application development framework |
| Focus | Module standardization, Schema, permissions | Chaining, Prompt, RAG |
| Relationship | Complementary — apcore modules can serve as LangChain Tools |
| apcore | CrewAI etc. | |
|---|---|---|
| Positioning | Module development framework | Agent orchestration framework |
| Focus | Standardizing individual modules | Multi-agent collaboration strategies |
| Relationship | Complementary — Agents can call apcore modules |
In short: apcore focuses on building standardized, AI-understandable modules, complementary rather than competitive with upper-layer AI protocols/frameworks.
Language SDK implementations of the apcore protocol specification:
| Language | Repository | Features | Install |
|---|---|---|---|
| Python | apcore-python | Schema validation, Registry, Executor, @module decorator, YAML bindings, ACL, Middleware, Observability, Async support | pip install apcore |
| Typescript | apcore-typescript | Schema validation, Registry, Executor, @module decorator, YAML bindings, ACL, Middleware, Observability, Async support | npm install apcore-js |
Interested in implementing apcore for another language? See the Protocol Specification and Conformance Definition.
The apcore ecosystem uses a core + independent adapters architecture. The core does not include any framework-specific implementations; adapters are developed in independent repositories by official or community contributors.
| Type | Examples | Description |
|---|---|---|
| Web Frameworks | nestjs-apcore, flask-apcore, express-apcore |
Expose modules as HTTP APIs |
| AI Protocols | apcore-mcp, apcore-openai-tools |
Expose modules as AI tools |
| RPC | apcore-grpc, apcore-thrift |
Expose modules as RPC services |
All adapters are built on the core's module() and External Binding mechanisms.
Development guide: see Adapter Development Guide.
| Document | Description |
|---|---|
| Protocol Specification | Complete framework specification (RFC 2119 Conformant) |
| Scope Definition | Responsibility boundaries (what's in/out of scope) |
| Document | Description |
|---|---|
| Core Concepts | Design philosophy and core concepts explained |
| Architecture Design | Internal architecture, component interaction, memory model |
| Document | Description |
|---|---|
| Module Interface | Module interface definition |
| Context Object | Execution context |
| Registry API | Registry API |
| Executor API | Executor API |
| Document | Description |
|---|---|
| ACL System | Pattern-based Access Control List with first-match-wins evaluation |
| Core Executor | Core execution engine with 10-step pipeline |
| Decorator & YAML Bindings | @module decorator and YAML-based module creation |
| Middleware System | Composable middleware pipeline with onion execution model |
| Observability | Distributed tracing, metrics, and structured logging |
| Registry System | Module discovery, registration, and querying |
| Schema System | Schema loading, validation, $ref resolution, and export |
| Approval System | Runtime enforcement of requires_approval via pluggable ApprovalHandler |
| Document | Description |
|---|---|
| Creating Modules | Module creation tutorial (including four approaches) |
| Schema Definition | Complete Schema usage |
| ACL Configuration | Access control configuration |
| Middleware | Middleware development |
| Adapter Development | Framework adapter development |
| Testing Modules | Module testing guide |
| Multi-Language Development | Cross-language development guide |
| Document | Description |
|---|---|
| Type Mapping | Cross-language type mapping |
| Conformance Definition | Implementation conformance levels |
| Algorithm Reference | Core algorithm summary (including namespace, redaction, etc.) |
Contributions are welcome in the following forms:
- Specification Feedback: Suggest improvements to the protocol specification in Issues
- SDK Implementation: Implement SDKs for other languages based on Protocol Specification
- Adapter Development: Develop adapters for web frameworks or AI protocols
- Documentation Improvements: Fix, translate, or supplement documentation
Apache 2.0