Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
5 changes: 5 additions & 0 deletions STYLE-GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
Expand All @@ -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.

Expand All @@ -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
Expand All @@ -88,24 +91,26 @@ 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

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.
Expand All @@ -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)
26 changes: 15 additions & 11 deletions docs/toolhive/guides-k8s/deploy-operator-helm.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ chart. To install a specific version, append `--version <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
Expand All @@ -52,7 +52,7 @@ chart. To install a specific version, append `--version <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:
Expand Down Expand Up @@ -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

Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
65 changes: 36 additions & 29 deletions docs/toolhive/guides-vmcp/authentication.mdx
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -14,83 +14,86 @@ flowchart LR
subgraph Boundary1[" "]
direction TB
Client[MCP Client]
B1Label["**Boundary 1**<br>Client → Virtual MCP"]
B1Label["**Boundary 1**<br>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**<br>Virtual MCP → Backend APIs"]
B2Label["**Boundary 2**<br>vMCP → Backend APIs"]
GitHub[GitHub API]
Jira[Jira API]
end

Client -->|"vMCP-scoped token"| Auth
Client -->|"vMCP-scoped<br>token"| Auth
Auth --> Backend
Backend -->|"Backend-scoped token"| GitHub
Backend -->|"Backend-scoped token"| Jira
Backend -->|"Backend-scoped<br>token"| GitHub
Backend -->|"Backend-scoped<br>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
```

:::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.

:::

### OIDC authentication

Validate tokens from an external identity provider:

```yaml
```yaml title="VirtualMCPServer resource"
spec:
incomingAuth:
type: oidc
oidcConfig:
type: inline
inline:
issuer: https://auth.example.com
clientId: your-client-id
clientId: <YOUR_CLIENT_ID>
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
oidcConfig:
type: inline
inline:
issuer: https://auth.example.com
clientId: your-client-id
clientId: <YOUR_CLIENT_ID>
audience: vmcp
clientSecretRef:
name: oidc-client-secret
Expand All @@ -107,14 +110,14 @@ metadata:
namespace: toolhive-system
type: Opaque
stringData:
clientSecret: your-client-secret
clientSecret: <YOUR_CLIENT_SECRET>
```

### Kubernetes service account tokens

Authenticate using Kubernetes service account tokens for in-cluster clients:

```yaml
```yaml title="VirtualMCPServer resource"
spec:
incomingAuth:
type: oidc
Expand All @@ -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
Expand All @@ -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)
Expand Down
Loading
Loading