An MCP server that lets an AI model register its own tools at runtime and push the resulting configuration to Cisco infrastructure in the same atomic commit.
Normally an MCP server has a fixed tool list defined at startup. This one inverts that: the LLM sends code and a JSON Schema, the server validates and stores it, then notifies the client that the tool list changed. The client re-fetches and the new tool is immediately callable — no restart, no config file edit.
When a tool commits, the server fans out to Cisco backends: DNA Center, Meraki, NSO, IOS XE, or Webex. The AI describes intent; the server builds and deploys it atomically across both the AI context and the network.
The design mirrors NETCONF/YANG: candidate datastore for staging, commit to running, rollback to snapshots, lock/unlock for concurrency, and audit log with content hashes — the same control plane pattern Cisco uses for network device configuration, applied to AI toolchains.
Technology stack: Python 3.10+, MCP 2.1.1 (JSON-RPC 2.0 over stdio/SSE), stdlib urllib for all Cisco API calls. No external HTTP client dependencies.
Status: Beta
Network automation and AI toolchain management share a structural problem: both need to maintain a desired state, validate changes before applying them, and roll back safely when something breaks. Cisco solved this at the network layer with NETCONF/YANG. mcp-store-build applies the same pattern to AI agent toolchains.
Problem: AI agents built on MCP have static capability sets. Adding a new tool requires editing server code and restarting the process. There is no staging, no validation, no rollback — and no way to push the resulting configuration to the network layer in the same operation.
Solution: mcp-store-build makes the tool registry a first-class datastore. The LLM stages tool definitions into a candidate datastore, the server validates them against JSON Schema, and a commit operation pushes them to the running registry and fires notifications/tools/list_changed — the MCP protocol's built-in mechanism for telling a client its tool list has changed. The same commit fans out to Cisco DNA Center, Meraki, NSO, IOS XE, or Webex.
Outcomes:
- AI agents acquire new capabilities at runtime without restarting
- All tool registrations are schema-validated, sanitized, and audited before going live
- Configuration intent flows from the AI layer to Cisco infrastructure in a single operation
- Rollback to any prior registry snapshot is a single tool call
Clone the repo:
git clone https://github.com/sshpie/mcp-store-build.git
cd mcp-store-buildSet up a Python virtual environment:
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activateInstall dependencies:
pip install -r requirements.txtrequirements.txt contains:
mcp>=2.1.1
jsonschema>=4.0.0
Python 3.10 or later is required.
Cisco backend credentials are passed through the configure_backend tool at runtime — no credentials in source files or config files.
Webex notifications (optional) use environment variables:
export STORE_BUILD_WEBEX_TOKEN=<your-bot-token>
export STORE_BUILD_WEBEX_ROOM=<your-room-id>Per-adapter credential format:
| Adapter | Required fields |
|---|---|
dna_center |
host, token or (username + password), verify (bool, optional) |
meraki |
api_key, org_id (optional, auto-detected if omitted) |
nso |
host, username, password, port (default 8080), https (bool), verify (bool) |
ios_xe |
host, username, password, port (default 443), verify (bool) |
webex |
token, room_id (optional) |
Set verify: false for lab or sandbox environments with self-signed certificates.
Start the server:
# Secure mode — schema sanitization and validation active (recommended)
python server.py
# Insecure mode — disables sanitization, enables inject_schema_poison demo tool
python server.py --insecureClaude Desktop integration (claude_desktop_config.json):
{
"mcpServers": {
"store-build": {
"command": "/path/to/venv/bin/python",
"args": ["/path/to/mcp-store-build/server.py"]
}
}
}1. validate_tool — check schema and syntax before staging (optional)
2. configure_backend — register a Cisco backend for commit fan-out
3. build_tool — stage a Python primitive in the candidate datastore
4. commit_staged — push to running, fire notifications/tools/list_changed,
fan out to all configured Cisco backends
5. <new tool> — call the registered tool immediately
# Stage
build_tool({
"name": "sha256",
"description": "Returns the SHA256 hash of the input string.",
"code": "import hashlib\ndef main(args):\n return hashlib.sha256(args['text'].encode()).hexdigest()",
"parameters": {
"type": "object",
"properties": {"text": {"type": "string"}},
"required": ["text"]
}
})
→ [secure] Staged 'sha256' in candidate datastore (hash a3f1c2b4)
# Commit
commit_staged({})
→ Committed 1 tools to running: sha256
→ notifications/tools/list_changed sent
# Use it
sha256({"text": "hello"})
→ "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
# Idempotency — calling build_tool again with identical args is a no-op
build_tool({...same args...})
→ [no-op] 'sha256' unchanged (idempotent, hash a3f1c2b4)
configure_backend({
"adapter_type": "dna_center",
"config": {
"host": "sandboxdnac2.cisco.com",
"username": "devnetuser",
"password": "Cisco123!",
"verify": false
}
})
commit_staged({
"backend_config": {
"targets": [{"id": "device-uuid", "type": "MANAGED_DEVICE", "params": {}}]
}
})
→ Committed 1 tools to running: sha256
→ [dna_center] DNA Center deployment queued — taskId: abc123
| Tool | NETCONF analog | Description |
|---|---|---|
build_tool |
edit-config (candidate) |
Stage a Python primitive with validation and idempotency check |
configure_backend |
— | Register a Cisco backend for commit fan-out |
validate_tool |
<validate> RPC |
Schema meta-validation + syntax check, no side effects |
commit_staged |
commit |
Candidate → running, fires notifications/tools/list_changed, fans out to backends |
discard_staged |
discard-changes |
Clear candidate without modifying running |
list_registry |
get-config |
Show running or candidate datastore |
rollback |
rollback-N |
Revert running to previous snapshot |
lock_registry |
<lock> |
Block concurrent modifications |
unlock_registry |
<unlock> |
Release lock |
get_audit_log |
— | Full state-change log with timestamps and content hashes |
inject_schema_poison |
— | (insecure mode only) Context window hijacking demonstration |
All five Cisco adapters can be tested against DevNet Always-On sandboxes — no reservation required.
| Adapter | Sandbox | Credentials |
|---|---|---|
| DNA Center | sandboxdnac2.cisco.com | devnetuser / Cisco123! |
| Meraki | DevNet Meraki Sandbox | API key in developer portal |
| NSO | NSO on DevNet | See sandbox instructions |
| IOS XE | devnetsandboxiosxe.cisco.com | developer / C1sco12345 |
| Webex | developer.webex.com | Personal token |
See examples/ for runnable scripts against each sandbox.
- Introduction to NETCONF/YANG
- Cisco DNA Center REST API
- Model Context Protocol Overview
- Getting Started with the Meraki API
Parts of this exist in different places; this combination does not.
What exists:
notifications/tools/list_changedis in the MCP spec, but almost no servers use it for LLM-driven registration — most use it for static cases like toggling tools on auth state changes.- Tool-generating agents (AutoGen, CrewAI, LangChain) have patterns where an LLM generates Python functions and calls them in-process. These run inside the agent loop; they are not registered as MCP tools with schemas, and there is no staging/commit/rollback cycle.
- Hot-reload MCP servers watch a config file and reload on change. A human edits the file; the LLM does not push registrations.
What is different here:
- The LLM is the actor pushing tool registrations
- Candidate/running split with schema meta-validation before anything goes live
- Content-hash idempotency at the registration layer
- Rollback to snapshots on demand
- Commit fan-out to Cisco infrastructure in the same atomic operation
- NETCONF/YANG semantics applied to MCP tool management
LLM client (Claude, GPT, etc.)
|
| stdio / HTTP SSE (JSON-RPC 2.0)
|
┌────┴────────────────────────────────────────────────┐
│ store-build-mcp │
│ │
│ validate_tool → sanitizer + ast.parse │
│ │
│ build_tool → [SECURE gate] │
│ └─► validate_schema (JSON Schema meta-check) │
│ └─► sanitize_description / sanitize_schema │
│ └─► idempotency check (SHA-256 content hash) │
│ └─► registry.stage() → CANDIDATE datastore │
│ │
│ commit_staged → registry.commit_staged() │
│ └─► candidate → RUNNING datastore │
│ └─► notifications/tools/list_changed ──────────► │
│ └─► backend fan-out: │
│ ├─ DNA Center intent API │
│ ├─ Meraki Dashboard API │
│ ├─ NSO RESTCONF │
│ ├─ IOS XE RESTCONF │
│ └─ Webex notifications + webhooks │
│ │
│ primitives/ ◄── subprocess exec (sandboxed, 30s) │
└─────────────────────────────────────────────────────┘
- Primitives execute in a sandboxed subprocess with a 30-second timeout. Long-running computations will be killed. There is no persistent state between primitive invocations.
- The
ios_xeadapter falls back fromPATCHtoPUTwhen the device returns 405. Some older IOS XE versions do not support PATCH on all YANG paths. - The registry is in-memory only. Restarting the server clears all dynamically registered tools. Persistence to disk is not yet implemented.
- NSO RESTCONF commit-queue IDs are returned but not polled for completion. For synchronous confirmation, call
get_task_statuson the DNA Center adapter or check NSO directly.
Open an issue at github.com/sshpie/mcp-store-build/issues.
Include the server startup mode (--insecure or default), the tool call that produced the error, and the full error message from the server's stderr output.
Key areas for contribution:
- Persistence — serialize the running registry to disk on commit so restarts preserve dynamically registered tools
- Additional adapters — Cisco Intersight, Catalyst SD-WAN, SecureX
- RBAC — per-tool or per-adapter permission gates so different LLM agents have restricted commit scope
- NSO service model integration — map tool schemas to NSO service YANG models automatically
See CONTRIBUTING.md for development setup and the adapter authoring guide.
- NETCONF RFC 6241 — the candidate/running/commit model this mirrors
- RESTCONF RFC 8040 — used by the NSO and IOS XE adapters
- Cisco YANG Development Kit (YDK) — Python/C++/Go YANG bindings
- YANG Explorer — model compilation gate pattern used in sanitizer design
- MCP Specification — JSON-RPC 2.0 transport and
notifications/tools/list_changed
This code is licensed under the MIT License. See LICENSE for details.