Page Menu
Home
VyOS Platform
Search
Configure Global Search
Log In
Files
F116353131
T9030-mcp-dynamic-schema.md
All Users
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
mihakralj
Jul 1 2026, 2:50 AM
2026-07-01 02:50:46 (UTC+0)
Size
9 KB
Referenced Files
None
Subscribers
None
T9030-mcp-dynamic-schema.md
View Options
#
T9030
:
Dynamic
Schema
Harvesting
&
Resources
|
Field
|
Value
|
|-------|-------|
|
Task
|
[
T9030
](
https
:
//vyos.dev/T9030) |
|
Status
|
Draft
/
design
|
|
Related
specs
|
[
Architecture
&
Transport
](
T9030
-
mcp
-
architecture
.
md
)
·
[
MCP
Tools
](
T9030
-
mcp
-
tools
.
md
)
·
[
Security
&
Concurrency
](
T9030
-
mcp
-
security
.
md
)
|
##
Principle
All
data
exposed
to
the
LLM
is
harvested
dynamically
from
the
VyOS
XML
schema
cache
.
No
tools
,
resources
,
or
schemas
are
hardcoded
.
When
VyOS
adds
new
features
(
EVPN
/
VXLAN
,
new
VPN
protocols
,
etc
.),
the
MCP
server
exposes
them
automatically
once
`
reftree
.
cache
`
is
rebuilt
at
image
-
build
time
.
##
Schema
Interrogation
The
`
vyos
.
xml_ref
`
library
(
`
python
/
vyos
/
xml_ref
/
`
)
is
the
single
source
of
truth
.
It
lazily
loads
the
compiled
reference
via
`
load_reference
()
`
and
exposes
the
following
public
functions
(
signatures
from
`
python
/
vyos
/
xml_ref
/
__init__
.
py
`
):
|
Function
|
Returns
|
Use
in
MCP
|
|----------|---------|------------|
|
`
cli_defined
(
path
,
node
,
non_local
=
False
)
`
|
`
bool
`
|
Validate
that
a
path
/
node
exists
before
staging
it
.
|
|
`
is_tag
(
path
)
`
|
`
bool
`
|
Mark
a
node
as
a
tag
node
(
named
instance
,
e
.
g
.
interface
name
).
|
|
`
is_tag_value
(
path
)
`
|
`
bool
`
|
Distinguish
the
tag
value
level
from
the
tag
node
.
|
|
`
is_multi
(
path
)
`
|
`
bool
`
|
Emit
a
JSON
array
vs
.
a
scalar
in
the
schema
projection
.
|
|
`
is_valueless
(
path
)
`
|
`
bool
`
|
Render
as
a
boolean
flag
(
no
value
expected
).
|
|
`
is_leaf
(
path
)
`
|
`
bool
`
|
Terminal
node
—
stop
schema
recursion
.
|
|
`
owner
(
path
,
with_tag
=
False
)
`
|
`
str
`
|
Identify
the
conf
-
mode
script
that
owns
the
subtree
.
|
|
`
default_value
(
path
)
`
|
`
str
\|
list
\|
None
`
|
Populate
`
default
`
in
the
JSON
Schema
.
|
|
`
get_defaults
(
path
,
get_first_key
=
False
,
recursive
=
False
)
`
|
`
dict
`
|
Bulk
default
expansion
for
a
subtree
.
|
|
`
component_version
()
`
|
`
dict
`
|
Surface
schema
/
component
versioning
to
clients
.
|
Config
nodes
resolve
against
`
vyos
.
xml_ref
.
cache
.
reference
`
;
op
-
mode
nodes
resolve
against
`
vyos
.
xml_ref
.
op_cache
.
op_reference
`
.
Both
are
generated
at
build
time
from
`
interface
-
definitions
/
`
and
`
op
-
mode
-
definitions
/
`
respectively
.
Each
node
entry
in
the
cache
carries
`
node_data
`
with
:
`
node_type
`
,
`
multi
`
,
`
valueless
`
,
`
default_value
`
,
`
owner
`
,
and
`
priority
`
—
these
are
the
raw
inputs
to
the
JSON
Schema
projection
below
.
##
Tool
Strategy
:
Meta
-
Tools
Instead
of
exposing
thousands
of
individual
tools
,
the
server
exposes
a
small
set
of
**
meta
-
tools
**
that
accept
string
arrays
of
config
paths
:
-
`
execute_operational_command
`
—
accepts
a
`
path
`
array
(
e
.
g
.,
`
[
"show"
,
"interfaces"
]
`
)
-
`
modify_configuration
`
—
accepts
an
`
operations
`
array
of
`
{
op
,
path
,
value
}
`
objects
The
LLM
learns
valid
paths
via
MCP
Resources
(
schema
introspection
)
rather
than
via
a
giant
tool
catalog
,
keeping
the
tool
list
compact
and
the
context
window
small
.
Full
tool
schemas
and
behavior
are
defined
in
[
T9030
-
mcp
-
tools
.
md
](
T9030
-
mcp
-
tools
.
md
).
##
Resource
URI
Scheme
Resources
expose
the
live
configuration
tree
and
operational
state
as
structured
JSON
via
resource
templates
.
The
LLM
requests
specific
slices
to
avoid
overwhelming
its
context
window
.
|
Resource
Template
|
Internal
Mechanism
|
Purpose
|
|------------------------------------|------------------------------------------------|--------------------------------------|
|
`
vyos
-
config
:
//running/{path}` | `Config.show_config(path)` | Active configuration subtree |
|
`
vyos
-
config
:
//effective/{path}` | `Config.exists_effective(path)` | Check if a node is effectively applied |
|
`
vyos
-
schema
:
//config/{path}` | `vyos.xml_ref.cli_defined()` + projection | JSON Schema of valid CLI nodes |
|
`
vyos
-
schema
:
//op/{path}` | `vyos.xml_ref.op_cache.op_reference` | Valid op-mode command continuations |
|
`
vyos
-
state
:
//operational/{path}` | `op_mode/*.py` script output | Operational state (routing, counters)|
###
Path
encoding
-
`
{
path
}
`
is
the
URL
-
encoded
,
slash
-
joined
CLI
path
(
`
vyos
-
config
:
//running/interfaces/ethernet/eth0`).
-
The
router
splits
`
{
path
}
`
into
a
list
,
URL
-
decodes
each
element
,
and
rejects
any
element
containing
shell
metacharacters
or
`
..
`
before
it
touches
a
VyOS
backend
(
defense
-
in
-
depth
in
addition
to
schema
validation
).
-
An
empty
`
{
path
}
`
addresses
the
root
of
the
respective
tree
.
###
Schema
Resource
(
`
vyos
-
schema
:
//`)
Resolves
the
"chicken-and-egg"
problem
:
the
LLM
must
understand
expected
syntax
before
constructing
configuration
payloads
.
Returns
dynamically
compiled
JSON
Schema
derived
from
XML
definitions
,
including
:
-
Node
existence
and
hierarchy
-
Validator
constraints
(
IPv4
format
,
MAC
format
,
integer
ranges
)
-
Multi
-
value
vs
.
scalar
indicators
(
`
is_multi
`
)
-
Valueless
flag
nodes
(
`
is_valueless
`
)
-
Tag
nodes
/
named
instances
(
`
is_tag
`
)
-
Default
values
(
`
default_value
`
)
####
Projection
algorithm
(
XML
reference
→
JSON
Schema
)
```
project
(
path
,
depth
=
1
):
if
is_leaf
(
path
):
node
=
{
"type"
:
"array"
if
is_multi
(
path
)
else
"string"
}
if
is_valueless
(
path
):
node
=
{
"type"
:
"boolean"
}
if
default_value
(
path
):
node
[
"default"
]
=
default_value
(
path
)
attach
validator
-
derived
constraints
(
pattern
/
range
/
enum
)
return
node
else
:
if
depth
<=
0
:
#
bound
the
payload
:
advertise
children
by
name
,
let
the
LLM
drill
down
return
{
"type"
:
"object"
,
"x-children"
:
children
(
path
)
}
properties
=
{}
for
child
in
children
(
path
):
properties
[
child
]
=
project
(
path
+
[
child
],
depth
-
1
)
if
is_tag
(
path
):
return
{
"type"
:
"object"
,
"additionalProperties"
:
project
(
tag_value_level
,
depth
-
1
)
}
return
{
"type"
:
"object"
,
"properties"
:
properties
}
```
The
`
depth
`
bound
is
a
defensive
guard
:
the
VyOS
schema
is
deep
(
interfaces
,
firewall
,
routing
),
and
an
unbounded
projection
at
the
tree
root
would
produce
a
multi
-
megabyte
response
that
blows
the
client
'
s
context
window
.
Responses
default
to
a
shallow
projection
(
`
depth
=
1
`
)
and
the
LLM
requests
deeper
`
{
path
}
`
s
to
drill
down
.
Validator
constraints
are
mapped
from
the
XML
`
<
constraint
>
`
elements
:
|
XML
constraint
|
JSON
Schema
|
|----------------|-------------|
|
`
<
validator
name
=
"numeric"
argument
=
"--range 1-256"
/>
`
|
`
"minimum"
:
1
,
"maximum"
:
256
`
|
|
`
<
regex
>...</
regex
>
`
|
`
"pattern"
:
"..."
`
|
|
`
<
completionHelp
><
list
>
key
token
</
list
></
completionHelp
>
`
|
`
"enum"
:
[
"key"
,
"token"
]
`
|
###
Op
-
mode
Schema
Resource
(
`
vyos
-
schema
:
//op/...`)
Mirrors
the
config
schema
resource
but
walks
`
op_reference
`
,
returning
the
set
of
valid
next
tokens
for
a
partial
op
-
mode
command
.
This
lets
the
LLM
construct
`
execute_operational_command
`
paths
without
guessing
.
###
Config
Resources
(
`
vyos
-
config
:
//`)
-
`
running
/{
path
}
`
:
mirrors
REST
`
/
retrieve
`
(
op
:
`
showConfig
`
);
backed
by
`
Config
.
show_config
(
path
)
`
(
JSON
format
).
-
`
effective
/{
path
}
`
:
mirrors
REST
`
/
retrieve
`
(
op
:
`
exists
`
);
backed
by
`
Config
.
exists_effective
(
path
)
`
.
-
Sanitizes
path
parameters
and
delegates
to
`
vyos
.
config
.
Config
`
objects
.
-
Returns
only
the
requested
subtree
,
not
the
entire
configuration
.
###
State
Resources
(
`
vyos
-
state
:
//`)
-
`
operational
/{
path
}
`
:
invokes
the
owning
`
op_mode
/*.py` script and returns its
structured output (preferring `--raw`/JSON where the script supports it), so
the LLM reads counters and routing state without an explicit tool call.
## Implementation
The `api.mcp.routers` / `api.mcp.resources` modules:
1. Register resource templates at startup using the SDK's resource-template
registration with the URI schemes above.
2. Intercept `resources/read` JSON-RPC requests.
3. Extract `{path}` from the URI, URL-decode, split, and **validate against the
`vyos.xml_ref` cache** (`cli_defined` for config, `op_reference` for op-mode).
4. Delegate to the VyOS config/op-mode backends (`Config`, op-mode scripts),
running blocking calls via `asyncio.to_thread()` (see
[Concurrency](T9030-mcp-security.md#concurrency--event-loop)).
5. Serialize the result as JSON resource content delivered over the Streamable HTTP
response.
### Caching & performance
- The reference cache is loaded once per process (memoized inside
`load_reference()` via its `cache=[]` default) — schema validation is an
in-memory dict walk, not a disk read.
- The JSON Schema projection for a given `{path}` is deterministic for the life
of the process and may be memoized; it is invalidated only on daemon restart
(i.e., a new image / schema).
- Resource reads never mutate state and are safe to serve concurrently.
- **Token optimization**: serialize responses as compact JSON to preserve the
client's context window and reduce token cost — use `json.dumps(obj,
separators=(',', ':'))`, omit keys whose value equals the schema default, and
strip repetitive boilerplate (e.g. redundant log preambles) from op-mode
output. Keep the output as valid JSON; do not invent a non-standard encoding
that MCP clients cannot parse.
### Failure modes
| Condition | Response |
|-----------|----------|
| `{path}` not in cache | JSON-RPC error: unknown resource path (the LLM self-corrects via the schema resource). |
| Path contains illegal characters | JSON-RPC error before any backend call. |
| Backend op-mode script error | Error string from `vyos.opmode` serialized into the JSON-RPC error. |
File Metadata
Details
Attached
Mime Type
text/plain
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3820422
Default Alt Text
T9030-mcp-dynamic-schema.md (9 KB)
Attached To
Mode
T9030: Native HTTPS Model Context Protocol (MCP) API Endpoint
Attached
Detach File
Event Timeline
Log In to Comment