VSS MCP Integration
Use this for sample-applications/video-search-and-summarization/mcp: the Search-mode MCP server that proxies selected VSS REST endpoints to agents via FastMCP.
Environment setup (run first)
This skill drives the Video Search & Summarization app through its real source files, so the VSS application must be present and you must run commands from its app root. Do this before anything else, and it works whether or not the VSS source is already in your workspace.
Run the bundled bootstrap. It first tries to find an existing VSS checkout -
walking up from the current directory and inspecting the enclosing git repo - and
reuses it without ever re-cloning. Only when no checkout is found does it do a
shallow, single-branch, sparse checkout of just
sample-applications/video-search-and-summarization from main. It prints the
resolved app root on stdout:
# SKILL_DIR is THIS skill's own directory (shown to you when the skill loads);
# in-repo it is .github/skills/vss-mcp-integration. Works the same if the skill is installed standalone.
SKILL_DIR=".github/skills/vss-mcp-integration"
APP_ROOT="$(bash "$SKILL_DIR/scripts/vss-bootstrap.sh")"
cd "$APP_ROOT"
Every command below assumes the working directory is this APP_ROOT. To pull
from a fork/branch or reuse a specific checkout dir, override VSS_REPO_URL,
VSS_REPO_BRANCH, or VSS_CLONE_DIR before running it.
Ground truth files
Read these before changing behavior: mcp/src/server.py, mcp/src/core/config.py, mcp/src/filters/config.py, mcp/src/openapi/{loader.py,mapping.py}, mcp/search.json, mcp/compose.yaml, mcp/.env.example, mcp/README.md, docs/user-guide/mcp-server.md, and mcp/tests/*.
How the proxy works
Startup path: src.main:main calls get_settings(), get_mcp(), then server.run(transport="streamable-http", host=MCP_HOST, port=MCP_PORT, path=MCP_PATH, stateless_http=MCP_STATELESS_HTTP).
create_mcp() in mcp/src/server.py:
- reads env into
Settings; - loads
FILTER_FILE_PATHwithload_filter_config(); - fetches the live VSS OpenAPI/Swagger JSON from
API_SPEC_URL; - creates an
httpx.AsyncClient(base_url=API_BASE_URL); - calls
FastMCP.from_openapi(...)with:mcp_names=build_mcp_names(spec, filter_config)route_map_fn=build_route_map_fn(filter_config)mcp_component_fn=build_component_fn(filter_config)
The filter is allow-list based. Operations not listed in apis are mapped to MCPType.EXCLUDE. This spec-driven design is why a path/method mismatch can look like a missing tool: the OpenAPI route is simply excluded rather than treated as an error.
Run and connect
From mcp/:
cp .env.example .env
docker compose up --build -d
Edit VSS_IP and HOST_IP first. Defaults: MCP server http://<HOST_IP>:8000/mcp; Inspector http://<HOST_IP>:6274. In Inspector, choose Streamable HTTP and connect to the MCP URL.
For local Poetry runs, set all required env vars explicitly:
cd sample-applications/video-search-and-summarization/mcp
API_SPEC_URL=http://<VSS_IP>:12345/manager/swagger/json \
API_BASE_URL=http://<VSS_IP>:12345/manager \
FILTER_FILE_PATH="$PWD/search.json" \
poetry run mcp-app
Real config keys
Required by mcp/src/core/config.py: API_SPEC_URL, API_BASE_URL, FILTER_FILE_PATH.
Optional: REQUEST_TIMEOUT default 60, LOG_LEVEL default INFO, MCP_HOST default 0.0.0.0, MCP_PORT default 8000, MCP_PATH default /mcp, MCP_STATELESS_HTTP default true.
Compose also uses VSS_IP, HOST_IP, INSPECTOR_CLIENT_PORT, DANGEROUSLY_OMIT_AUTH, MCP_PROXY_AUTH_TOKEN, and proxy vars.
Add or filter a tool/resource
Edit mcp/search.json or point FILTER_FILE_PATH to another JSON file. Format:
{
"server_name": "vss_search_mcp",
"prefix": "vss",
"apis": {
"POST /search/query": {
"type": "tool",
"name": "run_search_query",
"description": "Execute an immediate natural-language search."
}
}
}
Rules from mcp/src/filters/config.py:
- API keys must be exact canonical
"METHOD /path"entries matching the OpenAPI path, e.g."GET /videos/{videoId}". - Supported methods:
GET,PUT,POST,DELETE,PATCH,HEAD,OPTIONS; wildcards are rejected. typeis only"tool"or"resource"; resources must beGET.name,prefix, andserver_namemust be valid identifiers; final names are{prefix}_{name}.- Duplicate
namevalues are rejected across tools/resources. - Unknown fields such as old
expose/tool_nameare rejected.
Restart the MCP server after changing the filter; it reads the spec and filter at startup.
Debug missing tools
- Confirm the backend and spec URL work:
API_SPEC_URLmust return JSON withpaths. - Confirm
FILTER_FILE_PATHexists inside the process/container. Compose mounts./search.jsonto/app/search.json. - Compare the filter key with the live spec exactly: method and path must match, including
{param}names. - Check the endpoint has an OpenAPI
operationId;build_mcp_names()only renames filtered operations with operation IDs. - Check filter validation: resource on non-GET, duplicate names, invalid identifiers, wildcards, or extra fields fail startup.
- Run with
LOG_LEVEL=DEBUGto see[exclude],[tool],[resource], and rename logs frommcp/src/openapi/mapping.py. - Remember
POST /videosis intentionally not exposed in the user guide; uploads should use the REST API directly.
See references/mcp-spec-driven.md for the detailed spec/filter model and an example.