How Capture Adapters Are Declared in Claude-Obsidian: The Complete Guide
Capture adapters in claude-obsidian are declared in a centralized JSON manifest at config/adapters.json that follows the claude-obsidian.capture-adapters.v1 schema, defining each adapter's maturity state, input constraints, and execution requirements.
The claude-obsidian project uses a declarative configuration system to govern how content is ingested from diverse sources. By understanding how capture adapters are declared, developers can extend the tool to support new file formats and external resources without modifying the core runtime logic.
The Adapter Manifest Structure
All capture adapter definitions reside in the single JSON file located at config/adapters.json. This manifest follows the schema claude-obsidian.capture-adapters.v1 and establishes a privacy default alongside structured metadata that describes adapter capabilities and security constraints.
Maturity States
The manifest classifies each adapter into one of three maturity states that indicate implementation completeness:
implemented– Runs deterministically inside the portable standard-library core without external dependencies.metadata-only– The core recognizes the input type and can describe it, but does not extract full content.external-runner-required– Execution requires a separate, consent-gated runner (for example, network fetches).
Required and Optional Fields
Each adapter entry in the manifest is a JSON object containing the following fields:
| Field | Description |
|---|---|
id |
Symbolic identifier used on the CLI (e.g., filesystem, url). |
maturity |
One of the three maturity states defined above. |
input |
Type of input expected: local-file or https-url. |
execution |
Runtime model: internal or planned-command. |
network |
Boolean indicating if network access is required. |
llm |
Boolean indicating if the adapter calls an LLM directly. |
destructive |
Boolean indicating if the adapter can modify source data. |
extensions (optional) |
Array of accepted file extensions (e.g., [".md", ".txt"]). |
runner_capability (optional) |
Capability identifier required from external runners (e.g., fetch-https-with-pinned-dns-and-redirect-policy). |
approved_hosts (optional) |
Whitelist of hostnames for URL adapters (e.g., ["youtube.com", "youtu.be"]). |
Loading Adapters at Runtime
The core reads the manifest at startup using the project's generic JSON loader. In claude_obsidian/hook_adapter.py, these definitions are exposed to the Claude Code lifecycle, managing context feeding and runtime status.
When a user invokes a capture command, the CLI entry point in scripts/claude-obsidian.py looks up the requested id in the manifest, validates the supplied input against the declared extensions or URL schema, and dispatches either built-in logic (internal) or schedules a planned external command using the declared runner_capability.
import json
from pathlib import Path
ADAPTERS_PATH = Path(__file__).parent.parent / "config" / "adapters.json"
with ADAPTERS_PATH.open() as f:
manifest = json.load(f)
# Build a dict keyed by adapter id for quick lookup
adapters = {a["id"]: a for a in manifest["adapters"]}
# Example: retrieve the filesystem adapter definition
fs_adapter = adapters["filesystem"]
print(fs_adapter["extensions"])
# → ['.md', '.markdown', '.txt', '.json', …]
Declaring a New Capture Adapter
To add support for a new input type, append an adapter object to the "adapters" array in config/adapters.json. The CLI automatically recognizes new entries without requiring code changes.
For example, to add support for .mdx files:
{
"id": "mdx",
"maturity": "implemented",
"input": "local-file",
"execution": "internal",
"network": false,
"llm": false,
"destructive": false,
"extensions": [".mdx"]
}
After adding this configuration, the command claude-obsidian capture --adapter mdx ./file.mdx will function immediately.
CLI Usage and Execution Models
The manifest drives the capture pipeline by determining whether to execute logic internally or delegate to an external runner.
# Capture a local markdown file using the built-in filesystem adapter
claude-obsidian capture --adapter filesystem /path/to/notes.md
# Capture a YouTube video's metadata via the external runner
claude-obsidian capture --adapter youtube https://youtu.be/dQw4w9WgXcQ
Adapters with "execution": "internal" run within the standard library core, while those marked "external-runner-required" trigger the consent-gated runner system defined by their runner_capability field.
Summary
- Capture adapters are declared centrally in
config/adapters.jsonusing the schemaclaude-obsidian.capture-adapters.v1. - Each adapter specifies a maturity state (
implemented,metadata-only, orexternal-runner-required) that determines runtime behavior. - Required fields include
id,input,execution, and boolean flags fornetwork,llm, anddestructivecapabilities. - The core loads the manifest at runtime via
claude_obsidian/hook_adapter.py, whilescripts/claude-obsidian.pyhandles CLI argument parsing and dispatch. - New adapters require only JSON configuration changes, not core code modifications, enabling rapid extension of supported input types.
Frequently Asked Questions
Where exactly are capture adapters declared in claude-obsidian?
Capture adapters are declared in the JSON manifest file located at config/adapters.json in the repository root. This single file serves as the definitive registry for all adapter capabilities and constraints.
What determines whether an adapter runs internally or externally?
The execution field in the adapter declaration controls this behavior. Value internal routes execution through the standard library core, while planned-command defers to an external runner identified by the optional runner_capability field.
Can I add support for new file extensions without modifying Python code?
Yes. Append a new adapter object to the "adapters" array in config/adapters.json with the appropriate extensions list and maturity state. The CLI in scripts/claude-obsidian.py automatically detects and loads new adapters from the manifest without requiring code changes.
What is the difference between metadata-only and implemented maturity states?
metadata-only adapters recognize input types and describe them but do not extract full content, functioning as placeholders or partial implementations. implemented adapters run deterministically within the core and perform complete content extraction.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →