-
Notifications
You must be signed in to change notification settings - Fork 3
Add local mode guide for thv vmcp CLI commands #787
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,332 @@ | ||||||||||
| --- | ||||||||||
| title: Run vMCP locally with the CLI | ||||||||||
| sidebar_label: Local mode (CLI) | ||||||||||
| description: | ||||||||||
| Run Virtual MCP Server locally using the ToolHive CLI without a Kubernetes | ||||||||||
| cluster. | ||||||||||
| --- | ||||||||||
|
|
||||||||||
| Virtual MCP Server (vMCP) can run locally using the `thv vmcp` commands. This | ||||||||||
| lets you aggregate MCP servers on your laptop or CI environment without a | ||||||||||
| Kubernetes cluster. | ||||||||||
|
|
||||||||||
| ## Overview | ||||||||||
|
|
||||||||||
| `thv vmcp` has three subcommands: | ||||||||||
|
|
||||||||||
| | Command | Purpose | | ||||||||||
| | ------------------- | --------------------------------------------------- | | ||||||||||
| | `thv vmcp serve` | Start the vMCP server | | ||||||||||
| | `thv vmcp init` | Generate a starter config file from a running group | | ||||||||||
| | `thv vmcp validate` | Check a config file for errors | | ||||||||||
|
|
||||||||||
| ## Quick start (zero-config) | ||||||||||
|
|
||||||||||
| If you already have MCP servers running in a ToolHive group, you can start vMCP | ||||||||||
| with a single command. No config file needed: | ||||||||||
|
|
||||||||||
| ```bash | ||||||||||
| thv vmcp serve --group my-group | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| This starts a vMCP server on `127.0.0.1:4483` that aggregates all servers in the | ||||||||||
| group `my-group`. The server is accessible only from localhost — quick mode does | ||||||||||
| not allow binding to a non-loopback interface because it uses anonymous | ||||||||||
| authentication. | ||||||||||
|
|
||||||||||
| :::note | ||||||||||
|
|
||||||||||
| Quick mode uses anonymous auth by default. Anyone with local access can connect. | ||||||||||
| For access control, use [config file mode](#config-file-mode) with OIDC auth. | ||||||||||
|
|
||||||||||
| ::: | ||||||||||
|
|
||||||||||
| To use a different port: | ||||||||||
|
|
||||||||||
| ```bash | ||||||||||
| thv vmcp serve --group my-group --port 8080 | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| ## Config file mode | ||||||||||
|
|
||||||||||
| For production-like setups, authentication, or advanced features, use a config | ||||||||||
| file. | ||||||||||
|
|
||||||||||
| ### Generate a starter config | ||||||||||
|
|
||||||||||
| Run `thv vmcp init` to generate a config file from a running ToolHive group: | ||||||||||
|
|
||||||||||
| ```bash | ||||||||||
| thv vmcp init --group my-group --config vmcp.yaml | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| This discovers the MCP servers in `my-group` and writes a ready-to-use YAML file | ||||||||||
| to `vmcp.yaml`. The generated file includes one backend entry per server, inline | ||||||||||
| comments explaining each field, and sensible defaults. | ||||||||||
|
|
||||||||||
| ### Start the server | ||||||||||
|
|
||||||||||
| ```bash | ||||||||||
| thv vmcp serve --config vmcp.yaml | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| ### Validate before starting | ||||||||||
|
|
||||||||||
| Check a config file for errors without starting the server: | ||||||||||
|
|
||||||||||
| ```bash | ||||||||||
| thv vmcp validate --config vmcp.yaml | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| Exit code `0` means the file is valid; any other exit code prints a description | ||||||||||
| of the error. | ||||||||||
|
|
||||||||||
| ## Config file reference | ||||||||||
|
|
||||||||||
| Below is a minimal config file. See the sections that follow for each feature | ||||||||||
| area. | ||||||||||
|
|
||||||||||
| ```yaml title="vmcp.yaml" | ||||||||||
| name: my-vmcp | ||||||||||
| groupRef: my-group | ||||||||||
|
|
||||||||||
| incomingAuth: | ||||||||||
| type: anonymous | ||||||||||
|
|
||||||||||
| aggregation: | ||||||||||
| conflictResolution: prefix | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| ### Backends | ||||||||||
|
|
||||||||||
| vMCP discovers backends automatically from the group named in `groupRef`. You | ||||||||||
| can also declare backends explicitly when you need to point to a URL that isn't | ||||||||||
| managed by ToolHive: | ||||||||||
|
|
||||||||||
| ```yaml title="vmcp.yaml" | ||||||||||
| backends: | ||||||||||
| - name: fetch | ||||||||||
| url: http://127.0.0.1:8081/mcp | ||||||||||
| transport: streamable-http | ||||||||||
| - name: osv | ||||||||||
| url: http://127.0.0.1:8082/mcp | ||||||||||
| transport: streamable-http | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| When you provide an explicit `backends` list, vMCP uses that list instead of | ||||||||||
| discovering from `groupRef`. The `name` values must match the workload names | ||||||||||
| used in aggregation rules, conflict resolution prefixes, and composite tool | ||||||||||
| steps. | ||||||||||
|
|
||||||||||
| ### Conflict resolution | ||||||||||
|
|
||||||||||
| When two backends expose a tool with the same name, vMCP needs to know how to | ||||||||||
| handle the collision. Set `conflictResolution` in the `aggregation` block: | ||||||||||
|
|
||||||||||
| ```yaml title="vmcp.yaml" | ||||||||||
| aggregation: | ||||||||||
| conflictResolution: prefix | ||||||||||
| conflictResolutionConfig: | ||||||||||
| prefixFormat: '{workload}_' # produces: fetch_fetch_content, osv_query | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| | Strategy | Behavior | | ||||||||||
| | ---------- | ----------------------------------------------------------------- | | ||||||||||
| | `prefix` | Prepend the backend name to each tool name (default) | | ||||||||||
| | `priority` | First backend in the list wins; duplicates from others are hidden | | ||||||||||
| | `manual` | Explicit per-conflict mapping | | ||||||||||
|
|
||||||||||
| ### Incoming authentication | ||||||||||
|
|
||||||||||
| Control how clients authenticate to vMCP. | ||||||||||
|
|
||||||||||
| **Anonymous** (development only): | ||||||||||
|
|
||||||||||
| ```yaml title="vmcp.yaml" | ||||||||||
| incomingAuth: | ||||||||||
| type: anonymous | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| **OIDC** (recommended for shared or production use): | ||||||||||
|
|
||||||||||
| ```yaml title="vmcp.yaml" | ||||||||||
| incomingAuth: | ||||||||||
| type: oidc | ||||||||||
| oidc: | ||||||||||
| issuer: https://your-idp.example.com | ||||||||||
| clientId: vmcp-server | ||||||||||
| audience: vmcp-api | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| The OIDC middleware validates the `iss`, `aud`, and expiry claims on every | ||||||||||
| request. Set `insecureAllowHttp: true` and `jwksAllowPrivateIp: true` only when | ||||||||||
| testing against a local OIDC server. | ||||||||||
|
|
||||||||||
| For the full set of OIDC fields see [Authentication](./authentication.mdx). | ||||||||||
|
||||||||||
| For the full set of OIDC fields see [Authentication](./authentication.mdx). | |
| For general vMCP authentication and Kubernetes-based OIDC configuration, see | |
| [Authentication](./authentication.mdx). The local CLI configuration supports the | |
| fields shown above, including the local-testing flags. |
Copilot
AI
Apr 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid using em dashes (—) in headings; the writing guidelines recommend rephrasing or using a hyphenated alternative.
| **Tier 1 — keyword search** (no extra dependencies): | |
| **Tier 1 - keyword search** (no extra dependencies): |
Copilot
AI
Apr 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid using em dashes (—) in headings; the writing guidelines recommend rephrasing or using a hyphenated alternative.
| **Tier 2 — semantic search** (requires Docker; starts a TEI container | |
| **Tier 2 - semantic search** (requires Docker; starts a TEI container |
Copilot
AI
Apr 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This page is a how-to guide but it ends without a "Next steps" section. The repo’s doc standards require 1–3 links to the next logical pages so readers know where to go next (for example: Configuration, Authentication, or Optimize tool discovery).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid using em dashes (—) in docs per repo writing guidelines. Rephrase this sentence using commas/parentheses or split into two sentences.