store-build-mcp
by sshpie
README.md
# mcp-store-build — MCP Server with Runtime Tool Registration and Cisco Backend Fan-out
<div align="center">
<img src="https://img.shields.io/badge/Python-3.10+-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python 3.10+">
<img src="https://img.shields.io/badge/MCP-2.1.1-blueviolet?style=flat-square" alt="MCP 2.1.1">
<img src="https://img.shields.io/badge/Cisco-NETCONF%2FYANG-1BA0D7?style=flat-square&logo=cisco&logoColor=white" alt="NETCONF/YANG">
<img src="https://img.shields.io/badge/license-MIT-brightgreen?style=flat-square" alt="MIT">
</div>
<br />
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
---
## Use Case
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
---
## Installation
Clone the repo:
```bash
git clone https://github.com/sshpie/mcp-store-build.git
cd mcp-store-build
```
Set up a Python virtual environment:
```bash
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
```
Install dependencies:
```bash
pip install -r requirements.txt
```
`requirements.txt` contains:
```
mcp>=2.1.1
jsonschema>=4.0.0
```
Python 3.10 or later is required.
---
## Configuration
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:
```bash
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.
---
## Usage
Start the server:
```bash
# Secure mode — schema sanitization and validation active (recommended)
python server.py
# Insecure mode — disables sanitization, enables inject_schema_poison demo tool
python server.py --insecure
```
**Claude Desktop integration** (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"store-build": {
"command": "/path/to/venv/bin/python",
"args": ["/path/to/mcp-store-build/server.py"]
}
}
}
```
### Workflow
```
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
```
### Example: Register a tool at runtime
```
# 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)
```
### Example: Fan out to DNA Center
```
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
```
### Built-in Tools
| 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 |
---
## Related Sandbox
All five Cisco adapters can be tested against DevNet Always-On sandboxes — no reservation required.
| Adapter | Sandbox | Credentials |
|---|---|---|
| DNA Center | [sandboxdnac2.cisco.com](https://devnetsandbox.cisco.com/DevNet/catalog/dna-center-always-on) | devnetuser / Cisco123! |
| Meraki | [DevNet Meraki Sandbox](https://developer.cisco.com/meraki/meraki-platform/) | API key in developer portal |
| NSO | [NSO on DevNet](https://developer.cisco.com/site/nso/) | See sandbox instructions |
| IOS XE | [devnetsandboxiosxe.cisco.com](https://devnetsandbox.cisco.com/DevNet/catalog/ios-xe-always-on) | developer / C1sco12345 |
| Webex | [developer.webex.com](https://developer.webex.com) | Personal token |
See `examples/` for runnable scripts against each sandbox.
---
## Links to DevNet Learning Labs
- [Introduction to NETCONF/YANG](https://developer.cisco.com/learning/modules/intro-device-level-interfaces)
- [Cisco DNA Center REST API](https://developer.cisco.com/learning/modules/dnac-rest-apis)
- [Model Context Protocol Overview](https://modelcontextprotocol.io/introduction)
- [Getting Started with the Meraki API](https://developer.cisco.com/meraki/meraki-platform/)
---
## Prior Art
Parts of this exist in different places; this combination does not.
**What exists:**
- `notifications/tools/list_changed` is 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
---
## The Architecture
```
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) │
└─────────────────────────────────────────────────────┘
```
---
## Known Issues
- 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_xe` adapter falls back from `PATCH` to `PUT` when 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_status` on the DNA Center adapter or check NSO directly.
---
## Getting Help
Open an issue at [github.com/sshpie/mcp-store-build/issues](https://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.
---
## Getting Involved
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](./CONTRIBUTING.md) for development setup and the adapter authoring guide.
---
## Credits and References
- [NETCONF RFC 6241](https://datatracker.ietf.org/doc/html/rfc6241) — the candidate/running/commit model this mirrors
- [RESTCONF RFC 8040](https://datatracker.ietf.org/doc/html/rfc8040) — used by the NSO and IOS XE adapters
- [Cisco YANG Development Kit (YDK)](https://developer.cisco.com/site/ydk/) — Python/C++/Go YANG bindings
- [YANG Explorer](https://github.com/CiscoDevNet/yang-explorer) — model compilation gate pattern used in sanitizer design
- [MCP Specification](https://modelcontextprotocol.io/specification) — JSON-RPC 2.0 transport and `notifications/tools/list_changed`
---
## License
This code is licensed under the MIT License. See [LICENSE](./LICENSE) for details.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues