Apcore
Automatic MCP Server & OpenAI Tools Bridge for apcore
Ask AI about Apcore
Powered by Claude Β· Grounded in docs
I know everything about Apcore. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
apcore-mcp
Automatic MCP Server & OpenAI Tools Bridge for apcore.
Converts apcore module registries into Model Context Protocol (MCP) tool definitions and OpenAI-compatible function calling formats β zero boilerplate required.
Features
- MCP Server β Expose apcore modules as MCP tools over stdio, Streamable HTTP, or SSE
- OpenAI Tools β Convert modules to OpenAI function calling format with strict mode support
- Schema Conversion β Inline
$defs/$reffrom Pydantic-generated JSON Schema - Annotation Mapping β Map module annotations to MCP hints and OpenAI description suffixes
- Approval Mechanism β Built-in elicitation-based approval flow for sensitive tool executions
- Error Mapping β Sanitize internal errors for safe client-facing responses
- Dynamic Registration β Listen for registry changes and update tools at runtime
- Tool Explorer β Browser-based UI for browsing schemas and testing tools interactively
- CLI β Launch an MCP server from the command line
- Config Bus integration β Registers an
mcpnamespace with the apcore Config Bus; configure via unifiedapcore.yamlorAPCORE_MCP_*env vars - Error Formatter Registry β Registers an MCP-specific error formatter for ecosystem-wide consistent error handling
Documentation
For full documentation, including Quick Start guides for both Python and TypeScript, visit: https://aiperceivable.github.io/apcore-mcp/
Requirements
- Node.js >= 18.0.0
apcore-js >= 0.19.0
Installation
npm install apcore-mcp
apcore-js is included as a direct dependency β no separate install needed.
Quick Start
Programmatic API
import { serve, toOpenaiTools } from "apcore-mcp";
// Launch MCP server over stdio
await serve(executor);
// Launch over Streamable HTTP
await serve(executor, {
transport: "streamable-http",
host: "127.0.0.1",
port: 8000,
});
// Export OpenAI tool definitions
const tools = toOpenaiTools(registry, {
embedAnnotations: true,
strict: true,
});
CLI
# stdio (default)
npx apcore-mcp --extensions-dir ./extensions
# Streamable HTTP
npx apcore-mcp --extensions-dir ./extensions --transport streamable-http --port 8000
# SSE
npx apcore-mcp --extensions-dir ./extensions --transport sse --port 8000
CLI Arguments
| Argument | Default | Description |
|---|---|---|
--extensions-dir | (required) | Path to apcore extensions directory |
--transport | stdio | stdio, streamable-http, or sse |
--host | 127.0.0.1 | Host for HTTP transports |
--port | 8000 | Port for HTTP transports (1-65535) |
--name | apcore-mcp | MCP server name |
--version | package version | MCP server version |
--log-level | INFO | DEBUG, INFO, WARNING, ERROR |
--explorer | off | Enable the browser-based Tool Explorer UI (HTTP only) |
--explorer-prefix | /explorer | URL prefix for the explorer UI |
--allow-execute | off | Allow tool execution from the explorer UI |
--jwt-secret | β | JWT secret key for Bearer token authentication |
--jwt-key-file | β | Path to PEM key file for JWT verification (RS256/ES256) |
--jwt-algorithm | HS256 | JWT algorithm |
--jwt-audience | β | Expected JWT audience claim |
--jwt-issuer | β | Expected JWT issuer claim |
--jwt-require-auth | true | Require auth (use --jwt-permissive to override and allow unauthenticated requests) |
--jwt-permissive | false | Permissive mode: allow unauthenticated requests (overrides --jwt-require-auth) |
--exempt-paths | /health,/metrics,/usage | Comma-separated paths exempt from auth |
JWT key resolution priority: --jwt-key-file > --jwt-secret > APCORE_JWT_SECRET environment variable.
MCP Client Configuration
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"apcore": {
"command": "npx",
"args": ["apcore-mcp", "--extensions-dir", "/path/to/your/extensions"]
}
}
}
Claude Code
Add to .mcp.json in your project root:
{
"mcpServers": {
"apcore": {
"command": "npx",
"args": ["apcore-mcp", "--extensions-dir", "./extensions"]
}
}
}
Cursor
Add to .cursor/mcp.json in your project root:
{
"mcpServers": {
"apcore": {
"command": "npx",
"args": ["apcore-mcp", "--extensions-dir", "./extensions"]
}
}
}
Remote HTTP access
npx apcore-mcp --extensions-dir ./extensions \
--transport streamable-http \
--host 0.0.0.0 \
--port 9000
Connect any MCP client to http://your-host:9000/mcp.
API Reference
Programmatic API β APCoreMCP class
The APCoreMCP class is the recommended OOP entry point. It bundles a unified configuration object, lazy backend resolution (path / Registry / Executor), and exposes serve / asyncServe / toOpenaiTools as instance methods so you configure once and use everywhere.
import { APCoreMCP } from "apcore-mcp";
// 1. Point at an extensions directory (lazy discovery on first use)
const mcp = new APCoreMCP("./extensions", {
name: "my-server",
tags: ["public"],
observability: true,
});
// 2. Launch as MCP server (blocks until shutdown)
await mcp.serve({ transport: "streamable-http", port: 8000, explorer: true });
// 3. Or export OpenAI tool definitions
const tools = mcp.toOpenaiTools({ strict: true });
// 4. Or embed into an existing HTTP server
const app = await mcp.asyncServe({ explorer: true });
// app.handler is a Node.js request handler; call app.close() on shutdown
// 5. Or pass an existing Registry / Executor
import { Registry } from "apcore-js";
const registry = new Registry({ extensionsDir: "./extensions" });
await registry.discover();
const mcp2 = new APCoreMCP(registry, { name: "my-server", tags: ["public"] });
Constructor
new APCoreMCP(
extensionsDirOrBackend: string | Registry | Executor,
options?: APCoreMCPOptions,
);
The first argument is either a path to an apcore extensions directory (discovery is deferred to first use) or an existing Registry / Executor instance.
APCoreMCPOptions fields
nameβ MCP server name. Default:"apcore-mcp"versionβ MCP server version. Default: package versiontagsβ Filter modules by tag listprefixβ Filter modules by ID prefixlogLevelβ Minimum log level (DEBUG|INFO|WARNING|ERROR|CRITICAL)validateInputsβ Validate inputs against schemas. Default:falsemetricsCollectorβMetricsExporterortrueto auto-instantiateobservabilityβ Enable the full metrics + usage observability stackasyncβboolean | { enabled?, maxConcurrent?, maxTasks? }for the Async Task Bridge (F-043)authenticatorβ OptionalAuthenticator(HTTP transports only)requireAuthβ Iftrue(default), reject unauthenticated requests with 401exemptPathsβ Paths exempt from authenticationapprovalHandlerβ Optional approval handler passed to the ExecutoroutputFormatterβ Custom function to format tool execution resultsmiddlewareβ Array of apcoreMiddlewareinstalled viaexecutor.use()aclβ Optional apcoreACLinstance installed viaexecutor.setAcl()
Properties
.registryβ The underlying apcoreRegistry(resolved on first access).executorβ The underlying apcoreExecutor(populated afterserve()/asyncServe()).toolsβ List of discovered module IDs that will be exposed as tools (honourstags/prefix)
Methods
.serve(options?)β Launch an MCP server. AcceptsAPCoreMCPServeOptions:transport,host,port,onStartup,onShutdown,explorer,explorerPrefix,allowExecute,explorerTitle,explorerProjectName,explorerProjectUrl. Constructor-level options (auth, observability, middleware, acl, async, etc.) are applied automatically..asyncServe(options?)β Build an embeddable Node.js HTTP request handler. AcceptsAPCoreMCPAsyncServeOptions:explorer,explorerPrefix,allowExecute,explorerTitle,explorerProjectName,explorerProjectUrl,endpoint. Returns{ handler, close }..toOpenaiTools(options?)β Export modules as OpenAI-compatible tool definitions. AcceptsToOpenaiToolsOptions:embedAnnotations,strict.tags/prefixare inherited from the constructor.
serve(registryOrExecutor, options?)
Launch an MCP Server that exposes all apcore modules as tools.
function serve(
registryOrExecutor: Registry | Executor,
options?: {
// Transport
transport?: "stdio" | "streamable-http" | "sse";
host?: string;
port?: number;
// Identity
name?: string;
version?: string;
// Lifecycle
onStartup?: () => void | Promise<void>;
onShutdown?: () => void | Promise<void>;
// Module filtering / discovery
tags?: string[] | null;
prefix?: string | null;
dynamic?: boolean;
validateInputs?: boolean;
logLevel?: "DEBUG" | "INFO" | "WARNING" | "ERROR" | "CRITICAL";
// Async Task Bridge (F-043)
async?: boolean | { enabled?: boolean; maxConcurrent?: number; maxTasks?: number };
// Executor wiring
middleware?: unknown[];
acl?: unknown;
approvalHandler?: unknown;
strategy?: string;
// Observability (F-044)
metricsCollector?: MetricsExporter | boolean;
observability?: ObservabilityFlag;
trace?: boolean;
// Output handling
outputFormatter?: (result: Record<string, unknown>) => string;
redactOutput?: boolean;
// Auth (HTTP transports only)
authenticator?: Authenticator;
requireAuth?: boolean;
exemptPaths?: string[];
// Tool Explorer UI
explorer?: boolean;
explorerPrefix?: string;
allowExecute?: boolean;
explorerTitle?: string;
explorerProjectName?: string;
explorerProjectUrl?: string;
// Adapter overrides (advanced β Extension Bridge)
schemaConverter?: SchemaConverter;
annotationMapper?: AnnotationMapper;
errorMapper?: ErrorMapper;
}
): Promise<void>;
Options reference:
Transport
transportβ"stdio"(default),"streamable-http", or"sse"hostβ Host address for HTTP-based transports. Default:"127.0.0.1"portβ Port for HTTP-based transports. Default:8000
Identity
nameβ MCP server name. Default:"apcore-mcp"versionβ MCP server version. Default: package version
Lifecycle
onStartupβ Async callback invoked before the server startsonShutdownβ Async callback invoked after the server stops (or on error)
Module filtering / discovery
tagsβ Filter modules by tag list. Default:null(no filtering)prefixβ Filter modules by ID prefix. Default:null(no filtering)dynamicβ Enable dynamic tool registration viaRegistryListener. Default:falsevalidateInputsβ Validate inputs against schemas before dispatch. Default:falselogLevelβ Minimum log level. Suppresses console methods below this level
Async Task Bridge (F-043)
asyncβ Enable the AsyncTaskBridge and__apcore_task_*meta-tools. Passfalseto disable, or{ maxConcurrent, maxTasks }for fine-grained tuning. Default:true
Executor wiring
middlewareβ Array of apcoreMiddlewareinstances installed viaexecutor.use(). Appended to any middleware declared under Config Bus keymcp.middlewareaclβ Optional apcoreACLinstance installed viaexecutor.setAcl(). Caller-supplied ACL takes precedence overmcp.aclConfig Bus entryapprovalHandlerβ Optional approval handler passed to the Executor (e.g.ElicitationApprovalHandler)strategyβ Execution strategy name passed to the Executor (e.g."standard","internal")
Observability (F-044)
metricsCollectorβMetricsExporterinstance, ortrueto auto-instantiate apcore-js'sMetricsCollectorand installMetricsMiddlewareobservabilityβ Enable the full observability stack (metrics + usage middleware) and expose/metrics+/usageendpointstraceβ Whentrue, enables pipeline trace viacallWithTrace(). Adds_meta.traceto non-streaming tool responses. Default:false
Output handling
outputFormatterβ Custom function to format tool execution results. When undefined, results are serialized withJSON.stringify(result)redactOutputβ Whentrue(default), redact sensitive fields from tool output via apcore'sredactSensitive()before formatting
Auth (HTTP transports only)
authenticatorβAuthenticatorinstance for request authenticationrequireAuthβ Iftrue(default), unauthenticated requests are rejected with 401. Set tofalsefor permissive modeexemptPathsβ Paths exempt from authentication. Default:["/health", "/metrics"]
Tool Explorer UI
explorerβ Enable the browser-based Tool Explorer UI (HTTP only). Default:falseexplorerPrefixβ URL prefix for the explorer. Default:"/explorer"allowExecuteβ Allow tool execution from the explorer UI. Default:falseexplorerTitleβ Custom title for the Tool Explorer UI pageexplorerProjectNameβ Project name shown in the explorer UI footerexplorerProjectUrlβ Project URL shown in the explorer UI footer
Adapter overrides (advanced β Extension Bridge, F-042)
schemaConverterβ Override the defaultSchemaConverter(custom JSON Schema strictness/dialect)annotationMapperβ Override the defaultAnnotationMapper(custom annotation wire format)errorMapperβ Override the defaultErrorMapperconsumed byExecutionRouter
asyncServe(registryOrExecutor, options?)
Embed the MCP server into a larger Node.js HTTP application. Returns an HTTP request handler and a close function for lifecycle management.
import { asyncServe } from "apcore-mcp";
const { handler, close } = await asyncServe(executor, {
name: "apcore-mcp",
explorer: true,
allowExecute: true,
});
// Mount in a custom HTTP server
const server = http.createServer(handler);
server.listen(8000);
// Clean up when done
await close();
Accepts the same options as serve() except transport, host, port, onStartup, and onShutdown.
Tool Explorer
When explorer: true is passed to serve(), a browser-based Tool Explorer UI is mounted on HTTP transports. It provides an interactive page for browsing tool schemas and testing tool execution.
await serve(registry, {
transport: "streamable-http",
explorer: true,
allowExecute: true,
});
// Open http://127.0.0.1:8000/explorer/ in a browser
Endpoints:
| Endpoint | Description |
|---|---|
GET /explorer/ | Interactive HTML page (self-contained, no external dependencies) |
GET /explorer/tools | JSON array of all tools with name, description, annotations |
GET /explorer/tools/<name> | Full tool detail with inputSchema |
POST /explorer/tools/<name>/call | Execute a tool (requires allowExecute: true) |
- HTTP transports only (
streamable-http,sse). Silently ignored forstdio. - Execution disabled by default β set
allowExecute: trueto enable Try-it. - Custom prefix β use
explorerPrefix: "/browse"to mount at a different path. - Authorization UI β Swagger-UI-style Authorization input field. Paste a Bearer token to authenticate tool execution requests. Generated cURL commands automatically include the Authorization header.
JWT Authentication
apcore-mcp supports JWT Bearer token authentication for HTTP-based transports.
Programmatic Usage
import { serve, JWTAuthenticator } from "apcore-mcp";
const authenticator = new JWTAuthenticator({
key: "your-secret-key",
algorithms: ["HS256"],
audience: "my-app",
issuer: "auth-service",
// Map custom claims to Identity fields
claimMapping: {
id: "sub",
type: "type",
roles: "roles",
attrs: ["email", "org"], // Extra claims β Identity.attrs
},
// Claims that must be present in the token (default: ["sub"])
requireClaims: ["sub", "email"],
// Set to false for permissive mode (allow unauthenticated requests)
requireAuth: true,
});
await serve(executor, {
transport: "streamable-http",
authenticator,
// Custom exempt paths (default: ["/health", "/metrics"])
exemptPaths: ["/health", "/metrics", "/status"],
});
CLI Flags
| Flag | Default | Description |
|---|---|---|
--jwt-secret | β | JWT secret key for Bearer token authentication |
--jwt-key-file | β | Path to PEM key file for JWT verification |
--jwt-algorithm | HS256 | JWT algorithm |
--jwt-audience | β | Expected audience claim |
--jwt-issuer | β | Expected issuer claim |
--jwt-require-auth | true | Require auth. Use --jwt-permissive to allow unauthenticated requests |
--jwt-permissive | false | Overrides --jwt-require-auth and allows unauthenticated requests |
--exempt-paths | /health,/metrics,/usage | Comma-separated paths exempt from auth |
JWT key resolution priority: --jwt-key-file > --jwt-secret > APCORE_JWT_SECRET environment variable.
curl Examples
# Authenticated request
curl -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your-jwt-token>" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
# Health check (always exempt)
curl http://localhost:8000/health
toOpenaiTools(registryOrExecutor, options?)
Export apcore modules as OpenAI-compatible tool definitions.
function toOpenaiTools(
registryOrExecutor: Registry | Executor,
options?: {
embedAnnotations?: boolean;
strict?: boolean;
tags?: string[];
prefix?: string;
}
): OpenAIToolDef[];
Options:
embedAnnotationsβ Append annotation metadata to tool descriptions (default:false)strictβ Enable OpenAI strict mode: addsadditionalProperties: false, makes all properties required, wraps optional properties with nullable (default:false)tagsβ Filter modules by tagsprefixβ Filter modules by ID prefix
reportProgress(context, progress, total?, message?)
Report execution progress to the MCP client. No-ops silently when called outside an MCP context (no callback injected).
import { reportProgress } from "apcore-mcp";
// Inside a module's execute() method:
await reportProgress(context, 5, 10, "Processing item 5 of 10");
Parameters:
contextβ Object with adatadict (apcore Context or BridgeContext)progressβ Current progress valuetotalβ Optional total for percentage calculationmessageβ Optional human-readable progress message
elicit(context, message, requestedSchema?)
Ask the MCP client for user input via the elicitation protocol. Returns null when called outside an MCP context.
import { elicit } from "apcore-mcp";
import type { ElicitResult } from "apcore-mcp";
// Inside a module's execute() method:
const result: ElicitResult | null = await elicit(
context,
"Are you sure you want to proceed?",
{
type: "object",
properties: {
confirmed: { type: "boolean", description: "Confirm action" },
},
required: ["confirmed"],
},
);
if (result?.action === "accept") {
// User confirmed
}
Parameters:
contextβ Object with adatadict (apcore Context or BridgeContext)messageβ Message to display to the userrequestedSchemaβ Optional JSON Schema describing the expected input
Returns: ElicitResult with action ("accept", "decline", or "cancel") and optional content, or null if not in an MCP context.
Config Bus Integration
apcore-mcp registers an mcp namespace with the apcore Config Bus when serve() or asyncServe() is called. MCP settings can live alongside other apcore configuration in a single apcore.yaml:
apcore:
version: "1.0.0"
mcp:
transport: streamable-http
host: 0.0.0.0
port: 9000
explorer: true
require_auth: false
Environment variable overrides use the APCORE_MCP_ prefix:
APCORE_MCP_TRANSPORT=streamable-http
APCORE_MCP_PORT=9000
APCORE_MCP_EXPLORER=true
Defaults: transport=stdio, host=127.0.0.1, port=8000, explorer=false, require_auth=true.
The namespace, prefix, and defaults are also available as importable constants:
import { MCP_NAMESPACE, MCP_ENV_PREFIX, MCP_DEFAULTS, registerMcpNamespace } from "apcore-mcp";
Development
# Install dependencies
npm install
# Type check
npm run typecheck
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Build
npm run build
# Watch mode
npm run dev
License
Apache-2.0
