REST API server and MCP protocol integration
52
58%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./.ai-rulez/skills/api-server-mcp/SKILL.mdAxum server for document extraction, plus the rmcp Model Context Protocol surface
Locations: crates/xberg/src/api/ (router.rs, handlers.rs, startup.rs, types.rs,
error.rs, jobs.rs) and crates/xberg/src/mcp/. There is no api/server.rs.
Registered in api/router.rs, all on one Router:
| Route | Handler |
|---|---|
POST /extract | extract_handler — multipart files, URL fields, or JSON; builds ExtractInput |
POST /extract-async | extract_async_handler — queues a job |
GET/DELETE /jobs/{job_id} | job_status_handler / cancel_job_handler |
POST /detect | detect_handler |
GET /formats | formats_handler |
GET /health | health_handler |
GET /info, GET /version | info_handler, version_handler |
GET /cache/stats | cache_stats_handler |
DELETE /cache/clear | cache_clear_handler |
GET /cache/manifest, POST /cache/warm | cache_manifest_handler, cache_warm_handler |
PUT /process, POST /v1/convert/file | openweb_external_handler, openweb_docling_handler (api/openweb.rs) |
GET /openapi.json | openapi_schema_handler (feature api) |
GET /metrics | metrics_handler (feature prometheus) |
There is no POST /extract-url (URL ingestion is a field on ExtractInput passed to
/extract) and no POST /batch (batch is /extract-async + /jobs/{job_id}).
Middleware, in order: DefaultBodyLimit::max(limits.max_request_body_bytes) +
RequestBodyLimitLayer (default 100 MB), CORS, request-id, compression, catch-panic,
sensitive-header stripping, tracing. CORS is built explicitly as
CorsLayer::new().allow_origin(Any).allow_methods(Any).allow_headers(Any) and warns loudly;
restrict it with XBERG_CORS_ORIGINS.
crates/xberg/src/cache/ — GenericCache is a filesystem-backed store with LRU-style
eviction, not an in-memory map. Keys are BLAKE3 content hashes (blake3_hash_bytes /
blake3_hash_file, cache/utilities.rs). Eviction is bounded by max_age_days,
max_cache_size_mb and min_free_space_mb — there is no entry-count limit.
ApiError is a struct, not an enum: { status: StatusCode, body: ErrorResponse }
(api/error.rs). Status comes from the constructor, not a variant:
validation() → 400, unprocessable() → 422, internal() → 500, bad_gateway() → 502From<XbergError> picks one via error.api_status_category()
(Validation / Unprocessable / Internal)There is no 404, 413 or 503 path with a named variant. Do not match on ApiError.
crates/xberg/src/mcp/. Transport is a single nested rmcp streamable-HTTP service —
Router::new().nest_service("/mcp", http_service) — not a set of /mcp/* REST paths.
Tools, resources and prompts are JSON-RPC methods on it. Stdio transport serves the same
router over stdin/stdout.
extract, extract_batch, detect_mime_type, list_formats, cache_stats, cache_clear,
get_version, cache_manifest, cache_warm. The set is pinned by
test_all_tools_are_registered in mcp/server.rs. There is no get_capabilities.
extract, extract_batch and cache_warm are task-eligible (TASK_ELIGIBLE_TOOLS,
mcp/server.rs).
mcp/resources.rs: xberg://formats, xberg://models, xberg://languages/ocr, plus
xberg://presets/embeddings behind #[cfg(feature = "embeddings")].
mcp/prompts.rs: extract_document, extract_with_ocr, semantic_search.
There is no .env.example. Server-side vars are read in core/server_config/env.rs:
XBERG_HOST, XBERG_PORT (defaults 127.0.0.1:8000)XBERG_MAX_REQUEST_BODY_BYTES, XBERG_MAX_MULTIPART_FIELD_BYTES (both default 100 MB)XBERG_CORS_ORIGINS (comma-separated)Extraction-side vars are documented on ExtractionConfig::apply_env_overrides
(core/config/extraction/env.rs) — XBERG_OCR_BACKEND, XBERG_OCR_LANGUAGE,
XBERG_CHUNKING_MAX_CHARS, XBERG_CACHE_ENABLED, XBERG_LLM_*, and others. Read that doc
comment rather than guessing a name.
limits.max_request_body_bytes, never hardcode.ErrorResponse.XBERG_CORS_ORIGINS./extract-async — do not block a request thread on a multi-minute extraction.mcp/server.rs and extend test_all_tools_are_registered — the test is the contract.xberg://presets/embeddings is — a missing feature must not break
resources/list.04336bd
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.