diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index d5f664c6..d89f73fd 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -107,6 +107,7 @@ Common terms used in this project: - open source (not "open-source") - large language model (LLM) - Visual Studio Code ("VS Code" after first use) +- Virtual MCP Server (vMCP) - a feature of ToolHive that aggregates multiple MCP servers into a single endpoint; use "Virtual MCP Server (vMCP)" on first use, "vMCP" thereafter Check this list for consistent use within the documentation. If you find inconsistencies, update the text to match the preferred term. If you find a term that is not listed here, consider adding it to the list for future reference. diff --git a/AGENTS.md b/AGENTS.md index f619dc40..93567853 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -107,6 +107,7 @@ Common terms used in this project: - open source (not "open-source") - large language model (LLM) - Visual Studio Code ("VS Code" after first use) +- Virtual MCP Server (vMCP) - a feature of ToolHive that aggregates multiple MCP servers into a single endpoint; use "Virtual MCP Server (vMCP)" on first use, "vMCP" thereafter Check this list for consistent use within the documentation. If you find inconsistencies, update the text to match the preferred term. If you find a term that is not listed here, consider adding it to the list for future reference. diff --git a/STYLE-GUIDE.md b/STYLE-GUIDE.md index 6c6c051f..8e8a08d3 100644 --- a/STYLE-GUIDE.md +++ b/STYLE-GUIDE.md @@ -263,6 +263,11 @@ all caps. Written out, it is lower-cased. applications provide context to LLMs. MCP is an abbreviation, so it's written in all caps. Written out, it is proper-cased. +**vMCP**: Virtual MCP Server, a feature of ToolHive that aggregates multiple MCP +servers into a single endpoint. It's written with a lowercase "v" followed by +"MC" in all caps and a capital "P" (not "VMCP" or "Vmcp"). Use "Virtual MCP +Server (vMCP)" on first use, "vMCP" thereafter. + **npm**: the registry for JavaScript packages (the "npm registry"), and the default package manager for JavaScript. Since it's both the registry _and_ the package manager, it may be useful to disambiguate "the npm registry". It's not diff --git a/docs/toolhive/concepts/vmcp-architecture.mdx b/docs/toolhive/concepts/vmcp.mdx similarity index 64% rename from docs/toolhive/concepts/vmcp-architecture.mdx rename to docs/toolhive/concepts/vmcp.mdx index 7c846686..6b77b38c 100644 --- a/docs/toolhive/concepts/vmcp-architecture.mdx +++ b/docs/toolhive/concepts/vmcp.mdx @@ -1,12 +1,14 @@ --- title: Understanding Virtual MCP Server +sidebar_label: Virtual MCP Server (vMCP) description: Learn what Virtual MCP Server does, why it exists, and when to use it. --- -This document explains Virtual MCP Server (vMCP), why it exists, and when you -should use it. You'll learn how vMCP simplifies managing multiple MCP servers -and enables powerful multi-system workflows. +This document explains Virtual MCP Server (vMCP), a feature of the ToolHive +Kubernetes Operator. You'll learn why it exists, when to use it, and how it +simplifies managing multiple MCP servers while enabling powerful multi-system +workflows. ## The problem vMCP solves @@ -20,7 +22,7 @@ tools with sensible defaults. ## Core value propositions -Virtual MCP Server delivers four key benefits: +vMCP delivers four key benefits: 1. **Reduce complexity**: Many connections become one, dramatically simplifying configuration @@ -39,8 +41,8 @@ own configuration, authentication, and maintenance. vMCP aggregates all backends into one endpoint with automatic conflict resolution. **Example scenario**: An engineering team needs access to 8 backend servers -(GitHub, Jira, Slack, Confluence, PagerDuty, Datadog, AWS, Internal Docs). -Instead of configuring 8 separate connections, they configure one vMCP +(GitHub, Jira, Slack, Confluence, PagerDuty, Datadog, AWS, and internal company +docs). Instead of configuring 8 separate connections, they configure one vMCP connection with SSO. This significantly reduces configuration complexity and makes onboarding new team members much easier. @@ -57,14 +59,15 @@ error handling, and human-in-the-loop approval gates. **Example scenario**: During an incident investigation, you need logs from your logging system, metrics from your monitoring platform, traces from your tracing -service, and infrastructure status from your cloud provider. With vMCP, you -fetch all of this in parallel (4x faster than sequential), automatically -aggregate it into a formatted report, and create a Jira ticket with all the -data. This workflow is reusable for every incident. - -**Example scenario**: For a PR deployment, you might want to merge the PR, wait -for tests, ask a human for approval, deploy only if approved, and notify Slack. -vMCP handles this entire flow declaratively, with automatic rollback on +service, and infrastructure status from your cloud provider. Without vMCP, an +engineer manually runs 4 commands sequentially and aggregates results. With +vMCP, you fetch all of this in parallel, automatically aggregate it into a +formatted report, and create a Jira ticket with all the data. This workflow is +reusable for every incident. + +**Example scenario**: For an app deployment, merge the pull request, wait for +tests, ask a human for approval, deploy only if approved, and notify the team in +Slack. vMCP handles this entire flow declaratively, with automatic rollback on deployment failure. ### Tool customization and overrides @@ -88,13 +91,15 @@ Generic servers require repetitive parameter specification. You always use the same repo, channel, or database. vMCP lets you create specialized instances with pre-configured defaults. -**Example scenario**: All GitHub queries for your team should default to the -`stacklok/toolhive` repo. Engineers never need to specify the repo parameter, -and they can't accidentally query the wrong repository. +**Example scenario**: Without vMCP, every GitHub query requires specifying +`repo: stacklok/toolhive`. With vMCP, you pre-configure this default - engineers +never specify the repo parameter, and they can't accidentally query the wrong +repository. This eliminates hundreds of repetitive parameter entries per week. -**Example scenario**: Staging database is the default (safe for development), -while production queries require an explicit approval gate. Connection details -are centralized in the vMCP configuration. +**Example scenario**: Configure staging database as the default (safe for +development), while production queries require an explicit approval gate. +Connection details are centralized in vMCP configuration instead of scattered +across individual tool configurations. ### Authentication boundary separation @@ -102,10 +107,10 @@ Different auth is needed for clients versus backends, and managing credentials across multiple systems is complex. vMCP implements a two-boundary auth model that separates these concerns. -**How it works**: Clients authenticate to vMCP using the mechanism defined in -the MCP specification. vMCP then handles authentication to each backend MCP -server independently. Revoking access is simple: disable the user in your -identity provider and all backend access is revoked instantly. +**How it works**: Clients authenticate to vMCP using OAuth 2.1 authorization as +defined in the MCP specification. vMCP then handles authentication to each +backend MCP server independently. Revoking access is simple: disable the user in +your identity provider and all backend access is revoked instantly. This approach provides single sign-on for users, centralized access control, and a complete audit trail. @@ -128,14 +133,13 @@ a complete audit trail. ## Summary -Virtual MCP Server transforms MCP from individual servers into a unified -orchestration platform. The use cases range from simple aggregation to complex -workflows with approval gates, making it valuable for teams managing multiple -MCP servers. +vMCP transforms MCP from individual servers into a unified orchestration +platform. The use cases range from simple aggregation to complex workflows with +approval gates, making it valuable for teams managing multiple MCP servers. ## Related information -- [Deploy Virtual MCP Server](../guides-vmcp/intro.mdx) +- [Deploy vMCP](../guides-vmcp/intro.mdx) +- [Configure authentication](../guides-vmcp/authentication.mdx) - [Tool aggregation and conflict resolution](../guides-vmcp/tool-aggregation.mdx) -- [Authentication](../guides-vmcp/authentication.mdx) - [Composite tools and workflows](../guides-vmcp/composite-tools.mdx) diff --git a/docs/toolhive/guides-k8s/deploy-operator-helm.mdx b/docs/toolhive/guides-k8s/deploy-operator-helm.mdx index 53b7154e..768e8b28 100644 --- a/docs/toolhive/guides-k8s/deploy-operator-helm.mdx +++ b/docs/toolhive/guides-k8s/deploy-operator-helm.mdx @@ -33,7 +33,7 @@ chart. To install a specific version, append `--version ` to the command, for example: ```bash -helm upgrade --install toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds --version 0.0.52 +helm upgrade --install toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds --version 0.0.73 ``` ## Install the operator @@ -52,7 +52,7 @@ chart. To install a specific version, append `--version ` to the command, for example: ```bash -helm upgrade --install toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system --create-namespace --version 0.3.7 +helm upgrade --install toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system --create-namespace --version 0.5.6 ``` Verify the installation: @@ -237,21 +237,23 @@ and then apply the CRDs using `kubectl`. First, upgrade the CRD Helm chart to match your target operator version: ```bash -helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds --version 0.0.52 +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds --version 0.0.73 ``` Then apply the CRDs from the same version tag: ```bash -kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/toolhive-operator-crds-0.0.52/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml -kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/toolhive-operator-crds-0.0.52/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcptoolconfigs.yaml -kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/toolhive-operator-crds-0.0.52/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpremoteproxies.yaml -kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/toolhive-operator-crds-0.0.52/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpservers.yaml -kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/toolhive-operator-crds-0.0.52/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpgroups.yaml -kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/toolhive-operator-crds-0.0.52/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpregistries.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/toolhive-operator-crds-0.0.73/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/toolhive-operator-crds-0.0.73/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcptoolconfigs.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/toolhive-operator-crds-0.0.73/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpremoteproxies.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/toolhive-operator-crds-0.0.73/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpservers.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/toolhive-operator-crds-0.0.73/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpgroups.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/toolhive-operator-crds-0.0.73/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpregistries.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/toolhive-operator-crds-0.0.73/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/toolhive-operator-crds-0.0.73/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpservers.yaml ``` -Replace `0.0.52` in both commands with your target CRD version. +Replace `0.0.73` in both commands with your target CRD version. ### Upgrade the operator Helm release @@ -267,7 +269,7 @@ This upgrades the operator to the latest version available in the OCI registry. To upgrade to a specific version, add the `--version` flag: ```bash -helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system --reuse-values --version 0.3.7 +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system --reuse-values --version 0.5.6 ``` If you have a custom `values.yaml` file, include it with the `-f` flag: @@ -302,6 +304,8 @@ kubectl delete crd mcpremoteproxies.toolhive.stacklok.dev kubectl delete crd mcpservers.toolhive.stacklok.dev kubectl delete crd mcpgroups.toolhive.stacklok.dev kubectl delete crd mcpregistries.toolhive.stacklok.dev +kubectl delete crd virtualmcpcompositetooldefinitions.toolhive.stacklok.dev +kubectl delete crd virtualmcpservers.toolhive.stacklok.dev ``` Finally, uninstall the CRD Helm chart metadata: diff --git a/docs/toolhive/guides-vmcp/authentication.mdx b/docs/toolhive/guides-vmcp/authentication.mdx index ebba7d08..4a2c9036 100644 --- a/docs/toolhive/guides-vmcp/authentication.mdx +++ b/docs/toolhive/guides-vmcp/authentication.mdx @@ -1,10 +1,10 @@ --- title: Authentication -description: Configure client and backend authentication for Virtual MCP Server. +description: Configure client and backend authentication for vMCP. --- -Virtual MCP implements a two-boundary authentication model that separates client -authentication from backend authentication, giving you centralized control over +Virtual MCP Server (vMCP) implements a two-boundary authentication model that +separates client and backend authentication, giving you centralized control over access while supporting diverse backend requirements. ## Two-boundary authentication model @@ -14,44 +14,45 @@ flowchart LR subgraph Boundary1[" "] direction TB Client[MCP Client] - B1Label["**Boundary 1**
Client → Virtual MCP"] + B1Label["**Boundary 1**
Client → vMCP"] end - subgraph vMCP[Virtual MCP Server] - Auth[Token Validation] - Backend[Backend Auth] + subgraph vMCP["Virtual MCP Server (vMCP)"] + Auth[Token validation] + Backend[Backend auth] end subgraph Boundary2[" "] direction TB - B2Label["**Boundary 2**
Virtual MCP → Backend APIs"] + B2Label["**Boundary 2**
vMCP → Backend APIs"] GitHub[GitHub API] Jira[Jira API] end - Client -->|"vMCP-scoped token"| Auth + Client -->|"vMCP-scoped
token"| Auth Auth --> Backend - Backend -->|"Backend-scoped token"| GitHub - Backend -->|"Backend-scoped token"| Jira + Backend -->|"Backend-scoped
token"| GitHub + Backend -->|"Backend-scoped
token"| Jira ``` -**Boundary 1 (Incoming):** Clients authenticate to Virtual MCP using the -mechanism defined in the MCP specification. This is your organization's identity -layer. +**Boundary 1 (Incoming):** Clients authenticate to vMCP using OAuth 2.1 +authorization as defined in the +[MCP specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization). +This is your organization's identity layer. -**Boundary 2 (Outgoing):** Virtual MCP obtains appropriate credentials for each +**Boundary 2 (Outgoing):** vMCP obtains appropriate credentials for each backend. Each backend API receives a token or credential scoped to its requirements. ## Incoming authentication -Configure how clients authenticate to Virtual MCP. +Configure how clients authenticate to vMCP. ### Anonymous (development only) -No authentication required. Use only for local development. +No authentication required: -```yaml +```yaml title="VirtualMCPServer resource" spec: incomingAuth: type: anonymous @@ -59,7 +60,9 @@ spec: :::warning -Never use `anonymous` incoming authentication in production environments. +Do not use `anonymous` authentication in production environments. This setting +disables all access control, allowing anyone to use the vMCP without +credentials. ::: @@ -67,7 +70,7 @@ Never use `anonymous` incoming authentication in production environments. Validate tokens from an external identity provider: -```yaml +```yaml title="VirtualMCPServer resource" spec: incomingAuth: type: oidc @@ -75,14 +78,14 @@ spec: type: inline inline: issuer: https://auth.example.com - clientId: your-client-id + clientId: audience: vmcp ``` When using an identity provider that issues opaque OAuth tokens, add a `clientSecretRef` referencing a Kubernetes Secret to enable token introspection: -```yaml +```yaml title="VirtualMCPServer resource" spec: incomingAuth: type: oidc @@ -90,7 +93,7 @@ spec: type: inline inline: issuer: https://auth.example.com - clientId: your-client-id + clientId: audience: vmcp clientSecretRef: name: oidc-client-secret @@ -107,14 +110,14 @@ metadata: namespace: toolhive-system type: Opaque stringData: - clientSecret: your-client-secret + clientSecret: ``` ### Kubernetes service account tokens Authenticate using Kubernetes service account tokens for in-cluster clients: -```yaml +```yaml title="VirtualMCPServer resource" spec: incomingAuth: type: oidc @@ -132,15 +135,15 @@ validates service account tokens. The defaults work for most clusters: ## Outgoing authentication -Configure how Virtual MCP authenticates to backend MCP servers. +Configure how vMCP authenticates to backend MCP servers. ### Discovery mode -When using discovery mode, Virtual MCP checks each backend MCPServer's +When using discovery mode, vMCP checks each backend MCPServer's `externalAuthConfigRef` to determine how to authenticate. If a backend has no -auth config, Virtual MCP connects without authentication. +auth config, vMCP connects without authentication. -```yaml +```yaml title="VirtualMCPServer resource" spec: outgoingAuth: source: discovered @@ -150,6 +153,10 @@ This is the recommended approach for most deployments. Backends that don't require authentication work automatically, while backends with `externalAuthConfigRef` configured use their specified authentication method. +See +[Configure token exchange for backend authentication](../guides-k8s/token-exchange-k8s.mdx) +for details on using service account token exchange for backend authentication. + ## Related information - [Authentication framework concepts](../concepts/auth-framework.mdx) diff --git a/docs/toolhive/guides-vmcp/composite-tools.mdx b/docs/toolhive/guides-vmcp/composite-tools.mdx index 77bfb2ab..6b0b65c6 100644 --- a/docs/toolhive/guides-vmcp/composite-tools.mdx +++ b/docs/toolhive/guides-vmcp/composite-tools.mdx @@ -10,12 +10,13 @@ handling. ## Overview A composite tool combines multiple backend tool calls into a single workflow. -When a client calls a composite tool, Virtual MCP orchestrates the execution -across backends, handling dependencies and collecting results. +When a client calls a composite tool, vMCP orchestrates the execution across +backends, handling dependencies and collecting results. ## Key capabilities -- **DAG-based execution**: Steps run in parallel when dependencies allow +- **Parallel execution**: Independent steps run concurrently; dependent steps + wait for their prerequisites - **Template expansion**: Dynamic arguments using step outputs - **Elicitation**: Request user input mid-workflow (approval gates, choices) - **Error handling**: Configurable abort, continue, or retry behavior @@ -24,128 +25,199 @@ across backends, handling dependencies and collecting results. :::info Elicitation (user prompts during workflow execution) is defined in the CRD but -has not been extensively tested. Use with caution in production environments. +has not been extensively tested. Test thoroughly in non-production environments +first. ::: +## Configuration location + +Composite tools are defined in the VirtualMCPServer resource under the +`spec.compositeTools` array: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp +spec: + groupRef: + name: my-tools + # ... other configuration ... + compositeTools: + - name: my_workflow + description: A multi-step workflow + parameters: + # Input parameters (JSON Schema) + steps: + # Workflow steps +``` + +See the [CompositeToolSpec](../reference/crd-spec.mdx#compositetoolspec) +definition in the CRD for all available fields. + +## Simple example + +Here's a basic composite tool that fetches a URL and then summarizes it: + +```yaml title="VirtualMCPServer resource" +spec: + compositeTools: + - name: fetch_and_summarize + description: Fetch a URL and create a summary + parameters: + type: object + properties: + url: + type: string + required: + - url + steps: + - id: fetch + tool: fetch.fetch + arguments: + url: '{{.params.url}}' + - id: summarize + tool: llm.summarize + arguments: + text: '{{.steps.fetch.output.content}}' + dependsOn: [fetch] +``` + +**What's happening:** + +1. **Parameters**: Define the workflow inputs (just `url` in this case) +2. **Step 1 (fetch)**: Calls the `fetch.fetch` tool with the URL from parameters + using template syntax `{{.params.url}}` +3. **Step 2 (summarize)**: Waits for the fetch step (`dependsOn: [fetch]`), then + calls `llm.summarize` with the fetched content using + `{{.steps.fetch.output.content}}` + +When a client calls this composite tool, vMCP executes both steps in sequence +and returns the final summary. + ## Use cases ### Incident investigation Gather data from multiple monitoring systems in parallel: -```yaml -compositeTools: - - name: investigate_incident - description: Gather incident data from multiple sources in parallel - parameters: - type: object - properties: - incident_id: - type: string - required: - - incident_id - steps: - # These steps run in parallel (no dependencies) - - id: get_logs - tool: logging.search_logs - arguments: - query: 'incident_id={{.params.incident_id}}' - timerange: '1h' - - id: get_metrics - tool: monitoring.get_metrics - arguments: - filter: 'error_rate' - timerange: '1h' - - id: get_alerts - tool: pagerduty.list_alerts - arguments: - incident: '{{.params.incident_id}}' - # This step waits for all parallel steps to complete - - id: create_summary - tool: docs.create_document - arguments: - title: 'Incident {{.params.incident_id}} Summary' - content: 'Logs: {{.steps.get_logs.output.results}}' - dependsOn: [get_logs, get_metrics, get_alerts] +```yaml title="VirtualMCPServer resource" +spec: + compositeTools: + - name: investigate_incident + description: Gather incident data from multiple sources in parallel + parameters: + type: object + properties: + incident_id: + type: string + required: + - incident_id + steps: + # These steps run in parallel (no dependencies) + - id: get_logs + tool: logging.search_logs + arguments: + query: 'incident_id={{.params.incident_id}}' + timerange: '1h' + - id: get_metrics + tool: monitoring.get_metrics + arguments: + filter: 'error_rate' + timerange: '1h' + - id: get_alerts + tool: pagerduty.list_alerts + arguments: + incident: '{{.params.incident_id}}' + # This step waits for all parallel steps to complete + - id: create_summary + tool: docs.create_document + arguments: + title: 'Incident {{.params.incident_id}} Summary' + content: 'Logs: {{.steps.get_logs.output.results}}' + dependsOn: [get_logs, get_metrics, get_alerts] ``` ### Deployment with approval Human-in-the-loop workflow for production deployments: -```yaml -compositeTools: - - name: deploy_with_approval - description: Deploy to production with human approval gate - parameters: - type: object - properties: - pr_number: - type: string - environment: - type: string - default: production - required: - - pr_number - steps: - - id: get_pr_details - tool: github.get_pull_request - arguments: - pr: '{{.params.pr_number}}' - - id: approval - type: elicitation - message: 'Deploy PR #{{.params.pr_number}} to {{.params.environment}}?' - schema: - type: object - properties: - approved: - type: boolean - timeout: '10m' - dependsOn: [get_pr_details] - - id: deploy - tool: deploy.trigger_deployment - arguments: - ref: '{{.steps.get_pr_details.output.head_sha}}' - environment: '{{.params.environment}}' - condition: '{{.steps.approval.content.approved}}' - dependsOn: [approval] +```yaml title="VirtualMCPServer resource" +spec: + compositeTools: + - name: deploy_with_approval + description: Deploy to production with human approval gate + parameters: + type: object + properties: + pr_number: + type: string + environment: + type: string + default: production + required: + - pr_number + steps: + - id: get_pr_details + tool: github.get_pull_request + arguments: + pr: '{{.params.pr_number}}' + - id: approval + type: elicitation + message: + 'Deploy PR #{{.params.pr_number}} to {{.params.environment}}?' + schema: + type: object + properties: + approved: + type: boolean + timeout: '10m' + dependsOn: [get_pr_details] + - id: deploy + tool: deploy.trigger_deployment + arguments: + ref: '{{.steps.get_pr_details.output.head_sha}}' + environment: '{{.params.environment}}' + condition: '{{.steps.approval.content.approved}}' + dependsOn: [approval] ``` ### Cross-system data aggregation Collect and correlate data from multiple backends: -```yaml -compositeTools: - - name: security_scan_report - description: Run security scans and create consolidated report - parameters: - type: object - properties: - repo: - type: string - required: - - repo - steps: - - id: vulnerability_scan - tool: osv.scan_dependencies - arguments: - repository: '{{.params.repo}}' - - id: secret_scan - tool: gitleaks.scan_repo - arguments: - repository: '{{.params.repo}}' - - id: create_issue - tool: github.create_issue - arguments: - repo: '{{.params.repo}}' - title: 'Security Scan Results' - body: - 'Found {{.steps.vulnerability_scan.output.count}} vulnerabilities' - dependsOn: [vulnerability_scan, secret_scan] - onError: - action: continue +```yaml title="VirtualMCPServer resource" +spec: + compositeTools: + - name: security_scan_report + description: Run security scans and create consolidated report + parameters: + type: object + properties: + repo: + type: string + required: + - repo + steps: + - id: vulnerability_scan + tool: osv.scan_dependencies + arguments: + repository: '{{.params.repo}}' + - id: secret_scan + tool: gitleaks.scan_repo + arguments: + repository: '{{.params.repo}}' + - id: create_issue + tool: github.create_issue + arguments: + repo: '{{.params.repo}}' + title: 'Security Scan Results' + body: + 'Found {{.steps.vulnerability_scan.output.count}} vulnerabilities' + dependsOn: [vulnerability_scan, secret_scan] + onError: + action: continue ``` ## Workflow definition @@ -154,49 +226,59 @@ compositeTools: Define input parameters using JSON Schema format: -```yaml -parameters: - type: object - properties: - required_param: - type: string - optional_param: - type: integer - default: 10 - required: - - required_param +```yaml title="VirtualMCPServer resource" +spec: + compositeTools: + - name: + parameters: + type: object + properties: + required_param: + type: string + optional_param: + type: integer + default: 10 + required: + - required_param ``` ### Steps Each step can be a tool call or an elicitation: -```yaml -steps: - - id: step_name # Unique identifier - tool: backend.tool # Tool to call - arguments: # Arguments with template expansion - arg1: '{{.params.input}}' - dependsOn: [other_step] # Dependencies for DAG execution - condition: '{{.steps.check.output.approved}}' # Optional condition - timeout: '30s' # Step timeout - onError: - action: abort # abort | continue | retry +```yaml title="VirtualMCPServer resource" +spec: + compositeTools: + - name: + steps: + - id: step_name # Unique identifier + tool: backend.tool # Tool to call + arguments: # Arguments with template expansion + arg1: '{{.params.input}}' + dependsOn: [other_step] # Dependencies (this step waits for other_step) + condition: '{{.steps.check.output.approved}}' # Optional condition + timeout: '30s' # Step timeout + onError: + action: abort # abort | continue | retry ``` ### Elicitation (user prompts) Request input from users during workflow execution: -```yaml -- id: approval - type: elicitation - message: 'Proceed with deployment?' - schema: - type: object - properties: - confirm: { type: boolean } - timeout: '5m' +```yaml title="VirtualMCPServer resource" +spec: + compositeTools: + - name: + steps: + - id: approval + type: elicitation + message: 'Proceed with deployment?' + schema: + type: object + properties: + confirm: { type: boolean } + timeout: '5m' ``` ### Error handling @@ -209,10 +291,16 @@ Configure behavior when steps fail: | `continue` | Log error, proceed to next step | | `retry` | Retry with exponential backoff | -```yaml -onError: - action: retry - maxRetries: 3 +```yaml title="VirtualMCPServer resource" +spec: + compositeTools: + - name: + steps: + - id: + # ... other step config (tool, arguments, etc.) + onError: + action: retry + maxRetries: 3 ``` ## Template syntax @@ -274,4 +362,4 @@ resources and reference them with `compositeToolRefs`. ## Related information -- [VirtualMCPServer configuration](./configuration.mdx) +- [Configure vMCP servers](./configuration.mdx) diff --git a/docs/toolhive/guides-vmcp/configuration.mdx b/docs/toolhive/guides-vmcp/configuration.mdx index 4233bae3..0e84e8ed 100644 --- a/docs/toolhive/guides-vmcp/configuration.mdx +++ b/docs/toolhive/guides-vmcp/configuration.mdx @@ -1,16 +1,38 @@ --- -title: Configuring Virtual MCP Server -description: How to configure VirtualMCPServer for common scenarios. +title: Configure vMCP servers +description: How to configure a Virtual MCP Server for common scenarios. --- -This guide covers common configuration patterns for VirtualMCPServer. For a -complete field reference, see the +This guide covers common configuration patterns for vMCP using the +VirtualMCPServer resource. For a complete field reference, see the [VirtualMCPServer CRD specification](../reference/crd-spec.mdx#virtualmcpserver). -## Minimal configuration +## Create an MCPGroup -At minimum, a VirtualMCPServer requires a reference to an -[MCPGroup](../tutorials/quickstart-vmcp.mdx#step-1-create-an-mcpgroup) and an +Before creating a VirtualMCPServer, you need an +[MCPGroup](../reference/crd-spec.mdx#mcpgroup) to organize the backend MCP +servers. An MCPGroup is a logical container that groups related MCPServer +resources together. + +Create a basic MCPGroup: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPGroup +metadata: + name: my-group + namespace: toolhive-system +spec: + description: Group of backend MCP servers for vMCP aggregation +``` + +The MCPGroup must exist in the same namespace as your VirtualMCPServer and be in +a Ready state before the VirtualMCPServer can start. Backend MCPServers +reference this group using the `groupRef` field in their spec. + +## Create a VirtualMCPServer + +At minimum, a VirtualMCPServer requires a reference to an MCPGroup and an authentication type: ```yaml @@ -18,6 +40,7 @@ apiVersion: toolhive.stacklok.dev/v1alpha1 kind: VirtualMCPServer metadata: name: my-vmcp + namespace: toolhive-system spec: groupRef: name: my-group @@ -28,82 +51,37 @@ spec: The MCPGroup must exist in the same namespace and be in a Ready state before the VirtualMCPServer can start. -## Adding authentication +## Configure authentication -### Development (no authentication) +vMCP uses a two-boundary authentication model: client-to-vMCP (incoming) and +vMCP-to-backends (outgoing). See the +[Authentication guide](./authentication.mdx) for complete configuration options +including anonymous, OIDC, and Kubernetes service account authentication. -For local development only: +## Expose the service -```yaml -spec: - incomingAuth: - type: anonymous -``` - -:::warning +Choose how to expose the vMCP endpoint. The Service resource is created +automatically on port 4483. -Never use `anonymous` authentication in production environments. - -::: - -### OIDC authentication - -Validate tokens from an external identity provider: - -```yaml +```yaml title="VirtualMCPServer resource" spec: - incomingAuth: - type: oidc - oidcConfig: - type: inline - inline: - issuer: https://your-idp.example.com - audience: vmcp - clientId: your-client-id - # For token introspection, add clientSecretRef referencing a Secret -``` - -### Kubernetes service account tokens - -For in-cluster clients using service account tokens: - -```yaml -spec: - incomingAuth: - type: oidc - oidcConfig: - type: kubernetes - kubernetes: - audience: toolhive + serviceType: ClusterIP # Default: cluster-internal (can be exposed via Ingress/Gateway) + # serviceType: LoadBalancer # Direct external access via cloud load balancer + # serviceType: NodePort # Direct external access via node ports ``` -See [Authentication](./authentication.mdx) for complete authentication options. +**Service types:** -## Configuring backend authentication +- **ClusterIP** (default): For production, use with Ingress or Gateway API for + controlled external access with TLS termination +- **LoadBalancer**: Direct external access via cloud provider's load balancer + (simpler but less control) +- **NodePort**: Direct access via node ports (typically for development/testing) -Control how Virtual MCP authenticates to backend MCP servers: - -```yaml -spec: - outgoingAuth: - source: discovered -``` - -With discovery mode, Virtual MCP checks each backend MCPServer's -`externalAuthConfigRef` to determine how to authenticate. - -## Exposing the service - -Choose how to expose the Virtual MCP endpoint: - -```yaml -spec: - serviceType: ClusterIP # Default: internal access only - # serviceType: LoadBalancer # External access via cloud load balancer - # serviceType: NodePort # External access via node ports -``` +The Service is named `vmcp-`, where `` is from `metadata.name` in +the VirtualMCPServer resource. -## Monitoring status +## Monitor status Check the VirtualMCPServer status to verify it's ready: @@ -123,6 +101,6 @@ Key status fields: ## Related information - [VirtualMCPServer CRD specification](../reference/crd-spec.mdx#virtualmcpserver) -- [Introduction to Virtual MCP](./intro.mdx) +- [Introduction to vMCP](./intro.mdx) - [Tool aggregation](./tool-aggregation.mdx) - [Authentication](./authentication.mdx) diff --git a/docs/toolhive/guides-vmcp/index.mdx b/docs/toolhive/guides-vmcp/index.mdx index 4c37812e..b37f4255 100644 --- a/docs/toolhive/guides-vmcp/index.mdx +++ b/docs/toolhive/guides-vmcp/index.mdx @@ -3,17 +3,24 @@ title: Virtual MCP Server description: Aggregate multiple MCP servers into a single unified endpoint. --- -Virtual MCP Server (vMCP) aggregates multiple backend MCP servers into a single -endpoint, enabling unified tool access, centralized authentication, and -multi-step workflows. +Virtual MCP Server (vMCP) is a feature of the ToolHive Kubernetes Operator that +aggregates multiple backend MCP servers into a single endpoint, enabling unified +tool access, centralized authentication, and multi-step workflows. -## When to use Virtual MCP +## When to use vMCP -- Managing multiple MCP servers that should appear as one -- Centralizing authentication across backends -- Creating reusable workflows spanning multiple systems +- You manage multiple MCP servers that should appear as one +- You need to centralize authentication across backends +- You want to create reusable workflows spanning multiple systems -## Guides +## Get started + +- [Understanding Virtual MCP Server](../concepts/vmcp.mdx) - Learn what vMCP + does and when to use it +- [Quickstart: Virtual MCP Server](../tutorials/quickstart-vmcp.mdx) - Deploy + your first vMCP + +## Contents import DocCardList from '@theme/DocCardList'; diff --git a/docs/toolhive/guides-vmcp/intro.mdx b/docs/toolhive/guides-vmcp/intro.mdx index becb9224..0d867cbc 100644 --- a/docs/toolhive/guides-vmcp/intro.mdx +++ b/docs/toolhive/guides-vmcp/intro.mdx @@ -1,14 +1,15 @@ --- -title: Introduction to Virtual MCP -description: Learn what Virtual MCP Server is and when to use it. +title: Introduction to vMCP +description: Understand what Virtual MCP Server (vMCP) does and when to use it. --- ## Overview -Virtual MCP Server acts as an aggregation proxy that consolidates multiple -backend MCP servers into a single unified interface. Instead of configuring -clients to connect to each MCP server individually, you connect once to Virtual -MCP and access all backend tools through a single endpoint. +Virtual MCP Server (vMCP) is a feature of the ToolHive Kubernetes Operator that +acts as an aggregation proxy, consolidating multiple backend MCP servers into a +single unified interface. Instead of configuring clients to connect to each MCP +server individually, you connect once to vMCP and access all backend tools +through a single endpoint. ## Core capabilities @@ -19,20 +20,20 @@ MCP and access all backend tools through a single endpoint. - **Composite workflows**: Multi-step operations across backends with parallel execution, approval gates, and error handling -## When to use Virtual MCP +## When to use vMCP ### Good fit -- Teams managing 5+ MCP servers -- Cross-system workflows requiring coordination -- Centralized authentication and authorization requirements -- Reusable workflow definitions needed +- You manage 5+ MCP servers +- You need cross-system workflows requiring coordination +- You have centralized authentication and authorization requirements +- You need reusable workflow definitions ### Not needed -- Single MCP server usage -- Simple, one-step operations -- No orchestration requirements +- You use a single MCP server +- You have simple, one-step operations +- You have no orchestration requirements ## Architecture overview @@ -53,12 +54,15 @@ flowchart TB ## How it works -1. You define an MCPGroup containing your backend MCPServer resources +1. You define an MCPGroup (a resource that organizes related MCP servers) + containing your backend MCPServer resources 2. You create a VirtualMCPServer that references the group -3. The operator discovers all backends and aggregates their capabilities -4. Clients connect to the VirtualMCPServer endpoint and see all tools unified +3. The operator discovers all backend MCP servers in the group and aggregates + their capabilities +4. Clients connect to the VirtualMCPServer endpoint and see a unified view of + all tools ## Related information -- [Understanding Virtual MCP Server](../concepts/vmcp-architecture.mdx) -- [Quickstart: Virtual MCP](../tutorials/quickstart-vmcp.mdx) +- [Understanding Virtual MCP Server](../concepts/vmcp.mdx) +- [Quickstart: Virtual MCP Server](../tutorials/quickstart-vmcp.mdx) diff --git a/docs/toolhive/guides-vmcp/tool-aggregation.mdx b/docs/toolhive/guides-vmcp/tool-aggregation.mdx index dfc7fcc8..45ddb694 100644 --- a/docs/toolhive/guides-vmcp/tool-aggregation.mdx +++ b/docs/toolhive/guides-vmcp/tool-aggregation.mdx @@ -1,29 +1,30 @@ --- title: Tool aggregation and conflict resolution description: - How Virtual MCP aggregates tools from multiple backends and resolves naming + How vMCP aggregates tools from multiple backends and resolves naming conflicts. --- When aggregating multiple MCP servers, tool name conflicts can occur when -different backends expose tools with the same name. Virtual MCP provides -strategies to resolve these conflicts automatically. +different backends expose tools with the same name. Virtual MCP Server (vMCP) +provides strategies to resolve these conflicts automatically. ## Overview -Virtual MCP discovers tools from all backend MCPServers in the referenced group -and presents them as a unified set to clients. When two backends have tools with -the same name (for example, both GitHub and Jira have a `create_issue` tool), a +vMCP discovers tools from all backend MCPServers in the referenced group and +presents them as a unified set to clients. When two backends have tools with the +same name (for example, both GitHub and Jira have a `create_issue` tool), a conflict resolution strategy determines how to handle the collision. ## Conflict resolution strategies ### Prefix strategy (default) -Prefixes all tool names with the workload identifier. This guarantees unique -names and is the safest option for most deployments. +By default, vMCP prefixes all tool names with the workload identifier (the +`metadata.name` of each MCPServer resource). This guarantees unique names and is +the safest option for most deployments. -```yaml +```yaml title="VirtualMCPServer resource" spec: aggregation: conflictResolution: prefix @@ -48,10 +49,11 @@ With backends `github` and `jira`, both exposing `create_issue`: ### Priority strategy -The first workload in the priority order wins. Conflicting tools from -lower-priority backends are dropped. +When multiple backends offer tools with the same name, the `priority` strategy +keeps the tool from the first backend in the priority order and drops the +duplicate tools from lower-priority backends. -```yaml +```yaml title="VirtualMCPServer resource" spec: aggregation: conflictResolution: priority @@ -59,6 +61,9 @@ spec: priorityOrder: ['github', 'jira', 'slack'] ``` +In this example, if both GitHub and Jira provide a `create_issue` tool, only +GitHub's version is exposed. Jira's duplicate is dropped. + **When to use:** When you have a preferred backend for specific tools and want to hide duplicates. @@ -71,10 +76,11 @@ the intended behavior before using in production. ### Manual strategy -Requires explicit overrides for all conflicting tools. Fails at startup if -conflicts exist without overrides. +The `manual` strategy gives you explicit control over tool naming when conflicts +occur. You must provide overrides for all conflicting tools, or the vMCP will +fail to start. -```yaml +```yaml title="VirtualMCPServer resource" spec: aggregation: conflictResolution: manual @@ -94,9 +100,11 @@ tool names. ## Tool filtering -Limit which tools are exposed from a specific backend: +Use filters to expose only specific tools from a backend, excluding all others. +This is useful for reducing the tool surface area presented to LLM clients or +removing unnecessary tools: -```yaml +```yaml title="VirtualMCPServer resource" spec: aggregation: tools: @@ -108,9 +116,11 @@ Only the listed tools are included; all others from that backend are excluded. ## Tool overrides -Rename tools or update descriptions: +Use overrides to customize tool names and descriptions without modifying backend +configurations. This is useful for disambiguating similarly-named tools or +providing more context to LLM clients: -```yaml +```yaml title="VirtualMCPServer resource" spec: aggregation: tools: @@ -128,11 +138,11 @@ of inline filter and overrides. This feature is currently in development. ::: -## Combining filter and overrides +## Combine filters and overrides You can combine filtering and overrides for fine-grained control: -```yaml +```yaml title="VirtualMCPServer resource" spec: aggregation: conflictResolution: prefix @@ -214,4 +224,4 @@ With this configuration, tools from each backend are prefixed: ## Related information - [VirtualMCPServer configuration reference](./configuration.mdx) -- [Customize tools for MCPServers](../guides-k8s/customize-tools.mdx) +- [Customize MCP server tools](../guides-k8s/customize-tools.mdx) diff --git a/docs/toolhive/tutorials/quickstart-vmcp.mdx b/docs/toolhive/tutorials/quickstart-vmcp.mdx index 2070655b..944eae33 100644 --- a/docs/toolhive/tutorials/quickstart-vmcp.mdx +++ b/docs/toolhive/tutorials/quickstart-vmcp.mdx @@ -5,9 +5,9 @@ description: Virtual MCP Server. --- -In this tutorial, you'll learn how to deploy Virtual MCP Server to aggregate -multiple MCP servers into a single endpoint. By the end, you'll have a working -deployment that combines tools from multiple backends. +In this tutorial, you'll learn how to deploy Virtual MCP Server (vMCP) to +aggregate multiple MCP servers into a single endpoint. By the end, you'll have a +working deployment that combines tools from multiple backends. ## What you'll learn @@ -30,14 +30,14 @@ Before starting this tutorial, make sure you have: First, create an MCPGroup to organize your backend MCP servers: -```yaml +```yaml title="mcpgroup.yaml" apiVersion: toolhive.stacklok.dev/v1alpha1 kind: MCPGroup metadata: name: demo-tools namespace: toolhive-system spec: - description: Demo group for Virtual MCP aggregation + description: Demo group for vMCP aggregation ``` Apply the resource: @@ -54,9 +54,10 @@ kubectl get mcpgroups -n toolhive-system ## Step 2: Deploy backend MCPServers -Deploy two MCP servers that will be aggregated. Both reference the group: +Deploy two MCP servers that will be aggregated. Both reference the `demo-tools` +group in the `groupRef` field: -```yaml +```yaml {11,30} title="mcpservers.yaml" apiVersion: toolhive.stacklok.dev/v1alpha1 kind: MCPServer metadata: @@ -114,7 +115,7 @@ You should see both servers with `Running` status before continuing. Create a VirtualMCPServer that aggregates both backends: -```yaml +```yaml title="virtualmcpserver.yaml" apiVersion: toolhive.stacklok.dev/v1alpha1 kind: VirtualMCPServer metadata: @@ -140,7 +141,7 @@ spec: conflictResolutionConfig: prefixFormat: '{workload}_' - # Expose as ClusterIP (internal access only) + # Expose as ClusterIP (cluster-internal or exposed via Ingress/Gateway) serviceType: ClusterIP ``` @@ -156,18 +157,20 @@ Check the status: kubectl get virtualmcpservers -n toolhive-system ``` -You should see output similar to: +After about 30 seconds, you should see output similar to: ```text NAME PHASE URL BACKENDS AGE READY demo-vmcp Ready http://vmcp-demo-vmcp.toolhive-system.svc.cluster.local:4483 2 30s True ``` +Note the port number for step 5. + :::info[What's happening?] -The operator discovered both MCPServers in the group and configured Virtual MCP -to aggregate their tools. With the `prefix` conflict resolution strategy, all -tools are prefixed with the backend name. +The operator discovered both MCPServers in the group and configured vMCP to +aggregate their tools. With the `prefix` conflict resolution strategy, all tools +are prefixed with the backend name. ::: @@ -184,7 +187,7 @@ backends. ## Step 5: Connect your client -Port-forward to access Virtual MCP locally: +In a separate terminal, port-forward the vMCP service to your local machine: ```bash kubectl port-forward service/vmcp-demo-vmcp -n toolhive-system 4483:4483 @@ -198,10 +201,10 @@ curl http://localhost:4483/health You should see `{"status":"ok"}`. -Configure VS Code to connect to Virtual MCP. Add the following to your -`mcp.json` file: +Configure VS Code to connect to vMCP. Add the following to your `mcp.json` file: - **macOS**: `~/Library/Application Support/Code/User/mcp.json` +- **Windows**: `%APPDATA%\Code\User\mcp.json` - **Linux**: `~/.config/Code/User/mcp.json` ```json @@ -228,7 +231,7 @@ configuration file locations and formats. ## Step 6: Test the aggregated tools Try asking your AI assistant questions that use the aggregated tools. Both tools -work through the same Virtual MCP endpoint! +work through the same vMCP endpoint! ## Step 7: Clean up @@ -242,15 +245,15 @@ kubectl delete mcpgroup demo-tools -n toolhive-system ## What's next? -Congratulations! You've successfully deployed Virtual MCP Server and aggregated -multiple backends into a single endpoint. +Congratulations! You've successfully deployed vMCP and aggregated multiple +backends into a single endpoint. Next steps: - [Configure authentication](../guides-vmcp/authentication.mdx) for production - [Customize tool aggregation](../guides-vmcp/tool-aggregation.mdx) with filtering and overrides -- [Understanding Virtual MCP Server](../concepts/vmcp-architecture.mdx) +- [Understanding Virtual MCP Server](../concepts/vmcp.mdx) ## Troubleshooting diff --git a/docusaurus.config.ts b/docusaurus.config.ts index f2c85703..607581db 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -171,6 +171,10 @@ const config: Config = { label: 'Kubernetes Operator', to: 'toolhive/guides-k8s', }, + { + label: 'Virtual MCP Server', + to: 'toolhive/guides-vmcp', + }, ], }, { diff --git a/sidebars.ts b/sidebars.ts index ae71207b..6b32aacb 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -175,8 +175,8 @@ const sidebars: SidebarsConfig = { items: [ 'toolhive/guides-vmcp/intro', 'toolhive/guides-vmcp/configuration', - 'toolhive/guides-vmcp/tool-aggregation', 'toolhive/guides-vmcp/authentication', + 'toolhive/guides-vmcp/tool-aggregation', 'toolhive/guides-vmcp/composite-tools', ], }, @@ -198,7 +198,7 @@ const sidebars: SidebarsConfig = { 'toolhive/concepts/auth-framework', 'toolhive/concepts/cedar-policies', 'toolhive/concepts/backend-auth', - 'toolhive/concepts/vmcp-architecture', + 'toolhive/concepts/vmcp', ], },