Plugin Development — Your First VocabularyProviderPlugin¶
FCC's extensibility surface is eleven abstract base classes defined in
src/fcc/plugins/base.py. The newest, VocabularyProviderPlugin
(v1.2.1), is also the most common entry point for new integrators
because it is the bridge the sister projects use — Athenium,
Mnemosyne, AOME, CTO, and the ten constellation verticals all expose
their dataclass shapes to FCC's VocabularyMappingLoader through this
plugin type. This page walks you through writing one from scratch in
roughly thirty minutes.
What the plugin does¶
FCC ships 175 packaged YAML mapping files under
src/fcc/data/objectmodel/. Each file describes the FCC target side
of a cross-vocabulary relationship: it names the source namespace, the
source identifier, the target FCC concept, and the provenance. The
loader needs to verify, at runtime, that every source_id has a real
Python class behind it. A VocabularyProviderPlugin is that proof —
it returns a {source_id: python_class} map and declares the
namespace it owns.
The pattern is deliberately one-directional. FCC owns the mapping files; your sister project owns the source dataclass shapes; the plugin is the read-only bridge that lets FCC verify the contract without importing your project unconditionally.
The ABC you implement¶
The abstract base class is short. You implement three methods:
plugin_meta(), get_namespace(), and get_class_map(). All three
live in src/fcc/plugins/base.py starting at line 226.
# src/myproject/fcc_plugin.py
from __future__ import annotations
from fcc.plugins.base import (
PluginMeta,
PluginType,
VocabularyProviderPlugin,
)
class MyProjectVocabularyProvider(VocabularyProviderPlugin):
"""Bridge for the myproject namespace."""
def plugin_meta(self) -> PluginMeta:
return PluginMeta(
id="myproject-vocabulary",
name="MyProject Vocabulary Provider",
version="0.1.0",
plugin_type=PluginType.VOCABULARY_PROVIDERS,
description="Exposes MyProject dataclasses to FCC.",
author="MyProject Team",
source_package="myproject",
)
def get_namespace(self) -> str:
return "myproject"
def get_class_map(self) -> dict[str, type]:
try:
from myproject.models import Asset, Event, Measurement
except ImportError:
return {}
return {
"myproject:Asset": Asset,
"myproject:Event": Event,
"myproject:Measurement": Measurement,
}
Three observations:
- Graceful degradation. If
myprojectis not installed,get_class_mapreturns an empty dict. The loader treats that as "no coverage for this namespace" rather than a crash. - No
sys.pathhacks. The plugin imports its home package normally; FCC does not reach inside your code. - Frozen dataclass for metadata.
PluginMetais a frozen dataclass, not Pydantic, matching FCC's convention.
Declare the entry point¶
FCC discovers plugins through setuptools entry points. Add the
following to your project's pyproject.toml:
[project.entry-points."fcc.plugins.vocabulary_providers"]
myproject = "myproject.fcc_plugin:MyProjectVocabularyProvider"
The group name is fcc.plugins.vocabulary_providers; the key
(myproject) must be unique across all installed providers; the
value points at your ABC implementation.
Ship the companion YAML¶
In FCC (not in your project — FCC owns this file) you add a mapping
file at src/fcc/data/objectmodel/myproject_vocabulary_mappings.yaml
with entries like:
- source_vocabulary: myproject
source_id: myproject:Asset
target_concept: fcc:CoreAsset
notes: "Physical or digital asset tracked by MyProject."
provenance: "MyProject v0.9 domain model"
The source_id keys must exactly match the keys in your
get_class_map() return value. The VocabularyMappingLoader walks
both sides and reports mismatches as either missing_source_ids
(YAML entry with no Python class) or unmapped_classes (Python
class the YAML never names).
Verify the plugin is discovered¶
With both sides in place:
pip install -e . # in your project
pip install -e . # in FCC (to pick up the new YAML)
python -c "
from fcc.admin.readers import PluginReader
pr = PluginReader()
print(pr.load().get('fcc.plugins.vocabulary_providers', []))
print(pr.health())
"
Your plugin key should appear and its health should be "ok".
Best practices¶
- Version your plugin alongside your project. A plugin bump is MINOR; a breaking contract change is MAJOR.
- Keep the class map stable. Removing a key in a patch release is a semver violation. Add and deprecate; do not delete.
- Do not emit events from this plugin. Vocabulary providers are
read-only. If you need to react to runtime events, use a separate
EventSubscriberPlugin. - Test with FCC installed but your project NOT installed. The graceful-degradation path is the most commonly missed code path.
Related FCC documentation¶
- Testing Guide — how to test the plugin above.
- Integration Patterns — when
VocabularyProviderPluginis the right choice versus MCP, A2A, or a direct Python import. - Sample Prompts — Plugin Dev: Vocabulary Provider — six prompts that drive the FCC persona team through the same task.
- Sample Prompts — Developer Plugin Write — companion prompt for this page.
- Plugin Boundaries — the architectural boundary every plugin respects.
- Notebook
04_plugin_system.ipynb— plugin discovery lifecycle. - Notebook
34_developer_first_plugin.ipynb— interactive companion. - Book 2 Chapter 5: Plugin Development.